Merge "msm: clock-local: Avoid potential NULL dereference in branch_disable_reg()"
diff --git a/Documentation/devicetree/bindings/pil/pil-mba.txt b/Documentation/devicetree/bindings/pil/pil-mba.txt
index 9692059..ce6bb8f 100644
--- a/Documentation/devicetree/bindings/pil/pil-mba.txt
+++ b/Documentation/devicetree/bindings/pil/pil-mba.txt
@@ -12,6 +12,7 @@
the primary modem image metadata should be stored.
- reg-names: Names for the above base addresses. "rmb_base" and
"metadata_base" are expected.
+- interrupts: The modem watchdog interrupt
- qcom,firmware-name: Base name of the firmware image. Ex. "modem"
Optional properties:
@@ -24,6 +25,7 @@
reg = <0xfc820000 0x0020>,
<0x0d1f0000 0x4000>;
reg-names = "rmb_base", "metadata_base";
+ interrupts = <0 56 1>;
qcom,firmware-name = "modem";
qcom,depends-on = "mba";
diff --git a/Documentation/devicetree/bindings/pil/pil-pronto.txt b/Documentation/devicetree/bindings/pil/pil-pronto.txt
index e123bdb..e3108ac 100644
--- a/Documentation/devicetree/bindings/pil/pil-pronto.txt
+++ b/Documentation/devicetree/bindings/pil/pil-pronto.txt
@@ -10,6 +10,7 @@
- reg: offset and length of the register set for the device.
- reg-names: names of the bases for the above registers. "pmu_base", "clk_base",
and "halt_base" are expected.
+- interrupts: WCNSS to Apps watchdog bite interrupt
- vdd_pronto_pll-supply: regulator to supply pronto pll.
- qcom,firmware-name: Base name of the firmware image. Ex. "wcnss"
@@ -21,6 +22,7 @@
<0xfd485300 0xc>;
reg-names = "pmu_base", "clk_base", "halt_base";
vdd_pronto_pll-supply = <&pm8941_l12>;
+ interrupts = <0 231 1>;
qcom,firmware-name = "wcnss";
};
diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt
index d39c98c..ac9600d 100644
--- a/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt
+++ b/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt
@@ -11,6 +11,7 @@
memory mapped registers.
- reg-names: Names of the bases for the above registers. "qdsp6_base"
and "halt_base" are expected.
+- interrupts: The lpass watchdog interrupt
- qcom,firmware-name: Base name of the firmware image. Ex. "lpass"
Example:
@@ -19,6 +20,7 @@
reg = <0xfe200000 0x00100>,
<0xfd485100 0x00010>;
reg-names = "qdsp6_base", "halt_base";
+ interrupts = <0 194 1>;
qcom,firmware-name = "lpass";
};
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 3c8cb1b..9743d0d 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -54,6 +54,16 @@
- compatible : "qcom,msm-pcm-afe"
+* msm-dai-q6-hdmi
+
+Required properties:
+ - compatible : "msm-dai-q6-hdmi"
+ - qcom,msm-dai-q6-dev-id : The hdmi multi channel port ID.
+ It is passed onto the dsp from the apps to form an audio
+ path to the HDMI device. Currently the only supported value
+ is 8, which indicates the rx path used for audio playback
+ on HDMI device.
+
* msm-dai-q6
[First Level Nodes]
@@ -71,6 +81,7 @@
Value is from 16384 to 16393
BT SCO port ID value from 12288 to 12289
RT Proxy port ID values from 224 to 225 and 240 to 241
+ FM Rx and TX port ID values from 12292 to 12293
* msm-auxpcm
@@ -135,6 +146,25 @@
- qcom,msm_bus,vectors: Arrays of unsigned integers representing:
master-id, slave-id, arbitrated bandwidth,
instantaneous bandwidth
+* wcd9xxx_intc
+
+Required properties:
+
+ - compatible : "qcom,wcd9xxx-irq"
+
+ - interrupt-controller : Mark this device node as an interrupt
+ controller
+
+ - #interrupt-cells : Should be 1
+
+ - interrupt-parent : Parent interrupt controller
+
+ - interrupts : Interrupt number on the parent
+ interrupt controller
+
+ - interrupt-names : Name of interrupt on the parent
+ interrupt controller
+
Example:
qcom,msm-pcm {
@@ -165,6 +195,11 @@
compatible = "qcom,msm-dai-fe";
};
+ qcom,msm-dai-q6-hdmi {
+ compatible = "qcom,msm-dai-q6-hdmi";
+ qcom,msm-dai-q6-dev-id = <8>;
+ };
+
qcom,msm-dai-q6 {
compatible = "qcom,msm-dai-q6";
qcom,msm-dai-q6-sb-0-rx {
@@ -187,6 +222,16 @@
qcom,msm-dai-q6-dev-id = <12289>;
};
+ qcom,msm-dai-q6-int-fm-rx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <12292>;
+ };
+
+ qcom,msm-dai-q6-int-fm-tx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <12293>;
+ };
+
qcom,msm-dai-q6-be-afe-pcm-rx {
compatible = "qcom,msm-dai-q6-dev";
qcom,msm-dai-q6-dev-id = <224>;
@@ -245,6 +290,15 @@
<11 604 32505856 325058560>;
};
+ wcd9xxx_intc: wcd9xxx-irq {
+ compatible = "qcom,wcd9xxx-irq";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <72 0>;
+ interrupt-names = "cdc-int";
+ };
+
* MSM8974 ASoC Machine driver
Required properties:
diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
index 12fbfec..186a58d 100644
--- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
@@ -22,10 +22,13 @@
1 - PHY control
2 - PMIC control
3 - User control (via debugfs)
-- qcom,hsusb-otg-disable-reset: It present then core is RESET only during
- init, otherwise core is RESET for every cable disconnect as well
Optional properties :
+- qcom,hsusb-otg-disable-reset: If present then core is RESET only during
+ init, otherwise core is RESET for every cable disconnect as well
+- qcom,hsusb-otg-pnoc-errata-fix: If present then workaround for PNOC
+ performance issue is applied which requires changing the mem-type
+ attribute via VMIDMT.
- qcom,hsusb-otg-default-mode: The default USB mode after boot-up.
Applicable only when OTG is controlled by user. Can be one of
0 - None. Low power mode
@@ -56,6 +59,7 @@
qcom,hsusb-otg-mode = <1>;
qcom,hsusb-otg-otg-control = <1>;
qcom,hsusb-otg-disable-reset;
+ qcom,hsusb-otg-pnoc-errata-fix;
qcom,hsusb-otg-default-mode = <2>;
qcom,hsusb-otg-phy-init-seq = <0x01 0x90 0xffffffff>;
qcom,hsusb-otg-power-budget = <500>;
diff --git a/Documentation/mmc/mmc-dev-attrs.txt b/Documentation/mmc/mmc-dev-attrs.txt
index 08f7312..7dde34f 100644
--- a/Documentation/mmc/mmc-dev-attrs.txt
+++ b/Documentation/mmc/mmc-dev-attrs.txt
@@ -25,6 +25,15 @@
running parallel lmdd write and lmdd read operations and calculating
the max number of packed writes requests.
+ min_sectors_to_check_bkops_status This attribute is used to
+ determine whether the status bit that indicates the need for BKOPS
+ should be checked. The value is stored in this attribute represents
+ the minimum number of sectors that needs to be changed in the device
+ (written or discarded) in order to require the status-bit of BKOPS
+ to be checked. The value can modified via sysfs by writing the
+ required value to:
+ /sys/block/<block_dev_name>/min_sectors_to_check_bkops_status
+
SD and MMC Device Attributes
============================
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index f1e18cf..1b18d25 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -102,11 +102,12 @@
qcom,cxo-freq = <19200000>;
};
- pm8941-chg {
+ pm8941_chg: qcom,charger {
spmi-dev-container;
compatible = "qcom,qpnp-charger";
#address-cells = <1>;
#size-cells = <1>;
+ status = "disabled";
qcom,chg-vddmax-mv = <4200>;
qcom,chg-vddsafe-mv = <4200>;
@@ -115,6 +116,7 @@
qcom,chg-ibatterm-ma = <200>;
qcom,chg-chgr@1000 {
+ status = "disabled";
reg = <0x1000 0x100>;
interrupts = <0x0 0x10 0x0>,
<0x0 0x10 0x1>,
@@ -136,6 +138,7 @@
};
qcom,chg-buck@1100 {
+ status = "disabled";
reg = <0x1100 0x100>;
interrupts = <0x0 0x11 0x0>,
<0x0 0x11 0x1>,
@@ -155,6 +158,7 @@
};
qcom,chg-bat-if@1200 {
+ status = "disabled";
reg = <0x1200 0x100>;
interrupts = <0x0 0x12 0x0>,
<0x0 0x12 0x1>,
@@ -170,6 +174,7 @@
};
qcom,chg-usb-chgpth@1300 {
+ status = "disabled";
reg = <0x1300 0x100>;
interrupts = <0 0x13 0x0>,
<0 0x13 0x1>,
@@ -181,6 +186,7 @@
};
qcom,chg-dc-chgpth@1400 {
+ status = "disabled";
reg = <0x1400 0x100>;
interrupts = <0x0 0x14 0x0>,
<0x0 0x14 0x1>;
@@ -190,6 +196,7 @@
};
qcom,chg-boost@1500 {
+ status = "disabled";
reg = <0x1500 0x100>;
interrupts = <0x0 0x15 0x0>,
<0x0 0x15 0x1>;
@@ -199,6 +206,7 @@
};
qcom,chg-misc@1600 {
+ status = "disabled";
reg = <0x1600 0x100>;
};
};
@@ -1036,5 +1044,68 @@
qcom,label = "wled";
};
+ pwm@b100 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb100 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <0>;
+ };
+
+ pwm@b200 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb200 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <1>;
+ };
+
+ pwm@b300 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb300 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <2>;
+ };
+
+ pwm@b400 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb400 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <3>;
+ };
+
+ pwm@b500 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb500 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <4>;
+ };
+
+ pwm@b600 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb600 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <5>;
+ };
+
+ pwm@b700 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb700 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <6>;
+ };
+
+ pwm@b800 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb800 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <7>;
+ };
};
};
diff --git a/arch/arm/boot/dts/msm8910-sim.dts b/arch/arm/boot/dts/msm8910-sim.dts
index 268e1f8..aae88b1 100644
--- a/arch/arm/boot/dts/msm8910-sim.dts
+++ b/arch/arm/boot/dts/msm8910-sim.dts
@@ -11,41 +11,15 @@
*/
/dts-v1/;
-/include/ "skeleton.dtsi"
+
+/include/ "msm8910.dtsi"
/ {
model = "Qualcomm MSM 8910 Simulator";
compatible = "qcom,msm8910-sim", "qcom,msm8910";
qcom,msm-id = <147 1 0>;
- interrupt-parent = <&intc>;
-
- intc: interrupt-controller@f9000000 {
- compatible = "qcom,msm-qgic2";
- interrupt-controller;
- #interrupt-cells = <3>;
- reg = <0xf9000000 0x1000>,
- <0xf9002000 0x1000>;
- };
-
- msmgpio: gpio@fd510000 {
- compatible = "qcom,msm-gpio";
- interrupt-controller;
- #interrupt-cells = <2>;
- reg = <0xfd510000 0x4000>;
- #gpio-cells = <2>;
- };
-
- timer: msm-qtimer@f9021000 {
- compatible = "qcom,msm-qtimer", "arm,armv7-timer";
- reg = <0xf9021000 0x1000>;
- interrupts = <0 7 0 0 8 0>;
- irq-is-not-percpu;
- clock-frequency = <19200000>;
- };
serial@f991f000 {
- compatible = "qcom,msm-lsuart-v14";
- reg = <0xf991f000 0x1000>;
- interrupts = <0 109 0>;
+ status = "ok";
};
};
diff --git a/arch/arm/boot/dts/msm8910.dtsi b/arch/arm/boot/dts/msm8910.dtsi
new file mode 100644
index 0000000..c8a0f9c
--- /dev/null
+++ b/arch/arm/boot/dts/msm8910.dtsi
@@ -0,0 +1,48 @@
+/* Copyright (c) 2012, 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/ "skeleton.dtsi"
+
+/ {
+ model = "Qualcomm MSM 8910";
+ compatible = "qcom,msm8910";
+ interrupt-parent = <&intc>;
+
+ intc: interrupt-controller@f9000000 {
+ compatible = "qcom,msm-qgic2";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ reg = <0xf9000000 0x1000>,
+ <0xf9002000 0x1000>;
+ };
+
+ msmgpio: gpio@fd510000 {
+ compatible = "qcom,msm-gpio";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ reg = <0xfd510000 0x4000>;
+ #gpio-cells = <2>;
+ };
+
+ timer {
+ compatible = "qcom,msm-qtimer", "arm,armv7-timer";
+ interrupts = <1 2 0 1 3 0>;
+ clock-frequency = <19200000>;
+ };
+
+ serial@f991f000 {
+ compatible = "qcom,msm-lsuart-v14";
+ reg = <0xf991f000 0x1000>;
+ interrupts = <0 109 0>;
+ status = "disabled";
+ };
+};
diff --git a/arch/arm/boot/dts/msm8974-cdp.dts b/arch/arm/boot/dts/msm8974-cdp.dts
index 05fcc4f..0e0f6cf 100644
--- a/arch/arm/boot/dts/msm8974-cdp.dts
+++ b/arch/arm/boot/dts/msm8974-cdp.dts
@@ -30,6 +30,10 @@
};
};
+ qcom,hdmi_tx@fd922100 {
+ status = "ok";
+ };
+
i2c@f9924000 {
atmel_mxt_ts@4a {
compatible = "atmel,mxt-ts";
diff --git a/arch/arm/boot/dts/msm8974-fluid.dts b/arch/arm/boot/dts/msm8974-fluid.dts
index b1d467e..891379f 100644
--- a/arch/arm/boot/dts/msm8974-fluid.dts
+++ b/arch/arm/boot/dts/msm8974-fluid.dts
@@ -30,6 +30,10 @@
};
};
+ qcom,hdmi_tx@fd922100 {
+ status = "ok";
+ };
+
i2c@f9924000 {
atmel_mxt_ts@4a {
compatible = "atmel,mxt-ts";
@@ -112,6 +116,7 @@
gpio_keys {
compatible = "gpio-keys";
+ input-name = "gpio-keys";
camera_snapshot {
label = "camera_snapshot";
diff --git a/arch/arm/boot/dts/msm8974-liquid.dts b/arch/arm/boot/dts/msm8974-liquid.dts
index 2abc1d5..bc682ea 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dts
+++ b/arch/arm/boot/dts/msm8974-liquid.dts
@@ -22,27 +22,63 @@
serial@f991e000 {
status = "ok";
};
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ input-name = "gpio-keys";
+
+ home {
+ label = "home";
+ gpios = <&pm8941_gpios 1 0x1>;
+ linux,input-type = <1>;
+ linux,code = <102>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ vol_down {
+ label = "volume_down";
+ gpios = <&pm8941_gpios 2 0x1>;
+ linux,input-type = <1>;
+ linux,code = <114>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&pm8941_gpios 5 0x1>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+ };
+
+ qcom,hdmi_tx@fd922100 {
+ status = "ok";
+ };
};
&pm8941_gpios {
gpio@c000 { /* GPIO 1 */
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <2>;
+ qcom,select = <0>;
};
gpio@c100 { /* GPIO 2 */
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <2>;
+ qcom,select = <0>;
};
gpio@c200 { /* GPIO 3 */
- qcom,mode = <0>;
- qcom,pull = <0>;
- qcom,vin-sel = <2>;
- qcom,select = <0>;
};
gpio@c300 { /* GPIO 4 */
- qcom,mode = <0>;
- qcom,pull = <0>;
- qcom,vin-sel = <2>;
- qcom,select = <0>;
};
gpio@c400 { /* GPIO 5 */
diff --git a/arch/arm/boot/dts/msm8974-mtp.dts b/arch/arm/boot/dts/msm8974-mtp.dts
index e0d6ad3..f75ebbe 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dts
+++ b/arch/arm/boot/dts/msm8974-mtp.dts
@@ -30,6 +30,10 @@
};
};
+ qcom,hdmi_tx@fd922100 {
+ status = "disabled";
+ };
+
i2c@f9924000 {
atmel_mxt_ts@4a {
compatible = "atmel,mxt-ts";
@@ -169,6 +173,42 @@
cd-gpios = <&msmgpio 62 0x1>;
};
+&usb_otg {
+ qcom,hsusb-otg-otg-control = <2>;
+};
+
+&pm8941_chg {
+ status = "ok";
+
+ qcom,chg-chgr@1000 {
+ status = "ok";
+ };
+
+ qcom,chg-buck@1100 {
+ status = "ok";
+ };
+
+ qcom,chg-bat-if@1200 {
+ status = "ok";
+ };
+
+ qcom,chg-usb-chgpth@1300 {
+ status = "ok";
+ };
+
+ qcom,chg-dc-chgpth@1400 {
+ status = "ok";
+ };
+
+ qcom,chg-boost@1500 {
+ status = "ok";
+ };
+
+ qcom,chg-misc@1600 {
+ status = "ok";
+ };
+};
+
&pm8941_gpios {
gpio@c000 { /* GPIO 1 */
};
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index 8e6f1e6..9d985bf 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -42,6 +42,15 @@
reg = <0xfd510000 0x4000>;
};
+ wcd9xxx_intc: wcd9xxx-irq {
+ compatible = "qcom,wcd9xxx-irq";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <72 0>;
+ interrupt-names = "cdc-int";
+ };
+
timer {
compatible = "qcom,msm-qtimer", "arm,armv7-timer";
interrupts = <1 2 0 1 3 0>;
@@ -52,7 +61,7 @@
compatible = "qcom,msm-vidc";
reg = <0xfdc00000 0xff000>;
interrupts = <0 44 0>;
- vidc-cp-map = <0x1000000 0x40000000>;
+ vidc-cp-map = <0x1000000 0x3f000000>;
vidc-ns-map = <0x40000000 0x40000000>;
load-freq-tbl = <979200 410000000>,
<783360 410000000>,
@@ -85,7 +94,7 @@
status = "disabled";
};
- usb@f9a55000 {
+ usb_otg: usb@f9a55000 {
compatible = "qcom,hsusb-otg";
reg = <0xf9a55000 0x400>;
interrupts = <0 134 0 0 140 0>;
@@ -99,6 +108,7 @@
qcom,hsusb-otg-mode = <1>;
qcom,hsusb-otg-otg-control = <1>;
qcom,hsusb-otg-disable-reset;
+ qcom,hsusb-otg-pnoc-errata-fix;
qcom,msm_bus,name = "usb2";
qcom,msm_bus,num_cases = <2>;
@@ -268,6 +278,9 @@
compatible = "qcom,taiko-slim-pgd";
elemental-addr = [00 01 A0 00 17 02];
+ interrupt-parent = <&wcd9xxx_intc>;
+ interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28>;
+
qcom,cdc-reset-gpio = <&msmgpio 63 0>;
cdc-vdd-buck-supply = <&pm8941_s2>;
@@ -646,6 +659,7 @@
reg = <0xfe200000 0x00100>,
<0xfd485100 0x00010>;
reg-names = "qdsp6_base", "halt_base";
+ interrupts = <0 194 1>;
qcom,firmware-name = "adsp";
};
@@ -690,6 +704,11 @@
compatible = "qcom,msm-pcm-afe";
};
+ qcom,msm-dai-q6-hdmi {
+ compatible = "qcom,msm-dai-q6-hdmi";
+ qcom,msm-dai-q6-dev-id = <8>;
+ };
+
qcom,msm-dai-q6 {
compatible = "qcom,msm-dai-q6";
qcom,msm-dai-q6-sb-0-rx {
@@ -712,6 +731,16 @@
qcom,msm-dai-q6-dev-id = <12289>;
};
+ qcom,msm-dai-q6-int-fm-rx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <12292>;
+ };
+
+ qcom,msm-dai-q6-int-fm-tx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <12293>;
+ };
+
qcom,msm-dai-q6-be-afe-pcm-rx {
compatible = "qcom,msm-dai-q6-dev";
qcom,msm-dai-q6-dev-id = <224>;
@@ -791,6 +820,7 @@
reg = <0xfc820000 0x0020>,
<0x0d1fc000 0x4000>;
reg-names = "rmb_base", "metadata_base";
+ interrupts = <0 56 1>;
qcom,firmware-name = "modem";
qcom,depends-on = "mba";
@@ -802,6 +832,7 @@
<0xfc401700 0x4>,
<0xfd485300 0xc>;
reg-names = "pmu_base", "clk_base", "halt_base";
+ interrupts = <0 181 1>;
vdd_pronto_pll-supply = <&pm8941_l12>;
qcom,firmware-name = "wcnss";
@@ -906,9 +937,9 @@
qcom,ipi-ping = <1>;
};
- qcom,tz-log@fe805720 {
+ qcom,tz-log@fc03000 {
compatible = "qcom,tz-log";
- reg = <0xfe805720 0x1000>;
+ reg = <0x0fc03000 0x1000>;
};
qcom,venus@fdce0000 {
diff --git a/arch/arm/boot/dts/msm8974_pm.dtsi b/arch/arm/boot/dts/msm8974_pm.dtsi
index f108d37..b2f3fec 100644
--- a/arch/arm/boot/dts/msm8974_pm.dtsi
+++ b/arch/arm/boot/dts/msm8974_pm.dtsi
@@ -28,6 +28,8 @@
qcom,saw2-spm-dly= <0x20000400>;
qcom,saw2-spm-ctl = <0x1>;
qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d0 c0 a0 90 03 d0 98 a2 c0
+ 0b 00 42 1b 0f];
qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
10 0b 30 06 26 30 0f];
qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
@@ -49,6 +51,8 @@
qcom,saw2-spm-dly= <0x20000400>;
qcom,saw2-spm-ctl = <0x1>;
qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d0 c0 a0 90 03 d0 98 a2 c0
+ 0b 00 42 1b 0f];
qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
10 0b 30 06 26 30 0f];
qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
@@ -70,6 +74,8 @@
qcom,saw2-spm-dly= <0x20000400>;
qcom,saw2-spm-ctl = <0x1>;
qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d0 c0 a0 90 03 d0 98 a2 c0
+ 0b 00 42 1b 0f];
qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
10 0b 30 06 26 30 0f];
qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
@@ -91,6 +97,8 @@
qcom,saw2-spm-dly= <0x20000400>;
qcom,saw2-spm-ctl = <0x1>;
qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d0 c0 a0 90 03 d0 98 a2 c0
+ 0b 00 42 1b 0f];
qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
10 0b 30 06 26 30 0f];
qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
@@ -183,6 +191,22 @@
qcom,lpm-level@1 {
reg = <0x1>;
+ qcom,mode = <4>; /* MSM_PM_SLEEP_MODE_RETENTION*/
+ qcom,xo = <1>; /* ON */
+ qcom,l2 = <3>; /* ACTIVE */
+ qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+ qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+ qcom,vdd-dig-upper-bound = <5>; /* MAX */
+ qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
+ qcom,latency-us = <1500>;
+ qcom,ss-power = <200>;
+ qcom,energy-overhead = <576000>;
+ qcom,time-overhead = <2000>;
+ };
+
+
+ qcom,lpm-level@2 {
+ reg = <0x2>;
qcom,mode = <2>; /* MSM_PM_SLEEP_MODE_STANDALONE_POWER_COLLAPSE */
qcom,xo = <1>; /* ON */
qcom,l2 = <3>; /* ACTIVE */
@@ -196,8 +220,8 @@
qcom,time-overhead = <2000>;
};
- qcom,lpm-level@2 {
- reg = <0x2>;
+ qcom,lpm-level@3 {
+ reg = <0x3>;
qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <1>; /* ON */
qcom,l2 = <1>; /* GDHS */
@@ -211,8 +235,8 @@
qcom,time-overhead = <8500>;
};
- qcom,lpm-level@3 {
- reg = <0x3>;
+ qcom,lpm-level@4 {
+ reg = <0x4>;
qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <1>; /* ON */
qcom,l2 = <0>; /* OFF */
@@ -226,8 +250,8 @@
qcom,time-overhead = <9000>;
};
- qcom,lpm-level@4 {
- reg = <0x4>;
+ qcom,lpm-level@5 {
+ reg = <0x5>;
qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <1>; /* ON */
qcom,l2 = <0>; /* OFF */
@@ -241,8 +265,8 @@
qcom,time-overhead = <10000>;
};
- qcom,lpm-level@5 {
- reg = <0x5>;
+ qcom,lpm-level@6 {
+ reg = <0x6>;
qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <0>; /* OFF */
qcom,l2 = <1>; /* GDHS */
@@ -256,8 +280,8 @@
qcom,time-overhead = <12000>;
};
- qcom,lpm-level@6 {
- reg = <0x6>;
+ qcom,lpm-level@7 {
+ reg = <0x7>;
qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <0>; /* OFF */
qcom,l2 = <0>; /* OFF */
@@ -271,8 +295,8 @@
qcom,time-overhead = <18000>;
};
- qcom,lpm-level@7 {
- reg = <0x7>;
+ qcom,lpm-level@8 {
+ reg = <0x8>;
qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <0>; /* OFF */
qcom,l2 = <0>; /* OFF */
@@ -286,8 +310,8 @@
qcom,time-overhead = <23500>;
};
- qcom,lpm-level@8 {
- reg = <0x8>;
+ qcom,lpm-level@9 {
+ reg = <0x9>;
qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <0>; /* OFF */
qcom,l2 = <0>; /* OFF */
diff --git a/arch/arm/boot/dts/msm9625-cdp.dts b/arch/arm/boot/dts/msm9625-cdp.dts
index aa1ec92..6234017 100644
--- a/arch/arm/boot/dts/msm9625-cdp.dts
+++ b/arch/arm/boot/dts/msm9625-cdp.dts
@@ -32,6 +32,14 @@
};
gpio@c300 { /* GPIO 4 */
+ /* ext_2p95v regulator enable config */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS */
+ qcom,invert = <0>; /* Output low */
+ qcom,out-strength = <1>; /* Low */
+ qcom,vin-sel = <2>; /* PM8019 L11 - 1.8V */
+ qcom,src-select = <0>; /* Constant */
+ qcom,master-en = <1>; /* Enable GPIO */
};
gpio@c400 { /* GPIO 5 */
diff --git a/arch/arm/boot/dts/msm9625-mtp.dts b/arch/arm/boot/dts/msm9625-mtp.dts
index 3ec949f..be57dda 100644
--- a/arch/arm/boot/dts/msm9625-mtp.dts
+++ b/arch/arm/boot/dts/msm9625-mtp.dts
@@ -32,6 +32,14 @@
};
gpio@c300 { /* GPIO 4 */
+ /* ext_2p95v regulator enable config */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS */
+ qcom,invert = <0>; /* Output low */
+ qcom,out-strength = <1>; /* Low */
+ qcom,vin-sel = <2>; /* PM8019 L11 - 1.8V */
+ qcom,src-select = <0>; /* Constant */
+ qcom,master-en = <1>; /* Enable GPIO */
};
gpio@c400 { /* GPIO 5 */
diff --git a/arch/arm/boot/dts/msm9625-regulator.dtsi b/arch/arm/boot/dts/msm9625-regulator.dtsi
index 9184dfe..dccc723 100644
--- a/arch/arm/boot/dts/msm9625-regulator.dtsi
+++ b/arch/arm/boot/dts/msm9625-regulator.dtsi
@@ -184,6 +184,7 @@
regulator-max-microvolt = <7>;
qcom,use-voltage-corner;
status = "okay";
+ qcom,consumer-supplies = "vdd_dig", "";
};
pm8019_l10_corner_ao: regulator-l10-corner-ao {
compatible = "qcom,rpm-regulator-smd";
@@ -249,3 +250,12 @@
};
};
};
+
+/ {
+ ext_2p95v: regulator-isl80101 {
+ compatible = "regulator-fixed";
+ regulator-name = "ext_2p95v";
+ gpio = <&pm8019_gpios 4 0>;
+ enable-active-high;
+ };
+};
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index a8a2bf1..8cb7191 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -160,6 +160,84 @@
<0x0c50000c>, /* GPIO6 */
<0x0080000d>; /* PON */
};
+
+ i2c@f9925000 {
+ cell-index = <3>;
+ compatible = "qcom,i2c-qup";
+ reg = <0xf9925000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "qup_phys_addr";
+ interrupts = <0 97 0>;
+ interrupt-names = "qup_err_intr";
+ qcom,i2c-bus-freq = <100000>;
+ qcom,i2c-src-freq = <24000000>;
+ };
+
+ sdcc2: qcom,sdcc@f98a4000 {
+ cell-index = <2>; /* SDC2 SD card slot */
+ compatible = "qcom,msm-sdcc";
+ reg = <0xf98a4000 0x800>,
+ <0xf98a4800 0x100>,
+ <0xf9884000 0x7000>;
+ reg-names = "core_mem", "dml_mem", "bam_mem";
+
+ vdd-supply = <&ext_2p95v>;
+
+ vdd-io-supply = <&pm8019_l13>;
+ qcom,sdcc-vdd-io-always_on;
+ qcom,sdcc-vdd-io-lpm_sup;
+ qcom,sdcc-vdd-io-voltage_level = <1800000 2950000>;
+ qcom,sdcc-vdd-io-current_level = <6 22000>;
+
+ qcom,sdcc-pad-pull-on = <0x0 0x3 0x3>;
+ qcom,sdcc-pad-pull-off = <0x0 0x3 0x3>;
+ qcom,sdcc-pad-drv-on = <0x7 0x4 0x4>;
+ qcom,sdcc-pad-drv-off = <0x0 0x0 0x0>;
+
+ qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sdcc-sup-voltages = <2950 2950>;
+ qcom,sdcc-bus-width = <4>;
+ qcom,sdcc-xpc;
+ qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+ qcom,sdcc-current-limit = <800>;
+
+ interrupt-parent = <&sdcc2>;
+ #address-cells = <0>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 220 0
+ 2 &msmgpio 66 0x3>;
+ interrupt-names = "core_irq", "bam_irq", "status_irq";
+ cd-gpios = <&msmgpio 66 0>;
+ };
+
+ sdcc3: qcom,sdcc@f9864000 {
+ cell-index = <3>; /* SDC3 SDIO slot */
+ compatible = "qcom,msm-sdcc";
+ reg = <0xf9864000 0x800>,
+ <0xf9864800 0x100>,
+ <0xf9844000 0x7000>;
+ reg-names = "core_mem", "dml_mem", "bam_mem";
+ interrupts = <0 127 0>, <0 223 0>;
+ interrupt-names = "core_irq", "bam_irq";
+
+ gpios = <&msmgpio 25 0>,
+ <&msmgpio 24 0>,
+ <&msmgpio 16 0>,
+ <&msmgpio 17 0>,
+ <&msmgpio 18 0>,
+ <&msmgpio 19 0>;
+ qcom,sdcc-gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
+
+ qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000>;
+ qcom,sdcc-sup-voltages = <2950 2950>;
+ qcom,sdcc-bus-width = <4>;
+ qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50";
+ status = "disable";
+ };
};
/include/ "msm-pm8019-rpm-regulator.dtsi"
diff --git a/arch/arm/configs/msm7627a-perf_defconfig b/arch/arm/configs/msm7627a-perf_defconfig
index 9a0bfba..78edbb0c 100644
--- a/arch/arm/configs/msm7627a-perf_defconfig
+++ b/arch/arm/configs/msm7627a-perf_defconfig
@@ -167,10 +167,10 @@
CONFIG_IP_NF_ARPTABLES=y
CONFIG_IP_NF_ARPFILTER=y
CONFIG_IP_NF_ARP_MANGLE=y
-CONFIG_IP6_NF_MANGLE=y
-CONFIG_IP6_NF_RAW=y
CONFIG_IP6_NF_IPTABLES=y
CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
CONFIG_ATM=y
CONFIG_L2TP=y
CONFIG_L2TP_DEBUGFS=y
@@ -378,4 +378,3 @@
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_USER=y
CONFIG_CRYPTO_TWOFISH=y
-CONFIG_CRC_CCITT=y
diff --git a/arch/arm/configs/msm7627a_defconfig b/arch/arm/configs/msm7627a_defconfig
index 60a2d72..2c8c40e 100644
--- a/arch/arm/configs/msm7627a_defconfig
+++ b/arch/arm/configs/msm7627a_defconfig
@@ -169,10 +169,10 @@
CONFIG_IP_NF_ARPTABLES=y
CONFIG_IP_NF_ARPFILTER=y
CONFIG_IP_NF_ARP_MANGLE=y
-CONFIG_IP6_NF_MANGLE=y
-CONFIG_IP6_NF_RAW=y
CONFIG_IP6_NF_IPTABLES=y
CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
CONFIG_ATM=y
CONFIG_L2TP=y
CONFIG_L2TP_DEBUGFS=y
diff --git a/arch/arm/configs/msm7630-perf_defconfig b/arch/arm/configs/msm7630-perf_defconfig
index 401654d..f2d25ac 100644
--- a/arch/arm/configs/msm7630-perf_defconfig
+++ b/arch/arm/configs/msm7630-perf_defconfig
@@ -280,8 +280,8 @@
CONFIG_FB_MSM_TRIPLE_BUFFER=y
CONFIG_FB_MSM_MDP40=y
CONFIG_FB_MSM_OVERLAY=y
-CONFIG_FB_MSM_OVERLAY0_WRITEBACK=y
CONFIG_FB_MSM_NO_MDP_PIPE_CTRL=y
+CONFIG_FB_MSM_OVERLAY0_WRITEBACK=y
CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM=y
CONFIG_FB_MSM_HDMI_ADV7520_PANEL=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
diff --git a/arch/arm/configs/msm8660-perf_defconfig b/arch/arm/configs/msm8660-perf_defconfig
index 957dbcf..2ee3f3b 100644
--- a/arch/arm/configs/msm8660-perf_defconfig
+++ b/arch/arm/configs/msm8660-perf_defconfig
@@ -35,8 +35,6 @@
CONFIG_DEFAULT_DEADLINE=y
CONFIG_ARCH_MSM=y
CONFIG_ARCH_MSM8X60=y
-CONFIG_MACH_MSM8X60_RUMI3=y
-CONFIG_MACH_MSM8X60_SIM=y
CONFIG_MACH_MSM8X60_SURF=y
CONFIG_MACH_MSM8X60_FFA=y
CONFIG_MACH_MSM8X60_FLUID=y
@@ -64,13 +62,13 @@
CONFIG_MSM_RMT_STORAGE_CLIENT=y
CONFIG_MSM_SDIO_SMEM=y
# CONFIG_MSM_HW3D is not set
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_PIL_MODEM=y
CONFIG_MSM_PIL_QDSP6V3=y
CONFIG_MSM_PIL_TZAPPS=y
CONFIG_MSM_PIL_DSPS=y
CONFIG_MSM_PIL_VIDC=y
-CONFIG_MSM_SUBSYSTEM_RESTART=y
-CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
diff --git a/arch/arm/configs/msm8660_defconfig b/arch/arm/configs/msm8660_defconfig
index 4e5479a..25c5207 100644
--- a/arch/arm/configs/msm8660_defconfig
+++ b/arch/arm/configs/msm8660_defconfig
@@ -34,8 +34,6 @@
CONFIG_DEFAULT_DEADLINE=y
CONFIG_ARCH_MSM=y
CONFIG_ARCH_MSM8X60=y
-CONFIG_MACH_MSM8X60_RUMI3=y
-CONFIG_MACH_MSM8X60_SIM=y
CONFIG_MACH_MSM8X60_SURF=y
CONFIG_MACH_MSM8X60_FFA=y
CONFIG_MACH_MSM8X60_FLUID=y
@@ -63,20 +61,19 @@
CONFIG_MSM_RMT_STORAGE_CLIENT=y
CONFIG_MSM_SDIO_SMEM=y
# CONFIG_MSM_HW3D is not set
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_PIL_MODEM=y
CONFIG_MSM_PIL_QDSP6V3=y
CONFIG_MSM_PIL_TZAPPS=y
CONFIG_MSM_PIL_DSPS=y
CONFIG_MSM_PIL_VIDC=y
-CONFIG_MSM_SUBSYSTEM_RESTART=y
-CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
CONFIG_MSM_WATCHDOG=y
CONFIG_MSM_DLOAD_MODE=y
CONFIG_MSM_ETM=y
-CONFIG_MSM_SLEEP_STATS=y
CONFIG_MSM_GSBI9_UART=y
CONFIG_STRICT_MEMORY_RWX=y
CONFIG_NO_HZ=y
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index 6a6bfda..1558e11 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -39,8 +39,6 @@
CONFIG_ARCH_MSM8930=y
CONFIG_ARCH_APQ8064=y
CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER=y
-CONFIG_MACH_MSM8960_SIM=y
-CONFIG_MACH_MSM8960_RUMI3=y
CONFIG_MACH_MSM8960_CDP=y
CONFIG_MACH_MSM8960_MTP=y
CONFIG_MACH_MSM8960_FLUID=y
@@ -50,8 +48,6 @@
CONFIG_MACH_MSM8930_FLUID=y
CONFIG_MACH_MSM8627_CDP=y
CONFIG_MACH_MSM8627_MTP=y
-CONFIG_MACH_APQ8064_SIM=y
-CONFIG_MACH_APQ8064_RUMI3=y
CONFIG_MACH_APQ8064_CDP=y
CONFIG_MACH_APQ8064_MTP=y
CONFIG_MACH_APQ8064_LIQUID=y
@@ -66,11 +62,14 @@
CONFIG_MSM_SMD_PKG4=y
CONFIG_MSM_PCIE=y
CONFIG_MSM_BAM_DMUX=y
+CONFIG_MSM_IPC_LOGGING=y
CONFIG_MSM_DSPS=y
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_AVS_HW=y
# CONFIG_MSM_HW3D is not set
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_PIL_LPASS_QDSP6V4=y
CONFIG_MSM_PIL_MODEM_QDSP6V4=y
CONFIG_MSM_PIL_RIVA=y
@@ -78,27 +77,20 @@
CONFIG_MSM_PIL_DSPS=y
CONFIG_MSM_PIL_VIDC=y
CONFIG_MSM_PIL_GSS=y
-CONFIG_MSM_SUBSYSTEM_RESTART=y
-CONFIG_MSM_SYSMON_COMM=y
-CONFIG_MSM_MODEM_8960=y
-CONFIG_MSM_LPASS_8960=y
-CONFIG_MSM_WCNSS_SSR_8960=y
-CONFIG_MSM_GSS_SSR_8064=y
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_RPM_LOG=y
-CONFIG_MSM_RPM_RBCPR_STATS_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
+CONFIG_MSM_RPM_RBCPR_STATS_LOG=y
+CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_BUS_SCALING=y
CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED=y
CONFIG_MSM_WATCHDOG=y
CONFIG_MSM_DLOAD_MODE=y
-CONFIG_MSM_SLEEP_STATS=y
CONFIG_MSM_EBI_ERP=y
CONFIG_MSM_CACHE_ERP=y
CONFIG_MSM_L1_ERR_PANIC=y
CONFIG_MSM_L1_ERR_LOG=y
CONFIG_MSM_L2_ERP_2BIT_PANIC=y
-CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_DCVS=y
CONFIG_MSM_HSIC_SYSMON=y
CONFIG_STRICT_MEMORY_RWX=y
@@ -232,7 +224,6 @@
CONFIG_NET_EMATCH_META=y
CONFIG_NET_EMATCH_TEXT=y
CONFIG_NET_CLS_ACT=y
-CONFIG_MARIMBA_CORE=y
CONFIG_BT=y
CONFIG_BT_RFCOMM=y
CONFIG_BT_RFCOMM_TTY=y
@@ -243,8 +234,8 @@
CONFIG_BT_HCISMD=y
CONFIG_BT_HCIUART=y
CONFIG_BT_HCIUART_H4=y
-CONFIG_BT_HCIUART_IBS=y
CONFIG_BT_HCIUART_ATH3K=y
+CONFIG_BT_HCIUART_IBS=y
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=m
# CONFIG_CFG80211_WEXT is not set
@@ -285,6 +276,7 @@
CONFIG_USB_USBNET=y
CONFIG_MSM_RMNET_USB=y
CONFIG_WCNSS_CORE=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_EVBUG=m
CONFIG_KEYBOARD_GPIO=y
@@ -297,11 +289,10 @@
CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_PMIC8XXX_PWRKEY=y
+CONFIG_INPUT_MPU3050=y
CONFIG_INPUT_UINPUT=y
CONFIG_STM_LIS3DH=y
-CONFIG_INPUT_MPU3050=y
# CONFIG_LEGACY_PTYS is not set
-CONFIG_MSM_IPC_LOGGING=y
CONFIG_N_SMUX=y
CONFIG_N_SMUX_LOOPBACK=y
CONFIG_SMUX_CTL=y
@@ -335,6 +326,7 @@
CONFIG_THERMAL_TSENS8960=y
CONFIG_THERMAL_PM8XXX=y
CONFIG_THERMAL_MONITOR=y
+CONFIG_MARIMBA_CORE=y
CONFIG_MFD_PM8921_CORE=y
CONFIG_MFD_PM8821_CORE=y
CONFIG_MFD_PM8038_CORE=y
@@ -348,7 +340,6 @@
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_DEV=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
-CONFIG_MSM_WFD=y
CONFIG_USER_RC_INPUT=y
CONFIG_IR_GPIO_CIR=y
# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
@@ -373,6 +364,7 @@
CONFIG_MSM_CSI20_HEADER=y
CONFIG_S5K3L1YX=y
CONFIG_IMX091=y
+CONFIG_MSM_WFD=y
CONFIG_RADIO_IRIS=y
CONFIG_RADIO_IRIS_TRANSPORT=m
CONFIG_ION=y
@@ -438,7 +430,6 @@
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_CI13XXX_MSM=y
CONFIG_USB_G_ANDROID=y
-CONFIG_USB_ANDROID_RMNET_CTRL_SMD=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
CONFIG_MMC_UNSAFE_RESUME=y
@@ -474,6 +465,7 @@
CONFIG_MOBICORE_SUPPORT=m
CONFIG_MOBICORE_API=m
CONFIG_MSM_QDSS=y
+CONFIG_CONTROL_TRACE=m
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
@@ -506,5 +498,3 @@
CONFIG_CRYPTO_DEV_QCE=m
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_CRC_CCITT=y
-CONFIG_WCNSS_MEM_PRE_ALLOC=y
-CONFIG_CONTROL_TRACE=m
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index cf2dd23..981cdce 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -38,8 +38,6 @@
CONFIG_ARCH_MSM8930=y
CONFIG_ARCH_APQ8064=y
CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER=y
-CONFIG_MACH_MSM8960_SIM=y
-CONFIG_MACH_MSM8960_RUMI3=y
CONFIG_MACH_MSM8960_CDP=y
CONFIG_MACH_MSM8960_MTP=y
CONFIG_MACH_MSM8960_FLUID=y
@@ -49,8 +47,6 @@
CONFIG_MACH_MSM8930_FLUID=y
CONFIG_MACH_MSM8627_CDP=y
CONFIG_MACH_MSM8627_MTP=y
-CONFIG_MACH_APQ8064_SIM=y
-CONFIG_MACH_APQ8064_RUMI3=y
CONFIG_MACH_APQ8064_CDP=y
CONFIG_MACH_APQ8064_MTP=y
CONFIG_MACH_APQ8064_LIQUID=y
@@ -65,11 +61,14 @@
CONFIG_MSM_SMD_PKG4=y
CONFIG_MSM_PCIE=y
CONFIG_MSM_BAM_DMUX=y
+CONFIG_MSM_IPC_LOGGING=y
CONFIG_MSM_DSPS=y
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_AVS_HW=y
# CONFIG_MSM_HW3D is not set
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_PIL_LPASS_QDSP6V4=y
CONFIG_MSM_PIL_MODEM_QDSP6V4=y
CONFIG_MSM_PIL_RIVA=y
@@ -77,19 +76,13 @@
CONFIG_MSM_PIL_DSPS=y
CONFIG_MSM_PIL_VIDC=y
CONFIG_MSM_PIL_GSS=y
-CONFIG_MSM_SUBSYSTEM_RESTART=y
-CONFIG_MSM_SYSMON_COMM=y
-CONFIG_MSM_MODEM_8960=y
-CONFIG_MSM_LPASS_8960=y
-CONFIG_MSM_WCNSS_SSR_8960=y
-CONFIG_MSM_GSS_SSR_8064=y
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
CONFIG_MSM_RPM_RBCPR_STATS_LOG=y
+CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_BUS_SCALING=y
CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED=y
-CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_WATCHDOG=y
CONFIG_MSM_DLOAD_MODE=y
CONFIG_MSM_RTB=y
@@ -236,7 +229,6 @@
CONFIG_NET_EMATCH_META=y
CONFIG_NET_EMATCH_TEXT=y
CONFIG_NET_CLS_ACT=y
-CONFIG_MARIMBA_CORE=y
CONFIG_BT=y
CONFIG_BT_RFCOMM=y
CONFIG_BT_RFCOMM_TTY=y
@@ -246,8 +238,8 @@
CONFIG_BT_HIDP=y
CONFIG_BT_HCISMD=y
CONFIG_BT_HCIUART=y
-CONFIG_BT_HCIUART_ATH3K=y
CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_ATH3K=y
CONFIG_BT_HCIUART_IBS=y
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=m
@@ -289,6 +281,7 @@
CONFIG_USB_USBNET=y
CONFIG_MSM_RMNET_USB=y
CONFIG_WCNSS_CORE=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_EVBUG=m
CONFIG_KEYBOARD_GPIO=y
@@ -301,11 +294,10 @@
CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_PMIC8XXX_PWRKEY=y
+CONFIG_INPUT_MPU3050=y
CONFIG_INPUT_UINPUT=y
CONFIG_STM_LIS3DH=y
-CONFIG_INPUT_MPU3050=y
# CONFIG_LEGACY_PTYS is not set
-CONFIG_MSM_IPC_LOGGING=y
CONFIG_N_SMUX=y
CONFIG_N_SMUX_LOOPBACK=y
CONFIG_SMUX_CTL=y
@@ -339,6 +331,7 @@
CONFIG_THERMAL_TSENS8960=y
CONFIG_THERMAL_PM8XXX=y
CONFIG_THERMAL_MONITOR=y
+CONFIG_MARIMBA_CORE=y
CONFIG_MFD_PM8921_CORE=y
CONFIG_MFD_PM8821_CORE=y
CONFIG_MFD_PM8038_CORE=y
@@ -352,7 +345,6 @@
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_DEV=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
-CONFIG_MSM_WFD=y
CONFIG_USER_RC_INPUT=y
CONFIG_IR_GPIO_CIR=y
# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
@@ -376,6 +368,7 @@
CONFIG_MSM_CSI20_HEADER=y
CONFIG_S5K3L1YX=y
CONFIG_IMX091=y
+CONFIG_MSM_WFD=y
CONFIG_RADIO_IRIS=y
CONFIG_RADIO_IRIS_TRANSPORT=m
CONFIG_ION=y
@@ -440,7 +433,6 @@
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_CI13XXX_MSM=y
CONFIG_USB_G_ANDROID=y
-CONFIG_USB_ANDROID_RMNET_CTRL_SMD=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
CONFIG_MMC_UNSAFE_RESUME=y
@@ -477,6 +469,7 @@
CONFIG_MOBICORE_API=m
CONFIG_MSM_QDSS=y
CONFIG_MSM_QDSS_ETM_DEFAULT_ENABLE=y
+CONFIG_CONTROL_TRACE=m
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
@@ -514,7 +507,6 @@
CONFIG_FAULT_INJECTION_DEBUG_FS=y
CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
CONFIG_DEBUG_PAGEALLOC=y
-CONFIG_ENABLE_DEFAULT_TRACERS=y
CONFIG_CPU_FREQ_SWITCH_PROFILER=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_USER=y
@@ -524,5 +516,3 @@
CONFIG_CRYPTO_DEV_QCE=m
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_CRC_CCITT=y
-CONFIG_WCNSS_MEM_PRE_ALLOC=y
-CONFIG_CONTROL_TRACE=m
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index 2f1833e..b2ee503 100644
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -55,9 +55,6 @@
CONFIG_MSM_PIL_MBA=y
CONFIG_MSM_PIL_VENUS=y
CONFIG_MSM_PIL_PRONTO=y
-CONFIG_MSM_MODEM_SSR_8974=y
-CONFIG_MSM_ADSP_SSR_8974=y
-CONFIG_MSM_WCNSS_SSR_8974=y
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_BUS_SCALING=y
@@ -73,6 +70,7 @@
CONFIG_MSM_L1_ERR_PANIC=y
CONFIG_MSM_L1_ERR_LOG=y
CONFIG_MSM_L2_ERP_2BIT_PANIC=y
+CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
CONFIG_STRICT_MEMORY_RWX=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
@@ -287,6 +285,7 @@
CONFIG_VIDEOBUF2_MSM_MEM=y
CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_MSM_CAMERA_V4L2=y
+CONFIG_MT9M114=y
CONFIG_OV2720=y
CONFIG_MSM_CAMERA_SENSOR=y
CONFIG_MSM_ACTUATOR=y
@@ -298,7 +297,6 @@
CONFIG_MSM_CSI2_REGISTER=y
CONFIG_MSM_ISPIF=y
CONFIG_S5K3L1YX=y
-CONFIG_MT9M114=y
CONFIG_RADIO_IRIS=y
CONFIG_RADIO_IRIS_TRANSPORT=m
CONFIG_ION=y
@@ -402,4 +400,3 @@
CONFIG_CRYPTO_DEV_QCE=m
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_CRC_CCITT=y
-CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index 1230fbe..cb48c4d 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -55,9 +55,6 @@
CONFIG_MSM_PIL_MBA=y
CONFIG_MSM_PIL_VENUS=y
CONFIG_MSM_PIL_PRONTO=y
-CONFIG_MSM_MODEM_SSR_8974=y
-CONFIG_MSM_ADSP_SSR_8974=y
-CONFIG_MSM_WCNSS_SSR_8974=y
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_BUS_SCALING=y
@@ -76,6 +73,7 @@
CONFIG_MSM_L2_ERP_2BIT_PANIC=y
CONFIG_MSM_CACHE_DUMP=y
CONFIG_MSM_CACHE_DUMP_ON_PANIC=y
+CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
CONFIG_STRICT_MEMORY_RWX=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
@@ -275,6 +273,7 @@
CONFIG_GPIO_QPNP_PIN_DEBUG=y
CONFIG_POWER_SUPPLY=y
# CONFIG_BATTERY_MSM is not set
+CONFIG_QPNP_CHARGER=y
CONFIG_QPNP_BMS=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_SENSORS_QPNP_ADC_CURRENT=y
@@ -408,7 +407,6 @@
CONFIG_FAULT_INJECTION_DEBUG_FS=y
CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
CONFIG_DEBUG_PAGEALLOC=y
-CONFIG_ENABLE_DEFAULT_TRACERS=y
CONFIG_CPU_FREQ_SWITCH_PROFILER=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_USER=y
@@ -421,4 +419,3 @@
CONFIG_CRYPTO_DEV_QCE=m
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_CRC_CCITT=y
-CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
diff --git a/arch/arm/configs/msm9615_defconfig b/arch/arm/configs/msm9615_defconfig
index 7c3d5b0..81b853d 100644
--- a/arch/arm/configs/msm9615_defconfig
+++ b/arch/arm/configs/msm9615_defconfig
@@ -47,11 +47,8 @@
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
-# CONFIG_MSM_SYSMON_COMM is not set
-CONFIG_MSM_MODEM_8960=y
CONFIG_MSM_PIL_LPASS_QDSP6V4=y
CONFIG_MSM_PIL_MODEM_QDSP6V4=y
-CONFIG_MSM_LPASS_8960=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index d7cdbc4..4e34ebd 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -48,10 +48,11 @@
CONFIG_HIGHMEM=y
CONFIG_VMALLOC_RESERVE=0x19000000
CONFIG_USE_OF=y
+CONFIG_CPU_IDLE=y
CONFIG_VFP=y
CONFIG_NEON=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-# CONFIG_SUSPEND is not set
+CONFIG_PM_RUNTIME=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
@@ -92,12 +93,16 @@
CONFIG_SERIAL_MSM_HSL_CONSOLE=y
CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_QUP=y
CONFIG_SPI=y
CONFIG_SPI_QUP=y
CONFIG_SPI_SPIDEV=m
CONFIG_SPMI=y
CONFIG_SPMI_MSM_PMIC_ARB=y
CONFIG_MSM_QPNP_INT=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_DEBUG_GPIO=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_QPNP_PIN=y
@@ -113,6 +118,16 @@
CONFIG_USB_GADGET=y
CONFIG_USB_CI13XXX_MSM=y
CONFIG_USB_G_ANDROID=y
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_TEST=m
+CONFIG_MMC_MSM=y
+CONFIG_MMC_MSM_SPS_SUPPORT=y
CONFIG_RTC_CLASS=y
# CONFIG_RTC_DRV_MSM is not set
CONFIG_RTC_DRV_QPNP=y
@@ -154,12 +169,3 @@
# CONFIG_CRYPTO_HW is not set
CONFIG_CRC_CCITT=y
CONFIG_LIBCRC32C=y
-CONFIG_MMC=y
-CONFIG_MMC_PERF_PROFILING=y
-CONFIG_MMC_CLKGATE=y
-CONFIG_MMC_PARANOID_SD_INIT=y
-CONFIG_MMC_BLOCK_MINORS=32
-CONFIG_MMC_TEST=m
-CONFIG_MMC_MSM=y
-CONFIG_MMC_MSM_SPS_SUPPORT=y
-CONFIG_MMC_UNSAFE_RESUME=y
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 9f0c30b..f8e01b6 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -315,6 +315,7 @@
select GIC_SECURE
select ARCH_MSM_CORTEX_A5
select CPU_V7
+ select MIGHT_HAVE_CACHE_L2X0
select GPIO_MSM_V2
select MSM_GPIOMUX
select MSM_RPM
@@ -358,6 +359,9 @@
select MSM_RPM_SMD
select MSM_NATIVE_RESTART
select MSM_RESTART_V2
+ select MSM_SPM_V2
+ select MSM_PM8X60 if PM
+ select MSM_SCM if SMP
select MULTI_IRQ_HANDLER
select GPIO_MSM_V3
select MAY_HAVE_SPARSE_IRQ
@@ -665,6 +669,18 @@
help
Support for the Qualcomm MSM8625 Reference Design.
+config MACH_QRD_SKUD_PRIME
+ depends on ARCH_MSM8625
+ depends on !MSM_STACKED_MEMORY
+ default y
+ bool "MSM8625 SKUD PRIME"
+ help
+ Support for the Qualcomm MSM8625 SKUD prime Reference Design.
+ Add support for a SKUD prime reference design based on MSM8x25
+ chipset. This device is much closer to a phone than regular form
+ factor devices, with new touch, display panel and other hardware
+ configurations.
+
config MACH_MSM7X30_SURF
depends on ARCH_MSM7X30
depends on !MSM_STACKED_MEMORY
@@ -1954,11 +1970,13 @@
If unsure, say N.
config MSM_PIL_LPASS_QDSP6V5
- tristate "LPASS QDSP6v5 (Hexagon) Boot Support"
- depends on MSM_PIL
- help
- Support for booting and shutting down QDSP6v5 (Hexagon) processors
- in low power audio subsystems.
+ tristate "LPASS QDSP6v5 (Hexagon) Boot Support"
+ depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
+ help
+ Support for booting and shutting down QDSP6v5 (Hexagon) processors
+ in low power audio subsystems. This driver also monitors the ADSP
+ SMSM status bits and the ADSP's watchdog interrupt and restarts the
+ ADSP if the processor encounters a fatal error.
config MSM_PIL_MSS_QDSP6V5
tristate "MSS QDSP6v5 (Hexagon) Boot Support"
@@ -1969,7 +1987,7 @@
config MSM_PIL_MBA
tristate "Support for modem self-authentication"
- depends on MSM_PIL_MSS_QDSP6V5
+ depends on MSM_PIL_MSS_QDSP6V5 && MSM_SUBSYSTEM_RESTART
help
Support for booting self-authenticating modems using the Modem Boot
Authenticator.
@@ -1994,7 +2012,7 @@
config MSM_PIL_DSPS
tristate "DSPS Boot Support"
- depends on MSM_PIL
+ depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
help
Support for booting and shutting down ARM7 DSPS processors.
@@ -2023,7 +2041,7 @@
config MSM_PIL_PRONTO
tristate "PRONTO (WCNSS) Boot Support"
- depends on MSM_PIL
+ depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
help
Support for booting and shutting down the PRONTO processor (WCNSS).
PRONTO is the wireless subsystem processor used in bluetooth, wireless
@@ -2033,33 +2051,6 @@
bool "Secure Channel Manager (SCM) support"
default n
-config MSM_MODEM_SSR_8974
- bool "MSM 8974 Modem restart driver"
- depends on (ARCH_MSM8974)
- help
- This option enables the modem subsystem restart driver for the MSM8974.
- It monitors the modem SMSM status bits and the modem watchdog line and
- restarts the modem or the 8974 when the modem encounters a fatal error,
- depending on the restart level selected in the subsystem restart driver.
-
-config MSM_ADSP_SSR_8974
- bool "MSM 8974 adsp restart driver"
- depends on (ARCH_MSM8974)
- help
- This option enables the adsp restart driver for the MSM8974.
- It monitors the adsp SMSM status bits and the adsp watchdog line and
- restarts the adsp or the 8974 when the adsp encounters a fatal error,
- depending on the restart level selected in the subsystem restart driver.
-
-config MSM_WCNSS_SSR_8974
- tristate "MSM 8974 WCNSS restart module"
- depends on (ARCH_MSM8974)
- help
- This option enables the WCNSS restart module for MSM8974. It monitors
- WCNSS SMSM status bits and WCNSS hardware watchdog interrupt line; and
- depending on the restart level, it will restart WCNSS when a fatal error
- occurs at WCNSS.
-
config SCORPION_Uni_45nm_BUG
bool "Scorpion Uni 45nm(SC45U): Workaround for ICIMVAU and BPIMVA"
depends on ARCH_MSM7X30 || (ARCH_QSD8X50 && MSM_SOC_REV_A)
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 2424b53..34a81da 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -24,6 +24,9 @@
obj-y += acpuclock.o
obj-$(CONFIG_ARCH_MSM_KRAIT) += acpuclock-krait.o
+ifdef CONFIG_ARCH_MSM_KRAIT
+obj-$(CONFIG_DEBUG_FS) += acpuclock-krait-debug.o
+endif
obj-$(CONFIG_ARCH_MSM7X27) += acpuclock-7627.o clock-pll.o
obj-$(CONFIG_ARCH_MSM_SCORPION) += pmu.o
obj-$(CONFIG_ARCH_MSM_SCORPIONMP) += perf_event_msm_l2.o
@@ -45,8 +48,12 @@
ifdef CONFIG_ARCH_MSM8625
obj-$(CONFIG_SMP) += platsmp-8625.o
else
+ifdef CONFIG_ARCH_MSM8910
+ obj-$(CONFIG_SMP) += platsmp-8910.o
+else
obj-$(CONFIG_SMP) += platsmp.o
endif
+endif
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_MSM_CPU_AVS) += avs.o
@@ -200,15 +207,13 @@
obj-y += ramdump.o
endif
obj-$(CONFIG_MSM_SYSMON_COMM) += sysmon.o
-obj-$(CONFIG_MSM_MODEM_SSR_8974) += modem-ssr-8974.o
-obj-$(CONFIG_MSM_ADSP_SSR_8974) += adsp-8974.o
-obj-$(CONFIG_MSM_WCNSS_SSR_8974) += wcnss-ssr-8974.o
ifdef CONFIG_CPU_IDLE
obj-$(CONFIG_ARCH_APQ8064) += cpuidle.o
obj-$(CONFIG_ARCH_MSM8960) += cpuidle.o
obj-$(CONFIG_ARCH_MSM8X60) += cpuidle.o
obj-$(CONFIG_ARCH_MSM9615) += cpuidle.o
+ obj-$(CONFIG_ARCH_MSM9625) += cpuidle.o
obj-$(CONFIG_ARCH_MSM8974) += cpuidle.o
endif
@@ -251,6 +256,7 @@
obj-$(CONFIG_MACH_MSM8625_SURF) += board-msm7x27a.o board-7627a-all.o
obj-$(CONFIG_MACH_MSM8625_EVB) += board-qrd7627a.o board-7627a-all.o
obj-$(CONFIG_MACH_MSM8625_QRD7) += board-qrd7627a.o board-7627a-all.o
+obj-$(CONFIG_MACH_QRD_SKUD_PRIME) += board-qrd7627a.o board-7627a-all.o
obj-$(CONFIG_MACH_MSM8625_FFA) += board-msm7x27a.o board-7627a-all.o
obj-$(CONFIG_MACH_MSM8625_EVT) += board-msm7x27a.o board-7627a-all.o
obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o memory_topology.o
@@ -288,6 +294,7 @@
obj-$(CONFIG_ARCH_MSM8974) += gdsc.o
obj-$(CONFIG_ARCH_MSM8974) += krait-regulator.o
obj-$(CONFIG_ARCH_MSM9625) += board-9625.o board-9625-gpiomux.o
+obj-$(CONFIG_ARCH_MSM9625) += clock-local2.o clock-pll.o clock-9625.o clock-rpm.o clock-voter.o
obj-$(CONFIG_ARCH_MSM8930) += acpuclock-8930.o acpuclock-8627.o acpuclock-8930aa.o
obj-$(CONFIG_ARCH_MPQ8092) += board-8092.o board-8092-gpiomux.o
obj-$(CONFIG_ARCH_MSM8226) += board-8226.o board-8226-gpiomux.o
@@ -318,6 +325,7 @@
endif
ifdef CONFIG_MSM_RPM_SMD
obj-$(CONFIG_ARCH_MSM8974) += lpm_levels.o lpm_resources.o
+ obj-$(CONFIG_ARCH_MSM9625) += lpm_levels.o lpm_resources.o
endif
obj-$(CONFIG_MSM_MPM_OF) += mpm-of.o
obj-$(CONFIG_MSM_MPM) += mpm.o
diff --git a/arch/arm/mach-msm/acpuclock-krait-debug.c b/arch/arm/mach-msm/acpuclock-krait-debug.c
new file mode 100644
index 0000000..0ab70b4
--- /dev/null
+++ b/arch/arm/mach-msm/acpuclock-krait-debug.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/cpu.h>
+#include <linux/smp.h>
+
+#include <mach/msm_bus.h>
+#include <mach/msm-krait-l2-accessors.h>
+
+#include "acpuclock-krait.h"
+
+static struct drv_data *drv;
+static DEFINE_MUTEX(debug_lock);
+
+struct acg_action {
+ bool set;
+ bool enable;
+};
+static int l2_acg_en_val[MAX_SCALABLES];
+static struct dentry *base_dir;
+static struct dentry *sc_dir[MAX_SCALABLES];
+
+static void cpu_action(void *info)
+{
+ struct acg_action *action = info;
+
+ u32 val;
+ asm volatile ("mrc p15, 7, %[cpmr0], c15, c0, 5\n\t"
+ : [cpmr0]"=r" (val));
+ if (action->set) {
+ if (action->enable)
+ val &= ~BIT(0);
+ else
+ val |= BIT(0);
+ asm volatile ("mcr p15, 7, %[l2cpdr], c15, c0, 5\n\t"
+ : : [l2cpdr]"r" (val));
+ } else {
+ action->enable = !(val & BIT(0));
+ }
+}
+
+/* Disable auto clock-gating for a scalable. */
+static void disable_acg(int sc_id)
+{
+ u32 regval;
+
+ if (sc_id == L2) {
+ regval = get_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr);
+ l2_acg_en_val[sc_id] = regval & (0x3 << 10);
+ regval |= (0x3 << 10);
+ set_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr, regval);
+ } else {
+ struct acg_action action = { .set = true, .enable = false };
+ smp_call_function_single(sc_id, cpu_action, &action, 1);
+ }
+}
+
+/* Enable auto clock-gating for a scalable. */
+static void enable_acg(int sc_id)
+{
+ u32 regval;
+
+ if (sc_id == L2) {
+ regval = get_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr);
+ regval &= ~(0x3 << 10);
+ regval |= l2_acg_en_val[sc_id];
+ set_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr, regval);
+ } else {
+ struct acg_action action = { .set = true, .enable = true };
+ smp_call_function_single(sc_id, cpu_action, &action, 1);
+ }
+}
+
+/* Check if auto clock-gating for a scalable. */
+static bool acg_is_enabled(int sc_id)
+{
+ u32 regval;
+
+ if (sc_id == L2) {
+ regval = get_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr);
+ return ((regval >> 10) & 0x3) != 0x3;
+ } else {
+ struct acg_action action = { .set = false };
+ smp_call_function_single(sc_id, cpu_action, &action, 1);
+ return action.enable;
+ }
+}
+
+/* Enable/Disable auto clock gating. */
+static int acg_set(void *data, u64 val)
+{
+ int ret = 0;
+ int sc_id = (int)data;
+
+ mutex_lock(&debug_lock);
+ get_online_cpus();
+ if (!sc_dir[sc_id]) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (val == 0 && acg_is_enabled(sc_id))
+ disable_acg(sc_id);
+ else if (val == 1)
+ enable_acg(sc_id);
+out:
+ put_online_cpus();
+ mutex_unlock(&debug_lock);
+
+ return ret;
+}
+
+/* Get auto clock-gating state. */
+static int acg_get(void *data, u64 *val)
+{
+ int ret = 0;
+ int sc_id = (int)data;
+
+ mutex_lock(&debug_lock);
+ get_online_cpus();
+ if (!sc_dir[sc_id]) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ *val = acg_is_enabled(sc_id);
+out:
+ put_online_cpus();
+ mutex_unlock(&debug_lock);
+
+ return ret;
+}
+DEFINE_SIMPLE_ATTRIBUTE(acgd_fops, acg_get, acg_set, "%lld\n");
+
+/* Get the rate */
+static int rate_get(void *data, u64 *val)
+{
+ int sc_id = (int)data;
+ *val = drv->scalable[sc_id].cur_speed->khz;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(rate_fops, rate_get, NULL, "%lld\n");
+
+/* Get the HFPLL's L-value. */
+static int hfpll_l_get(void *data, u64 *val)
+{
+ int sc_id = (int)data;
+ *val = drv->scalable[sc_id].cur_speed->pll_l_val;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(hfpll_l_fops, hfpll_l_get, NULL, "%lld\n");
+
+/* Get the L2 rate vote. */
+static int l2_vote_get(void *data, u64 *val)
+{
+ int level, sc_id = (int)data;
+
+ level = drv->scalable[sc_id].l2_vote;
+ *val = drv->l2_freq_tbl[level].speed.khz;
+
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(l2_vote_fops, l2_vote_get, NULL, "%lld\n");
+
+/* Get the bandwidth vote. */
+static int bw_vote_get(void *data, u64 *val)
+{
+ struct l2_level *l;
+
+ l = container_of(drv->scalable[L2].cur_speed,
+ struct l2_level, speed);
+ *val = drv->bus_scale->usecase[l->bw_level].vectors->ib;
+
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(bw_vote_fops, bw_vote_get, NULL, "%lld\n");
+
+/* Get the name of the currently-selected clock source. */
+static int src_name_show(struct seq_file *m, void *unused)
+{
+ const char *const src_names[NUM_SRC_ID] = {
+ [PLL_0] = "PLL0",
+ [HFPLL] = "HFPLL",
+ [PLL_8] = "PLL8",
+ };
+ int src, sc_id = (int)m->private;
+
+ src = drv->scalable[sc_id].cur_speed->src;
+ if (src > ARRAY_SIZE(src_names))
+ return -EINVAL;
+
+ seq_printf(m, "%s\n", src_names[src]);
+
+ return 0;
+}
+
+static int src_name_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, src_name_show, inode->i_private);
+}
+
+static const struct file_operations src_name_fops = {
+ .open = src_name_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+/* Get speed_bin ID */
+static int speed_bin_get(void *data, u64 *val)
+{
+ *val = drv->speed_bin;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(speed_bin_fops, speed_bin_get, NULL, "%lld\n");
+
+/* Get pvs_bin ID */
+static int pvs_bin_get(void *data, u64 *val)
+{
+ *val = drv->pvs_bin;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(pvs_bin_fops, pvs_bin_get, NULL, "%lld\n");
+
+/* Get boost_uv */
+static int boost_get(void *data, u64 *val)
+{
+ *val = drv->boost_uv;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(boost_fops, boost_get, NULL, "%lld\n");
+
+static void __cpuinit add_scalable_dir(int sc_id)
+{
+ char sc_name[8];
+
+ if (sc_id == L2)
+ snprintf(sc_name, sizeof(sc_name), "l2");
+ else
+ snprintf(sc_name, sizeof(sc_name), "cpu%d", sc_id);
+
+ sc_dir[sc_id] = debugfs_create_dir(sc_name, base_dir);
+ if (!sc_dir[sc_id])
+ return;
+
+ debugfs_create_file("auto_gating", S_IRUGO | S_IWUSR,
+ sc_dir[sc_id], (void *)sc_id, &acgd_fops);
+
+ debugfs_create_file("rate", S_IRUGO,
+ sc_dir[sc_id], (void *)sc_id, &rate_fops);
+
+ debugfs_create_file("hfpll_l", S_IRUGO,
+ sc_dir[sc_id], (void *)sc_id, &hfpll_l_fops);
+
+ debugfs_create_file("src", S_IRUGO,
+ sc_dir[sc_id], (void *)sc_id, &src_name_fops);
+
+ if (sc_id == L2)
+ debugfs_create_file("bw_ib_vote", S_IRUGO,
+ sc_dir[sc_id], (void *)sc_id, &bw_vote_fops);
+ else
+ debugfs_create_file("l2_vote", S_IRUGO,
+ sc_dir[sc_id], (void *)sc_id, &l2_vote_fops);
+}
+
+static void __cpuinit remove_scalable_dir(int sc_id)
+{
+ debugfs_remove_recursive(sc_dir[sc_id]);
+ sc_dir[sc_id] = NULL;
+}
+
+static int __cpuinit debug_cpu_callback(struct notifier_block *nfb,
+ unsigned long action, void *hcpu)
+{
+ int cpu = (int)hcpu;
+
+ switch (action & ~CPU_TASKS_FROZEN) {
+ case CPU_DOWN_FAILED:
+ case CPU_UP_PREPARE:
+ add_scalable_dir(cpu);
+ break;
+ case CPU_UP_CANCELED:
+ case CPU_DOWN_PREPARE:
+ remove_scalable_dir(cpu);
+ break;
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata debug_cpu_notifier = {
+ .notifier_call = debug_cpu_callback,
+};
+
+void __init acpuclk_krait_debug_init(struct drv_data *drv_data)
+{
+ int cpu;
+ drv = drv_data;
+
+ base_dir = debugfs_create_dir("acpuclk", NULL);
+ if (!base_dir)
+ return;
+
+ debugfs_create_file("speed_bin", S_IRUGO, base_dir, NULL,
+ &speed_bin_fops);
+ debugfs_create_file("pvs_bin", S_IRUGO, base_dir, NULL, &pvs_bin_fops);
+ debugfs_create_file("boost_uv", S_IRUGO, base_dir, NULL, &boost_fops);
+
+ for_each_online_cpu(cpu)
+ add_scalable_dir(cpu);
+ add_scalable_dir(L2);
+
+ register_hotcpu_notifier(&debug_cpu_notifier);
+}
diff --git a/arch/arm/mach-msm/acpuclock-krait.c b/arch/arm/mach-msm/acpuclock-krait.c
index 57c4411..d38396d 100644
--- a/arch/arm/mach-msm/acpuclock-krait.c
+++ b/arch/arm/mach-msm/acpuclock-krait.c
@@ -48,16 +48,7 @@
static DEFINE_MUTEX(driver_lock);
static DEFINE_SPINLOCK(l2_lock);
-static struct drv_data {
- struct acpu_level *acpu_freq_tbl;
- const struct l2_level *l2_freq_tbl;
- struct scalable *scalable;
- struct hfpll_data *hfpll_data;
- u32 bus_perf_client;
- struct msm_bus_scale_pdata *bus_scale;
- int boost_uv;
- struct device *dev;
-} drv;
+static struct drv_data drv;
static unsigned long acpuclk_krait_get_rate(int cpu)
{
@@ -986,7 +977,7 @@
struct pvs_table (*pvs_tables)[NUM_PVS])
{
void __iomem *pte_efuse;
- u32 pte_efuse_val, tbl_idx, bin_idx;
+ u32 pte_efuse_val;
pte_efuse = ioremap(pte_efuse_phys, 4);
if (!pte_efuse) {
@@ -998,10 +989,10 @@
iounmap(pte_efuse);
/* Select frequency tables. */
- bin_idx = get_speed_bin(pte_efuse_val);
- tbl_idx = get_pvs_bin(pte_efuse_val);
+ drv.speed_bin = get_speed_bin(pte_efuse_val);
+ drv.pvs_bin = get_pvs_bin(pte_efuse_val);
- return &pvs_tables[bin_idx][tbl_idx];
+ return &pvs_tables[drv.speed_bin][drv.pvs_bin];
}
static void __init drv_data_init(struct device *dev,
@@ -1091,5 +1082,7 @@
acpuclk_register(&acpuclk_krait_data);
register_hotcpu_notifier(&acpuclk_cpu_notifier);
+ acpuclk_krait_debug_init(&drv);
+
return 0;
}
diff --git a/arch/arm/mach-msm/acpuclock-krait.h b/arch/arm/mach-msm/acpuclock-krait.h
index 3fa10e3..245f276 100644
--- a/arch/arm/mach-msm/acpuclock-krait.h
+++ b/arch/arm/mach-msm/acpuclock-krait.h
@@ -39,6 +39,7 @@
PLL_0 = 0,
HFPLL,
PLL_8,
+ NUM_SRC_ID
};
/**
@@ -66,6 +67,7 @@
CPU2,
CPU3,
L2,
+ MAX_SCALABLES
};
@@ -261,6 +263,32 @@
};
/**
+ * struct drv_data - Driver state
+ * @acpu_freq_tbl: CPU frequency table.
+ * @l2_freq_tbl: L2 frequency table.
+ * @scalable: Array of scalables (CPUs and L2).
+ * @hfpll_data: High-frequency PLL data.
+ * @bus_perf_client: Bus driver client handle.
+ * @bus_scale: Bus driver scaling data.
+ * @boost_uv: Voltage boost amount
+ * @speed_bin: Speed bin ID.
+ * @pvs_bin: PVS bin ID.
+ * @dev: Device.
+ */
+struct drv_data {
+ struct acpu_level *acpu_freq_tbl;
+ const struct l2_level *l2_freq_tbl;
+ struct scalable *scalable;
+ struct hfpll_data *hfpll_data;
+ u32 bus_perf_client;
+ struct msm_bus_scale_pdata *bus_scale;
+ int boost_uv;
+ int speed_bin;
+ int pvs_bin;
+ struct device *dev;
+};
+
+/**
* struct acpuclk_platform_data - PMIC configuration data.
* @uses_pm8917: Boolean indicates presence of pm8917.
*/
@@ -273,4 +301,14 @@
*/
extern int acpuclk_krait_init(struct device *dev,
const struct acpuclk_krait_params *params);
+
+#ifdef CONFIG_DEBUG_FS
+/**
+ * acpuclk_krait_debug_init - Initialize debugfs interface.
+ */
+extern void __init acpuclk_krait_debug_init(struct drv_data *drv);
+#else
+static inline void acpuclk_krait_debug_init(void) { }
+#endif
+
#endif
diff --git a/arch/arm/mach-msm/adsp-8974.c b/arch/arm/mach-msm/adsp-8974.c
deleted file mode 100644
index fa7d9d4..0000000
--- a/arch/arm/mach-msm/adsp-8974.c
+++ /dev/null
@@ -1,301 +0,0 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/reboot.h>
-#include <linux/workqueue.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/err.h>
-
-#include <mach/irqs.h>
-#include <mach/scm.h>
-#include <mach/peripheral-loader.h>
-#include <mach/subsystem_restart.h>
-#include <mach/subsystem_notif.h>
-
-#include "smd_private.h"
-#include "ramdump.h"
-#include "sysmon.h"
-
-#define SCM_Q6_NMI_CMD 0x1
-#define MODULE_NAME "adsp_8974"
-#define MAX_BUF_SIZE 0x51
-
-/* Interrupt line for WDOG bite*/
-#define ADSP_Q6SS_WDOG_EXPIRED 194
-
-/* Subsystem restart: QDSP6 data, functions */
-static void adsp_fatal_fn(struct work_struct *);
-static DECLARE_WORK(adsp_fatal_work, adsp_fatal_fn);
-
-struct adsp_ssr {
- void *adsp_ramdump_dev;
-} adsp_ssr;
-
-static struct adsp_ssr adsp_ssr_8974;
-static int q6_crash_shutdown;
-
-static int riva_notifier_cb(struct notifier_block *this, unsigned long code,
- void *ss_handle)
-{
- int ret;
- switch (code) {
- case SUBSYS_BEFORE_SHUTDOWN:
- pr_debug("%s: R-Notify: Shutdown started\n", __func__);
- ret = sysmon_send_event(SYSMON_SS_LPASS, "wcnss",
- SUBSYS_BEFORE_SHUTDOWN);
- if (ret < 0)
- pr_err("%s: sysmon_send_event error %d", __func__,
- ret);
- break;
- }
- return NOTIFY_DONE;
-}
-
-static void *ssr_notif_hdle;
-static struct notifier_block rnb = {
- .notifier_call = riva_notifier_cb,
-};
-
-static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
- void *ss_handle)
-{
- int ret;
- switch (code) {
- case SUBSYS_BEFORE_SHUTDOWN:
- pr_debug("%s: M-Notify: Shutdown started\n", __func__);
- ret = sysmon_send_event(SYSMON_SS_LPASS, "modem",
- SUBSYS_BEFORE_SHUTDOWN);
- if (ret < 0)
- pr_err("%s: sysmon_send_event error %d", __func__,
- ret);
- break;
- }
- return NOTIFY_DONE;
-}
-
-static void *ssr_modem_notif_hdle;
-static struct notifier_block mnb = {
- .notifier_call = modem_notifier_cb,
-};
-
-static void adsp_log_failure_reason(void)
-{
- char *reason;
- char buffer[MAX_BUF_SIZE];
- unsigned size;
-
- reason = smem_get_entry(SMEM_SSR_REASON_LPASS0, &size);
-
- if (!reason) {
- pr_err("%s: subsystem failure reason: (unknown, smem_get_entry failed).",
- MODULE_NAME);
- return;
- }
-
- if (reason[0] == '\0') {
- pr_err("%s: subsystem failure reason: (unknown, init value found)",
- MODULE_NAME);
- return;
- }
-
- size = size < MAX_BUF_SIZE ? size : (MAX_BUF_SIZE-1);
- memcpy(buffer, reason, size);
- buffer[size] = '\0';
- pr_err("%s: subsystem failure reason: %s", MODULE_NAME, buffer);
- memset((void *)reason, 0x0, size);
- wmb();
-}
-
-static void restart_adsp(void)
-{
- adsp_log_failure_reason();
- subsystem_restart("adsp");
-}
-
-static void adsp_fatal_fn(struct work_struct *work)
-{
- pr_err("%s %s: Watchdog bite received from Q6!\n", MODULE_NAME,
- __func__);
- restart_adsp();
-}
-
-static void adsp_smsm_state_cb(void *data, uint32_t old_state,
- uint32_t new_state)
-{
- /* Ignore if we're the one that set SMSM_RESET */
- if (q6_crash_shutdown)
- return;
-
- if (new_state & SMSM_RESET) {
- pr_debug("%s: ADSP SMSM state changed to SMSM_RESET, new_state= 0x%x, old_state = 0x%x\n",
- __func__, new_state, old_state);
- restart_adsp();
- }
-}
-
-static void send_q6_nmi(void)
-{
- /* Send NMI to QDSP6 via an SCM call. */
- scm_call_atomic1(SCM_SVC_UTIL, SCM_Q6_NMI_CMD, 0x1);
- pr_debug("%s: Q6 NMI was sent.\n", __func__);
-}
-
-static int adsp_shutdown(const struct subsys_desc *subsys)
-{
- send_q6_nmi();
-
- /* The write needs to go through before the q6 is shutdown. */
- mb();
-
- pil_force_shutdown("adsp");
- disable_irq_nosync(ADSP_Q6SS_WDOG_EXPIRED);
-
- return 0;
-}
-
-static int adsp_powerup(const struct subsys_desc *subsys)
-{
- int ret;
-
- if (get_restart_level() == RESET_SUBSYS_INDEPENDENT) {
- pr_debug("%s: Wait for ADSP power up!", __func__);
- msleep(10000);
- }
-
- ret = pil_force_boot("adsp");
- enable_irq(ADSP_Q6SS_WDOG_EXPIRED);
- return ret;
-}
-/* RAM segments - address and size for 8974 */
-static struct ramdump_segment q6_segment = {0xdc00000, 0x1800000};
-
-static int adsp_ramdump(int enable, const struct subsys_desc *subsys)
-{
- pr_debug("%s: enable[%d]\n", __func__, enable);
- if (enable)
- return do_ramdump(adsp_ssr_8974.adsp_ramdump_dev,
- &q6_segment, 1);
- else
- return 0;
-}
-
-static void adsp_crash_shutdown(const struct subsys_desc *subsys)
-{
- q6_crash_shutdown = 1;
- send_q6_nmi();
-}
-
-static irqreturn_t adsp_wdog_bite_irq(int irq, void *dev_id)
-{
- int ret;
-
- pr_debug("%s: rxed irq[0x%x]", __func__, irq);
- disable_irq_nosync(ADSP_Q6SS_WDOG_EXPIRED);
- ret = schedule_work(&adsp_fatal_work);
-
- return IRQ_HANDLED;
-}
-
-static struct subsys_device *adsp_8974_dev;
-
-static struct subsys_desc adsp_8974 = {
- .name = "adsp",
- .shutdown = adsp_shutdown,
- .powerup = adsp_powerup,
- .ramdump = adsp_ramdump,
- .crash_shutdown = adsp_crash_shutdown
-};
-
-static int __init adsp_restart_init(void)
-{
- adsp_8974_dev = subsys_register(&adsp_8974);
- if (IS_ERR(adsp_8974_dev))
- return PTR_ERR(adsp_8974_dev);
- return 0;
-}
-
-static int __init adsp_fatal_init(void)
-{
- int ret;
-
- ret = smsm_state_cb_register(SMSM_Q6_STATE, SMSM_RESET,
- adsp_smsm_state_cb, 0);
-
- if (ret < 0)
- pr_err("%s: Unable to register SMSM callback! (%d)\n",
- __func__, ret);
-
- ret = request_irq(ADSP_Q6SS_WDOG_EXPIRED, adsp_wdog_bite_irq,
- IRQF_TRIGGER_RISING, "q6_wdog", NULL);
-
- if (ret < 0) {
- pr_err("%s: Unable to request ADSP_Q6SS_WDOG_EXPIRED irq.",
- __func__);
- goto out;
- }
- ret = adsp_restart_init();
- if (ret < 0) {
- pr_err("%s: Unable to reg with adsp ssr. (%d)\n",
- __func__, ret);
- goto out;
- }
-
- adsp_ssr_8974.adsp_ramdump_dev = create_ramdump_device("adsp");
-
- if (!adsp_ssr_8974.adsp_ramdump_dev) {
- pr_err("%s: Unable to create ramdump device.\n",
- __func__);
- ret = -ENOMEM;
- goto out;
- }
- ssr_notif_hdle = subsys_notif_register_notifier("riva",
- &rnb);
- if (IS_ERR(ssr_notif_hdle) < 0) {
- ret = PTR_ERR(ssr_notif_hdle);
- pr_err("%s: subsys_register_notifier for Riva: err = %d\n",
- __func__, ret);
- free_irq(ADSP_Q6SS_WDOG_EXPIRED, NULL);
- goto out;
- }
-
- ssr_modem_notif_hdle = subsys_notif_register_notifier("modem",
- &mnb);
- if (IS_ERR(ssr_modem_notif_hdle) < 0) {
- ret = PTR_ERR(ssr_modem_notif_hdle);
- pr_err("%s: subsys_register_notifier for Modem: err = %d\n",
- __func__, ret);
- subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
- free_irq(ADSP_Q6SS_WDOG_EXPIRED, NULL);
- goto out;
- }
-
- pr_info("%s: adsp ssr driver init'ed.\n", __func__);
-out:
- return ret;
-}
-
-static void __exit adsp_fatal_exit(void)
-{
- subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
- subsys_notif_unregister_notifier(ssr_modem_notif_hdle, &mnb);
- subsys_unregister(adsp_8974_dev);
- free_irq(ADSP_Q6SS_WDOG_EXPIRED, NULL);
-}
-
-module_init(adsp_fatal_init);
-module_exit(adsp_fatal_exit);
-
-MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/board-8930-regulator-pm8917.c b/arch/arm/mach-msm/board-8930-regulator-pm8917.c
index b7f554c..8898b50 100644
--- a/arch/arm/mach-msm/board-8930-regulator-pm8917.c
+++ b/arch/arm/mach-msm/board-8930-regulator-pm8917.c
@@ -551,7 +551,7 @@
RPM_SMPS(S8, 1, 1, 1, 2050000, 2050000, NULL, 100000, 1p60, NONE, NONE),
/* ID a_on pd ss min_uV max_uV supply sys_uA init_ip */
- RPM_LDO(L1, 1, 1, 0, 1050000, 1050000, "8917_s4", 0, 10000),
+ RPM_LDO(L1, 0, 1, 0, 1050000, 1050000, "8917_s4", 0, 10000),
RPM_LDO(L2, 0, 1, 0, 1200000, 1200000, "8917_s4", 0, 0),
RPM_LDO(L3, 0, 1, 0, 3075000, 3075000, NULL, 0, 0),
RPM_LDO(L4, 1, 1, 0, 1800000, 1800000, NULL, 10000, 10000),
diff --git a/arch/arm/mach-msm/board-8974-gpiomux.c b/arch/arm/mach-msm/board-8974-gpiomux.c
index 8568340..50b59e1 100644
--- a/arch/arm/mach-msm/board-8974-gpiomux.c
+++ b/arch/arm/mach-msm/board-8974-gpiomux.c
@@ -128,6 +128,12 @@
.dir = GPIOMUX_OUT_LOW,
};
+static struct gpiomux_setting taiko_int = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
static struct msm_gpiomux_config msm_touch_configs[] __initdata = {
{
.gpio = 60, /* TOUCH RESET */
@@ -344,6 +350,21 @@
},
};
+static struct gpiomux_setting sd_card_det_config = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_IN,
+};
+
+static struct msm_gpiomux_config sd_card_det __initdata = {
+ .gpio = 62,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sd_card_det_config,
+ [GPIOMUX_SUSPENDED] = &sd_card_det_config,
+ },
+};
+
static struct msm_gpiomux_config msm_sensor_configs[] __initdata = {
{
.gpio = 15, /* CAM_MCLK0 */
@@ -516,7 +537,13 @@
.settings = {
[GPIOMUX_SUSPENDED] = &taiko_reset,
},
- }
+ },
+ {
+ .gpio = 72, /* CDC_INT */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &taiko_int,
+ },
+ },
};
void __init msm_8974_init_gpiomux(void)
@@ -543,6 +570,8 @@
msm_gpiomux_install(msm_sensor_configs, ARRAY_SIZE(msm_sensor_configs));
+ msm_gpiomux_install(&sd_card_det, 1);
+
msm_gpiomux_install(msm_taiko_config, ARRAY_SIZE(msm_taiko_config));
msm_gpiomux_install(msm_hdmi_configs, ARRAY_SIZE(msm_hdmi_configs));
diff --git a/arch/arm/mach-msm/board-8974.c b/arch/arm/mach-msm/board-8974.c
index dcc0d01..7fe45b8 100644
--- a/arch/arm/mach-msm/board-8974.c
+++ b/arch/arm/mach-msm/board-8974.c
@@ -27,6 +27,7 @@
#include <linux/regulator/machine.h>
#include <linux/regulator/krait-regulator.h>
#include <linux/msm_thermal.h>
+#include <linux/mfd/wcd9xxx/core.h>
#include <asm/mach/map.h>
#include <asm/hardware/gic.h>
#include <mach/board.h>
@@ -488,6 +489,7 @@
{ .compatible = "qcom,msm-qgic2", .data = gic_of_init, },
{ .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, },
{ .compatible = "qcom,spmi-pmic-arb", .data = qpnpint_of_init, },
+ { .compatible = "qcom,wcd9xxx-irq", .data = wcd9xxx_irq_of_init, },
{}
};
static struct of_device_id mpm_match[] __initdata = {
diff --git a/arch/arm/mach-msm/board-9615.c b/arch/arm/mach-msm/board-9615.c
index ed5e2b7..b9da615 100644
--- a/arch/arm/mach-msm/board-9615.c
+++ b/arch/arm/mach-msm/board-9615.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, 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
@@ -910,6 +910,8 @@
&msm_voip,
&msm_i2s_cpudai0,
&msm_i2s_cpudai1,
+ &msm_i2s_cpudai4,
+ &msm_i2s_cpudai5,
&msm_pcm_hostless,
&msm_cpudai_afe_01_rx,
&msm_cpudai_afe_01_tx,
diff --git a/arch/arm/mach-msm/board-9625-gpiomux.c b/arch/arm/mach-msm/board-9625-gpiomux.c
index fe7670b..c4e174b 100644
--- a/arch/arm/mach-msm/board-9625-gpiomux.c
+++ b/arch/arm/mach-msm/board-9625-gpiomux.c
@@ -76,6 +76,82 @@
};
+static struct gpiomux_setting sdc3_clk_active_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting sdc3_cmd_active_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting sdc3_data_0_3_active_cfg = {
+ .func = GPIOMUX_FUNC_6,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting sdc3_suspended_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct gpiomux_setting sdc3_data_1_suspended_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct msm_gpiomux_config sdc3_configs[] __initdata = {
+ {
+ .gpio = 25,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_clk_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+ },
+ },
+ {
+ .gpio = 24,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_cmd_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+ },
+
+ },
+ {
+ .gpio = 16,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+ },
+ },
+ {
+ .gpio = 17,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_data_1_suspended_cfg,
+ },
+ },
+ {
+ .gpio = 18,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+ },
+ },
+ {
+ .gpio = 19,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+ },
+ },
+};
+
void __init msm9625_init_gpiomux(void)
{
int rc;
@@ -87,4 +163,5 @@
}
msm_gpiomux_install(msm_blsp_configs, ARRAY_SIZE(msm_blsp_configs));
+ msm_gpiomux_install(sdc3_configs, ARRAY_SIZE(sdc3_configs));
}
diff --git a/arch/arm/mach-msm/board-9625.c b/arch/arm/mach-msm/board-9625.c
index 2c641ed..9b2bf25 100644
--- a/arch/arm/mach-msm/board-9625.c
+++ b/arch/arm/mach-msm/board-9625.c
@@ -39,6 +39,8 @@
#include <mach/rpm-regulator-smd.h>
#include "clock.h"
#include "modem_notifier.h"
+#include "lpm_resources.h"
+#include "spm.h"
#define MSM_KERNEL_EBI_SIZE 0x51000
@@ -69,7 +71,6 @@
.paddr_to_memtype = msm9625_paddr_to_memtype,
};
-
#define L2CC_AUX_CTRL ((0x1 << L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT) | \
(0x2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) | \
(0x1 << L2X0_AUX_CTRL_EVNT_MON_BUS_EN_SHIFT))
@@ -134,9 +135,14 @@
.init = msm_dt_timer_init
};
-static void __init msm9625_reserve(void)
+static void __init msm9625_early_memory(void)
{
reserve_info = &msm9625_reserve_info;
+ of_scan_flat_dt(dt_scan_for_memory_reserve, msm9625_reserve_table);
+}
+
+static void __init msm9625_reserve(void)
+{
msm_reserve();
}
@@ -285,7 +291,10 @@
msm_init_modem_notifier_list();
msm_smd_init();
msm_rpm_driver_init();
+ msm_lpmrs_module_init();
rpm_regulator_smd_driver_init();
+ msm_spm_device_init();
+ msm_clock_init(&msm9625_clock_init_data);
}
void __init msm9625_init(void)
@@ -294,7 +303,6 @@
pr_err("%s: socinfo_init() failed\n", __func__);
msm9625_init_gpiomux();
- msm_clock_init(&msm_dummy_clock_init_data);
of_platform_populate(NULL, of_default_bus_match_table,
msm9625_auxdata_lookup, NULL);
msm9625_add_devices();
@@ -309,5 +317,6 @@
.timer = &msm_dt_timer,
.dt_compat = msm9625_dt_match,
.reserve = msm9625_reserve,
+ .init_very_early = msm9625_early_memory,
.restart = msm_restart,
MACHINE_END
diff --git a/arch/arm/mach-msm/board-msm7627a-bt.c b/arch/arm/mach-msm/board-msm7627a-bt.c
index e4edf9b..bcc9645 100644
--- a/arch/arm/mach-msm/board-msm7627a-bt.c
+++ b/arch/arm/mach-msm/board-msm7627a-bt.c
@@ -103,7 +103,8 @@
if (machine_is_msm7627a_qrd1())
gpio_bt_sys_rest_en = 114;
if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt())
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime())
gpio_bt_sys_rest_en = 16;
if (machine_is_msm8625_qrd7())
gpio_bt_sys_rest_en = 88;
@@ -975,6 +976,8 @@
int i, rc = 0;
struct device *dev;
+ if (machine_is_qrd_skud_prime())
+ return;
gpio_bt_config();
diff --git a/arch/arm/mach-msm/board-msm7627a-camera.c b/arch/arm/mach-msm/board-msm7627a-camera.c
index b5f214b..79ad996 100644
--- a/arch/arm/mach-msm/board-msm7627a-camera.c
+++ b/arch/arm/mach-msm/board-msm7627a-camera.c
@@ -403,7 +403,8 @@
if (machine_is_msm8625_evb() || machine_is_msm7627a_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
sensor_board_info_ov7692.cam_vreg =
ov7692_gpio_vreg;
sensor_board_info_ov7692.num_vreg =
@@ -420,7 +421,8 @@
platform_device_register(&msm_camera_server);
if (machine_is_msm8625_surf() || machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
platform_device_register(&msm8625_device_csic0);
platform_device_register(&msm8625_device_csic1);
} else {
@@ -429,7 +431,8 @@
}
if (machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
- || machine_is_msm8625_qrd7())
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime())
*(int *) msm7x27a_device_clkctl.dev.platform_data = 1;
platform_device_register(&msm7x27a_device_clkctl);
platform_device_register(&msm7x27a_device_vfe);
@@ -1175,7 +1178,6 @@
#ifndef CONFIG_MSM_CAMERA_V4L2
int rc;
#endif
-
pr_debug("msm7627a_camera_init Entered\n");
if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()) {
@@ -1194,7 +1196,8 @@
if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
#ifndef CONFIG_MSM_CAMERA_V4L2
lcd_camera_power_init();
#endif
@@ -1210,7 +1213,8 @@
|| machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
platform_add_devices(camera_devices_evb,
ARRAY_SIZE(camera_devices_evb));
} else if (machine_is_msm7627a_qrd3())
@@ -1223,7 +1227,8 @@
|| !machine_is_msm8625_evb()
|| !machine_is_msm8625_evt()
|| !machine_is_msm7627a_qrd3()
- || !machine_is_msm8625_qrd7())
+ || !machine_is_msm8625_qrd7()
+ || !machine_is_qrd_skud_prime())
register_i2c_devices();
#ifndef CONFIG_MSM_CAMERA_V4L2
rc = regulator_bulk_get(NULL, ARRAY_SIZE(regs_camera), regs_camera);
@@ -1253,7 +1258,8 @@
|| machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
pr_debug("machine_is_msm7627a_evb i2c_register_board_info\n");
i2c_register_board_info(MSM_GSBI0_QUP_I2C_BUS_ID,
i2c_camera_devices_evb,
diff --git a/arch/arm/mach-msm/board-msm7627a-display.c b/arch/arm/mach-msm/board-msm7627a-display.c
index d62254a..1249c7b 100644
--- a/arch/arm/mach-msm/board-msm7627a-display.c
+++ b/arch/arm/mach-msm/board-msm7627a-display.c
@@ -542,7 +542,8 @@
if (!strncmp(name, "lcdc_truly_hvga_ips3p2335_pt", 28))
ret = 0;
} else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb() ||
- machine_is_msm8625_evt()) {
+ machine_is_msm8625_evt() ||
+ machine_is_qrd_skud_prime()) {
if (!strncmp(name, "mipi_cmd_nt35510_wvga", 21))
ret = 0;
}
@@ -796,7 +797,8 @@
if (machine_is_msm7625a_surf() || machine_is_msm7625a_ffa())
fb_size = MSM7x25A_MSM_FB_SIZE;
else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt())
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime())
fb_size = MSM8x25_MSM_FB_SIZE;
else
fb_size = MSM_FB_SIZE;
@@ -1017,7 +1019,8 @@
if (machine_is_msm7627a_qrd1())
rc = msm_fb_dsi_client_qrd1_reset();
else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt())
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime())
rc = msm_fb_dsi_client_qrd3_reset();
else
rc = msm_fb_dsi_client_msm_reset();
@@ -1124,10 +1127,12 @@
wmb();
lcdc_reset_cfg |= 1;
writel_relaxed(lcdc_reset_cfg, lcdc_reset_ptr);
+ msleep(20);
} else {
gpio_set_value_cansleep(GPIO_LCDC_BRDG_RESET_N, 0);
msleep(20);
gpio_set_value_cansleep(GPIO_LCDC_BRDG_RESET_N, 1);
+ msleep(20);
}
} else {
gpio_set_value_cansleep(GPIO_LCDC_BRDG_PD, 1);
@@ -1325,7 +1330,8 @@
if (machine_is_msm7627a_qrd1())
rc = mipi_dsi_panel_qrd1_power(on);
else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt())
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime())
rc = mipi_dsi_panel_qrd3_power(on);
else
rc = mipi_dsi_panel_msm_power(on);
@@ -1389,7 +1395,8 @@
platform_add_devices(qrd_fb_devices,
ARRAY_SIZE(qrd_fb_devices));
} else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt()) {
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime()) {
mipi_NT35510_pdata.bl_lock = 1;
mipi_NT35516_pdata.bl_lock = 1;
if (disable_splash)
@@ -1398,7 +1405,10 @@
platform_add_devices(evb_fb_devices,
ARRAY_SIZE(evb_fb_devices));
} else if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()) {
- mdp_pdata.cont_splash_enabled = 0x1;
+ if (machine_is_msm7627a_qrd3())
+ mdp_pdata.cont_splash_enabled = 0x0;
+ else
+ mdp_pdata.cont_splash_enabled = 0x1;
platform_add_devices(qrd3_fb_devices,
ARRAY_SIZE(qrd3_fb_devices));
} else {
@@ -1418,7 +1428,8 @@
msm_fb_register_device("mipi_dsi", &mipi_dsi_pdata);
#endif
if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt()) {
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime()) {
gpio_reg_2p85v = regulator_get(&mipi_dsi_device.dev,
"lcd_vdd");
if (IS_ERR(gpio_reg_2p85v))
diff --git a/arch/arm/mach-msm/board-msm7627a-storage.c b/arch/arm/mach-msm/board-msm7627a-storage.c
index 49ff393..07ff389 100644
--- a/arch/arm/mach-msm/board-msm7627a-storage.c
+++ b/arch/arm/mach-msm/board-msm7627a-storage.c
@@ -378,9 +378,9 @@
if (mmc_regulator_init(1, "mmc", 2850000))
return;
/* 8x25 EVT do not use hw detector */
- if (!(machine_is_msm8625_evt()))
+ if (!((machine_is_msm8625_evt() || machine_is_qrd_skud_prime())))
sdc1_plat_data.status_irq = MSM_GPIO_TO_INT(gpio_sdc1_hw_det);
- if (machine_is_msm8625_evt())
+ if (machine_is_msm8625_evt() || machine_is_qrd_skud_prime())
sdc1_plat_data.status = NULL;
msm_add_sdcc(1, &sdc1_plat_data);
diff --git a/arch/arm/mach-msm/board-msm7627a-wlan.c b/arch/arm/mach-msm/board-msm7627a-wlan.c
index 79f213e..ab29fc5 100644
--- a/arch/arm/mach-msm/board-msm7627a-wlan.c
+++ b/arch/arm/mach-msm/board-msm7627a-wlan.c
@@ -23,6 +23,7 @@
#define GPIO_WLAN_3V3_EN 119
static const char *id = "WLAN";
+static bool wlan_powered_up;
enum {
WLAN_VREG_S3 = 0,
@@ -52,7 +53,8 @@
|| machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7())
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime())
gpio_wlan_sys_rest_en = 124;
}
@@ -199,6 +201,11 @@
int rc = 0;
static bool init_done;
+ if (wlan_powered_up) {
+ pr_info("WLAN already powered up\n");
+ return 0;
+ }
+
if (unlikely(!init_done)) {
gpio_wlan_config();
rc = qrf6285_init_regs();
@@ -237,7 +244,8 @@
|| machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
rc = gpio_tlmm_config(GPIO_CFG(gpio_wlan_sys_rest_en, 0,
GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
GPIO_CFG_2MA), GPIO_CFG_ENABLE);
@@ -279,13 +287,17 @@
}
pr_info("WLAN power-up success\n");
+ wlan_powered_up = true;
return 0;
set_clock_fail:
setup_wlan_clock(0);
set_gpio_fail:
setup_wlan_gpio(0);
gpio_fail:
- gpio_free(gpio_wlan_sys_rest_en);
+ if (!(machine_is_msm7627a_qrd1() || machine_is_msm7627a_evb() ||
+ machine_is_msm8625_evb() || machine_is_msm8625_evt() ||
+ machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()))
+ gpio_free(gpio_wlan_sys_rest_en);
qrd_gpio_fail:
/* GPIO_WLAN_3V3_EN is only required for the QRD7627a */
if (machine_is_msm7627a_qrd1())
@@ -294,6 +306,7 @@
wlan_switch_regulators(0);
out:
pr_info("WLAN power-up failed\n");
+ wlan_powered_up = false;
return rc;
}
@@ -301,6 +314,11 @@
{
int rc = 0;
+ if (!wlan_powered_up) {
+ pr_info("WLAN is not powered up, returning success\n");
+ return 0;
+ }
+
/* Disable the A0 clock */
rc = setup_wlan_clock(on);
if (rc) {
@@ -316,7 +334,8 @@
|| machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
rc = gpio_tlmm_config(GPIO_CFG(gpio_wlan_sys_rest_en, 0,
GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
GPIO_CFG_2MA), GPIO_CFG_ENABLE);
@@ -327,20 +346,12 @@
}
gpio_set_value(gpio_wlan_sys_rest_en, 0);
} else {
- rc = gpio_request(gpio_wlan_sys_rest_en, "WLAN_DEEP_SLEEP_N");
- if (!rc) {
- rc = setup_wlan_gpio(on);
- if (rc) {
- pr_err("%s: setup_wlan_gpio = %d\n",
- __func__, rc);
- goto set_gpio_fail;
- }
- gpio_free(gpio_wlan_sys_rest_en);
- } else {
- pr_err("%s: WLAN sys_rest_en GPIO %d request failed %d\n",
- __func__, gpio_wlan_sys_rest_en, rc);
- goto out;
+ rc = setup_wlan_gpio(on);
+ if (rc) {
+ pr_err("%s: setup_wlan_gpio = %d\n", __func__, rc);
+ goto set_gpio_fail;
}
+ gpio_free(gpio_wlan_sys_rest_en);
}
/* GPIO_WLAN_3V3_EN is only required for the QRD7627a */
@@ -362,7 +373,7 @@
__func__, rc);
goto reg_disable;
}
-
+ wlan_powered_up = false;
pr_info("WLAN power-down success\n");
return 0;
set_clock_fail:
@@ -370,14 +381,16 @@
set_gpio_fail:
setup_wlan_gpio(0);
gpio_fail:
- gpio_free(gpio_wlan_sys_rest_en);
+ if (!(machine_is_msm7627a_qrd1() || machine_is_msm7627a_evb() ||
+ machine_is_msm8625_evb() || machine_is_msm8625_evt() ||
+ machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()))
+ gpio_free(gpio_wlan_sys_rest_en);
qrd_gpio_fail:
/* GPIO_WLAN_3V3_EN is only required for the QRD7627a */
if (machine_is_msm7627a_qrd1())
gpio_free(GPIO_WLAN_3V3_EN);
reg_disable:
wlan_switch_regulators(0);
-out:
pr_info("WLAN power-down failed\n");
return rc;
}
diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c
index ec8e438..3045f07 100644
--- a/arch/arm/mach-msm/board-qrd7627a.c
+++ b/arch/arm/mach-msm/board-qrd7627a.c
@@ -884,7 +884,8 @@
static void __init add_platform_devices(void)
{
if (machine_is_msm8625_evb() || machine_is_msm8625_qrd7()
- || machine_is_msm8625_evt()) {
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime()) {
platform_add_devices(msm8625_evb_devices,
ARRAY_SIZE(msm8625_evb_devices));
platform_add_devices(qrd3_devices,
@@ -899,7 +900,8 @@
ARRAY_SIZE(qrd3_devices));
if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt())
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime())
platform_add_devices(msm8625_lcd_camera_devices,
ARRAY_SIZE(msm8625_lcd_camera_devices));
else if (machine_is_msm8625_qrd7())
@@ -1070,3 +1072,13 @@
.init_early = qrd7627a_init_early,
.handle_irq = gic_handle_irq,
MACHINE_END
+MACHINE_START(QRD_SKUD_PRIME, "QRD MSM8625 SKUD PRIME")
+ .atag_offset = 0x100,
+ .map_io = msm8625_map_io,
+ .reserve = msm8625_reserve,
+ .init_irq = msm8625_init_irq,
+ .init_machine = msm_qrd_init,
+ .timer = &msm_timer,
+ .init_early = qrd7627a_init_early,
+ .handle_irq = gic_handle_irq,
+MACHINE_END
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index 285b037..adf1733 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -4502,6 +4502,7 @@
F_AIF_OSR( 8192000, pll4, 4, 1, 15),
F_AIF_OSR(12288000, pll4, 4, 1, 10),
F_AIF_OSR(24576000, pll4, 4, 1, 5),
+ F_AIF_OSR(27000000, pxo, 1, 0, 0),
F_END
};
@@ -4518,6 +4519,7 @@
F_AIF_OSR( 8192000, pll4, 4, 1, 12),
F_AIF_OSR(12288000, pll4, 4, 1, 8),
F_AIF_OSR(24576000, pll4, 4, 1, 4),
+ F_AIF_OSR(27000000, pxo, 1, 0, 0),
F_END
};
@@ -4543,7 +4545,7 @@
.c = { \
.dbg_name = #i "_clk", \
.ops = &clk_ops_rcg, \
- VDD_DIG_FMAX_MAP1(LOW, 24576000), \
+ VDD_DIG_FMAX_MAP1(LOW, 27000000), \
CLK_INIT(i##_clk.c), \
}, \
}
@@ -4569,7 +4571,7 @@
.c = { \
.dbg_name = #i "_clk", \
.ops = &clk_ops_rcg, \
- VDD_DIG_FMAX_MAP1(LOW, 24576000), \
+ VDD_DIG_FMAX_MAP1(LOW, 27000000), \
CLK_INIT(i##_clk.c), \
}, \
}
@@ -4659,6 +4661,7 @@
F_PCM( 8192000, pll4, 4, 1, 15),
F_PCM(12288000, pll4, 4, 1, 10),
F_PCM(24576000, pll4, 4, 1, 5),
+ F_PCM(27000000, pxo, 1, 0, 0),
F_END
};
@@ -4676,6 +4679,7 @@
F_PCM( 8192000, pll4, 4, 1, 12),
F_PCM(12288000, pll4, 4, 1, 8),
F_PCM(24576000, pll4, 4, 1, 4),
+ F_PCM(27000000, pxo, 1, 0, 0),
F_END
};
@@ -4700,7 +4704,7 @@
.c = {
.dbg_name = "pcm_clk",
.ops = &clk_ops_rcg,
- VDD_DIG_FMAX_MAP1(LOW, 24576000),
+ VDD_DIG_FMAX_MAP1(LOW, 27000000),
CLK_INIT(pcm_clk.c),
.rate = ULONG_MAX,
},
@@ -4727,7 +4731,7 @@
.c = {
.dbg_name = "audio_slimbus_clk",
.ops = &clk_ops_rcg,
- VDD_DIG_FMAX_MAP1(LOW, 24576000),
+ VDD_DIG_FMAX_MAP1(LOW, 27000000),
CLK_INIT(audio_slimbus_clk.c),
},
};
@@ -6706,22 +6710,22 @@
struct clk *mmfpb_a_clk = clk_get_sys("clock-8960", "mmfpb_a_clk");
struct clk *cfpb_a_clk = clk_get_sys("clock-8960", "cfpb_a_clk");
- /* Vote for MMFPB to be at least 76.8MHz when an Apps CPU is active. */
+ /* Vote for MMFPB to be on when Apps is active. */
if (WARN(IS_ERR(mmfpb_a_clk), "mmfpb_a_clk not found (%ld)\n",
PTR_ERR(mmfpb_a_clk)))
return PTR_ERR(mmfpb_a_clk);
- rc = clk_set_rate(mmfpb_a_clk, 76800000);
+ rc = clk_set_rate(mmfpb_a_clk, 38400000);
if (WARN(rc, "mmfpb_a_clk rate was not set (%d)\n", rc))
return rc;
rc = clk_prepare_enable(mmfpb_a_clk);
if (WARN(rc, "mmfpb_a_clk not enabled (%d)\n", rc))
return rc;
- /* Vote for CFPB to be at least 64MHz when an Apps CPU is active. */
+ /* Vote for CFPB to be on when Apps is active. */
if (WARN(IS_ERR(cfpb_a_clk), "cfpb_a_clk not found (%ld)\n",
PTR_ERR(cfpb_a_clk)))
return PTR_ERR(cfpb_a_clk);
- rc = clk_set_rate(cfpb_a_clk, 64000000);
+ rc = clk_set_rate(cfpb_a_clk, 32000000);
if (WARN(rc, "cfpb_a_clk rate was not set (%d)\n", rc))
return rc;
rc = clk_prepare_enable(cfpb_a_clk);
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index 0adb9a2..b21e60e 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -632,7 +632,6 @@
#define CXO_ID 0x0
#define QDSS_ID 0x1
-#define RPM_SCALING_ENABLE_ID 0x2
#define PNOC_ID 0x0
#define SNOC_ID 0x1
@@ -2932,7 +2931,7 @@
};
static struct clk_freq_tbl ftbl_mdss_edplink_clk[] = {
- F_MDSS(135000000, edppll_270, 2, 0, 0),
+ F_MDSS(162000000, edppll_270, 2, 0, 0),
F_MDSS(270000000, edppll_270, 11, 0, 0),
F_END
};
@@ -2952,7 +2951,7 @@
};
static struct clk_freq_tbl ftbl_mdss_edppixel_clk[] = {
- F_MDSS(175000000, edppll_350, 2, 0, 0),
+ F_MDSS(148500000, edppll_350, 2, 0, 0),
F_MDSS(350000000, edppll_350, 11, 0, 0),
F_END
};
@@ -5126,8 +5125,9 @@
/* Multimedia clocks */
CLK_LOOKUP("bus_clk_src", axi_clk_src.c, ""),
CLK_LOOKUP("bus_clk", mmss_mmssnoc_axi_clk.c, ""),
- CLK_LOOKUP("core_clk", mdss_edpaux_clk.c, ""),
- CLK_LOOKUP("core_clk", mdss_edppixel_clk.c, ""),
+ CLK_LOOKUP("core_clk", mdss_edpaux_clk.c, "fd923400.qcom,mdss_edp"),
+ CLK_LOOKUP("pixel_clk", mdss_edppixel_clk.c, "fd923400.qcom,mdss_edp"),
+ CLK_LOOKUP("link_clk", mdss_edplink_clk.c, "fd923400.qcom,mdss_edp"),
CLK_LOOKUP("byte_clk", mdss_byte0_clk.c, "fd922800.qcom,mdss_dsi"),
CLK_LOOKUP("byte_clk", mdss_byte1_clk.c, ""),
CLK_LOOKUP("core_clk", mdss_esc0_clk.c, "fd922800.qcom,mdss_dsi"),
@@ -5287,6 +5287,7 @@
CLK_LOOKUP("core_clk", camss_vfe_cpp_clk.c, "fda44000.qcom,iommu"),
CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "mdp.0"),
CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "mdss_dsi_clk_ctrl"),
+ CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "fd923400.qcom,mdss_edp"),
CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "fd928000.qcom,iommu"),
CLK_LOOKUP("core_clk", mdss_axi_clk.c, "fd928000.qcom,iommu"),
CLK_LOOKUP("bus_clk", mdss_axi_clk.c, "mdp.0"),
@@ -5749,24 +5750,6 @@
#define APCS_GCC_CC_PHYS 0xF9011000
#define APCS_GCC_CC_SIZE SZ_4K
-static void __init enable_rpm_scaling(void)
-{
- int rc, value = 0x1;
- struct msm_rpm_kvp kvp = {
- .key = RPM_SMD_KEY_ENABLE,
- .data = (void *)&value,
- .length = sizeof(value),
- };
-
- rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_SLEEP_SET,
- RPM_MISC_CLK_TYPE, RPM_SCALING_ENABLE_ID, &kvp, 1);
- WARN(rc < 0, "RPM clock scaling (sleep set) did not enable!\n");
-
- rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_ACTIVE_SET,
- RPM_MISC_CLK_TYPE, RPM_SCALING_ENABLE_ID, &kvp, 1);
- WARN(rc < 0, "RPM clock scaling (active set) did not enable!\n");
-}
-
static void __init msm8974_clock_pre_init(void)
{
virt_bases[GCC_BASE] = ioremap(GCC_CC_PHYS, GCC_CC_SIZE);
diff --git a/arch/arm/mach-msm/clock-8x60.c b/arch/arm/mach-msm/clock-8x60.c
index 6ae66fe..bc4bb2e 100644
--- a/arch/arm/mach-msm/clock-8x60.c
+++ b/arch/arm/mach-msm/clock-8x60.c
@@ -2971,6 +2971,7 @@
F_AIF_OSR( 8192000, pll4, 2, 1, 33),
F_AIF_OSR(12288000, pll4, 4, 1, 11),
F_AIF_OSR(24576000, pll4, 2, 1, 11),
+ F_AIF_OSR(27000000, pxo, 1, 0, 0),
F_END
};
@@ -2996,7 +2997,7 @@
.c = { \
.dbg_name = #i "_clk", \
.ops = &clk_ops_rcg, \
- VDD_DIG_FMAX_MAP1(LOW, 24576000), \
+ VDD_DIG_FMAX_MAP1(LOW, 27000000), \
CLK_INIT(i##_clk.c), \
}, \
}
@@ -3065,6 +3066,7 @@
F_PCM( 8192000, pll4, 2, 1, 33),
F_PCM(12288000, pll4, 4, 1, 11),
F_PCM(24580000, pll4, 2, 1, 11),
+ F_PCM(27000000, pxo, 1, 0, 0),
F_END
};
diff --git a/arch/arm/mach-msm/clock-9625.c b/arch/arm/mach-msm/clock-9625.c
new file mode 100644
index 0000000..69a52e9
--- /dev/null
+++ b/arch/arm/mach-msm/clock-9625.c
@@ -0,0 +1,2395 @@
+/* Copyright (c) 2012, 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/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/iopoll.h>
+
+#include <mach/clk.h>
+#include <mach/rpm-regulator-smd.h>
+#include <mach/socinfo.h>
+
+#include "clock-local2.h"
+#include "clock-pll.h"
+#include "clock-rpm.h"
+#include "clock-voter.h"
+#include "clock.h"
+
+enum {
+ GCC_BASE,
+ LPASS_BASE,
+ APCS_BASE,
+ APCS_PLL_BASE,
+ N_BASES,
+};
+
+static void __iomem *virt_bases[N_BASES];
+
+#define GCC_REG_BASE(x) (void __iomem *)(virt_bases[GCC_BASE] + (x))
+#define LPASS_REG_BASE(x) (void __iomem *)(virt_bases[LPASS_BASE] + (x))
+#define APCS_REG_BASE(x) (void __iomem *)(virt_bases[APCS_BASE] + (x))
+#define APCS_PLL_REG_BASE(x) (void __iomem *)(virt_bases[APCS_PLL_BASE] + (x))
+
+/* GCC registers */
+#define GPLL0_MODE_REG 0x0000
+#define GPLL0_L_REG 0x0004
+#define GPLL0_M_REG 0x0008
+#define GPLL0_N_REG 0x000C
+#define GPLL0_USER_CTL_REG 0x0010
+#define GPLL0_CONFIG_CTL_REG 0x0014
+#define GPLL0_TEST_CTL_REG 0x0018
+#define GPLL0_STATUS_REG 0x001C
+
+#define GPLL1_MODE_REG 0x0040
+#define GPLL1_L_REG 0x0044
+#define GPLL1_M_REG 0x0048
+#define GPLL1_N_REG 0x004C
+#define GPLL1_USER_CTL_REG 0x0050
+#define GPLL1_CONFIG_CTL_REG 0x0054
+#define GPLL1_TEST_CTL_REG 0x0058
+#define GPLL1_STATUS_REG 0x005C
+
+#define GCC_DEBUG_CLK_CTL_REG 0x1880
+#define CLOCK_FRQ_MEASURE_CTL_REG 0x1884
+#define CLOCK_FRQ_MEASURE_STATUS_REG 0x1888
+#define GCC_PLLTEST_PAD_CFG_REG 0x188C
+#define GCC_XO_DIV4_CBCR_REG 0x10C8
+#define APCS_GPLL_ENA_VOTE_REG 0x1480
+#define APCS_CLOCK_BRANCH_ENA_VOTE 0x1484
+#define APCS_CLOCK_SLEEP_ENA_VOTE 0x1488
+
+#define APCS_CLK_DIAG_REG 0x001C
+
+#define APCS_CPU_PLL_MODE_REG 0x0000
+#define APCS_CPU_PLL_L_REG 0x0004
+#define APCS_CPU_PLL_M_REG 0x0008
+#define APCS_CPU_PLL_N_REG 0x000C
+#define APCS_CPU_PLL_USER_CTL_REG 0x0010
+#define APCS_CPU_PLL_CONFIG_CTL_REG 0x0014
+#define APCS_CPU_PLL_TEST_CTL_REG 0x0018
+#define APCS_CPU_PLL_STATUS_REG 0x001C
+
+#define USB_HSIC_SYSTEM_CMD_RCGR 0x041C
+#define USB_HSIC_XCVR_FS_CMD_RCGR 0x0424
+#define USB_HSIC_CMD_RCGR 0x0440
+#define USB_HSIC_IO_CAL_CMD_RCGR 0x0458
+#define USB_HS_SYSTEM_CMD_RCGR 0x0490
+#define SDCC2_APPS_CMD_RCGR 0x0510
+#define SDCC3_APPS_CMD_RCGR 0x0550
+#define BLSP1_QUP1_SPI_APPS_CMD_RCGR 0x064C
+#define BLSP1_UART1_APPS_CMD_RCGR 0x068C
+#define BLSP1_QUP2_SPI_APPS_CMD_RCGR 0x06CC
+#define BLSP1_UART2_APPS_CMD_RCGR 0x070C
+#define BLSP1_QUP3_SPI_APPS_CMD_RCGR 0x074C
+#define BLSP1_UART3_APPS_CMD_RCGR 0x078C
+#define BLSP1_QUP4_SPI_APPS_CMD_RCGR 0x07CC
+#define BLSP1_UART4_APPS_CMD_RCGR 0x080C
+#define BLSP1_QUP5_SPI_APPS_CMD_RCGR 0x084C
+#define BLSP1_UART5_APPS_CMD_RCGR 0x088C
+#define BLSP1_QUP6_SPI_APPS_CMD_RCGR 0x08CC
+#define BLSP1_UART6_APPS_CMD_RCGR 0x090C
+#define PDM2_CMD_RCGR 0x0CD0
+#define CE1_CMD_RCGR 0x1050
+#define GP1_CMD_RCGR 0x1904
+#define GP2_CMD_RCGR 0x1944
+#define GP3_CMD_RCGR 0x1984
+#define QPIC_CMD_RCGR 0x1A50
+#define IPA_CMD_RCGR 0x1A90
+
+#define USB_HS_HSIC_BCR 0x0400
+#define USB_HS_BCR 0x0480
+#define SDCC2_BCR 0x0500
+#define SDCC3_BCR 0x0540
+#define BLSP1_BCR 0x05C0
+#define BLSP1_QUP1_BCR 0x0640
+#define BLSP1_UART1_BCR 0x0680
+#define BLSP1_QUP2_BCR 0x06C0
+#define BLSP1_UART2_BCR 0x0700
+#define BLSP1_QUP3_BCR 0x0740
+#define BLSP1_UART3_BCR 0x0780
+#define BLSP1_QUP4_BCR 0x07C0
+#define BLSP1_UART4_BCR 0x0800
+#define BLSP1_QUP5_BCR 0x0840
+#define BLSP1_UART5_BCR 0x0880
+#define BLSP1_QUP6_BCR 0x08C0
+#define BLSP1_UART6_BCR 0x0900
+#define PDM_BCR 0x0CC0
+#define PRNG_BCR 0x0D00
+#define BAM_DMA_BCR 0x0D40
+#define BOOT_ROM_BCR 0x0E00
+#define CE1_BCR 0x1040
+#define QPIC_BCR 0x1040
+#define IPA_BCR 0x1A80
+
+
+#define SYS_NOC_IPA_AXI_CBCR 0x0128
+#define USB_HSIC_AHB_CBCR 0x0408
+#define USB_HSIC_SYSTEM_CBCR 0x040C
+#define USB_HSIC_CBCR 0x0410
+#define USB_HSIC_IO_CAL_CBCR 0x0414
+#define USB_HSIC_XCVR_FS_CBCR 0x042C
+#define USB_HS_SYSTEM_CBCR 0x0484
+#define USB_HS_AHB_CBCR 0x0488
+#define SDCC2_APPS_CBCR 0x0504
+#define SDCC2_AHB_CBCR 0x0508
+#define SDCC3_APPS_CBCR 0x0544
+#define SDCC3_AHB_CBCR 0x0548
+#define BLSP1_AHB_CBCR 0x05C4
+#define BLSP1_QUP1_SPI_APPS_CBCR 0x0644
+#define BLSP1_QUP1_I2C_APPS_CBCR 0x0648
+#define BLSP1_UART1_APPS_CBCR 0x0684
+#define BLSP1_UART1_SIM_CBCR 0x0688
+#define BLSP1_QUP2_SPI_APPS_CBCR 0x06C4
+#define BLSP1_QUP2_I2C_APPS_CBCR 0x06C8
+#define BLSP1_UART2_APPS_CBCR 0x0704
+#define BLSP1_UART2_SIM_CBCR 0x0708
+#define BLSP1_QUP3_SPI_APPS_CBCR 0x0744
+#define BLSP1_QUP3_I2C_APPS_CBCR 0x0748
+#define BLSP1_UART3_APPS_CBCR 0x0784
+#define BLSP1_UART3_SIM_CBCR 0x0788
+#define BLSP1_QUP4_SPI_APPS_CBCR 0x07C4
+#define BLSP1_QUP4_I2C_APPS_CBCR 0x07C8
+#define BLSP1_UART4_APPS_CBCR 0x0804
+#define BLSP1_UART4_SIM_CBCR 0x0808
+#define BLSP1_QUP5_SPI_APPS_CBCR 0x0844
+#define BLSP1_QUP5_I2C_APPS_CBCR 0x0848
+#define BLSP1_UART5_APPS_CBCR 0x0884
+#define BLSP1_UART5_SIM_CBCR 0x0888
+#define BLSP1_QUP6_SPI_APPS_CBCR 0x08C4
+#define BLSP1_QUP6_I2C_APPS_CBCR 0x08C8
+#define BLSP1_UART6_APPS_CBCR 0x0904
+#define BLSP1_UART6_SIM_CBCR 0x0908
+#define BOOT_ROM_AHB_CBCR 0x0E04
+#define PDM_AHB_CBCR 0x0CC4
+#define PDM_XO4_CBCR 0x0CC8
+#define PDM_AHB_CBCR 0x0CC4
+#define PDM_XO4_CBCR 0x0CC8
+#define PDM2_CBCR 0x0CCC
+#define PRNG_AHB_CBCR 0x0D04
+#define BAM_DMA_AHB_CBCR 0x0D44
+#define MSG_RAM_AHB_CBCR 0x0E44
+#define CE1_CBCR 0x1044
+#define CE1_AXI_CBCR 0x1048
+#define CE1_AHB_CBCR 0x104C
+#define GCC_AHB_CBCR 0x10C0
+#define GP1_CBCR 0x1900
+#define GP2_CBCR 0x1940
+#define GP3_CBCR 0x1980
+#define QPIC_CBCR 0x1A44
+#define QPIC_AHB_CBCR 0x1A48
+#define IPA_CBCR 0x1A84
+#define IPA_CNOC_CBCR 0x1A88
+#define IPA_SLEEP_CBCR 0x1A8C
+
+/* LPASS registers */
+/* TODO: Needs to double check lpass regiserts after get the SWI for hw */
+#define LPAPLL_MODE_REG 0x0000
+#define LPAPLL_L_REG 0x0004
+#define LPAPLL_M_REG 0x0008
+#define LPAPLL_N_REG 0x000C
+#define LPAPLL_USER_CTL_REG 0x0010
+#define LPAPLL_CONFIG_CTL_REG 0x0014
+#define LPAPLL_TEST_CTL_REG 0x0018
+#define LPAPLL_STATUS_REG 0x001C
+
+#define LPASS_DEBUG_CLK_CTL_REG 0x29000
+#define LPASS_LPA_PLL_VOTE_APPS_REG 0x2000
+
+#define LPAIF_PRI_CMD_RCGR 0xB000
+#define LPAIF_SEC_CMD_RCGR 0xC000
+#define LPAIF_PCM0_CMD_RCGR 0xF000
+#define LPAIF_PCM1_CMD_RCGR 0x10000
+#define SLIMBUS_CMD_RCGR 0x12000
+#define LPAIF_PCMOE_CMD_RCGR 0x13000
+
+#define AUDIO_CORE_BCR 0x4000
+
+#define AUDIO_CORE_GDSCR 0x7000
+#define AUDIO_CORE_LPAIF_PRI_OSR_CBCR 0xB014
+#define AUDIO_CORE_LPAIF_PRI_IBIT_CBCR 0xB018
+#define AUDIO_CORE_LPAIF_PRI_EBIT_CBCR 0xB01C
+#define AUDIO_CORE_LPAIF_SEC_OSR_CBCR 0xC014
+#define AUDIO_CORE_LPAIF_SEC_IBIT_CBCR 0xC018
+#define AUDIO_CORE_LPAIF_SEC_EBIT_CBCR 0xC01C
+#define AUDIO_CORE_LPAIF_PCM0_IBIT_CBCR 0xF014
+#define AUDIO_CORE_LPAIF_PCM0_EBIT_CBCR 0xF018
+#define AUDIO_CORE_LPAIF_PCM1_IBIT_CBCR 0x10014
+#define AUDIO_CORE_LPAIF_PCM1_EBIT_CBCR 0x10018
+#define AUDIO_CORE_RESAMPLER_CORE_CBCR 0x11014
+#define AUDIO_CORE_RESAMPLER_LFABIF_CBCR 0x11018
+#define AUDIO_CORE_SLIMBUS_CORE_CBCR 0x12014
+#define AUDIO_CORE_SLIMBUS_LFABIF_CBCR 0x12018
+#define AUDIO_CORE_LPAIF_PCM_DATA_OE_CBCR 0x13014
+
+/* Mux source select values */
+#define cxo_source_val 0
+#define gpll0_source_val 1
+#define gpll1_hsic_source_val 4
+#define gnd_source_val 5
+#define cxo_lpass_source_val 0
+#define lpapll0_lpass_source_val 1
+#define gpll0_lpass_source_val 5
+
+#define F(f, s, div, m, n) \
+ { \
+ .freq_hz = (f), \
+ .src_clk = &s##_clk_src.c, \
+ .m_val = (m), \
+ .n_val = ~((n)-(m)) * !!(n), \
+ .d_val = ~(n),\
+ .div_src_val = BVAL(4, 0, (int)(2*(div) - 1)) \
+ | BVAL(10, 8, s##_source_val), \
+ }
+
+#define F_HSIC(f, s, div, m, n) \
+ { \
+ .freq_hz = (f), \
+ .src_clk = &s##_clk_src.c, \
+ .m_val = (m), \
+ .n_val = ~((n)-(m)) * !!(n), \
+ .d_val = ~(n),\
+ .div_src_val = BVAL(4, 0, (int)(2*(div) - 1)) \
+ | BVAL(10, 8, s##_hsic_source_val), \
+ }
+
+#define F_LPASS(f, s, div, m, n) \
+ { \
+ .freq_hz = (f), \
+ .src_clk = &s##_clk_src.c, \
+ .m_val = (m), \
+ .n_val = ~((n)-(m)) * !!(n), \
+ .d_val = ~(n),\
+ .div_src_val = BVAL(4, 0, (int)(2*(div) - 1)) \
+ | BVAL(10, 8, s##_lpass_source_val), \
+ }
+
+
+#define VDD_DIG_FMAX_MAP1(l1, f1) \
+ .vdd_class = &vdd_dig, \
+ .fmax[VDD_DIG_##l1] = (f1)
+#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
+ .vdd_class = &vdd_dig, \
+ .fmax[VDD_DIG_##l1] = (f1), \
+ .fmax[VDD_DIG_##l2] = (f2)
+#define VDD_DIG_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \
+ .vdd_class = &vdd_dig, \
+ .fmax[VDD_DIG_##l1] = (f1), \
+ .fmax[VDD_DIG_##l2] = (f2), \
+ .fmax[VDD_DIG_##l3] = (f3)
+
+enum vdd_dig_levels {
+ VDD_DIG_NONE,
+ VDD_DIG_LOW,
+ VDD_DIG_NOMINAL,
+ VDD_DIG_HIGH
+};
+
+static const int vdd_corner[] = {
+ [VDD_DIG_NONE] = RPM_REGULATOR_CORNER_NONE,
+ [VDD_DIG_LOW] = RPM_REGULATOR_CORNER_SVS_SOC,
+ [VDD_DIG_NOMINAL] = RPM_REGULATOR_CORNER_NORMAL,
+ [VDD_DIG_HIGH] = RPM_REGULATOR_CORNER_SUPER_TURBO,
+};
+
+static struct regulator *vdd_dig_reg;
+
+int set_vdd_dig(struct clk_vdd_class *vdd_class, int level)
+{
+ return regulator_set_voltage(vdd_dig_reg, vdd_corner[level],
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+}
+
+static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig);
+
+/* TODO: Needs to confirm the below values */
+#define RPM_MISC_CLK_TYPE 0x306b6c63
+#define RPM_BUS_CLK_TYPE 0x316b6c63
+#define RPM_MEM_CLK_TYPE 0x326b6c63
+
+#define RPM_SMD_KEY_ENABLE 0x62616E45
+
+#define CXO_ID 0x0
+#define QDSS_ID 0x1
+
+#define PNOC_ID 0x0
+#define SNOC_ID 0x1
+#define CNOC_ID 0x2
+
+#define BIMC_ID 0x0
+
+#define D0_ID 1
+#define D1_ID 2
+#define A0_ID 3
+#define A1_ID 4
+#define A2_ID 5
+
+DEFINE_CLK_RPM_SMD_BRANCH(cxo_clk_src, cxo_a_clk_src,
+ RPM_MISC_CLK_TYPE, CXO_ID, 19200000);
+
+DEFINE_CLK_RPM_SMD(cnoc_clk, cnoc_a_clk, RPM_BUS_CLK_TYPE, CNOC_ID, NULL);
+DEFINE_CLK_RPM_SMD(pnoc_clk, pnoc_a_clk, RPM_BUS_CLK_TYPE, PNOC_ID, NULL);
+DEFINE_CLK_RPM_SMD(snoc_clk, snoc_a_clk, RPM_BUS_CLK_TYPE, SNOC_ID, NULL);
+
+DEFINE_CLK_RPM_SMD(bimc_clk, bimc_a_clk, RPM_MEM_CLK_TYPE, BIMC_ID, NULL);
+
+DEFINE_CLK_RPM_SMD_QDSS(qdss_clk, qdss_a_clk, RPM_MISC_CLK_TYPE, QDSS_ID);
+
+DEFINE_CLK_RPM_SMD_XO_BUFFER(cxo_d0, cxo_d0_a, D0_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER(cxo_d1, cxo_d1_a, D1_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER(cxo_a0, cxo_a0_a, A0_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER(cxo_a1, cxo_a1_a, A1_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER(cxo_a2, cxo_a2_a, A2_ID);
+
+DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(cxo_d0_pin, cxo_d0_a_pin, D0_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(cxo_d1_pin, cxo_d1_a_pin, D1_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(cxo_a0_pin, cxo_a0_a_pin, A0_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(cxo_a1_pin, cxo_a1_a_pin, A1_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(cxo_a2_pin, cxo_a2_a_pin, A2_ID);
+
+static struct pll_vote_clk gpll0_clk_src = {
+ .en_reg = (void __iomem *)APCS_GPLL_ENA_VOTE_REG,
+ .status_reg = (void __iomem *)GPLL0_STATUS_REG,
+ .status_mask = BIT(17),
+ .parent = &cxo_clk_src.c,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .rate = 600000000,
+ .dbg_name = "gpll0_clk_src",
+ .ops = &clk_ops_pll_vote,
+ CLK_INIT(gpll0_clk_src.c),
+ },
+};
+
+static struct pll_vote_clk lpapll0_clk_src = {
+ .en_reg = (void __iomem *)LPASS_LPA_PLL_VOTE_APPS_REG,
+ .en_mask = BIT(0),
+ .status_reg = (void __iomem *)LPAPLL_STATUS_REG,
+ .status_mask = BIT(17),
+ .parent = &cxo_clk_src.c,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .rate = 393216000,
+ .dbg_name = "lpapll0_clk_src",
+ .ops = &clk_ops_pll_vote,
+ CLK_INIT(lpapll0_clk_src.c),
+ },
+};
+
+static struct pll_vote_clk gpll1_clk_src = {
+ .en_reg = (void __iomem *)APCS_GPLL_ENA_VOTE_REG,
+ .en_mask = BIT(1),
+ .status_reg = (void __iomem *)GPLL1_STATUS_REG,
+ .status_mask = BIT(17),
+ .parent = &cxo_clk_src.c,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .rate = 480000000,
+ .dbg_name = "gpll1_clk_src",
+ .ops = &clk_ops_pll_vote,
+ CLK_INIT(gpll1_clk_src.c),
+ },
+};
+
+/*
+ * Need to skip handoff of the acpu pll to avoid handoff code
+ * to turn off the pll when the acpu is running off this pll.
+ */
+static struct pll_clk apcspll_clk_src = {
+ .mode_reg = (void __iomem *)APCS_CPU_PLL_MODE_REG,
+ .status_reg = (void __iomem *)APCS_CPU_PLL_STATUS_REG,
+ .parent = &cxo_clk_src.c,
+ .base = &virt_bases[APCS_PLL_BASE],
+ .c = {
+ .rate = 998400000,
+ .dbg_name = "apcspll_clk_src",
+ .ops = &clk_ops_local_pll,
+ CLK_INIT(apcspll_clk_src.c),
+ .flags = CLKFLAG_SKIP_HANDOFF,
+ },
+};
+
+static DEFINE_CLK_VOTER(pnoc_msmbus_clk, &pnoc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(snoc_msmbus_clk, &snoc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(cnoc_msmbus_clk, &cnoc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(pnoc_msmbus_a_clk, &pnoc_a_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(snoc_msmbus_a_clk, &snoc_a_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(cnoc_msmbus_a_clk, &cnoc_a_clk.c, LONG_MAX);
+
+static DEFINE_CLK_VOTER(bimc_msmbus_clk, &bimc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(bimc_msmbus_a_clk, &bimc_a_clk.c, LONG_MAX);
+
+static DEFINE_CLK_VOTER(pnoc_sdcc2_clk, &pnoc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(pnoc_sdcc3_clk, &pnoc_clk.c, LONG_MAX);
+
+static DEFINE_CLK_VOTER(pnoc_sps_clk, &pnoc_clk.c, LONG_MAX);
+
+static struct clk_freq_tbl ftbl_gcc_ipa_clk[] = {
+ F( 50000000, gpll0, 12, 0, 0),
+ F( 92310000, gpll0, 6.5, 0, 0),
+ F(100000000, gpll0, 6, 0, 0),
+ F_END
+};
+
+static struct rcg_clk ipa_clk_src = {
+ .cmd_rcgr_reg = IPA_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_ipa_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "ipa_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 50000000, NOMINAL, 100000000),
+ CLK_INIT(ipa_clk_src.c)
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_blsp1_qup1_6_spi_apps_clk[] = {
+ F( 960000, cxo, 10, 1, 2),
+ F( 4800000, cxo, 4, 0, 0),
+ F( 9600000, cxo, 2, 0, 0),
+ F(15000000, gpll0, 10, 1, 4),
+ F(19200000, cxo, 1, 0, 0),
+ F(25000000, gpll0, 12, 1, 2),
+ F(50000000, gpll0, 12, 0, 0),
+ F_END
+};
+
+static struct rcg_clk blsp1_qup1_spi_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_QUP1_SPI_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_qup1_spi_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000),
+ CLK_INIT(blsp1_qup1_spi_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk blsp1_qup2_spi_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_QUP2_SPI_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_qup2_spi_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000),
+ CLK_INIT(blsp1_qup2_spi_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk blsp1_qup3_spi_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_QUP3_SPI_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_qup3_spi_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000),
+ CLK_INIT(blsp1_qup3_spi_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk blsp1_qup4_spi_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_QUP4_SPI_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_qup4_spi_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000),
+ CLK_INIT(blsp1_qup4_spi_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk blsp1_qup5_spi_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_QUP5_SPI_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_qup5_spi_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000),
+ CLK_INIT(blsp1_qup5_spi_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk blsp1_qup6_spi_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_QUP6_SPI_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_qup6_spi_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000),
+ CLK_INIT(blsp1_qup6_spi_apps_clk_src.c)
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_blsp1_uart1_6_apps_clk[] = {
+ F( 3686400, gpll0, 1, 96, 15625),
+ F( 7372800, gpll0, 1, 192, 15625),
+ F(14745600, gpll0, 1, 384, 15625),
+ F(16000000, gpll0, 5, 2, 15),
+ F(19200000, cxo, 1, 0, 0),
+ F(24000000, gpll0, 5, 1, 5),
+ F(32000000, gpll0, 1, 4, 75),
+ F(40000000, gpll0, 15, 0, 0),
+ F(46400000, gpll0, 1, 29, 375),
+ F(48000000, gpll0, 12.5, 0, 0),
+ F(51200000, gpll0, 1, 32, 375),
+ F(56000000, gpll0, 1, 7, 75),
+ F(58982400, gpll0, 1, 1536, 15625),
+ F(60000000, gpll0, 10, 0, 0),
+ F_END
+};
+
+static struct rcg_clk blsp1_uart1_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_UART1_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_uart1_6_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_uart1_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000),
+ CLK_INIT(blsp1_uart1_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk blsp1_uart2_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_UART2_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_uart1_6_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_uart2_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000),
+ CLK_INIT(blsp1_uart2_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk blsp1_uart3_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_UART3_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_uart1_6_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_uart3_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000),
+ CLK_INIT(blsp1_uart3_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk blsp1_uart4_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_UART4_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_uart1_6_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_uart4_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000),
+ CLK_INIT(blsp1_uart4_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk blsp1_uart5_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_UART5_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_uart1_6_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_uart5_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000),
+ CLK_INIT(blsp1_uart5_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk blsp1_uart6_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_UART6_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_uart1_6_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_uart6_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000),
+ CLK_INIT(blsp1_uart6_apps_clk_src.c)
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_ce1_clk[] = {
+ F( 50000000, gpll0, 12, 0, 0),
+ F(100000000, gpll0, 6, 0, 0),
+ F_END
+};
+
+static struct rcg_clk ce1_clk_src = {
+ .cmd_rcgr_reg = CE1_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_ce1_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "ce1_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP2(LOW, 50000000, NOMINAL, 100000000),
+ CLK_INIT(ce1_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_gp_clk[] = {
+ F(19200000, cxo, 1, 0, 0),
+ F_END
+};
+
+static struct rcg_clk gp1_clk_src = {
+ .cmd_rcgr_reg = GP1_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_gp_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gp1_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000),
+ CLK_INIT(gp1_clk_src.c)
+ },
+};
+
+static struct rcg_clk gp2_clk_src = {
+ .cmd_rcgr_reg = GP2_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_gp_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gp2_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000),
+ CLK_INIT(gp2_clk_src.c)
+ },
+};
+
+static struct rcg_clk gp3_clk_src = {
+ .cmd_rcgr_reg = GP3_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_gp_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gp3_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000),
+ CLK_INIT(gp3_clk_src.c)
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_pdm2_clk[] = {
+ F(60000000, gpll0, 10, 0, 0),
+ F_END
+};
+
+static struct rcg_clk pdm2_clk_src = {
+ .cmd_rcgr_reg = PDM2_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_pdm2_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "pdm2_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP1(LOW, 60000000),
+ CLK_INIT(pdm2_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_qpic_clk[] = {
+ F( 50000000, gpll0, 12, 0, 0),
+ F(100000000, gpll0, 6, 0, 0),
+ F_END
+};
+
+static struct rcg_clk qpic_clk_src = {
+ .cmd_rcgr_reg = QPIC_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_qpic_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "qpic_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 50000000, NOMINAL, 100000000),
+ CLK_INIT(qpic_clk_src.c)
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_sdcc2_apps_clk[] = {
+ F( 144000, cxo, 16, 3, 25),
+ F( 400000, cxo, 12, 1, 4),
+ F( 20000000, gpll0, 15, 1, 2),
+ F( 25000000, gpll0, 12, 1, 2),
+ F( 50000000, gpll0, 12, 0, 0),
+ F(100000000, gpll0, 6, 0, 0),
+ F(200000000, gpll0, 3, 0, 0),
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_sdcc3_apps_clk[] = {
+ F( 144000, cxo, 16, 3, 25),
+ F( 400000, cxo, 12, 1, 4),
+ F( 20000000, gpll0, 15, 1, 2),
+ F( 25000000, gpll0, 12, 1, 2),
+ F( 50000000, gpll0, 12, 0, 0),
+ F(100000000, gpll0, 6, 0, 0),
+ F_END
+};
+
+static struct rcg_clk sdcc2_apps_clk_src = {
+ .cmd_rcgr_reg = SDCC2_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_sdcc2_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "sdcc2_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000),
+ CLK_INIT(sdcc2_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk sdcc3_apps_clk_src = {
+ .cmd_rcgr_reg = SDCC3_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_sdcc3_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "sdcc3_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 50000000, NOMINAL, 100000000),
+ CLK_INIT(sdcc3_apps_clk_src.c)
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_usb_hs_system_clk[] = {
+ F(75000000, gpll0, 8, 0, 0),
+ F_END
+};
+
+static struct rcg_clk usb_hs_system_clk_src = {
+ .cmd_rcgr_reg = USB_HS_SYSTEM_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_usb_hs_system_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "usb_hs_system_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP2(LOW, 37500000, NOMINAL, 75000000),
+ CLK_INIT(usb_hs_system_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_usb_hsic_clk[] = {
+ F_HSIC(480000000, gpll1, 1, 0, 0),
+ F_END
+};
+
+static struct rcg_clk usb_hsic_clk_src = {
+ .cmd_rcgr_reg = USB_HSIC_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_usb_hsic_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "usb_hsic_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP1(LOW, 480000000),
+ CLK_INIT(usb_hsic_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_usb_hsic_io_cal_clk[] = {
+ F(9600000, cxo, 2, 0, 0),
+ F_END
+};
+
+static struct rcg_clk usb_hsic_io_cal_clk_src = {
+ .cmd_rcgr_reg = USB_HSIC_IO_CAL_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_usb_hsic_io_cal_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "usb_hsic_io_cal_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP1(LOW, 9600000),
+ CLK_INIT(usb_hsic_io_cal_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_usb_hsic_system_clk[] = {
+ F(75000000, gpll0, 8, 0, 0),
+ F_END
+};
+
+static struct rcg_clk usb_hsic_system_clk_src = {
+ .cmd_rcgr_reg = USB_HSIC_SYSTEM_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_usb_hsic_system_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "usb_hsic_system_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP2(LOW, 60000000, NOMINAL, 75000000),
+ CLK_INIT(usb_hsic_system_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_usb_hsic_xcvr_fs_clk[] = {
+ F(60000000, gpll0, 10, 0, 0),
+ F_END
+};
+
+static struct rcg_clk usb_hsic_xcvr_fs_clk_src = {
+ .cmd_rcgr_reg = USB_HSIC_XCVR_FS_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_usb_hsic_xcvr_fs_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "usb_hsic_xcvr_fs_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP1(LOW, 60000000),
+ CLK_INIT(usb_hsic_xcvr_fs_clk_src.c),
+ },
+};
+
+static struct local_vote_clk gcc_bam_dma_ahb_clk = {
+ .cbcr_reg = BAM_DMA_AHB_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(12),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_bam_dma_ahb_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_bam_dma_ahb_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_blsp1_ahb_clk = {
+ .cbcr_reg = BLSP1_AHB_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(17),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_ahb_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_blsp1_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup1_i2c_apps_clk = {
+ .cbcr_reg = BLSP1_QUP1_I2C_APPS_CBCR,
+ .parent = &cxo_clk_src.c,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup1_i2c_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup1_i2c_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup1_spi_apps_clk = {
+ .cbcr_reg = BLSP1_QUP1_SPI_APPS_CBCR,
+ .parent = &blsp1_qup1_spi_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup1_spi_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup1_spi_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup2_i2c_apps_clk = {
+ .cbcr_reg = BLSP1_QUP2_I2C_APPS_CBCR,
+ .parent = &cxo_clk_src.c,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup2_i2c_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup2_i2c_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup2_spi_apps_clk = {
+ .cbcr_reg = BLSP1_QUP2_SPI_APPS_CBCR,
+ .parent = &blsp1_qup2_spi_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup2_spi_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup2_spi_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup3_i2c_apps_clk = {
+ .cbcr_reg = BLSP1_QUP3_I2C_APPS_CBCR,
+ .parent = &cxo_clk_src.c,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup3_i2c_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup3_i2c_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup3_spi_apps_clk = {
+ .cbcr_reg = BLSP1_QUP3_SPI_APPS_CBCR,
+ .parent = &blsp1_qup3_spi_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup3_spi_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup3_spi_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup4_i2c_apps_clk = {
+ .cbcr_reg = BLSP1_QUP4_I2C_APPS_CBCR,
+ .parent = &cxo_clk_src.c,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup4_i2c_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup4_i2c_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup4_spi_apps_clk = {
+ .cbcr_reg = BLSP1_QUP4_SPI_APPS_CBCR,
+ .parent = &blsp1_qup4_spi_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup4_spi_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup4_spi_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup5_i2c_apps_clk = {
+ .cbcr_reg = BLSP1_QUP5_I2C_APPS_CBCR,
+ .parent = &cxo_clk_src.c,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup5_i2c_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup5_i2c_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup5_spi_apps_clk = {
+ .cbcr_reg = BLSP1_QUP5_SPI_APPS_CBCR,
+ .parent = &blsp1_qup5_spi_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup5_spi_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup5_spi_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup6_i2c_apps_clk = {
+ .cbcr_reg = BLSP1_QUP6_I2C_APPS_CBCR,
+ .parent = &cxo_clk_src.c,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup6_i2c_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup6_i2c_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup6_spi_apps_clk = {
+ .cbcr_reg = BLSP1_QUP6_SPI_APPS_CBCR,
+ .parent = &blsp1_qup6_spi_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup6_spi_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup6_spi_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_uart1_apps_clk = {
+ .cbcr_reg = BLSP1_UART1_APPS_CBCR,
+ .parent = &blsp1_uart1_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_uart1_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_uart1_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_uart2_apps_clk = {
+ .cbcr_reg = BLSP1_UART2_APPS_CBCR,
+ .parent = &blsp1_uart2_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_uart2_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_uart2_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_uart3_apps_clk = {
+ .cbcr_reg = BLSP1_UART3_APPS_CBCR,
+ .parent = &blsp1_uart3_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_uart3_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_uart3_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_uart4_apps_clk = {
+ .cbcr_reg = BLSP1_UART4_APPS_CBCR,
+ .parent = &blsp1_uart4_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_uart4_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_uart4_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_uart5_apps_clk = {
+ .cbcr_reg = BLSP1_UART5_APPS_CBCR,
+ .parent = &blsp1_uart5_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_uart5_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_uart5_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_uart6_apps_clk = {
+ .cbcr_reg = BLSP1_UART6_APPS_CBCR,
+ .parent = &blsp1_uart6_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_uart6_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_uart6_apps_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_boot_rom_ahb_clk = {
+ .cbcr_reg = BOOT_ROM_AHB_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(10),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_boot_rom_ahb_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_boot_rom_ahb_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_ce1_ahb_clk = {
+ .cbcr_reg = CE1_AHB_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(3),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_ce1_ahb_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_ce1_ahb_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_ce1_axi_clk = {
+ .cbcr_reg = CE1_AXI_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(4),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_ce1_axi_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_ce1_axi_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_ce1_clk = {
+ .cbcr_reg = CE1_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(5),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_ce1_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_ce1_clk.c),
+ },
+};
+
+static struct branch_clk gcc_gp1_clk = {
+ .cbcr_reg = GP1_CBCR,
+ .parent = &gp1_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_gp1_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_gp1_clk.c),
+ },
+};
+
+static struct branch_clk gcc_gp2_clk = {
+ .cbcr_reg = GP2_CBCR,
+ .parent = &gp2_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_gp2_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_gp2_clk.c),
+ },
+};
+
+static struct branch_clk gcc_gp3_clk = {
+ .cbcr_reg = GP3_CBCR,
+ .parent = &gp3_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_gp3_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_gp3_clk.c),
+ },
+};
+
+static struct branch_clk gcc_ipa_clk = {
+ .cbcr_reg = IPA_CBCR,
+ .parent = &ipa_clk_src.c,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_ipa_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_ipa_clk.c),
+ },
+};
+
+static struct branch_clk gcc_ipa_cnoc_clk = {
+ .cbcr_reg = IPA_CNOC_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_ipa_cnoc_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_ipa_cnoc_clk.c),
+ },
+};
+
+static struct branch_clk gcc_pdm2_clk = {
+ .cbcr_reg = PDM2_CBCR,
+ .parent = &pdm2_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_pdm2_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_pdm2_clk.c),
+ },
+};
+
+static struct branch_clk gcc_pdm_ahb_clk = {
+ .cbcr_reg = PDM_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_pdm_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_pdm_ahb_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_prng_ahb_clk = {
+ .cbcr_reg = PRNG_AHB_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(13),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_prng_ahb_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_prng_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_qpic_ahb_clk = {
+ .cbcr_reg = QPIC_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_qpic_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_qpic_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_qpic_clk = {
+ .cbcr_reg = QPIC_CBCR,
+ .parent = &qpic_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_qpic_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_qpic_clk.c),
+ },
+};
+
+static struct branch_clk gcc_sdcc2_ahb_clk = {
+ .cbcr_reg = SDCC2_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_sdcc2_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_sdcc2_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_sdcc2_apps_clk = {
+ .cbcr_reg = SDCC2_APPS_CBCR,
+ .parent = &sdcc2_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_sdcc2_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_sdcc2_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_sdcc3_ahb_clk = {
+ .cbcr_reg = SDCC3_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_sdcc3_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_sdcc3_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_sdcc3_apps_clk = {
+ .cbcr_reg = SDCC3_APPS_CBCR,
+ .parent = &sdcc3_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_sdcc3_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_sdcc3_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_sys_noc_ipa_axi_clk = {
+ .cbcr_reg = SYS_NOC_IPA_AXI_CBCR,
+ .parent = &ipa_clk_src.c,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_sys_noc_ipa_axi_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_sys_noc_ipa_axi_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb_hs_ahb_clk = {
+ .cbcr_reg = USB_HS_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb_hs_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb_hs_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb_hs_system_clk = {
+ .cbcr_reg = USB_HS_SYSTEM_CBCR,
+ .bcr_reg = USB_HS_BCR,
+ .parent = &usb_hs_system_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb_hs_system_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb_hs_system_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb_hsic_ahb_clk = {
+ .cbcr_reg = USB_HSIC_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb_hsic_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb_hsic_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb_hsic_clk = {
+ .cbcr_reg = USB_HSIC_CBCR,
+ .parent = &usb_hsic_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb_hsic_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb_hsic_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb_hsic_io_cal_clk = {
+ .cbcr_reg = USB_HSIC_IO_CAL_CBCR,
+ .parent = &usb_hsic_io_cal_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb_hsic_io_cal_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb_hsic_io_cal_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb_hsic_system_clk = {
+ .cbcr_reg = USB_HSIC_SYSTEM_CBCR,
+ .bcr_reg = USB_HS_HSIC_BCR,
+ .parent = &usb_hsic_system_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb_hsic_system_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb_hsic_system_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb_hsic_xcvr_fs_clk = {
+ .cbcr_reg = USB_HSIC_XCVR_FS_CBCR,
+ .parent = &usb_hsic_xcvr_fs_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb_hsic_xcvr_fs_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb_hsic_xcvr_fs_clk.c),
+ },
+};
+
+/* LPASS clock data */
+static struct clk_freq_tbl ftbl_audio_core_lpaif_clock[] = {
+ F_LPASS( 512000, lpapll0, 16, 1, 48),
+ F_LPASS( 768000, lpapll0, 16, 1, 32),
+ F_LPASS( 1024000, lpapll0, 16, 1, 24),
+ F_LPASS( 1536000, lpapll0, 16, 1, 16),
+ F_LPASS( 2048000, lpapll0, 16, 1, 12),
+ F_LPASS( 3072000, lpapll0, 16, 1, 8),
+ F_LPASS( 4096000, lpapll0, 16, 1, 6),
+ F_LPASS( 6144000, lpapll0, 16, 1, 4),
+ F_LPASS( 8192000, lpapll0, 16, 1, 3),
+ F_LPASS(12288000, lpapll0, 16, 1, 2),
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_audio_core_lpaif_pcm_clock[] = {
+ F_LPASS( 512000, lpapll0, 16, 1, 48),
+ F_LPASS( 768000, lpapll0, 16, 1, 32),
+ F_LPASS( 1024000, lpapll0, 16, 1, 24),
+ F_LPASS( 1536000, lpapll0, 16, 1, 16),
+ F_LPASS( 2048000, lpapll0, 16, 1, 12),
+ F_LPASS( 3072000, lpapll0, 16, 1, 8),
+ F_LPASS( 4096000, lpapll0, 16, 1, 6),
+ F_LPASS( 6144000, lpapll0, 16, 1, 4),
+ F_LPASS( 8192000, lpapll0, 16, 1, 3),
+ F_END
+};
+
+static struct rcg_clk audio_core_lpaif_pcmoe_clk_src = {
+ .cmd_rcgr_reg = LPAIF_PCMOE_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_audio_core_lpaif_clock,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pcmoe_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP1(LOW, 12288000),
+ CLK_INIT(audio_core_lpaif_pcmoe_clk_src.c)
+ },
+};
+
+static struct rcg_clk audio_core_lpaif_pri_clk_src = {
+ .cmd_rcgr_reg = LPAIF_PRI_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_audio_core_lpaif_clock,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pri_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 12288000, NOMINAL, 24576000),
+ CLK_INIT(audio_core_lpaif_pri_clk_src.c)
+ },
+};
+
+static struct rcg_clk audio_core_lpaif_sec_clk_src = {
+ .cmd_rcgr_reg = LPAIF_SEC_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_audio_core_lpaif_clock,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_sec_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 12288000, NOMINAL, 24576000),
+ CLK_INIT(audio_core_lpaif_sec_clk_src.c)
+ },
+};
+
+static struct clk_freq_tbl ftbl_audio_core_slimbus_core_clock[] = {
+ F_LPASS(26041000, lpapll0, 1, 10, 151),
+ F_END
+};
+
+static struct rcg_clk audio_core_slimbus_core_clk_src = {
+ .cmd_rcgr_reg = SLIMBUS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_audio_core_slimbus_core_clock,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_slimbus_core_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 13107000, NOMINAL, 26214000),
+ CLK_INIT(audio_core_slimbus_core_clk_src.c)
+ },
+};
+
+static struct rcg_clk audio_core_lpaif_pcm0_clk_src = {
+ .cmd_rcgr_reg = LPAIF_PCM0_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_audio_core_lpaif_pcm_clock,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pcm0_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 4096000, NOMINAL, 8192000),
+ CLK_INIT(audio_core_lpaif_pcm0_clk_src.c)
+ },
+};
+
+static struct rcg_clk audio_core_lpaif_pcm1_clk_src = {
+ .cmd_rcgr_reg = LPAIF_PCM1_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_audio_core_lpaif_pcm_clock,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pcm1_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 4096000, NOMINAL, 8192000),
+ CLK_INIT(audio_core_lpaif_pcm1_clk_src.c)
+ },
+};
+
+static struct branch_clk audio_core_slimbus_lfabif_clk = {
+ .cbcr_reg = AUDIO_CORE_SLIMBUS_LFABIF_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_slimbus_lfabif_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_slimbus_lfabif_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_pcm_data_oe_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_PCM_DATA_OE_CBCR,
+ .parent = &audio_core_lpaif_pcmoe_clk_src.c,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pcm_data_oe_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_pcm_data_oe_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_slimbus_core_clk = {
+ .cbcr_reg = AUDIO_CORE_SLIMBUS_CORE_CBCR,
+ .parent = &audio_core_slimbus_core_clk_src.c,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_slimbus_core_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_slimbus_core_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_pri_ebit_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_PRI_EBIT_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pri_ebit_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_pri_ebit_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_pri_ibit_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_PRI_IBIT_CBCR,
+ .parent = &audio_core_lpaif_pri_clk_src.c,
+ .has_sibling = 1,
+ .max_div = 15,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pri_ibit_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_pri_ibit_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_pri_osr_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_PRI_OSR_CBCR,
+ .parent = &audio_core_lpaif_pri_clk_src.c,
+ .has_sibling = 1,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pri_osr_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_pri_osr_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_pcm0_ebit_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_PCM0_EBIT_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pcm0_ebit_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_pcm0_ebit_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_pcm0_ibit_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_PCM0_IBIT_CBCR,
+ .parent = &audio_core_lpaif_pcm0_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pcm0_ibit_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_pcm0_ibit_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_sec_ebit_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_SEC_EBIT_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_sec_ebit_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_sec_ebit_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_sec_ibit_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_SEC_IBIT_CBCR,
+ .parent = &audio_core_lpaif_sec_clk_src.c,
+ .has_sibling = 1,
+ .max_div = 15,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_sec_ibit_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_sec_ibit_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_sec_osr_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_SEC_OSR_CBCR,
+ .parent = &audio_core_lpaif_sec_clk_src.c,
+ .has_sibling = 1,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_sec_osr_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_sec_osr_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_pcm1_ebit_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_PCM1_EBIT_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pcm1_ebit_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_pcm1_ebit_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_pcm1_ibit_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_PCM1_IBIT_CBCR,
+ .parent = &audio_core_lpaif_pcm1_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pcm1_ibit_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_pcm1_ibit_clk.c),
+ },
+};
+
+static DEFINE_CLK_MEASURE(a5_m_clk);
+
+#ifdef CONFIG_DEBUG_FS
+
+struct measure_mux_entry {
+ struct clk *c;
+ int base;
+ u32 debug_mux;
+};
+
+struct measure_mux_entry measure_mux[] = {
+ {&gcc_pdm_ahb_clk.c, GCC_BASE, 0x00d0},
+ {&gcc_usb_hsic_xcvr_fs_clk.c, GCC_BASE, 0x005d},
+ {&gcc_usb_hsic_system_clk.c, GCC_BASE, 0x0059},
+ {&gcc_usb_hsic_io_cal_clk.c, GCC_BASE, 0x005b},
+ {&gcc_sdcc3_ahb_clk.c, GCC_BASE, 0x0079},
+ {&gcc_blsp1_qup5_i2c_apps_clk.c, GCC_BASE, 0x009d},
+ {&gcc_blsp1_qup1_spi_apps_clk.c, GCC_BASE, 0x008a},
+ {&gcc_blsp1_uart2_apps_clk.c, GCC_BASE, 0x0091},
+ {&gcc_blsp1_qup4_spi_apps_clk.c, GCC_BASE, 0x0098},
+ {&gcc_blsp1_qup3_spi_apps_clk.c, GCC_BASE, 0x0093},
+ {&gcc_blsp1_qup6_i2c_apps_clk.c, GCC_BASE, 0x00a2},
+ {&gcc_bam_dma_ahb_clk.c, GCC_BASE, 0x00e0},
+ {&gcc_sdcc3_apps_clk.c, GCC_BASE, 0x0078},
+ {&gcc_usb_hs_system_clk.c, GCC_BASE, 0x0060},
+ {&gcc_blsp1_ahb_clk.c, GCC_BASE, 0x0088},
+ {&gcc_blsp1_uart4_apps_clk.c, GCC_BASE, 0x009a},
+ {&gcc_blsp1_qup2_spi_apps_clk.c, GCC_BASE, 0x008e},
+ {&gcc_usb_hsic_ahb_clk.c, GCC_BASE, 0x0058},
+ {&gcc_blsp1_uart3_apps_clk.c, GCC_BASE, 0x0095},
+ {&gcc_ce1_axi_clk.c, GCC_BASE, 0x0139},
+ {&gcc_blsp1_qup5_spi_apps_clk.c, GCC_BASE, 0x009c},
+ {&gcc_usb_hs_ahb_clk.c, GCC_BASE, 0x0061},
+ {&gcc_blsp1_qup6_spi_apps_clk.c, GCC_BASE, 0x00a1},
+ {&gcc_prng_ahb_clk.c, GCC_BASE, 0x00d8},
+ {&gcc_blsp1_qup3_i2c_apps_clk.c, GCC_BASE, 0x0094},
+ {&gcc_usb_hsic_clk.c, GCC_BASE, 0x005a},
+ {&gcc_blsp1_uart6_apps_clk.c, GCC_BASE, 0x00a3},
+ {&gcc_sdcc2_apps_clk.c, GCC_BASE, 0x0070},
+ {&gcc_blsp1_uart1_apps_clk.c, GCC_BASE, 0x008c},
+ {&gcc_blsp1_qup4_i2c_apps_clk.c, GCC_BASE, 0x0099},
+ {&gcc_boot_rom_ahb_clk.c, GCC_BASE, 0x00f8},
+ {&gcc_ce1_ahb_clk.c, GCC_BASE, 0x013a},
+ {&gcc_pdm2_clk.c, GCC_BASE, 0x00d2},
+ {&gcc_blsp1_uart5_apps_clk.c, GCC_BASE, 0x009e},
+ {&gcc_blsp1_qup2_i2c_apps_clk.c, GCC_BASE, 0x0090},
+ {&gcc_blsp1_qup1_i2c_apps_clk.c, GCC_BASE, 0x008b},
+ {&gcc_sdcc2_ahb_clk.c, GCC_BASE, 0x0071},
+ {&gcc_ce1_clk.c, GCC_BASE, 0x0138},
+ {&gcc_sys_noc_ipa_axi_clk.c, GCC_BASE, 0x0007},
+
+ {&audio_core_lpaif_pcm_data_oe_clk.c, LPASS_BASE, 0x0030},
+ {&audio_core_slimbus_core_clk.c, LPASS_BASE, 0x003d},
+ {&audio_core_lpaif_pri_clk_src.c, LPASS_BASE, 0x0017},
+ {&audio_core_lpaif_sec_clk_src.c, LPASS_BASE, 0x0016},
+ {&audio_core_slimbus_core_clk_src.c, LPASS_BASE, 0x0011},
+ {&audio_core_lpaif_pcm1_clk_src.c, LPASS_BASE, 0x0012},
+ {&audio_core_lpaif_pcm0_clk_src.c, LPASS_BASE, 0x0013},
+ {&audio_core_lpaif_pcmoe_clk_src.c, LPASS_BASE, 0x000f},
+ {&audio_core_slimbus_lfabif_clk.c, LPASS_BASE, 0x003e},
+
+ {&a5_m_clk, APCS_BASE, 0x3},
+
+ {&dummy_clk, N_BASES, 0x0000},
+};
+
+static int measure_clk_set_parent(struct clk *c, struct clk *parent)
+{
+ struct measure_clk *clk = to_measure_clk(c);
+ unsigned long flags;
+ u32 regval, clk_sel, i;
+
+ if (!parent)
+ return -EINVAL;
+
+ for (i = 0; i < (ARRAY_SIZE(measure_mux) - 1); i++)
+ if (measure_mux[i].c == parent)
+ break;
+
+ if (measure_mux[i].c == &dummy_clk)
+ return -EINVAL;
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ /*
+ * Program the test vector, measurement period (sample_ticks)
+ * and scaling multiplier.
+ */
+ clk->sample_ticks = 0x10000;
+ clk->multiplier = 1;
+
+ writel_relaxed(0, LPASS_REG_BASE(LPASS_DEBUG_CLK_CTL_REG));
+ writel_relaxed(0, GCC_REG_BASE(GCC_DEBUG_CLK_CTL_REG));
+
+ switch (measure_mux[i].base) {
+
+ case GCC_BASE:
+ clk_sel = measure_mux[i].debug_mux;
+ break;
+
+ case LPASS_BASE:
+ clk_sel = 0x161;
+ regval = BVAL(15, 0, measure_mux[i].debug_mux);
+ writel_relaxed(regval, LPASS_REG_BASE(LPASS_DEBUG_CLK_CTL_REG));
+
+ /* Activate debug clock output */
+ regval |= BIT(20);
+ writel_relaxed(regval, LPASS_REG_BASE(LPASS_DEBUG_CLK_CTL_REG));
+ break;
+
+ case APCS_BASE:
+ clk_sel = 0x16A;
+ regval = BVAL(5, 3, measure_mux[i].debug_mux);
+ writel_relaxed(regval, APCS_REG_BASE(APCS_CLK_DIAG_REG));
+
+ /* Activate debug clock output */
+ regval |= BIT(7);
+ writel_relaxed(regval, APCS_REG_BASE(APCS_CLK_DIAG_REG));
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* Set debug mux clock index */
+ regval = BVAL(8, 0, clk_sel);
+ writel_relaxed(regval, GCC_REG_BASE(GCC_DEBUG_CLK_CTL_REG));
+
+ /* Activate debug clock output */
+ regval |= BIT(16);
+ writel_relaxed(regval, GCC_REG_BASE(GCC_DEBUG_CLK_CTL_REG));
+
+ /* Make sure test vector is set before starting measurements. */
+ mb();
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+
+ return 0;
+}
+
+/* Sample clock for 'ticks' reference clock ticks. */
+static u32 run_measurement(unsigned ticks)
+{
+ /* Stop counters and set the XO4 counter start value. */
+ writel_relaxed(ticks, GCC_REG_BASE(CLOCK_FRQ_MEASURE_CTL_REG));
+
+ /* Wait for timer to become ready. */
+ while ((readl_relaxed(GCC_REG_BASE(CLOCK_FRQ_MEASURE_STATUS_REG)) &
+ BIT(25)) != 0)
+ cpu_relax();
+
+ /* Run measurement and wait for completion. */
+ writel_relaxed(BIT(20)|ticks, GCC_REG_BASE(CLOCK_FRQ_MEASURE_CTL_REG));
+ while ((readl_relaxed(GCC_REG_BASE(CLOCK_FRQ_MEASURE_STATUS_REG)) &
+ BIT(25)) == 0)
+ cpu_relax();
+
+ /* Return measured ticks. */
+ return readl_relaxed(GCC_REG_BASE(CLOCK_FRQ_MEASURE_STATUS_REG)) &
+ BM(24, 0);
+}
+
+/*
+ * Perform a hardware rate measurement for a given clock.
+ * FOR DEBUG USE ONLY: Measurements take ~15 ms!
+ */
+static unsigned long measure_clk_get_rate(struct clk *c)
+{
+ unsigned long flags;
+ u32 gcc_xo4_reg_backup;
+ u64 raw_count_short, raw_count_full;
+ struct measure_clk *clk = to_measure_clk(c);
+ unsigned ret;
+
+ ret = clk_prepare_enable(&cxo_clk_src.c);
+ if (ret) {
+ pr_warning("CXO clock failed to enable. Can't measure\n");
+ return 0;
+ }
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+
+ /* Enable CXO/4 and RINGOSC branch. */
+ gcc_xo4_reg_backup = readl_relaxed(GCC_REG_BASE(GCC_XO_DIV4_CBCR_REG));
+ writel_relaxed(0x1, GCC_REG_BASE(GCC_XO_DIV4_CBCR_REG));
+
+ /*
+ * The ring oscillator counter will not reset if the measured clock
+ * is not running. To detect this, run a short measurement before
+ * the full measurement. If the raw results of the two are the same
+ * then the clock must be off.
+ */
+
+ /* Run a short measurement. (~1 ms) */
+ raw_count_short = run_measurement(0x1000);
+ /* Run a full measurement. (~14 ms) */
+ raw_count_full = run_measurement(clk->sample_ticks);
+
+ writel_relaxed(gcc_xo4_reg_backup, GCC_REG_BASE(GCC_XO_DIV4_CBCR_REG));
+
+ /* Return 0 if the clock is off. */
+ if (raw_count_full == raw_count_short) {
+ ret = 0;
+ } else {
+ /* Compute rate in Hz. */
+ raw_count_full = ((raw_count_full * 10) + 15) * 4800000;
+ do_div(raw_count_full, ((clk->sample_ticks * 10) + 35));
+ ret = (raw_count_full * clk->multiplier);
+ }
+
+ writel_relaxed(0x51A00, GCC_REG_BASE(GCC_PLLTEST_PAD_CFG_REG));
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+
+ clk_disable_unprepare(&cxo_clk_src.c);
+
+ return ret;
+}
+#else /* !CONFIG_DEBUG_FS */
+static int measure_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ return -EINVAL;
+}
+
+static unsigned long measure_clk_get_rate(struct clk *clk)
+{
+ return 0;
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static struct clk_ops clk_ops_measure = {
+ .set_parent = measure_clk_set_parent,
+ .get_rate = measure_clk_get_rate,
+};
+
+static struct measure_clk measure_clk = {
+ .c = {
+ .dbg_name = "measure_clk",
+ .ops = &clk_ops_measure,
+ CLK_INIT(measure_clk.c),
+ },
+ .multiplier = 1,
+};
+
+static struct clk_lookup msm_clocks_9625[] = {
+ CLK_LOOKUP("xo", cxo_clk_src.c, ""),
+ CLK_LOOKUP("measure", measure_clk.c, "debug"),
+
+ CLK_LOOKUP("pll0", gpll0_clk_src.c, "f9010008.qcom,acpuclk"),
+ CLK_LOOKUP("pll14", apcspll_clk_src.c, "f9010008.qcom,acpuclk"),
+
+ CLK_LOOKUP("dma_bam_pclk", gcc_bam_dma_ahb_clk.c, "msm_sps"),
+ CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "msm_serial_hsl.0"),
+ CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "spi_qsd.1"),
+ CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "f9925000.i2c"),
+ CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup1_i2c_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup1_spi_apps_clk.c, "spi_qsd.1"),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup2_i2c_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup2_spi_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup3_i2c_apps_clk.c, "f9925000.i2c"),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup3_spi_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup4_i2c_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup4_spi_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup5_i2c_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup5_spi_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup6_i2c_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup6_spi_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_uart1_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_uart2_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_uart3_apps_clk.c, "msm_serial_hsl.0"),
+ CLK_LOOKUP("core_clk", gcc_blsp1_uart4_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_uart5_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_uart6_apps_clk.c, ""),
+
+ CLK_LOOKUP("core_clk_src", ce1_clk_src.c, ""),
+ CLK_LOOKUP("core_clk", gcc_ce1_clk.c, ""),
+ CLK_LOOKUP("iface_clk", gcc_ce1_ahb_clk.c, ""),
+ CLK_LOOKUP("bus_clk", gcc_ce1_axi_clk.c, ""),
+
+ CLK_LOOKUP("core_clk", gcc_gp1_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_gp2_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_gp3_clk.c, ""),
+
+ CLK_LOOKUP("core_src_clk", ipa_clk_src.c, "fd4c0000.qcom,ipa"),
+ CLK_LOOKUP("core_clk", gcc_ipa_clk.c, "fd4c0000.qcom,ipa"),
+ CLK_LOOKUP("bus_clk", gcc_sys_noc_ipa_axi_clk.c, "fd4c0000.qcom,ipa"),
+ CLK_LOOKUP("iface_clk", gcc_ipa_cnoc_clk.c, "fd4c0000.qcom,ipa"),
+
+ CLK_LOOKUP("core_clk", gcc_pdm2_clk.c, ""),
+ CLK_LOOKUP("iface_clk", gcc_pdm_ahb_clk.c, ""),
+
+ CLK_LOOKUP("iface_clk", gcc_sdcc2_ahb_clk.c, "f98a4000.qcom,sdcc"),
+ CLK_LOOKUP("core_clk", gcc_sdcc2_apps_clk.c, "f98a4000.qcom,sdcc"),
+ CLK_LOOKUP("bus_clk", pnoc_sdcc2_clk.c, "f98a4000.qcom,sdcc"),
+ CLK_LOOKUP("iface_clk", gcc_sdcc3_ahb_clk.c, "f9864000.qcom,sdcc"),
+ CLK_LOOKUP("core_clk", gcc_sdcc3_apps_clk.c, "f9864000.qcom,sdcc"),
+ CLK_LOOKUP("bus_clk", pnoc_sdcc3_clk.c, "f9864000.qcom,sdcc"),
+
+ CLK_LOOKUP("iface_clk", gcc_usb_hs_ahb_clk.c, "f9a55000.usb"),
+ CLK_LOOKUP("core_clk", gcc_usb_hs_system_clk.c, "f9a55000.usb"),
+ CLK_LOOKUP("iface_clk", gcc_usb_hsic_ahb_clk.c, "f9a15000.hsic"),
+ CLK_LOOKUP("phy_clk", gcc_usb_hsic_clk.c, "f9a15000.hsic"),
+ CLK_LOOKUP("cal_clk", gcc_usb_hsic_io_cal_clk.c, "f9a15000.hsic"),
+ CLK_LOOKUP("core_clk", gcc_usb_hsic_system_clk.c, "f9a15000.hsic"),
+ CLK_LOOKUP("alt_core_clk", gcc_usb_hsic_xcvr_fs_clk.c,
+ "f9a15000.hsic"),
+
+ /* LPASS clocks */
+ CLK_LOOKUP("core_clk", audio_core_slimbus_core_clk.c, "fe12f000.slim"),
+ CLK_LOOKUP("iface_clk", audio_core_slimbus_lfabif_clk.c, ""),
+ CLK_LOOKUP("core_clk", audio_core_lpaif_pri_clk_src.c, ""),
+ CLK_LOOKUP("osr_clk", audio_core_lpaif_pri_osr_clk.c, ""),
+ CLK_LOOKUP("ebit_clk", audio_core_lpaif_pri_ebit_clk.c, ""),
+ CLK_LOOKUP("ibit_clk", audio_core_lpaif_pri_ibit_clk.c, ""),
+ CLK_LOOKUP("core_clk", audio_core_lpaif_sec_clk_src.c, ""),
+ CLK_LOOKUP("osr_clk", audio_core_lpaif_sec_osr_clk.c, ""),
+ CLK_LOOKUP("ebit_clk", audio_core_lpaif_sec_ebit_clk.c, ""),
+ CLK_LOOKUP("ibit_clk", audio_core_lpaif_sec_ibit_clk.c, ""),
+ CLK_LOOKUP("core_clk", audio_core_lpaif_pcm0_clk_src.c, ""),
+ CLK_LOOKUP("ebit_clk", audio_core_lpaif_pcm0_ebit_clk.c, ""),
+ CLK_LOOKUP("ibit_clk", audio_core_lpaif_pcm0_ibit_clk.c, ""),
+ CLK_LOOKUP("core_clk", audio_core_lpaif_pcm1_clk_src.c, ""),
+ CLK_LOOKUP("ebit_clk", audio_core_lpaif_pcm1_ebit_clk.c, ""),
+ CLK_LOOKUP("ibit_clk", audio_core_lpaif_pcm1_ibit_clk.c, ""),
+ CLK_LOOKUP("core_oe_src_clk", audio_core_lpaif_pcmoe_clk_src.c, ""),
+ CLK_LOOKUP("core_oe_clk", audio_core_lpaif_pcm_data_oe_clk.c, ""),
+
+ /* RPM and voter clocks */
+ CLK_LOOKUP("bus_clk", snoc_clk.c, ""),
+ CLK_LOOKUP("bus_clk", pnoc_clk.c, ""),
+ CLK_LOOKUP("bus_clk", cnoc_clk.c, ""),
+ CLK_LOOKUP("mem_clk", bimc_clk.c, ""),
+ CLK_LOOKUP("bus_clk", snoc_a_clk.c, ""),
+ CLK_LOOKUP("bus_clk", pnoc_a_clk.c, ""),
+ CLK_LOOKUP("bus_clk", cnoc_a_clk.c, ""),
+ CLK_LOOKUP("mem_clk", bimc_a_clk.c, ""),
+
+ CLK_LOOKUP("bus_clk", cnoc_msmbus_clk.c, "msm_config_noc"),
+ CLK_LOOKUP("bus_a_clk", cnoc_msmbus_a_clk.c, "msm_config_noc"),
+ CLK_LOOKUP("bus_clk", snoc_msmbus_clk.c, "msm_sys_noc"),
+ CLK_LOOKUP("bus_a_clk", snoc_msmbus_a_clk.c, "msm_sys_noc"),
+ CLK_LOOKUP("bus_clk", pnoc_msmbus_clk.c, "msm_periph_noc"),
+ CLK_LOOKUP("bus_a_clk", pnoc_msmbus_a_clk.c, "msm_periph_noc"),
+ CLK_LOOKUP("mem_clk", bimc_msmbus_clk.c, "msm_bimc"),
+ CLK_LOOKUP("mem_a_clk", bimc_msmbus_a_clk.c, "msm_bimc"),
+
+ CLK_LOOKUP("dfab_clk", pnoc_sps_clk.c, "msm_sps"),
+
+ CLK_LOOKUP("a5_m_clk", a5_m_clk, ""),
+};
+
+static struct pll_config_regs gpll0_regs __initdata = {
+ .l_reg = (void __iomem *)GPLL0_L_REG,
+ .m_reg = (void __iomem *)GPLL0_M_REG,
+ .n_reg = (void __iomem *)GPLL0_N_REG,
+ .config_reg = (void __iomem *)GPLL0_USER_CTL_REG,
+ .mode_reg = (void __iomem *)GPLL0_MODE_REG,
+ .base = &virt_bases[GCC_BASE],
+};
+
+/* GPLL0 at 600 MHz, main output enabled. */
+static struct pll_config gpll0_config __initdata = {
+ .l = 0x1f,
+ .m = 0x1,
+ .n = 0x4,
+ .vco_val = 0x0,
+ .vco_mask = BM(21, 20),
+ .pre_div_val = 0x0,
+ .pre_div_mask = BM(14, 12),
+ .post_div_val = 0x0,
+ .post_div_mask = BM(9, 8),
+ .mn_ena_val = BIT(24),
+ .mn_ena_mask = BIT(24),
+ .main_output_val = BIT(0),
+ .main_output_mask = BIT(0),
+};
+
+static struct pll_config_regs gpll1_regs __initdata = {
+ .l_reg = (void __iomem *)GPLL1_L_REG,
+ .m_reg = (void __iomem *)GPLL1_M_REG,
+ .n_reg = (void __iomem *)GPLL1_N_REG,
+ .config_reg = (void __iomem *)GPLL1_USER_CTL_REG,
+ .mode_reg = (void __iomem *)GPLL1_MODE_REG,
+ .base = &virt_bases[GCC_BASE],
+};
+
+/* GPLL1 at 480 MHz, main output enabled. */
+static struct pll_config gpll1_config __initdata = {
+ .l = 0x19,
+ .m = 0x0,
+ .n = 0x1,
+ .vco_val = 0x0,
+ .vco_mask = BM(21, 20),
+ .pre_div_val = 0x0,
+ .pre_div_mask = BM(14, 12),
+ .post_div_val = 0x0,
+ .post_div_mask = BM(9, 8),
+ .main_output_val = BIT(0),
+ .main_output_mask = BIT(0),
+};
+
+static struct pll_config_regs lpapll0_regs __initdata = {
+ .l_reg = (void __iomem *)LPAPLL_L_REG,
+ .m_reg = (void __iomem *)LPAPLL_M_REG,
+ .n_reg = (void __iomem *)LPAPLL_N_REG,
+ .config_reg = (void __iomem *)LPAPLL_USER_CTL_REG,
+ .mode_reg = (void __iomem *)LPAPLL_MODE_REG,
+ .base = &virt_bases[LPASS_BASE],
+};
+
+/* LPAPLL0 at 393.216 MHz, main output enabled. */
+static struct pll_config lpapll0_config __initdata = {
+ .l = 0x28,
+ .m = 0x18,
+ .n = 0x19,
+ .vco_val = 0x0,
+ .vco_mask = BM(21, 20),
+ .pre_div_val = 0x0,
+ .pre_div_mask = BM(14, 12),
+ .post_div_val = BVAL(9, 8, 0x1),
+ .post_div_mask = BM(9, 8),
+ .mn_ena_val = BIT(24),
+ .mn_ena_mask = BIT(24),
+ .main_output_val = BIT(0),
+ .main_output_mask = BIT(0),
+};
+
+static struct pll_config_regs apcspll_regs __initdata = {
+ .l_reg = (void __iomem *)APCS_CPU_PLL_L_REG,
+ .m_reg = (void __iomem *)APCS_CPU_PLL_M_REG,
+ .n_reg = (void __iomem *)APCS_CPU_PLL_N_REG,
+ .config_reg = (void __iomem *)APCS_CPU_PLL_USER_CTL_REG,
+ .mode_reg = (void __iomem *)APCS_CPU_PLL_MODE_REG,
+ .base = &virt_bases[APCS_PLL_BASE],
+};
+
+/* A5PLL with 998.4MHz */
+static struct pll_config apcspll_config __initdata = {
+ .l = 0x34,
+ .m = 0x0,
+ .n = 0x1,
+ .vco_val = 0x0,
+ .vco_mask = BM(21, 20),
+ .pre_div_val = 0x0,
+ .pre_div_mask = BM(14, 12),
+ .post_div_val = BVAL(9, 8, 0x0),
+ .post_div_mask = BM(9, 8),
+ .mn_ena_val = BIT(24),
+ .mn_ena_mask = BIT(24),
+ .main_output_val = BIT(0),
+ .main_output_mask = BIT(0),
+};
+
+#define PLL_AUX_OUTPUT_BIT 1
+#define PLL_AUX2_OUTPUT_BIT 2
+
+/*
+ * TODO: Need to remove this function when the v2 hardware
+ * fix the broken lock status bit.
+ */
+#define PLL_OUTCTRL BIT(0)
+#define PLL_BYPASSNL BIT(1)
+#define PLL_RESET_N BIT(2)
+
+static DEFINE_SPINLOCK(sr_pll_reg_lock);
+
+static int sr_pll_clk_enable_9625(struct clk *c)
+{
+ unsigned long flags;
+ struct pll_clk *pll = to_pll_clk(c);
+ u32 mode;
+ void __iomem *mode_reg = *pll->base + (u32)pll->mode_reg;
+
+ spin_lock_irqsave(&sr_pll_reg_lock, flags);
+
+ /* Disable PLL bypass mode and de-assert reset. */
+ mode = readl_relaxed(mode_reg);
+ mode |= PLL_BYPASSNL | PLL_RESET_N;
+ writel_relaxed(mode, mode_reg);
+
+ /* Wait for pll to lock. */
+ udelay(100);
+
+ /* Enable PLL output. */
+ mode |= PLL_OUTCTRL;
+ writel_relaxed(mode, mode_reg);
+
+ /* Ensure the write above goes through before returning. */
+ mb();
+
+ spin_unlock_irqrestore(&sr_pll_reg_lock, flags);
+ return 0;
+}
+
+static void __init configure_apcs_pll(void)
+{
+ u32 regval;
+
+ configure_sr_hpm_lp_pll(&apcspll_config, &apcspll_regs, 0);
+ writel_relaxed(0x00141200,
+ APCS_PLL_REG_BASE(APCS_CPU_PLL_CONFIG_CTL_REG));
+ regval = readl_relaxed(APCS_PLL_REG_BASE(APCS_CPU_PLL_USER_CTL_REG));
+ regval |= BIT(PLL_AUX_OUTPUT_BIT) | BIT(PLL_AUX2_OUTPUT_BIT);
+ writel_relaxed(regval, APCS_PLL_REG_BASE(APCS_CPU_PLL_USER_CTL_REG));
+}
+
+#define PWR_ON_MASK BIT(31)
+#define EN_REST_WAIT_MASK (0xF << 20)
+#define EN_FEW_WAIT_MASK (0xF << 16)
+#define CLK_DIS_WAIT_MASK (0xF << 12)
+#define SW_OVERRIDE_MASK BIT(2)
+#define HW_CONTROL_MASK BIT(1)
+#define SW_COLLAPSE_MASK BIT(0)
+
+/* Wait 2^n CXO cycles between all states. Here, n=2 (4 cycles). */
+#define EN_REST_WAIT_VAL (0x2 << 20)
+#define EN_FEW_WAIT_VAL (0x2 << 16)
+#define CLK_DIS_WAIT_VAL (0x2 << 12)
+#define GDSC_TIMEOUT_US 50000
+
+static void __init reg_init(void)
+{
+ u32 regval, status;
+ int ret;
+
+ if (!(readl_relaxed(GCC_REG_BASE(GPLL0_STATUS_REG))
+ & gpll0_clk_src.status_mask))
+ configure_sr_hpm_lp_pll(&gpll0_config, &gpll0_regs, 1);
+
+ if (!(readl_relaxed(GCC_REG_BASE(GPLL1_STATUS_REG))
+ & gpll1_clk_src.status_mask))
+ configure_sr_hpm_lp_pll(&gpll1_config, &gpll1_regs, 1);
+
+ configure_sr_hpm_lp_pll(&lpapll0_config, &lpapll0_regs, 1);
+
+ /* TODO: Remove A5 pll configuration once the bootloader is avaiable */
+ regval = readl_relaxed(APCS_PLL_REG_BASE(APCS_CPU_PLL_MODE_REG));
+ if ((regval & BM(2, 0)) != 0x7)
+ configure_apcs_pll();
+
+ /* TODO:
+ * 1) do we need to turn on AUX2 output too?
+ * 2) if need to vote off all sleep clocks
+ */
+
+ /* Enable GPLL0's aux outputs. */
+ regval = readl_relaxed(GCC_REG_BASE(GPLL0_USER_CTL_REG));
+ regval |= BIT(PLL_AUX_OUTPUT_BIT) | BIT(PLL_AUX2_OUTPUT_BIT);
+ writel_relaxed(regval, GCC_REG_BASE(GPLL0_USER_CTL_REG));
+
+ /* Vote for GPLL0 to turn on. Needed by acpuclock. */
+ regval = readl_relaxed(GCC_REG_BASE(APCS_GPLL_ENA_VOTE_REG));
+ regval |= BIT(0);
+ writel_relaxed(regval, GCC_REG_BASE(APCS_GPLL_ENA_VOTE_REG));
+
+ /*
+ * TODO: Confirm that no clocks need to be voted on in this sleep vote
+ * register.
+ */
+ writel_relaxed(0x0, GCC_REG_BASE(APCS_CLOCK_SLEEP_ENA_VOTE));
+
+ /*
+ * TODO: The following sequence enables the LPASS audio core GDSC.
+ * Remove when this becomes unnecessary.
+ */
+
+ /*
+ * Disable HW trigger: collapse/restore occur based on registers writes.
+ * Disable SW override: Use hardware state-machine for sequencing.
+ */
+ regval = readl_relaxed(LPASS_REG_BASE(AUDIO_CORE_GDSCR));
+ regval &= ~(HW_CONTROL_MASK | SW_OVERRIDE_MASK);
+
+ /* Configure wait time between states. */
+ regval &= ~(EN_REST_WAIT_MASK | EN_FEW_WAIT_MASK | CLK_DIS_WAIT_MASK);
+ regval |= EN_REST_WAIT_VAL | EN_FEW_WAIT_VAL | CLK_DIS_WAIT_VAL;
+ writel_relaxed(regval, LPASS_REG_BASE(AUDIO_CORE_GDSCR));
+
+ regval = readl_relaxed(LPASS_REG_BASE(AUDIO_CORE_GDSCR));
+ regval &= ~BIT(0);
+ writel_relaxed(regval, LPASS_REG_BASE(AUDIO_CORE_GDSCR));
+
+ ret = readl_poll_timeout(LPASS_REG_BASE(AUDIO_CORE_GDSCR), status,
+ status & PWR_ON_MASK, 50, GDSC_TIMEOUT_US);
+ WARN(ret, "LPASS Audio Core GDSC did not power on.\n");
+}
+
+static void __init msm9625_clock_post_init(void)
+{
+ /*
+ * Hold an active set vote for CXO; this is because CXO is expected
+ * to remain on whenever CPUs aren't power collapsed.
+ */
+ clk_prepare_enable(&cxo_a_clk_src.c);
+
+ /*
+ * TODO: This call is to prevent sending 0Hz to rpm to turn off pnoc.
+ * Needs to remove this after vote of pnoc from sdcc driver is ready.
+ */
+ clk_prepare_enable(&pnoc_msmbus_a_clk.c);
+
+ /* Set rates for single-rate clocks. */
+ clk_set_rate(&usb_hs_system_clk_src.c,
+ usb_hs_system_clk_src.freq_tbl[0].freq_hz);
+ clk_set_rate(&usb_hsic_clk_src.c,
+ usb_hsic_clk_src.freq_tbl[0].freq_hz);
+ clk_set_rate(&usb_hsic_io_cal_clk_src.c,
+ usb_hsic_io_cal_clk_src.freq_tbl[0].freq_hz);
+ clk_set_rate(&usb_hsic_system_clk_src.c,
+ usb_hsic_system_clk_src.freq_tbl[0].freq_hz);
+ clk_set_rate(&usb_hsic_xcvr_fs_clk_src.c,
+ usb_hsic_xcvr_fs_clk_src.freq_tbl[0].freq_hz);
+ clk_set_rate(&pdm2_clk_src.c, pdm2_clk_src.freq_tbl[0].freq_hz);
+ clk_set_rate(&audio_core_slimbus_core_clk_src.c,
+ audio_core_slimbus_core_clk_src.freq_tbl[0].freq_hz);
+}
+
+#define GCC_CC_PHYS 0xFC400000
+#define GCC_CC_SIZE SZ_16K
+
+#define LPASS_CC_PHYS 0xFE000000
+#define LPASS_CC_SIZE SZ_256K
+
+#define APCS_GCC_CC_PHYS 0xF9011000
+#define APCS_GCC_CC_SIZE SZ_4K
+
+#define APCS_PLL_PHYS 0xF9008018
+#define APCS_PLL_SIZE 0x18
+
+static void __init msm9625_clock_pre_init(void)
+{
+ virt_bases[GCC_BASE] = ioremap(GCC_CC_PHYS, GCC_CC_SIZE);
+ if (!virt_bases[GCC_BASE])
+ panic("clock-9625: Unable to ioremap GCC memory!");
+
+ virt_bases[LPASS_BASE] = ioremap(LPASS_CC_PHYS, LPASS_CC_SIZE);
+ if (!virt_bases[LPASS_BASE])
+ panic("clock-9625: Unable to ioremap LPASS_CC memory!");
+
+ virt_bases[APCS_BASE] = ioremap(APCS_GCC_CC_PHYS, APCS_GCC_CC_SIZE);
+ if (!virt_bases[APCS_BASE])
+ panic("clock-9625: Unable to ioremap APCS_GCC_CC memory!");
+
+ virt_bases[APCS_PLL_BASE] = ioremap(APCS_PLL_PHYS, APCS_PLL_SIZE);
+ if (!virt_bases[APCS_PLL_BASE])
+ panic("clock-9625: Unable to ioremap APCS_PLL memory!");
+
+ clk_ops_local_pll.enable = sr_pll_clk_enable_9625;
+
+ vdd_dig_reg = regulator_get(NULL, "vdd_dig");
+ if (IS_ERR(vdd_dig_reg))
+ panic("clock-9625: Unable to get the vdd_dig regulator!");
+
+ vote_vdd_level(&vdd_dig, VDD_DIG_HIGH);
+ regulator_enable(vdd_dig_reg);
+
+ enable_rpm_scaling();
+
+ reg_init();
+}
+
+static int __init msm9625_clock_late_init(void)
+{
+ return unvote_vdd_level(&vdd_dig, VDD_DIG_HIGH);
+}
+
+struct clock_init_data msm9625_clock_init_data __initdata = {
+ .table = msm_clocks_9625,
+ .size = ARRAY_SIZE(msm_clocks_9625),
+ .pre_init = msm9625_clock_pre_init,
+ .post_init = msm9625_clock_post_init,
+ .late_init = msm9625_clock_late_init,
+};
diff --git a/arch/arm/mach-msm/clock-rpm.c b/arch/arm/mach-msm/clock-rpm.c
index e06eb4b..daf83e2 100644
--- a/arch/arm/mach-msm/clock-rpm.c
+++ b/arch/arm/mach-msm/clock-rpm.c
@@ -297,6 +297,27 @@
return HANDOFF_ENABLED_CLK;
}
+#define RPM_MISC_CLK_TYPE 0x306b6c63
+#define RPM_SCALING_ENABLE_ID 0x2
+
+void enable_rpm_scaling(void)
+{
+ int rc, value = 0x1;
+ struct msm_rpm_kvp kvp = {
+ .key = RPM_SMD_KEY_ENABLE,
+ .data = (void *)&value,
+ .length = sizeof(value),
+ };
+
+ rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_SLEEP_SET,
+ RPM_MISC_CLK_TYPE, RPM_SCALING_ENABLE_ID, &kvp, 1);
+ WARN(rc < 0, "RPM clock scaling (sleep set) did not enable!\n");
+
+ rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_ACTIVE_SET,
+ RPM_MISC_CLK_TYPE, RPM_SCALING_ENABLE_ID, &kvp, 1);
+ WARN(rc < 0, "RPM clock scaling (active set) did not enable!\n");
+}
+
struct clk_ops clk_ops_rpm = {
.enable = rpm_clk_enable,
.disable = rpm_clk_disable,
diff --git a/arch/arm/mach-msm/clock-rpm.h b/arch/arm/mach-msm/clock-rpm.h
index 2f0b729..252e8cb 100644
--- a/arch/arm/mach-msm/clock-rpm.h
+++ b/arch/arm/mach-msm/clock-rpm.h
@@ -54,6 +54,12 @@
return container_of(clk, struct rpm_clk, c);
}
+/*
+ * RPM scaling enable function used for target that has an RPM resource for
+ * rpm clock scaling enable.
+ */
+void enable_rpm_scaling(void);
+
extern struct clk_rpmrs_data clk_rpmrs_data;
extern struct clk_rpmrs_data clk_rpmrs_data_smd;
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index c30bd79..1f12bc7 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -425,6 +425,19 @@
static struct clock_init_data *clk_init_data;
+static void init_sibling_lists(struct clk_lookup *clock_tbl, size_t num_clocks)
+{
+ struct clk *clk, *parent;
+ unsigned n;
+
+ for (n = 0; n < num_clocks; n++) {
+ clk = clock_tbl[n].clk;
+ parent = clk_get_parent(clk);
+ if (parent && list_empty(&clk->siblings))
+ list_add(&clk->siblings, &parent->children);
+ }
+}
+
/**
* msm_clock_register() - Register additional clock tables
* @table: Table of clocks
@@ -443,6 +456,7 @@
if (!table)
return -EINVAL;
+ init_sibling_lists(table, size);
clkdev_add_table(table, size);
clock_debug_register(table, size);
@@ -514,11 +528,12 @@
unsigned n;
struct clk_lookup *clock_tbl;
size_t num_clocks;
- struct clk *clk;
if (!data)
return -EINVAL;
+ clock_debug_init();
+
clk_init_data = data;
if (clk_init_data->pre_init)
clk_init_data->pre_init();
@@ -526,14 +541,6 @@
clock_tbl = data->table;
num_clocks = data->size;
- for (n = 0; n < num_clocks; n++) {
- struct clk *parent;
- clk = clock_tbl[n].clk;
- parent = clk_get_parent(clk);
- if (parent && list_empty(&clk->siblings))
- list_add(&clk->siblings, &parent->children);
- }
-
/*
* Detect and preserve initial clock state until clock_late_init() or
* a driver explicitly changes it, whichever is first.
@@ -541,14 +548,11 @@
for (n = 0; n < num_clocks; n++)
__handoff_clk(clock_tbl[n].clk);
- clkdev_add_table(clock_tbl, num_clocks);
+ msm_clock_register(clock_tbl, num_clocks);
if (clk_init_data->post_init)
clk_init_data->post_init();
- clock_debug_init();
- clock_debug_register(clock_tbl, num_clocks);
-
return 0;
}
diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h
index 48f897b..8a75d390 100644
--- a/arch/arm/mach-msm/clock.h
+++ b/arch/arm/mach-msm/clock.h
@@ -34,6 +34,7 @@
};
extern struct clock_init_data msm9615_clock_init_data;
+extern struct clock_init_data msm9625_clock_init_data;
extern struct clock_init_data apq8064_clock_init_data;
extern struct clock_init_data fsm9xxx_clock_init_data;
extern struct clock_init_data msm7x01a_clock_init_data;
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index a49a145..d727c54 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -2404,19 +2404,6 @@
/* Sensors DSPS platform data */
-#define PPSS_DSPS_TCM_CODE_BASE 0x12000000
-#define PPSS_DSPS_TCM_CODE_SIZE 0x28000
-#define PPSS_DSPS_TCM_BUF_BASE 0x12040000
-#define PPSS_DSPS_TCM_BUF_SIZE 0x4000
-#define PPSS_DSPS_PIPE_BASE 0x12800000
-#define PPSS_DSPS_PIPE_SIZE 0x4000
-#define PPSS_DSPS_DDR_BASE 0x8fe00000
-#define PPSS_DSPS_DDR_SIZE 0x100000
-#define PPSS_SMEM_BASE 0x80000000
-#define PPSS_SMEM_SIZE 0x200000
-#define PPSS_REG_PHYS_BASE 0x12080000
-#define PPSS_WDOG_UNMASKED_INT_EN 0x1808
-
static struct dsps_clk_info dsps_clks[] = {};
static struct dsps_regulator_info dsps_regs[] = {};
@@ -2425,6 +2412,8 @@
* apq8064_init_dsps().
*/
+#define PPSS_REG_PHYS_BASE 0x12080000
+
struct msm_dsps_platform_data msm_dsps_pdata_8064 = {
.clks = dsps_clks,
.clks_num = ARRAY_SIZE(dsps_clks),
@@ -2433,17 +2422,6 @@
.regs = dsps_regs,
.regs_num = ARRAY_SIZE(dsps_regs),
.dsps_pwr_ctl_en = 1,
- .tcm_code_start = PPSS_DSPS_TCM_CODE_BASE,
- .tcm_code_size = PPSS_DSPS_TCM_CODE_SIZE,
- .tcm_buf_start = PPSS_DSPS_TCM_BUF_BASE,
- .tcm_buf_size = PPSS_DSPS_TCM_BUF_SIZE,
- .pipe_start = PPSS_DSPS_PIPE_BASE,
- .pipe_size = PPSS_DSPS_PIPE_SIZE,
- .ddr_start = PPSS_DSPS_DDR_BASE,
- .ddr_size = PPSS_DSPS_DDR_SIZE,
- .smem_start = PPSS_SMEM_BASE,
- .smem_size = PPSS_SMEM_SIZE,
- .ppss_wdog_unmasked_int_en_reg = PPSS_WDOG_UNMASKED_INT_EN,
.signature = DSPS_SIGNATURE,
};
@@ -2454,13 +2432,6 @@
.name = "ppss_reg",
.flags = IORESOURCE_MEM,
},
-
- {
- .start = PPSS_WDOG_TIMER_IRQ,
- .end = PPSS_WDOG_TIMER_IRQ,
- .name = "ppss_wdog",
- .flags = IORESOURCE_IRQ,
- },
};
struct platform_device msm_dsps_device_8064 = {
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index 5b04fa7..0f71bc4 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -1462,9 +1462,24 @@
.id = -1,
};
+static struct resource msm_pil_dsps_resources[] = {
+ {
+ .start = PPSS_WDOG_TIMER_IRQ,
+ .end = PPSS_WDOG_TIMER_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = 0x12080000,
+ .end = 0x12080000 + SZ_8K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
struct platform_device msm_pil_dsps = {
- .name = "pil_dsps",
- .id = -1,
+ .name = "pil_dsps",
+ .id = -1,
+ .resource = msm_pil_dsps_resources,
+ .num_resources = ARRAY_SIZE(msm_pil_dsps_resources),
.dev.platform_data = "dsps",
};
@@ -3801,18 +3816,7 @@
/* Sensors DSPS platform data */
#ifdef CONFIG_MSM_DSPS
-#define PPSS_DSPS_TCM_CODE_BASE 0x12000000
-#define PPSS_DSPS_TCM_CODE_SIZE 0x28000
-#define PPSS_DSPS_TCM_BUF_BASE 0x12040000
-#define PPSS_DSPS_TCM_BUF_SIZE 0x4000
-#define PPSS_DSPS_PIPE_BASE 0x12800000
-#define PPSS_DSPS_PIPE_SIZE 0x4000
-#define PPSS_DSPS_DDR_BASE 0x8fe00000
-#define PPSS_DSPS_DDR_SIZE 0x100000
-#define PPSS_SMEM_BASE 0x80000000
-#define PPSS_SMEM_SIZE 0x200000
-#define PPSS_REG_PHYS_BASE 0x12080000
-#define PPSS_WDOG_UNMASKED_INT_EN 0x1808
+#define PPSS_REG_PHYS_BASE 0x12080000
static struct dsps_clk_info dsps_clks[] = {};
static struct dsps_regulator_info dsps_regs[] = {};
@@ -3830,17 +3834,6 @@
.regs = dsps_regs,
.regs_num = ARRAY_SIZE(dsps_regs),
.dsps_pwr_ctl_en = 1,
- .tcm_code_start = PPSS_DSPS_TCM_CODE_BASE,
- .tcm_code_size = PPSS_DSPS_TCM_CODE_SIZE,
- .tcm_buf_start = PPSS_DSPS_TCM_BUF_BASE,
- .tcm_buf_size = PPSS_DSPS_TCM_BUF_SIZE,
- .pipe_start = PPSS_DSPS_PIPE_BASE,
- .pipe_size = PPSS_DSPS_PIPE_SIZE,
- .ddr_start = PPSS_DSPS_DDR_BASE,
- .ddr_size = PPSS_DSPS_DDR_SIZE,
- .smem_start = PPSS_SMEM_BASE,
- .smem_size = PPSS_SMEM_SIZE,
- .ppss_wdog_unmasked_int_en_reg = PPSS_WDOG_UNMASKED_INT_EN,
.signature = DSPS_SIGNATURE,
};
@@ -3851,12 +3844,6 @@
.name = "ppss_reg",
.flags = IORESOURCE_MEM,
},
- {
- .start = PPSS_WDOG_TIMER_IRQ,
- .end = PPSS_WDOG_TIMER_IRQ,
- .name = "ppss_wdog",
- .flags = IORESOURCE_IRQ,
- },
};
struct platform_device msm_dsps_device = {
diff --git a/arch/arm/mach-msm/devices-9615.c b/arch/arm/mach-msm/devices-9615.c
index 8e8fa83..fe3a4d5 100644
--- a/arch/arm/mach-msm/devices-9615.c
+++ b/arch/arm/mach-msm/devices-9615.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, 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
@@ -571,6 +571,15 @@
.name = "msm-dai-q6",
.id = PRIMARY_I2S_TX,
};
+struct platform_device msm_i2s_cpudai4 = {
+ .name = "msm-dai-q6",
+ .id = SECONDARY_I2S_RX,
+};
+
+struct platform_device msm_i2s_cpudai5 = {
+ .name = "msm-dai-q6",
+ .id = SECONDARY_I2S_TX,
+};
struct platform_device msm_voip = {
.name = "msm-voip-dsp",
.id = -1,
diff --git a/arch/arm/mach-msm/devices-msm8x60.c b/arch/arm/mach-msm/devices-msm8x60.c
index 37cdc98..5554eb8 100644
--- a/arch/arm/mach-msm/devices-msm8x60.c
+++ b/arch/arm/mach-msm/devices-msm8x60.c
@@ -1622,16 +1622,6 @@
/* Sensors DSPS platform data */
#ifdef CONFIG_MSM_DSPS
-#define PPSS_DSPS_TCM_CODE_BASE 0x12000000
-#define PPSS_DSPS_TCM_CODE_SIZE 0x28000
-#define PPSS_DSPS_TCM_BUF_BASE 0x12040000
-#define PPSS_DSPS_TCM_BUF_SIZE 0x4000
-#define PPSS_DSPS_PIPE_BASE 0x12800000
-#define PPSS_DSPS_PIPE_SIZE 0x0 /* 8660 V2 does not use PIPE memory */
-#define PPSS_DSPS_DDR_BASE 0x8fe00000
-#define PPSS_DSPS_DDR_SIZE 0x0 /* 8660 V2 does not use DDR memory */
-#define PPSS_SMEM_BASE 0x40000000
-#define PPSS_SMEM_SIZE 0x4000
#define PPSS_REG_PHYS_BASE 0x12080000
#define PPSS_PAUSE_REG 0x1804
@@ -1696,16 +1686,6 @@
.regs = dsps_regs,
.regs_num = ARRAY_SIZE(dsps_regs),
.init = dsps_init1,
- .tcm_code_start = PPSS_DSPS_TCM_CODE_BASE,
- .tcm_code_size = PPSS_DSPS_TCM_CODE_SIZE,
- .tcm_buf_start = PPSS_DSPS_TCM_BUF_BASE,
- .tcm_buf_size = PPSS_DSPS_TCM_BUF_SIZE,
- .pipe_start = PPSS_DSPS_PIPE_BASE,
- .pipe_size = PPSS_DSPS_PIPE_SIZE,
- .ddr_start = PPSS_DSPS_DDR_BASE,
- .ddr_size = PPSS_DSPS_DDR_SIZE,
- .smem_start = PPSS_SMEM_BASE,
- .smem_size = PPSS_SMEM_SIZE,
.ppss_pause_reg = PPSS_PAUSE_REG,
.signature = DSPS_SIGNATURE,
};
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index 950a1be..8f02050 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -244,7 +244,8 @@
extern struct platform_device msm_cpudai_incall_record_tx;
extern struct platform_device msm_i2s_cpudai0;
extern struct platform_device msm_i2s_cpudai1;
-
+extern struct platform_device msm_i2s_cpudai4;
+extern struct platform_device msm_i2s_cpudai5;
extern struct platform_device msm_pil_q6v3;
extern struct platform_device msm_pil_modem;
extern struct platform_device msm_pil_tzapps;
diff --git a/arch/arm/mach-msm/include/mach/msm_dsps.h b/arch/arm/mach-msm/include/mach/msm_dsps.h
index a876798..ac81616 100644
--- a/arch/arm/mach-msm/include/mach/msm_dsps.h
+++ b/arch/arm/mach-msm/include/mach/msm_dsps.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -75,16 +75,6 @@
* @regs_num - number of regulators.
* @dsps_pwr_ctl_en - to enable DSPS to do power control if set 1
* otherwise the apps will do power control
- * @tcm_code_start - start of the TCM code region as physical address
- * @tcm_code_size - size of the TCM code region in bytes
- * @tcm_buf_start - start of the TCM buf region as physical address
- * @tcm_buf_size - size of the TCM buf region in bytes
- * @pipe_start - start of the PIPE region as physical address
- * @pipe_size - size of the PIPE region in bytes
- * @ddr_start - start of the DDR region as physical address
- * @ddr_size - size of the DDR region in bytes
- * @smem_start - start of the smem region as physical address
- * @smem_size - size of the smem region in bytes
* @ppss_pause_reg - Offset to the PPSS_PAUSE register
* @ppss_wdog_unmasked_int_en_reg - Offset to PPSS_WDOG_UNMASKED_INT_EN register
* @signature - signature for validity check.
@@ -99,16 +89,6 @@
int regs_num;
int dsps_pwr_ctl_en;
void (*init)(struct msm_dsps_platform_data *data);
- int tcm_code_start;
- int tcm_code_size;
- int tcm_buf_start;
- int tcm_buf_size;
- int pipe_start;
- int pipe_size;
- int ddr_start;
- int ddr_size;
- int smem_start;
- int smem_size;
int ppss_pause_reg;
int ppss_wdog_unmasked_int_en_reg;
u32 signature;
diff --git a/arch/arm/mach-msm/lpm_levels.c b/arch/arm/mach-msm/lpm_levels.c
index 8218a42..4854fc48 100644
--- a/arch/arm/mach-msm/lpm_levels.c
+++ b/arch/arm/mach-msm/lpm_levels.c
@@ -22,6 +22,18 @@
#include "pm.h"
#include "rpm-notifier.h"
+
+enum {
+ MSM_LPM_LVL_DBG_SUSPEND_LIMITS = BIT(0),
+ MSM_LPM_LVL_DBG_IDLE_LIMITS = BIT(1),
+};
+
+static int msm_lpm_lvl_dbg_msk;
+
+module_param_named(
+ debug_mask, msm_lpm_lvl_dbg_msk, int, S_IRUGO | S_IWUSR | S_IWGRP
+);
+
static struct msm_rpmrs_level *msm_lpm_levels;
static int msm_lpm_level_count;
@@ -41,6 +53,7 @@
bool from_idle, bool notify_rpm)
{
int ret = 0;
+ int debug_mask;
struct msm_rpmrs_limits *l = (struct msm_rpmrs_limits *)limits;
ret = msm_rpm_enter_sleep();
@@ -49,6 +62,21 @@
__func__, ret);
goto bail;
}
+ if (from_idle)
+ debug_mask = msm_lpm_lvl_dbg_msk &
+ MSM_LPM_LVL_DBG_IDLE_LIMITS;
+ else
+ debug_mask = msm_lpm_lvl_dbg_msk &
+ MSM_LPM_LVL_DBG_SUSPEND_LIMITS;
+
+ if (debug_mask)
+ pr_info("%s(): pxo:%d l2:%d mem:0x%x(0x%x) dig:0x%x(0x%x)\n",
+ __func__, l->pxo, l->l2_cache,
+ l->vdd_mem_lower_bound,
+ l->vdd_mem_upper_bound,
+ l->vdd_dig_lower_bound,
+ l->vdd_dig_upper_bound);
+
ret = msm_lpmrs_enter_sleep(sclk_count, l, from_idle, notify_rpm);
bail:
return ret;
diff --git a/arch/arm/mach-msm/lpm_resources.c b/arch/arm/mach-msm/lpm_resources.c
index 364f297..2db92f3 100644
--- a/arch/arm/mach-msm/lpm_resources.c
+++ b/arch/arm/mach-msm/lpm_resources.c
@@ -387,7 +387,7 @@
static bool msm_lpm_beyond_limits_l2(struct msm_rpmrs_limits *limits)
{
uint32_t l2;
- bool ret = true;
+ bool ret = false;
struct msm_lpm_resource *rs = &msm_lpm_l2;
if (rs->valid) {
@@ -669,7 +669,7 @@
msm_lpm_get_rpm_notif = false;
for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
rs = msm_lpm_resources[i];
- if (rs->flush)
+ if (rs->valid && rs->flush)
rs->flush(notify_rpm);
}
msm_lpm_get_rpm_notif = true;
diff --git a/arch/arm/mach-msm/modem-ssr-8974.c b/arch/arm/mach-msm/modem-ssr-8974.c
deleted file mode 100644
index 942eca5..0000000
--- a/arch/arm/mach-msm/modem-ssr-8974.c
+++ /dev/null
@@ -1,212 +0,0 @@
-/* Copyright (c) 2012, 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/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/err.h>
-
-#include <mach/peripheral-loader.h>
-#include <mach/subsystem_restart.h>
-#include <mach/msm_smsm.h>
-
-#include "ramdump.h"
-
-static int crash_shutdown;
-static int modem_ssr_ignore_errors;
-static struct subsys_device *modem_ssr_dev;
-
-#define MAX_SSR_REASON_LEN 81U
-#define Q6SS_WDOG_ENABLE 0xFC802004
-#define MSS_Q6SS_WDOG_EXP_IRQ 56
-
-static void log_modem_sfr(void)
-{
- u32 size;
- char *smem_reason, reason[MAX_SSR_REASON_LEN];
-
- smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
- if (!smem_reason || !size) {
- pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n");
- return;
- }
- if (!smem_reason[0]) {
- pr_err("modem subsystem failure reason: (unknown, empty string found).\n");
- return;
- }
-
- strlcpy(reason, smem_reason, min(size, sizeof(reason)));
- pr_err("modem subsystem failure reason: %s.\n", reason);
-
- smem_reason[0] = '\0';
- wmb();
-}
-
-static void restart_modem(void)
-{
- log_modem_sfr();
- modem_ssr_ignore_errors = 1;
- subsystem_restart("modem");
-}
-
-static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
-{
- /* Ignore if we're the one that set SMSM_RESET */
- if (crash_shutdown)
- return;
-
- if (new_state & SMSM_RESET) {
- pr_err("Probable fatal error on the modem.\n");
- restart_modem();
- }
-}
-
-static int modem_shutdown(const struct subsys_desc *subsys)
-{
- pil_force_shutdown("modem");
- pil_force_shutdown("mba");
- return 0;
-}
-
-static int modem_powerup(const struct subsys_desc *subsys)
-{
- /*
- * At this time, the modem is shutdown. Therefore this function cannot
- * run concurrently with either the watchdog bite error handler or the
- * SMSM callback, making it safe to unset the flag below.
- */
- modem_ssr_ignore_errors = 0;
- pil_force_boot("mba");
- pil_force_boot("modem");
- return 0;
-}
-
-void modem_crash_shutdown(const struct subsys_desc *subsys)
-{
- crash_shutdown = 1;
- smsm_reset_modem(SMSM_RESET);
-}
-
-static struct ramdump_segment modem_segments[] = {
- {0x08400000, 0x0D100000 - 0x08400000},
-};
-
-static struct ramdump_segment smem_segments[] = {
- {0x0FA00000, 0x0FC00000 - 0x0FA00000},
-};
-
-static void *modem_ramdump_dev;
-static void *smem_ramdump_dev;
-
-static int modem_ramdump(int enable, const struct subsys_desc *crashed_subsys)
-{
- int ret = 0;
-
- if (!enable)
- return ret;
-
- pil_force_boot("mba");
-
- ret = do_ramdump(modem_ramdump_dev, modem_segments,
- ARRAY_SIZE(modem_segments));
-
- if (ret < 0) {
- pr_err("Unable to dump modem fw memory (rc = %d).\n",
- ret);
- goto out;
- }
-
- ret = do_ramdump(smem_ramdump_dev, smem_segments,
- ARRAY_SIZE(smem_segments));
-
- if (ret < 0) {
- pr_err("Unable to dump smem memory (rc = %d).\n", ret);
- goto out;
- }
-
-out:
- pil_force_shutdown("mba");
- return ret;
-}
-
-static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
-{
- if (modem_ssr_ignore_errors)
- return IRQ_HANDLED;
- pr_err("Watchdog bite received from the modem!\n");
- restart_modem();
- return IRQ_HANDLED;
-}
-
-static struct subsys_desc modem_8974 = {
- .name = "modem",
- .shutdown = modem_shutdown,
- .powerup = modem_powerup,
- .ramdump = modem_ramdump,
- .crash_shutdown = modem_crash_shutdown
-};
-
-static int __init modem_8974_init(void)
-{
- int ret;
-
- ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
- smsm_state_cb, 0);
-
- if (ret < 0) {
- pr_err("%s: Unable to register SMSM callback! (%d)\n",
- __func__, ret);
- goto out;
- }
-
- ret = request_irq(MSS_Q6SS_WDOG_EXP_IRQ, modem_wdog_bite_irq,
- IRQF_TRIGGER_RISING, "modem_wdog_sw", NULL);
-
- if (ret < 0) {
- pr_err("%s: Unable to request q6sw watchdog IRQ. (%d)\n",
- __func__, ret);
- goto out;
- }
-
- modem_ssr_dev = subsys_register(&modem_8974);
-
- if (IS_ERR_OR_NULL(modem_ssr_dev)) {
- pr_err("%s: Unable to reg with subsystem restart. (%ld)\n",
- __func__, PTR_ERR(modem_ssr_dev));
- ret = PTR_ERR(modem_ssr_dev);
- goto out;
- }
-
- modem_ramdump_dev = create_ramdump_device("modem");
-
- if (!modem_ramdump_dev) {
- pr_err("%s: Unable to create a modem ramdump device.\n",
- __func__);
- ret = -ENOMEM;
- goto out;
- }
-
- smem_ramdump_dev = create_ramdump_device("smem-modem");
-
- if (!smem_ramdump_dev) {
- pr_err("%s: Unable to create an smem ramdump device.\n",
- __func__);
- ret = -ENOMEM;
- goto out;
- }
-
- pr_info("%s: modem subsystem restart driver init'ed.\n", __func__);
-out:
- return ret;
-}
-
-module_init(modem_8974_init);
diff --git a/arch/arm/mach-msm/msm_dsps.c b/arch/arm/mach-msm/msm_dsps.c
index c39829b..b85c812 100644
--- a/arch/arm/mach-msm/msm_dsps.c
+++ b/arch/arm/mach-msm/msm_dsps.c
@@ -15,8 +15,6 @@
*
*/
-#include <asm/atomic.h>
-
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/module.h>
@@ -39,7 +37,6 @@
#include <mach/msm_smsm.h>
#include <mach/msm_dsps.h>
#include <mach/subsystem_restart.h>
-#include <mach/subsystem_notif.h>
#include "ramdump.h"
#include "timer.h"
@@ -66,8 +63,6 @@
* @smem_ramdump_segments - Ramdump segment information for smem
* @is_on - DSPS is on.
* @ref_count - open/close reference count.
- * @wdog_irq - DSPS Watchdog IRQ
- * @crash_in_progress - 1 if crash recovery is in progress
* @ppss_base - ppss registers virtual base address.
*/
struct dsps_drv {
@@ -81,17 +76,9 @@
void *pil;
- void *dspsfw_ramdump_dev;
- struct ramdump_segment dspsfw_ramdump_segments[4];
-
- void *smem_ramdump_dev;
- struct ramdump_segment smem_ramdump_segments[1];
-
int is_on;
int ref_count;
- int wdog_irq;
- atomic_t crash_in_progress;
void __iomem *ppss_base;
};
@@ -101,13 +88,6 @@
static struct dsps_drv *drv;
/**
- * self-initiated shutdown flag
- */
-static int dsps_crash_shutdown_g;
-
-static void dsps_restart_handler(void);
-
-/**
* Load DSPS Firmware.
*/
static int dsps_load(const char *name)
@@ -382,41 +362,6 @@
}
/**
- *
- * Log subsystem restart failure reason
- */
-static void dsps_log_sfr(void)
-{
- const char dflt_reason[] = "Died too early due to unknown reason";
- char *smem_reset_reason;
- unsigned smem_reset_size;
-
- smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_DSPS0,
- &smem_reset_size);
- if (smem_reset_reason != NULL && smem_reset_reason[0] != 0) {
- smem_reset_reason[smem_reset_size-1] = 0;
- pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
- __func__, smem_reset_reason);
- memset(smem_reset_reason, 0, smem_reset_size);
- wmb();
- } else
- pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
- __func__, dflt_reason);
-}
-
-/**
- * Watchdog interrupt handler
- *
- */
-static irqreturn_t dsps_wdog_bite_irq(int irq, void *dev_id)
-{
- pr_err("%s\n", __func__);
- dsps_log_sfr();
- dsps_restart_handler();
- return IRQ_HANDLED;
-}
-
-/**
* IO Control - handle commands from client.
*
*/
@@ -452,7 +397,7 @@
case DSPS_IOCTL_RESET:
pr_err("%s: User-initiated DSPS reset.\nResetting DSPS\n",
__func__);
- dsps_restart_handler();
+ subsystem_restart("dsps");
ret = 0;
break;
default:
@@ -471,7 +416,6 @@
{
int ret = -ENODEV;
struct resource *ppss_res;
- struct resource *ppss_wdog;
int i;
pr_debug("%s.\n", __func__);
@@ -541,58 +485,11 @@
drv->ppss_base = ioremap(ppss_res->start,
resource_size(ppss_res));
- ppss_wdog = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
- "ppss_wdog");
- if (ppss_wdog) {
- drv->wdog_irq = ppss_wdog->start;
- ret = request_irq(drv->wdog_irq, dsps_wdog_bite_irq,
- IRQF_TRIGGER_RISING, "dsps_wdog", NULL);
- if (ret) {
- pr_err("%s: request_irq fail %d\n", __func__, ret);
- goto request_irq_err;
- }
- } else {
- drv->wdog_irq = -1;
- pr_debug("%s: ppss_wdog not supported.\n", __func__);
- }
-
- drv->dspsfw_ramdump_segments[0].address = drv->pdata->tcm_code_start;
- drv->dspsfw_ramdump_segments[0].size = drv->pdata->tcm_code_size;
- drv->dspsfw_ramdump_segments[1].address = drv->pdata->tcm_buf_start;
- drv->dspsfw_ramdump_segments[1].size = drv->pdata->tcm_buf_size;
- drv->dspsfw_ramdump_segments[2].address = drv->pdata->pipe_start;
- drv->dspsfw_ramdump_segments[2].size = drv->pdata->pipe_size;
- drv->dspsfw_ramdump_segments[3].address = drv->pdata->ddr_start;
- drv->dspsfw_ramdump_segments[3].size = drv->pdata->ddr_size;
-
- drv->dspsfw_ramdump_dev = create_ramdump_device("dsps");
- if (!drv->dspsfw_ramdump_dev) {
- pr_err("%s: create_ramdump_device(\"dsps\") fail\n",
- __func__);
- goto create_ramdump_err;
- }
-
- drv->smem_ramdump_segments[0].address = drv->pdata->smem_start;
- drv->smem_ramdump_segments[0].size = drv->pdata->smem_size;
- drv->smem_ramdump_dev = create_ramdump_device("smem-dsps");
- if (!drv->smem_ramdump_dev) {
- pr_err("%s: create_ramdump_device(\"smem\") fail\n",
- __func__);
- goto create_ramdump_err;
- }
-
if (drv->pdata->init)
drv->pdata->init(drv->pdata);
return 0;
-create_ramdump_err:
- disable_irq_nosync(drv->wdog_irq);
- free_irq(drv->wdog_irq, NULL);
-
-request_irq_err:
- iounmap(drv->ppss_base);
-
reg_err:
for (i = 0; i < drv->pdata->regs_num; i++) {
if (drv->pdata->regs[i].reg) {
@@ -678,8 +575,6 @@
}
}
- free_irq(drv->wdog_irq, NULL);
-
iounmap(drv->ppss_base);
}
@@ -722,138 +617,6 @@
.unlocked_ioctl = dsps_ioctl,
};
-static struct subsys_device *dsps_dev;
-
-/**
- * Fatal error handler
- * Resets DSPS.
- */
-static void dsps_restart_handler(void)
-{
- pr_debug("%s: Restart lvl %d\n",
- __func__, get_restart_level());
-
- if (atomic_add_return(1, &drv->crash_in_progress) > 1) {
- pr_err("%s: DSPS already resetting. Count %d\n", __func__,
- atomic_read(&drv->crash_in_progress));
- } else {
- subsystem_restart_dev(dsps_dev);
- }
-}
-
-
-/**
- * SMSM state change callback
- *
- */
-static void dsps_smsm_state_cb(void *data, uint32_t old_state,
- uint32_t new_state)
-{
- pr_debug("%s\n", __func__);
- if (dsps_crash_shutdown_g == 1) {
- pr_debug("%s: SMSM_RESET state change ignored\n",
- __func__);
- dsps_crash_shutdown_g = 0;
- return;
- }
- if (new_state & SMSM_RESET) {
- dsps_log_sfr();
- dsps_restart_handler();
- }
-}
-
-/**
- * Shutdown function
- * called by the restart notifier
- *
- */
-static int dsps_shutdown(const struct subsys_desc *subsys)
-{
- pr_debug("%s\n", __func__);
- disable_irq_nosync(drv->wdog_irq);
- if (drv->pdata->ppss_wdog_unmasked_int_en_reg) {
- writel_relaxed(0, (drv->ppss_base+
- drv->pdata->ppss_wdog_unmasked_int_en_reg));
- mb(); /* Make sure wdog is disabled before shutting down */
- }
- pil_force_shutdown(drv->pdata->pil_name);
- dsps_power_off_handler();
- return 0;
-}
-
-/**
- * Powerup function
- * called by the restart notifier
- *
- */
-static int dsps_powerup(const struct subsys_desc *subsys)
-{
- pr_debug("%s\n", __func__);
- dsps_power_on_handler();
- pil_force_boot(drv->pdata->pil_name);
- atomic_set(&drv->crash_in_progress, 0);
- enable_irq(drv->wdog_irq);
- return 0;
-}
-
-/**
- * Crash shutdown function
- * called by the restart notifier
- *
- */
-static void dsps_crash_shutdown(const struct subsys_desc *subsys)
-{
- pr_debug("%s\n", __func__);
- disable_irq_nosync(drv->wdog_irq);
- dsps_crash_shutdown_g = 1;
- smsm_change_state(SMSM_DSPS_STATE, SMSM_RESET, SMSM_RESET);
-}
-
-/**
- * Ramdump function
- * called by the restart notifier
- *
- */
-static int dsps_ramdump(int enable, const struct subsys_desc *subsys)
-{
- int ret = 0;
- pr_debug("%s\n", __func__);
-
- if (enable) {
- if (drv->dspsfw_ramdump_dev != NULL) {
- ret = do_ramdump(drv->dspsfw_ramdump_dev,
- drv->dspsfw_ramdump_segments,
- ARRAY_SIZE(drv->dspsfw_ramdump_segments));
- if (ret < 0) {
- pr_err("%s: Unable to dump DSPS memory (rc = %d).\n",
- __func__, ret);
- goto dsps_ramdump_out;
- }
- }
- if (drv->smem_ramdump_dev != NULL) {
- ret = do_ramdump(drv->smem_ramdump_dev,
- drv->smem_ramdump_segments,
- ARRAY_SIZE(drv->smem_ramdump_segments));
- if (ret < 0) {
- pr_err("%s: Unable to dump smem memory (rc = %d).\n",
- __func__, ret);
- goto dsps_ramdump_out;
- }
- }
- }
-
-dsps_ramdump_out:
- return ret;
-}
-
-static struct subsys_desc dsps_ssrops = {
- .name = "dsps",
- .shutdown = dsps_shutdown,
- .powerup = dsps_powerup,
- .ramdump = dsps_ramdump,
- .crash_shutdown = dsps_crash_shutdown
-};
-
/**
* platform driver
*
@@ -874,8 +637,6 @@
pr_err("%s: kzalloc fail.\n", __func__);
goto alloc_err;
}
- atomic_set(&drv->crash_in_progress, 0);
-
drv->pdata = pdev->dev.platform_data;
drv->dev_class = class_create(THIS_MODULE, DRV_NAME);
@@ -918,31 +679,6 @@
goto cdev_add_err;
}
- ret =
- smsm_state_cb_register(SMSM_DSPS_STATE, SMSM_RESET,
- dsps_smsm_state_cb, 0);
- if (ret) {
- pr_err("%s: smsm_state_cb_register fail %d\n", __func__,
- ret);
- goto smsm_register_err;
- }
-
- dsps_dev = subsys_register(&dsps_ssrops);
- if (IS_ERR(dsps_dev)) {
- ret = PTR_ERR(dsps_dev);
- pr_err("%s: subsys_register fail %d\n", __func__,
- ret);
- goto ssr_register_err;
- }
-
- return 0;
-
-ssr_register_err:
- smsm_state_cb_deregister(SMSM_DSPS_STATE, SMSM_RESET,
- dsps_smsm_state_cb,
- 0);
-smsm_register_err:
- cdev_del(drv->cdev);
cdev_add_err:
kfree(drv->cdev);
cdev_alloc_err:
@@ -962,7 +698,6 @@
{
pr_debug("%s.\n", __func__);
- subsys_unregister(dsps_dev);
dsps_power_off_handler();
dsps_free_resources();
diff --git a/arch/arm/mach-msm/msm_watchdog_v2.c b/arch/arm/mach-msm/msm_watchdog_v2.c
index 48a57cd..f88c611 100644
--- a/arch/arm/mach-msm/msm_watchdog_v2.c
+++ b/arch/arm/mach-msm/msm_watchdog_v2.c
@@ -228,13 +228,19 @@
static void pet_watchdog(struct msm_watchdog_data *wdog_dd)
{
- int slack;
+ int slack, i, count, prev_count = 0;
unsigned long long time_ns;
unsigned long long slack_ns;
unsigned long long bark_time_ns = wdog_dd->bark_time * 1000000ULL;
- slack = __raw_readl(wdog_dd->base + WDT0_STS) >> 3;
- slack = ((wdog_dd->bark_time*WDT_HZ)/1000) - slack;
+ for (i = 0; i < 2; i++) {
+ count = (__raw_readl(wdog_dd->base + WDT0_STS) >> 1) & 0xFFFFF;
+ if (count != prev_count) {
+ prev_count = count;
+ i = 0;
+ }
+ }
+ slack = ((wdog_dd->bark_time * WDT_HZ) / 1000) - count;
if (slack < wdog_dd->min_slack_ticks)
wdog_dd->min_slack_ticks = slack;
__raw_writel(1, wdog_dd->base + WDT0_RST);
diff --git a/arch/arm/mach-msm/pil-dsps.c b/arch/arm/mach-msm/pil-dsps.c
index 81f5330..dc80a3a 100644
--- a/arch/arm/mach-msm/pil-dsps.c
+++ b/arch/arm/mach-msm/pil-dsps.c
@@ -17,11 +17,17 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/delay.h>
+#include <linux/atomic.h>
+#include <linux/interrupt.h>
#include <mach/msm_iomap.h>
+#include <mach/subsystem_restart.h>
+#include <mach/msm_smsm.h>
+#include <mach/peripheral-loader.h>
#include "peripheral-loader.h"
#include "scm-pas.h"
+#include "ramdump.h"
#define PPSS_RESET (MSM_CLK_CTL_BASE + 0x2594)
#define PPSS_RESET_PROC_RESET 0x2
@@ -31,6 +37,28 @@
#define PPSS_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2580)
#define CLK_HALT_DFAB_STATE (MSM_CLK_CTL_BASE + 0x2FC8)
+#define PPSS_WDOG_UNMASKED_INT_EN 0x1808
+
+struct dsps_data {
+ struct pil_device *pil;
+ struct pil_desc desc;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
+ int crash;
+ int wdog_irq;
+ atomic_t wd_crash;
+ atomic_t crash_in_progress;
+ void __iomem *ppss_base;
+
+ void *ramdump_dev;
+ struct ramdump_segment fw_ramdump_segments[4];
+
+ void *smem_ramdump_dev;
+ struct ramdump_segment smem_ramdump_segments[1];
+};
+
+#define desc_to_drv(d) container_of(d, struct dsps_data, subsys_desc)
+
static int init_image_dsps(struct pil_desc *pil, const u8 *metadata,
size_t size)
{
@@ -88,15 +116,143 @@
.shutdown = shutdown_dsps_trusted,
};
+static void dsps_log_sfr(void)
+{
+ const char dflt_reason[] = "Died too early due to unknown reason";
+ char *smem_reset_reason;
+ unsigned smem_reset_size;
+
+ smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_DSPS0,
+ &smem_reset_size);
+ if (smem_reset_reason != NULL && smem_reset_reason[0] != 0) {
+ smem_reset_reason[smem_reset_size-1] = 0;
+ pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
+ __func__, smem_reset_reason);
+ memset(smem_reset_reason, 0, smem_reset_size);
+ wmb();
+ } else
+ pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
+ __func__, dflt_reason);
+}
+
+
+static void dsps_restart_handler(struct dsps_data *drv)
+{
+ pr_debug("%s: Restart lvl %d\n",
+ __func__, get_restart_level());
+
+ if (atomic_add_return(1, &drv->crash_in_progress) > 1) {
+ pr_err("%s: DSPS already resetting. Count %d\n", __func__,
+ atomic_read(&drv->crash_in_progress));
+ } else {
+ subsystem_restart_dev(drv->subsys);
+ }
+}
+
+static void dsps_smsm_state_cb(void *data, uint32_t old_state,
+ uint32_t new_state)
+{
+ struct dsps_data *drv = data;
+
+ if (drv->crash == 1) {
+ pr_debug("SMSM_RESET state change ignored\n");
+ drv->crash = 0;
+ } else if (new_state & SMSM_RESET) {
+ dsps_log_sfr();
+ dsps_restart_handler(drv);
+ }
+}
+
+static int dsps_shutdown(const struct subsys_desc *desc)
+{
+ struct dsps_data *drv = desc_to_drv(desc);
+ disable_irq_nosync(drv->wdog_irq);
+ if (drv->ppss_base) {
+ writel_relaxed(0, drv->ppss_base + PPSS_WDOG_UNMASKED_INT_EN);
+ mb(); /* Make sure wdog is disabled before shutting down */
+ }
+ pil_force_shutdown(drv->desc.name);
+ return 0;
+}
+
+static int dsps_powerup(const struct subsys_desc *desc)
+{
+ struct dsps_data *drv = desc_to_drv(desc);
+
+ pil_force_boot(drv->desc.name);
+ atomic_set(&drv->crash_in_progress, 0);
+ enable_irq(drv->wdog_irq);
+
+ return 0;
+}
+
+static int dsps_ramdump(int enable, const struct subsys_desc *desc)
+{
+ int ret;
+ struct dsps_data *drv = desc_to_drv(desc);
+
+ if (!enable)
+ return 0;
+
+ ret = do_ramdump(drv->ramdump_dev,
+ drv->fw_ramdump_segments,
+ ARRAY_SIZE(drv->fw_ramdump_segments));
+ if (ret < 0) {
+ pr_err("%s: Unable to dump DSPS memory (rc = %d).\n",
+ __func__, ret);
+ return ret;
+ }
+ ret = do_ramdump(drv->smem_ramdump_dev,
+ drv->smem_ramdump_segments,
+ ARRAY_SIZE(drv->smem_ramdump_segments));
+ if (ret < 0) {
+ pr_err("%s: Unable to dump smem memory (rc = %d).\n",
+ __func__, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static void dsps_crash_shutdown(const struct subsys_desc *desc)
+{
+ struct dsps_data *drv = desc_to_drv(desc);
+
+ disable_irq_nosync(drv->wdog_irq);
+ drv->crash = 1;
+ smsm_change_state(SMSM_DSPS_STATE, SMSM_RESET, SMSM_RESET);
+}
+
+static irqreturn_t dsps_wdog_bite_irq(int irq, void *dev_id)
+{
+ struct dsps_data *drv = dev_id;
+
+ atomic_set(&drv->wd_crash, 1);
+ dsps_log_sfr();
+ dsps_restart_handler(drv);
+ return IRQ_HANDLED;
+}
+
static int __devinit pil_dsps_driver_probe(struct platform_device *pdev)
{
+ struct dsps_data *drv;
struct pil_desc *desc;
- struct pil_device *pil;
+ int ret;
+ struct resource *res;
- desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
- if (!desc)
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
return -ENOMEM;
+ platform_set_drvdata(pdev, drv);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res) {
+ drv->ppss_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!drv->ppss_base)
+ return -ENOMEM;
+ }
+
+ desc = &drv->desc;
desc->name = pdev->dev.platform_data;
desc->dev = &pdev->dev;
desc->owner = THIS_MODULE;
@@ -107,17 +263,85 @@
desc->ops = &pil_dsps_ops;
dev_info(&pdev->dev, "using non-secure boot\n");
}
- pil = msm_pil_register(desc);
- if (IS_ERR(pil))
- return PTR_ERR(pil);
- platform_set_drvdata(pdev, pil);
+ drv->pil = msm_pil_register(desc);
+ if (IS_ERR(drv->pil))
+ return PTR_ERR(drv->pil);
+
+ drv->fw_ramdump_segments[0].address = 0x12000000;
+ drv->fw_ramdump_segments[0].size = 0x28000;
+ drv->fw_ramdump_segments[1].address = 0x12040000;
+ drv->fw_ramdump_segments[1].size = 0x4000;
+ drv->fw_ramdump_segments[2].address = 0x12800000;
+ drv->fw_ramdump_segments[2].size = 0x4000;
+ drv->fw_ramdump_segments[3].address = 0x8fe00000;
+ drv->fw_ramdump_segments[3].size = 0x100000;
+ drv->ramdump_dev = create_ramdump_device("dsps");
+ if (!drv->ramdump_dev) {
+ ret = -ENOMEM;
+ goto err_ramdump;
+ }
+
+ drv->smem_ramdump_segments[0].address = PHYS_OFFSET - SZ_2M;
+ drv->smem_ramdump_segments[0].size = SZ_2M;
+ drv->smem_ramdump_dev = create_ramdump_device("smem-dsps");
+ if (!drv->smem_ramdump_dev) {
+ ret = -ENOMEM;
+ goto err_smem_ramdump;
+ }
+
+ drv->subsys_desc.name = "dsps";
+ drv->subsys_desc.shutdown = dsps_shutdown;
+ drv->subsys_desc.powerup = dsps_powerup;
+ drv->subsys_desc.ramdump = dsps_ramdump,
+ drv->subsys_desc.crash_shutdown = dsps_crash_shutdown;
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ ret = PTR_ERR(drv->subsys);
+ goto err_subsys;
+ }
+
+ ret = smsm_state_cb_register(SMSM_DSPS_STATE, SMSM_RESET,
+ dsps_smsm_state_cb, drv);
+ if (ret)
+ goto err_smsm;
+
+ drv->wdog_irq = platform_get_irq(pdev, 0);
+ if (drv->wdog_irq >= 0) {
+ ret = devm_request_irq(&pdev->dev, drv->wdog_irq,
+ dsps_wdog_bite_irq, IRQF_TRIGGER_RISING,
+ "dsps_wdog", drv);
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq failed\n");
+ goto err_smsm;
+ }
+ } else {
+ drv->wdog_irq = -1;
+ dev_dbg(&pdev->dev, "ppss_wdog not supported\n");
+ }
+
return 0;
+
+err_smsm:
+ subsys_unregister(drv->subsys);
+err_subsys:
+ destroy_ramdump_device(drv->smem_ramdump_dev);
+err_smem_ramdump:
+ destroy_ramdump_device(drv->ramdump_dev);
+err_ramdump:
+ msm_pil_unregister(drv->pil);
+ return ret;
}
static int __devexit pil_dsps_driver_exit(struct platform_device *pdev)
{
- struct pil_device *pil = platform_get_drvdata(pdev);
- msm_pil_unregister(pil);
+ struct dsps_data *drv = platform_get_drvdata(pdev);
+ smsm_state_cb_deregister(SMSM_DSPS_STATE, SMSM_RESET,
+ dsps_smsm_state_cb, drv);
+ subsys_unregister(drv->subsys);
+ destroy_ramdump_device(drv->smem_ramdump_dev);
+ destroy_ramdump_device(drv->ramdump_dev);
+ msm_pil_unregister(drv->pil);
return 0;
}
diff --git a/arch/arm/mach-msm/pil-mba.c b/arch/arm/mach-msm/pil-mba.c
index 0207f0b..8432328 100644
--- a/arch/arm/mach-msm/pil-mba.c
+++ b/arch/arm/mach-msm/pil-mba.c
@@ -23,8 +23,14 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/of.h>
+#include <linux/interrupt.h>
+
+#include <mach/subsystem_restart.h>
+#include <mach/msm_smsm.h>
+#include <mach/peripheral-loader.h>
#include "peripheral-loader.h"
+#include "ramdump.h"
#define RMB_MBA_COMMAND 0x08
#define RMB_MBA_STATUS 0x0C
@@ -41,6 +47,8 @@
#define PROXY_TIMEOUT_MS 10000
#define POLL_INTERVAL_US 50
+#define MAX_SSR_REASON_LEN 81U
+
static int modem_auth_timeout_ms = 10000;
module_param(modem_auth_timeout_ms, int, S_IRUGO | S_IWUSR);
@@ -49,7 +57,13 @@
void __iomem *metadata_base;
unsigned long metadata_phys;
struct pil_device *pil;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
struct clk *xo;
+ void *ramdump_dev;
+ void *smem_ramdump_dev;
+ bool crash_shutdown;
+ bool ignore_errors;
u32 img_length;
};
@@ -160,18 +174,142 @@
.shutdown = pil_mba_shutdown,
};
+#define subsys_to_drv(d) container_of(d, struct mba_data, subsys_desc)
+
+static void log_modem_sfr(void)
+{
+ u32 size;
+ char *smem_reason, reason[MAX_SSR_REASON_LEN];
+
+ smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
+ if (!smem_reason || !size) {
+ pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n");
+ return;
+ }
+ if (!smem_reason[0]) {
+ pr_err("modem subsystem failure reason: (unknown, empty string found).\n");
+ return;
+ }
+
+ strlcpy(reason, smem_reason, min(size, sizeof(reason)));
+ pr_err("modem subsystem failure reason: %s.\n", reason);
+
+ smem_reason[0] = '\0';
+ wmb();
+}
+
+static void restart_modem(struct mba_data *drv)
+{
+ log_modem_sfr();
+ drv->ignore_errors = true;
+ subsystem_restart_dev(drv->subsys);
+}
+
+static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
+{
+ struct mba_data *drv = data;
+
+ /* Ignore if we're the one that set SMSM_RESET */
+ if (drv->crash_shutdown)
+ return;
+
+ if (new_state & SMSM_RESET) {
+ pr_err("Probable fatal error on the modem.\n");
+ restart_modem(drv);
+ }
+}
+
+static int modem_shutdown(const struct subsys_desc *subsys)
+{
+ pil_force_shutdown("modem");
+ pil_force_shutdown("mba");
+ return 0;
+}
+
+static int modem_powerup(const struct subsys_desc *subsys)
+{
+ struct mba_data *drv = subsys_to_drv(subsys);
+ /*
+ * At this time, the modem is shutdown. Therefore this function cannot
+ * run concurrently with either the watchdog bite error handler or the
+ * SMSM callback, making it safe to unset the flag below.
+ */
+ drv->ignore_errors = 0;
+ pil_force_boot("mba");
+ pil_force_boot("modem");
+ return 0;
+}
+
+static void modem_crash_shutdown(const struct subsys_desc *subsys)
+{
+ struct mba_data *drv = subsys_to_drv(subsys);
+ drv->crash_shutdown = true;
+ smsm_reset_modem(SMSM_RESET);
+}
+
+static struct ramdump_segment modem_segments[] = {
+ {0x08400000, 0x0D100000 - 0x08400000},
+};
+
+static struct ramdump_segment smem_segments[] = {
+ {0x0FA00000, 0x0FC00000 - 0x0FA00000},
+};
+
+static int modem_ramdump(int enable, const struct subsys_desc *subsys)
+{
+ struct mba_data *drv = subsys_to_drv(subsys);
+ int ret;
+
+ if (!enable)
+ return 0;
+
+ pil_force_boot("mba");
+
+ ret = do_ramdump(drv->ramdump_dev, modem_segments,
+ ARRAY_SIZE(modem_segments));
+ if (ret < 0) {
+ pr_err("Unable to dump modem fw memory (rc = %d).\n", ret);
+ goto out;
+ }
+
+ ret = do_ramdump(drv->smem_ramdump_dev, smem_segments,
+ ARRAY_SIZE(smem_segments));
+ if (ret < 0) {
+ pr_err("Unable to dump smem memory (rc = %d).\n", ret);
+ goto out;
+ }
+
+out:
+ pil_force_shutdown("mba");
+ return ret;
+}
+
+static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
+{
+ struct mba_data *drv = dev_id;
+ if (drv->ignore_errors)
+ return IRQ_HANDLED;
+ pr_err("Watchdog bite received from modem software!\n");
+ restart_modem(drv);
+ return IRQ_HANDLED;
+}
+
static int __devinit pil_mba_driver_probe(struct platform_device *pdev)
{
struct mba_data *drv;
struct resource *res;
struct pil_desc *desc;
- int ret;
+ int ret, irq;
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
if (!drv)
return -ENOMEM;
platform_set_drvdata(pdev, drv);
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rmb_base");
if (!res)
return -EINVAL;
@@ -215,12 +353,71 @@
if (IS_ERR(drv->pil))
return PTR_ERR(drv->pil);
+ drv->subsys_desc.name = desc->name;
+ drv->subsys_desc.dev = &pdev->dev;
+ drv->subsys_desc.owner = THIS_MODULE;
+ drv->subsys_desc.shutdown = modem_shutdown;
+ drv->subsys_desc.powerup = modem_powerup;
+ drv->subsys_desc.ramdump = modem_ramdump;
+ drv->subsys_desc.crash_shutdown = modem_crash_shutdown;
+
+ drv->ramdump_dev = create_ramdump_device("modem");
+ if (!drv->ramdump_dev) {
+ pr_err("%s: Unable to create a modem ramdump device.\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err_ramdump;
+ }
+
+ drv->smem_ramdump_dev = create_ramdump_device("smem-modem");
+ if (!drv->smem_ramdump_dev) {
+ pr_err("%s: Unable to create an smem ramdump device.\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err_ramdump_smem;
+ }
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ goto err_subsys;
+ ret = PTR_ERR(drv->subsys);
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, modem_wdog_bite_irq,
+ IRQF_TRIGGER_RISING, "modem_wdog", drv);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Unable to request watchdog IRQ.\n");
+ goto err_irq;
+ }
+
+ ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
+ smsm_state_cb, drv);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Unable to register SMSM callback!\n");
+ goto err_irq;
+ }
+
return 0;
+
+err_irq:
+ subsys_unregister(drv->subsys);
+err_subsys:
+ destroy_ramdump_device(drv->smem_ramdump_dev);
+err_ramdump_smem:
+ destroy_ramdump_device(drv->ramdump_dev);
+err_ramdump:
+ msm_pil_unregister(drv->pil);
+ return ret;
}
static int __devexit pil_mba_driver_exit(struct platform_device *pdev)
{
struct mba_data *drv = platform_get_drvdata(pdev);
+ smsm_state_cb_deregister(SMSM_MODEM_STATE, SMSM_RESET,
+ smsm_state_cb, drv);
+ subsys_unregister(drv->subsys);
+ destroy_ramdump_device(drv->smem_ramdump_dev);
+ destroy_ramdump_device(drv->ramdump_dev);
msm_pil_unregister(drv->pil);
return 0;
}
diff --git a/arch/arm/mach-msm/pil-pronto.c b/arch/arm/mach-msm/pil-pronto.c
index 1e39043..5685787 100644
--- a/arch/arm/mach-msm/pil-pronto.c
+++ b/arch/arm/mach-msm/pil-pronto.c
@@ -22,6 +22,14 @@
#include <linux/iopoll.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/wcnss_wlan.h>
+
+#include <mach/subsystem_restart.h>
+#include <mach/peripheral-loader.h>
+#include <mach/msm_smsm.h>
#include "peripheral-loader.h"
#include "scm-pas.h"
@@ -67,8 +75,14 @@
void __iomem *axi_halt_base;
unsigned long start_addr;
struct pil_device *pil;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
struct clk *cxo;
struct regulator *vreg;
+ bool restart_inprogress;
+ bool crash;
+ struct delayed_work cancel_vote_work;
+ int irq;
};
static int pil_pronto_make_proxy_vote(struct pil_desc *pil)
@@ -225,6 +239,129 @@
.proxy_unvote = pil_pronto_remove_proxy_vote,
};
+#define subsys_to_drv(d) container_of(d, struct pronto_data, subsys_desc)
+
+static void log_wcnss_sfr(void)
+{
+ char *smem_reset_reason;
+ unsigned smem_reset_size;
+
+ smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_WCNSS0,
+ &smem_reset_size);
+
+ if (!smem_reset_reason || !smem_reset_size) {
+ pr_err("wcnss subsystem failure reason:\n"
+ "(unknown, smem_get_entry failed)");
+ } else if (!smem_reset_reason[0]) {
+ pr_err("wcnss subsystem failure reason:\n"
+ "(unknown, init string found)");
+ } else {
+ pr_err("wcnss subsystem failure reason: %.81s\n",
+ smem_reset_reason);
+ memset(smem_reset_reason, 0, smem_reset_size);
+ wmb();
+ }
+}
+
+static void restart_wcnss(struct pronto_data *drv)
+{
+ log_wcnss_sfr();
+ subsystem_restart_dev(drv->subsys);
+}
+
+static void smsm_state_cb_hdlr(void *data, uint32_t old_state,
+ uint32_t new_state)
+{
+ struct pronto_data *drv = data;
+
+ drv->crash = true;
+
+ pr_err("wcnss smsm state changed\n");
+
+ if (!(new_state & SMSM_RESET))
+ return;
+
+ if (drv->restart_inprogress) {
+ pr_err("wcnss: Ignoring smsm reset req, restart in progress\n");
+ return;
+ }
+
+ drv->restart_inprogress = true;
+ restart_wcnss(drv);
+}
+
+static irqreturn_t wcnss_wdog_bite_irq_hdlr(int irq, void *dev_id)
+{
+ struct pronto_data *drv = dev_id;
+
+ drv->crash = true;
+
+ if (drv->restart_inprogress) {
+ pr_err("Ignoring wcnss bite irq, restart in progress\n");
+ return IRQ_HANDLED;
+ }
+
+ drv->restart_inprogress = true;
+ restart_wcnss(drv);
+
+ return IRQ_HANDLED;
+}
+
+static void wcnss_post_bootup(struct work_struct *work)
+{
+ struct platform_device *pdev = wcnss_get_platform_device();
+ struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
+
+ wcnss_wlan_power(&pdev->dev, pwlanconfig, WCNSS_WLAN_SWITCH_OFF);
+}
+
+static int wcnss_shutdown(const struct subsys_desc *subsys)
+{
+ struct pronto_data *drv = subsys_to_drv(subsys);
+
+ pil_force_shutdown("wcnss");
+ flush_delayed_work(&drv->cancel_vote_work);
+ wcnss_flush_delayed_boot_votes();
+ disable_irq_nosync(drv->irq);
+
+ return 0;
+}
+
+static int wcnss_powerup(const struct subsys_desc *subsys)
+{
+ struct pronto_data *drv = subsys_to_drv(subsys);
+ struct platform_device *pdev = wcnss_get_platform_device();
+ struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
+ int ret = -1;
+
+ if (pdev && pwlanconfig)
+ ret = wcnss_wlan_power(&pdev->dev, pwlanconfig,
+ WCNSS_WLAN_SWITCH_ON);
+ if (!ret) {
+ msleep(1000);
+ pil_force_boot("wcnss");
+ }
+ drv->restart_inprogress = false;
+ enable_irq(drv->irq);
+ schedule_delayed_work(&drv->cancel_vote_work, msecs_to_jiffies(5000));
+
+ return 0;
+}
+
+static void crash_shutdown(const struct subsys_desc *subsys)
+{
+ struct pronto_data *drv = subsys_to_drv(subsys);
+
+ pr_err("wcnss crash shutdown %d\n", drv->crash);
+ if (!drv->crash)
+ smsm_change_state(SMSM_APPS_STATE, SMSM_RESET, SMSM_RESET);
+}
+
+static int wcnss_ramdump(int enable, const struct subsys_desc *crashed_subsys)
+{
+ return 0;
+}
+
static int __devinit pil_pronto_probe(struct platform_device *pdev)
{
struct pronto_data *drv;
@@ -242,6 +379,10 @@
return -ENOMEM;
platform_set_drvdata(pdev, drv);
+ drv->irq = platform_get_irq(pdev, 0);
+ if (drv->irq < 0)
+ return drv->irq;
+
drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!drv->base)
return -ENOMEM;
@@ -303,6 +444,32 @@
if (IS_ERR(drv->pil))
return PTR_ERR(drv->pil);
+ ret = smsm_state_cb_register(SMSM_WCNSS_STATE, SMSM_RESET,
+ smsm_state_cb_hdlr, drv);
+ if (ret < 0)
+ goto err_smsm;
+
+ drv->subsys_desc.name = desc->name;
+ drv->subsys_desc.dev = &pdev->dev;
+ drv->subsys_desc.owner = THIS_MODULE;
+ drv->subsys_desc.shutdown = wcnss_shutdown;
+ drv->subsys_desc.powerup = wcnss_powerup;
+ drv->subsys_desc.ramdump = wcnss_ramdump;
+ drv->subsys_desc.crash_shutdown = crash_shutdown;
+
+ INIT_DELAYED_WORK(&drv->cancel_vote_work, wcnss_post_bootup);
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ ret = PTR_ERR(drv->subsys);
+ goto err_subsys;
+ }
+
+ ret = devm_request_irq(&pdev->dev, drv->irq, wcnss_wdog_bite_irq_hdlr,
+ IRQF_TRIGGER_HIGH, "wcnss_wdog", drv);
+ if (ret < 0)
+ goto err_irq;
+
/* Initialize common_ss GDSCR to wait 4 cycles between states */
regval = readl_relaxed(drv->base + PRONTO_PMU_COMMON_GDSCR)
& PRONTO_PMU_COMMON_GDSCR_SW_COLLAPSE;
@@ -311,11 +478,22 @@
writel_relaxed(regval, drv->base + PRONTO_PMU_COMMON_GDSCR);
return 0;
+err_irq:
+ subsys_unregister(drv->subsys);
+err_subsys:
+ smsm_state_cb_deregister(SMSM_WCNSS_STATE, SMSM_RESET,
+ smsm_state_cb_hdlr, drv);
+err_smsm:
+ msm_pil_unregister(drv->pil);
+ return ret;
}
static int __devexit pil_pronto_remove(struct platform_device *pdev)
{
struct pronto_data *drv = platform_get_drvdata(pdev);
+ subsys_unregister(drv->subsys);
+ smsm_state_cb_deregister(SMSM_WCNSS_STATE, SMSM_RESET,
+ smsm_state_cb_hdlr, drv);
msm_pil_unregister(drv->pil);
return 0;
}
diff --git a/arch/arm/mach-msm/pil-q6v5-lpass.c b/arch/arm/mach-msm/pil-q6v5-lpass.c
index ed072ea..c48ea02 100644
--- a/arch/arm/mach-msm/pil-q6v5-lpass.c
+++ b/arch/arm/mach-msm/pil-q6v5-lpass.c
@@ -18,14 +18,39 @@
#include <linux/err.h>
#include <linux/of.h>
#include <linux/clk.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
#include <mach/clk.h>
+#include <mach/subsystem_restart.h>
+#include <mach/subsystem_notif.h>
+#include <mach/scm.h>
+#include <mach/peripheral-loader.h>
+
#include "peripheral-loader.h"
#include "pil-q6v5.h"
#include "scm-pas.h"
+#include "ramdump.h"
+#include "sysmon.h"
#define QDSP6SS_RST_EVB 0x010
#define PROXY_TIMEOUT_MS 10000
+struct lpass_data {
+ struct q6v5_data *q6;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
+ void *ramdump_dev;
+ int wdog_irq;
+ struct work_struct work;
+ void *riva_notif_hdle;
+ void *modem_notif_hdle;
+ int crash_shutdown;
+};
+
+#define subsys_to_drv(d) container_of(d, struct lpass_data, subsys_desc)
+
static int pil_lpass_enable_clks(struct q6v5_data *drv)
{
int ret;
@@ -71,7 +96,7 @@
static int pil_lpass_shutdown(struct pil_desc *pil)
{
- struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
pil_q6v5_halt_axi_port(pil, drv->axi_halt_base);
@@ -93,7 +118,7 @@
static int pil_lpass_reset(struct pil_desc *pil)
{
- struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
int ret;
ret = pil_lpass_enable_clks(drv);
@@ -147,38 +172,211 @@
.shutdown = pil_lpass_shutdown_trusted,
};
+static int riva_notifier_cb(struct notifier_block *this, unsigned long code,
+ void *ss_handle)
+{
+ int ret;
+ switch (code) {
+ case SUBSYS_BEFORE_SHUTDOWN:
+ pr_debug("%s: R-Notify: Shutdown started\n", __func__);
+ ret = sysmon_send_event(SYSMON_SS_LPASS, "wcnss",
+ SUBSYS_BEFORE_SHUTDOWN);
+ if (ret < 0)
+ pr_err("%s: sysmon_send_event error %d", __func__, ret);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block rnb = {
+ .notifier_call = riva_notifier_cb,
+};
+
+static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
+ void *ss_handle)
+{
+ int ret;
+ switch (code) {
+ case SUBSYS_BEFORE_SHUTDOWN:
+ pr_debug("%s: M-Notify: Shutdown started\n", __func__);
+ ret = sysmon_send_event(SYSMON_SS_LPASS, "modem",
+ SUBSYS_BEFORE_SHUTDOWN);
+ if (ret < 0)
+ pr_err("%s: sysmon_send_event error %d", __func__, ret);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block mnb = {
+ .notifier_call = modem_notifier_cb,
+};
+
+static void adsp_log_failure_reason(void)
+{
+ char *reason;
+ char buffer[81];
+ unsigned size;
+
+ reason = smem_get_entry(SMEM_SSR_REASON_LPASS0, &size);
+
+ if (!reason) {
+ pr_err("ADSP subsystem failure reason: (unknown, smem_get_entry failed).");
+ return;
+ }
+
+ if (reason[0] == '\0') {
+ pr_err("ADSP subsystem failure reason: (unknown, init value found)");
+ return;
+ }
+
+ size = min(size, sizeof(buffer) - 1);
+ memcpy(buffer, reason, size);
+ buffer[size] = '\0';
+ pr_err("ADSP subsystem failure reason: %s", buffer);
+ memset((void *)reason, 0x0, size);
+ wmb();
+}
+
+static void restart_adsp(struct lpass_data *drv)
+{
+ adsp_log_failure_reason();
+ subsystem_restart_dev(drv->subsys);
+}
+
+static void adsp_fatal_fn(struct work_struct *work)
+{
+ struct lpass_data *drv = container_of(work, struct lpass_data, work);
+
+ pr_err("Watchdog bite received from ADSP!\n");
+ restart_adsp(drv);
+}
+
+static void adsp_smsm_state_cb(void *data, uint32_t old_state,
+ uint32_t new_state)
+{
+ struct lpass_data *drv = data;
+
+ /* Ignore if we're the one that set SMSM_RESET */
+ if (drv->crash_shutdown)
+ return;
+
+ if (new_state & SMSM_RESET) {
+ pr_err("%s: ADSP SMSM state changed to SMSM_RESET, new_state = %#x, old_state = %#x\n",
+ __func__, new_state, old_state);
+ restart_adsp(drv);
+ }
+}
+
+#define SCM_Q6_NMI_CMD 0x1
+
+static void send_q6_nmi(void)
+{
+ /* Send NMI to QDSP6 via an SCM call. */
+ scm_call_atomic1(SCM_SVC_UTIL, SCM_Q6_NMI_CMD, 0x1);
+ pr_debug("%s: Q6 NMI was sent.\n", __func__);
+}
+
+#define subsys_to_lpass(d) container_of(d, struct lpass_data, subsys_desc)
+
+static int adsp_shutdown(const struct subsys_desc *subsys)
+{
+ struct lpass_data *drv = subsys_to_lpass(subsys);
+
+ send_q6_nmi();
+ /* The write needs to go through before the q6 is shutdown. */
+ mb();
+ pil_force_shutdown("adsp");
+ disable_irq_nosync(drv->wdog_irq);
+
+ return 0;
+}
+
+static int adsp_powerup(const struct subsys_desc *subsys)
+{
+ struct lpass_data *drv = subsys_to_lpass(subsys);
+ int ret = 0;
+
+ if (get_restart_level() == RESET_SUBSYS_INDEPENDENT) {
+ pr_debug("%s: Wait for ADSP power up!", __func__);
+ msleep(10000);
+ }
+
+ ret = pil_force_boot("adsp");
+ enable_irq(drv->wdog_irq);
+
+ return ret;
+}
+
+static struct ramdump_segment segments = { 0xdc00000, 0x1800000 };
+
+static int adsp_ramdump(int enable, const struct subsys_desc *subsys)
+{
+ struct lpass_data *drv = subsys_to_lpass(subsys);
+
+ if (!enable)
+ return 0;
+ return do_ramdump(drv->ramdump_dev, &segments, 1);
+}
+
+static void adsp_crash_shutdown(const struct subsys_desc *subsys)
+{
+ struct lpass_data *drv = subsys_to_lpass(subsys);
+
+ drv->crash_shutdown = 1;
+ send_q6_nmi();
+}
+
+static irqreturn_t adsp_wdog_bite_irq(int irq, void *dev_id)
+{
+ struct lpass_data *drv = dev_id;
+
+ disable_irq_nosync(drv->wdog_irq);
+ schedule_work(&drv->work);
+
+ return IRQ_HANDLED;
+}
+
static int __devinit pil_lpass_driver_probe(struct platform_device *pdev)
{
- struct q6v5_data *drv;
+ struct lpass_data *drv;
+ struct q6v5_data *q6;
struct pil_desc *desc;
+ int ret;
- desc = pil_q6v5_init(pdev);
- if (IS_ERR(desc))
- return PTR_ERR(desc);
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, drv);
- drv = platform_get_drvdata(pdev);
- if (drv == NULL)
- return -ENODEV;
+ drv->wdog_irq = platform_get_irq(pdev, 0);
+ if (drv->wdog_irq < 0)
+ return drv->wdog_irq;
- desc->ops = &pil_lpass_ops;
+ q6 = pil_q6v5_init(pdev);
+ if (IS_ERR(q6))
+ return PTR_ERR(q6);
+ drv->q6 = q6;
+
+ desc = &q6->desc;
desc->owner = THIS_MODULE;
desc->proxy_timeout = PROXY_TIMEOUT_MS;
- drv->core_clk = devm_clk_get(&pdev->dev, "core_clk");
- if (IS_ERR(drv->core_clk))
- return PTR_ERR(drv->core_clk);
+ q6->core_clk = devm_clk_get(&pdev->dev, "core_clk");
+ if (IS_ERR(q6->core_clk))
+ return PTR_ERR(q6->core_clk);
- drv->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
- if (IS_ERR(drv->ahb_clk))
- return PTR_ERR(drv->ahb_clk);
+ q6->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
+ if (IS_ERR(q6->ahb_clk))
+ return PTR_ERR(q6->ahb_clk);
- drv->axi_clk = devm_clk_get(&pdev->dev, "bus_clk");
- if (IS_ERR(drv->axi_clk))
- return PTR_ERR(drv->axi_clk);
+ q6->axi_clk = devm_clk_get(&pdev->dev, "bus_clk");
+ if (IS_ERR(q6->axi_clk))
+ return PTR_ERR(q6->axi_clk);
- drv->reg_clk = devm_clk_get(&pdev->dev, "reg_clk");
- if (IS_ERR(drv->reg_clk))
- return PTR_ERR(drv->reg_clk);
+ q6->reg_clk = devm_clk_get(&pdev->dev, "reg_clk");
+ if (IS_ERR(q6->reg_clk))
+ return PTR_ERR(q6->reg_clk);
if (pas_supported(PAS_Q6) > 0) {
desc->ops = &pil_lpass_ops_trusted;
@@ -188,17 +386,79 @@
dev_info(&pdev->dev, "using non-secure boot\n");
}
- drv->pil = msm_pil_register(desc);
- if (IS_ERR(drv->pil))
- return PTR_ERR(drv->pil);
+ drv->q6->pil = msm_pil_register(desc);
+ if (IS_ERR(drv->q6->pil))
+ return PTR_ERR(drv->q6->pil);
+ drv->subsys_desc.name = desc->name;
+ drv->subsys_desc.owner = THIS_MODULE;
+ drv->subsys_desc.dev = &pdev->dev;
+ drv->subsys_desc.shutdown = adsp_shutdown;
+ drv->subsys_desc.powerup = adsp_powerup;
+ drv->subsys_desc.ramdump = adsp_ramdump;
+ drv->subsys_desc.crash_shutdown = adsp_crash_shutdown;
+
+ INIT_WORK(&drv->work, adsp_fatal_fn);
+
+ drv->ramdump_dev = create_ramdump_device("adsp");
+ if (!drv->ramdump_dev) {
+ ret = -ENOMEM;
+ goto err_ramdump;
+ }
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ ret = PTR_ERR(drv->subsys);
+ goto err_subsys;
+ }
+
+ ret = devm_request_irq(&pdev->dev, drv->wdog_irq, adsp_wdog_bite_irq,
+ IRQF_TRIGGER_RISING, dev_name(&pdev->dev), drv);
+ if (ret)
+ goto err_irq;
+
+ ret = smsm_state_cb_register(SMSM_Q6_STATE, SMSM_RESET,
+ adsp_smsm_state_cb, drv);
+ if (ret < 0)
+ goto err_smsm;
+
+ drv->riva_notif_hdle = subsys_notif_register_notifier("riva", &rnb);
+ if (IS_ERR(drv->riva_notif_hdle)) {
+ ret = PTR_ERR(drv->riva_notif_hdle);
+ goto err_notif_riva;
+ }
+
+ drv->modem_notif_hdle = subsys_notif_register_notifier("modem", &mnb);
+ if (IS_ERR(drv->modem_notif_hdle)) {
+ ret = PTR_ERR(drv->modem_notif_hdle);
+ goto err_notif_modem;
+ }
+ return 0;
+err_notif_modem:
+ subsys_notif_unregister_notifier(drv->riva_notif_hdle, &rnb);
+err_notif_riva:
+ smsm_state_cb_deregister(SMSM_Q6_STATE, SMSM_RESET,
+ adsp_smsm_state_cb, drv);
+err_smsm:
+err_irq:
+ subsys_unregister(drv->subsys);
+err_subsys:
+ destroy_ramdump_device(drv->ramdump_dev);
+err_ramdump:
+ msm_pil_unregister(drv->q6->pil);
return 0;
}
static int __devexit pil_lpass_driver_exit(struct platform_device *pdev)
{
- struct q6v5_data *drv = platform_get_drvdata(pdev);
- msm_pil_unregister(drv->pil);
+ struct lpass_data *drv = platform_get_drvdata(pdev);
+ subsys_notif_unregister_notifier(drv->riva_notif_hdle, &rnb);
+ subsys_notif_unregister_notifier(drv->modem_notif_hdle, &mnb);
+ smsm_state_cb_deregister(SMSM_Q6_STATE, SMSM_RESET,
+ adsp_smsm_state_cb, drv);
+ subsys_unregister(drv->subsys);
+ destroy_ramdump_device(drv->ramdump_dev);
+ msm_pil_unregister(drv->q6->pil);
return 0;
}
diff --git a/arch/arm/mach-msm/pil-q6v5-mss.c b/arch/arm/mach-msm/pil-q6v5-mss.c
index 9ff1234..f3c731f 100644
--- a/arch/arm/mach-msm/pil-q6v5-mss.c
+++ b/arch/arm/mach-msm/pil-q6v5-mss.c
@@ -57,10 +57,10 @@
static int pbl_mba_boot_timeout_ms = 100;
module_param(pbl_mba_boot_timeout_ms, int, S_IRUGO | S_IWUSR);
-static int pil_mss_power_up(struct device *dev)
+static int pil_mss_power_up(struct q6v5_data *drv)
{
int ret;
- struct q6v5_data *drv = dev_get_drvdata(dev);
+ struct device *dev = drv->desc.dev;
ret = regulator_enable(drv->vreg);
if (ret)
@@ -69,10 +69,8 @@
return ret;
}
-static int pil_mss_power_down(struct device *dev)
+static int pil_mss_power_down(struct q6v5_data *drv)
{
- struct q6v5_data *drv = dev_get_drvdata(dev);
-
return regulator_disable(drv->vreg);
}
@@ -107,9 +105,9 @@
clk_disable_unprepare(drv->ahb_clk);
}
-static int wait_for_mba_ready(struct device *dev)
+static int wait_for_mba_ready(struct q6v5_data *drv)
{
- struct q6v5_data *drv = dev_get_drvdata(dev);
+ struct device *dev = drv->desc.dev;
int ret;
u32 status;
@@ -143,7 +141,7 @@
static int pil_mss_shutdown(struct pil_desc *pil)
{
- struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_Q6_HALT_BASE);
pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_MODEM_HALT_BASE);
@@ -155,13 +153,13 @@
* writes performed during the shutdown succeed.
*/
if (drv->is_booted == false) {
- pil_mss_power_up(pil->dev);
+ pil_mss_power_up(drv);
pil_mss_enable_clks(drv);
}
pil_q6v5_shutdown(pil);
pil_mss_disable_clks(drv);
- pil_mss_power_down(pil->dev);
+ pil_mss_power_down(drv);
writel_relaxed(1, drv->restart_reg);
@@ -172,7 +170,7 @@
static int pil_mss_reset(struct pil_desc *pil)
{
- struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
int ret;
/* Deassert reset to subsystem and wait for propagation */
@@ -184,7 +182,7 @@
* Bring subsystem out of reset and enable required
* regulators and clocks.
*/
- ret = pil_mss_power_up(pil->dev);
+ ret = pil_mss_power_up(drv);
if (ret)
goto err_power;
@@ -211,7 +209,7 @@
/* Wait for MBA to start. Check for PBL and MBA errors while waiting. */
if (drv->self_auth) {
- ret = wait_for_mba_ready(pil->dev);
+ ret = wait_for_mba_ready(drv);
if (ret)
goto err_auth;
}
@@ -225,7 +223,7 @@
err_q6v5_reset:
pil_mss_disable_clks(drv);
err_clks:
- pil_mss_power_down(pil->dev);
+ pil_mss_power_down(drv);
err_power:
return ret;
}
@@ -245,13 +243,12 @@
struct resource *res;
int ret;
- desc = pil_q6v5_init(pdev);
- if (IS_ERR(desc))
- return PTR_ERR(desc);
- drv = platform_get_drvdata(pdev);
- if (drv == NULL)
- return -ENODEV;
+ drv = pil_q6v5_init(pdev);
+ if (IS_ERR(drv))
+ return PTR_ERR(drv);
+ platform_set_drvdata(pdev, drv);
+ desc = &drv->desc;
desc->ops = &pil_mss_ops;
desc->owner = THIS_MODULE;
desc->proxy_timeout = PROXY_TIMEOUT_MS;
diff --git a/arch/arm/mach-msm/pil-q6v5.c b/arch/arm/mach-msm/pil-q6v5.c
index f4e8844..70a12de 100644
--- a/arch/arm/mach-msm/pil-q6v5.c
+++ b/arch/arm/mach-msm/pil-q6v5.c
@@ -61,7 +61,7 @@
int pil_q6v5_make_proxy_votes(struct pil_desc *pil)
{
int ret;
- struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
ret = clk_prepare_enable(drv->xo);
if (ret) {
@@ -74,7 +74,7 @@
void pil_q6v5_remove_proxy_votes(struct pil_desc *pil)
{
- struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
clk_disable_unprepare(drv->xo);
}
EXPORT_SYMBOL(pil_q6v5_remove_proxy_votes);
@@ -104,7 +104,7 @@
size_t size)
{
const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
- struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
drv->start_addr = ehdr->e_entry;
return 0;
}
@@ -113,7 +113,7 @@
void pil_q6v5_shutdown(struct pil_desc *pil)
{
u32 val;
- struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
/* Turn off core clock */
val = readl_relaxed(drv->reg_base + QDSP6SS_GFMUX_CTL);
@@ -145,7 +145,7 @@
int pil_q6v5_reset(struct pil_desc *pil)
{
- struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
u32 val;
/* Assert resets, stop core */
@@ -193,7 +193,7 @@
}
EXPORT_SYMBOL(pil_q6v5_reset);
-struct pil_desc __devinit *pil_q6v5_init(struct platform_device *pdev)
+struct q6v5_data __devinit *pil_q6v5_init(struct platform_device *pdev)
{
struct q6v5_data *drv;
struct resource *res;
@@ -203,7 +203,6 @@
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
if (!drv)
return ERR_PTR(-ENOMEM);
- platform_set_drvdata(pdev, drv);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6_base");
if (!res)
@@ -218,10 +217,7 @@
if (!drv->axi_halt_base)
return ERR_PTR(-ENOMEM);
- desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
- if (!desc)
- return ERR_PTR(-ENOMEM);
-
+ desc = &drv->desc;
ret = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name",
&desc->name);
if (ret)
@@ -233,6 +229,6 @@
desc->dev = &pdev->dev;
- return desc;
+ return drv;
}
EXPORT_SYMBOL(pil_q6v5_init);
diff --git a/arch/arm/mach-msm/pil-q6v5.h b/arch/arm/mach-msm/pil-q6v5.h
index 03f93fa..f176d2d 100644
--- a/arch/arm/mach-msm/pil-q6v5.h
+++ b/arch/arm/mach-msm/pil-q6v5.h
@@ -13,10 +13,11 @@
#ifndef __MSM_PIL_Q6V5_H
#define __MSM_PIL_Q6V5_H
+#include "peripheral-loader.h"
+
struct regulator;
struct clk;
struct pil_device;
-struct pil_desc;
struct platform_device;
struct q6v5_data {
@@ -36,6 +37,7 @@
bool is_booted;
int self_auth;
struct pil_device *pil;
+ struct pil_desc desc;
};
int pil_q6v5_make_proxy_votes(struct pil_desc *pil);
@@ -45,6 +47,6 @@
size_t size);
void pil_q6v5_shutdown(struct pil_desc *pil);
int pil_q6v5_reset(struct pil_desc *pil);
-struct pil_desc *pil_q6v5_init(struct platform_device *pdev);
+struct q6v5_data *pil_q6v5_init(struct platform_device *pdev);
#endif
diff --git a/arch/arm/mach-msm/platsmp-8910.c b/arch/arm/mach-msm/platsmp-8910.c
new file mode 100644
index 0000000..5d055bd
--- /dev/null
+++ b/arch/arm/mach-msm/platsmp-8910.c
@@ -0,0 +1,190 @@
+/* Copyright (c) 2012, 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/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/smp.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include <asm/cacheflush.h>
+#include <asm/smp_plat.h>
+#include <asm/hardware/gic.h>
+#include <mach/msm_iomap.h>
+#include "pm.h"
+
+#define BOOT_REMAP_ENABLE 0x01
+
+/*
+ * control for which core is the next to come out of the secondary
+ * boot "holding pen"
+ */
+volatile int __cpuinitdata pen_release = -1;
+
+/*
+ * Write pen_release in a way that is guaranteed to be visible to all
+ * observers, irrespective of whether they're taking part in coherency
+ * or not. This is necessary for the hotplug code to work reliably.
+ */
+static void __cpuinit write_pen_release(int val)
+{
+ pen_release = val;
+ smp_wmb();
+ __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
+ outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
+}
+
+static DEFINE_SPINLOCK(boot_lock);
+
+void __cpuinit platform_secondary_init(unsigned int cpu)
+{
+ WARN_ON(msm_platform_secondary_init(cpu));
+
+ /*
+ * if any interrupts are already enabled for the primary
+ * core (e.g. timer irq), then they will not have been enabled
+ * for us: do so
+ */
+ gic_secondary_init(0);
+
+ /*
+ * let the primary processor know we're out of the
+ * pen, then head off into the C entry point
+ */
+ write_pen_release(-1);
+
+ /*
+ * Synchronise with the boot thread.
+ */
+ spin_lock(&boot_lock);
+ spin_unlock(&boot_lock);
+}
+
+static int __cpuinit release_secondary_sim(unsigned long base, int cpu)
+{
+ void *base_ptr;
+
+ base_ptr = ioremap_nocache(base + (cpu * 0x10000), SZ_4K);
+ if (!base_ptr) {
+ pr_err("Failed to release core %u\n", cpu);
+ return -ENODEV;
+ }
+
+ writel_relaxed(0x800, base_ptr+0x04);
+ writel_relaxed(0x3FFF, base_ptr+0x14);
+ mb();
+
+ return 0;
+}
+
+DEFINE_PER_CPU(int, cold_boot_done);
+
+int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ unsigned long timeout;
+
+ preset_lpj = loops_per_jiffy;
+
+ if (per_cpu(cold_boot_done, cpu) == false) {
+ release_secondary_sim(0xF9088000, cpu);
+ per_cpu(cold_boot_done, cpu) = true;
+ }
+
+ /*
+ * Set synchronisation state between this boot processor
+ * and the secondary one
+ */
+ spin_lock(&boot_lock);
+
+ /*
+ * The secondary processor is waiting to be released from
+ * the holding pen - release it, then wait for it to flag
+ * that it has been released by resetting pen_release.
+ *
+ * Note that "pen_release" is the hardware CPU ID, whereas
+ * "cpu" is Linux's internal ID.
+ */
+ write_pen_release(cpu_logical_map(cpu));
+
+ /*
+ * Send the secondary CPU a soft interrupt, thereby causing
+ * the boot monitor to read the system wide flags register,
+ * and branch to the address found there.
+ */
+
+ gic_raise_softirq(cpumask_of(cpu), 1);
+
+ timeout = jiffies + (1 * HZ);
+ while (time_before(jiffies, timeout)) {
+ smp_rmb();
+ if (pen_release == -1)
+ break;
+
+ udelay(10);
+ }
+
+ /*
+ * now the secondary core is starting up let it run its
+ * calibrations, then wait for it to finish
+ */
+ spin_unlock(&boot_lock);
+
+ return 0;
+}
+
+/*
+ * Initialise the CPU possible map early - this describes the CPUs
+ * which may be present or become present in the system
+ */
+void __init smp_init_cpus(void)
+{
+ unsigned int i, ncores;
+
+ ncores = (__raw_readl(MSM_APCS_GCC_BASE + 0x30)) & 0xF;
+
+ for (i = 0; i < ncores; i++)
+ set_cpu_possible(i, true);
+
+ set_smp_cross_call(gic_raise_softirq);
+}
+
+void __init platform_smp_prepare_cpus(unsigned int max_cpus)
+{
+ int i;
+ void __iomem *remap_ptr;
+
+ /*
+ * Initialise the present map, which describes the set of CPUs
+ * actually populated at the present time
+ */
+ for (i = 0; i < max_cpus; i++)
+ set_cpu_present(i, true);
+
+ /*
+ * Enable boot remapping and write the address of secondary
+ * startup into boot remapper register
+ */
+ remap_ptr = ioremap_nocache(0xF9010000, SZ_4K); /* APCS_CFG_SECURE */
+ if (!remap_ptr) {
+ pr_err("Failed to ioremap for secondary cores\n");
+ return;
+ }
+
+ __raw_writel((virt_to_phys(msm_secondary_startup)|BOOT_REMAP_ENABLE),
+ (remap_ptr + 0x4));
+ mb();
+ iounmap(remap_ptr);
+}
diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index 0339c21..5c40750 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.c
@@ -781,11 +781,17 @@
switch (mode) {
case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
+ if (num_online_cpus() > 1) {
+ allow = false;
+ break;
+ }
+ /* fall through */
case MSM_PM_SLEEP_MODE_RETENTION:
if (!allow)
break;
- if (num_online_cpus() > 1) {
+ if (msm_pm_retention_tz_call &&
+ num_online_cpus() > 1) {
allow = false;
break;
}
diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c
index 969af98..0beb952 100644
--- a/arch/arm/mach-msm/socinfo.c
+++ b/arch/arm/mach-msm/socinfo.c
@@ -35,6 +35,8 @@
HW_PLATFORM_LIQUID = 9,
/* Dragonboard platform id is assigned as 10 in CDT */
HW_PLATFORM_DRAGON = 10,
+ HW_PLATFORM_HRD = 13,
+ HW_PLATFORM_DTV = 14,
HW_PLATFORM_INVALID
};
@@ -47,7 +49,9 @@
[HW_PLATFORM_SVLTE_SURF] = "SLVTE_SURF",
[HW_PLATFORM_MTP] = "MTP",
[HW_PLATFORM_LIQUID] = "Liquid",
- [HW_PLATFORM_DRAGON] = "Dragon"
+ [HW_PLATFORM_DRAGON] = "Dragon",
+ [HW_PLATFORM_HRD] = "HRD",
+ [HW_PLATFORM_DTV] = "DTV",
};
enum {
diff --git a/arch/arm/mach-msm/spm_devices.c b/arch/arm/mach-msm/spm_devices.c
index 2cbed94..12a6f08 100644
--- a/arch/arm/mach-msm/spm_devices.c
+++ b/arch/arm/mach-msm/spm_devices.c
@@ -48,7 +48,6 @@
static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);
-/* Must be called on the same cpu as the one being set to */
static void msm_spm_smp_set_vdd(void *data)
{
struct msm_spm_device *dev;
@@ -66,10 +65,27 @@
info.cpu = cpu;
info.vlevel = vlevel;
- /* Set to true to block on vdd change */
- ret = smp_call_function_single(cpu, msm_spm_smp_set_vdd, &info, true);
- if (!ret)
+ if (cpu_online(cpu)) {
+ /**
+ * We do not want to set the voltage of another core from
+ * this core, as its possible that we may race the vdd change
+ * with the SPM state machine of that core, which could also
+ * be changing the voltage of that core during power collapse.
+ * Hence, set the function to be executed on that core and block
+ * until the vdd change is complete.
+ */
+ ret = smp_call_function_single(cpu, msm_spm_smp_set_vdd,
+ &info, true);
+ if (!ret)
+ ret = info.err;
+ } else {
+ /**
+ * Since the core is not online, it is safe to set the vdd
+ * directly.
+ */
+ msm_spm_smp_set_vdd(&info);
ret = info.err;
+ }
return ret;
}
diff --git a/arch/arm/mach-msm/tz_log.c b/arch/arm/mach-msm/tz_log.c
index db797cd..5a7ec69 100644
--- a/arch/arm/mach-msm/tz_log.c
+++ b/arch/arm/mach-msm/tz_log.c
@@ -496,17 +496,23 @@
*/
tzdiag_phy_iobase = readl_relaxed(virt_iobase);
- /*
- * Map the 4KB diagnostic information area
- */
- tzdbg.virt_iobase = devm_ioremap_nocache(&pdev->dev,
- tzdiag_phy_iobase, DEBUG_MAX_RW_BUF);
+ if (!pdev->dev.of_node) {
- if (!tzdbg.virt_iobase) {
- dev_err(&pdev->dev,
- "%s: ERROR could not ioremap: start=%p, len=%u\n",
- __func__, (void *) tzdiag_phy_iobase, DEBUG_MAX_RW_BUF);
- return -ENXIO;
+ /*
+ * Map the 4KB diagnostic information area
+ */
+ tzdbg.virt_iobase = devm_ioremap_nocache(&pdev->dev,
+ tzdiag_phy_iobase, DEBUG_MAX_RW_BUF);
+
+ if (!tzdbg.virt_iobase) {
+ dev_err(&pdev->dev,
+ "%s: ERROR could not ioremap: start=%p, len=%u\n",
+ __func__, (void *) tzdiag_phy_iobase,
+ DEBUG_MAX_RW_BUF);
+ return -ENXIO;
+ }
+ } else {
+ tzdbg.virt_iobase = virt_iobase;
}
ptr = kzalloc(DEBUG_MAX_RW_BUF, GFP_KERNEL);
diff --git a/arch/arm/mach-msm/wcnss-ssr-8974.c b/arch/arm/mach-msm/wcnss-ssr-8974.c
deleted file mode 100644
index 8841b52..0000000
--- a/arch/arm/mach-msm/wcnss-ssr-8974.c
+++ /dev/null
@@ -1,200 +0,0 @@
-/* Copyright (c) 2012, 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/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/err.h>
-#include <linux/platform_device.h>
-#include <linux/workqueue.h>
-#include <linux/wcnss_wlan.h>
-#include <linux/delay.h>
-#include <mach/peripheral-loader.h>
-#include <mach/subsystem_restart.h>
-#include <mach/msm_smsm.h>
-
-#define MODULE_NAME "wcnss_8974"
-#define MAX_SSR_REASON_LEN 0x51
-
-static int ss_restart_inprogress;
-static int wcnss_crash;
-static struct subsys_device *wcnss_ssr_dev;
-static struct delayed_work cancel_vote_work;
-
-#define WCNSS_APSS_WDOG_BITE_RESET_RDY_IRQ 181
-
-static void log_wcnss_sfr(void)
-{
- char *smem_reset_reason;
- char buffer[MAX_SSR_REASON_LEN];
- unsigned smem_reset_size;
- unsigned size;
-
- smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_WCNSS0,
- &smem_reset_size);
-
- if (!smem_reset_reason || !smem_reset_size) {
- pr_err("%s: wcnss subsystem failure reason: %s\n",
- __func__, "(unknown, smem_get_entry failed)");
- } else if (!smem_reset_reason[0]) {
- pr_err("%s: wcnss subsystem failure reason: %s\n",
- __func__, "(unknown, init string found)");
- } else {
- size = smem_reset_size < MAX_SSR_REASON_LEN ? smem_reset_size :
- (MAX_SSR_REASON_LEN - 1);
- memcpy(buffer, smem_reset_reason, size);
- buffer[size] = '\0';
- pr_err("%s: wcnss subsystem failure reason: %s\n",
- __func__, buffer);
- memset(smem_reset_reason, 0, smem_reset_size);
- wmb();
- }
-}
-
-static void restart_wcnss(void)
-{
- log_wcnss_sfr();
- subsystem_restart("wcnss");
-}
-
-static void smsm_state_cb_hdlr(void *data, uint32_t old_state,
- uint32_t new_state)
-{
- wcnss_crash = true;
-
- pr_err("%s: smsm state changed\n", MODULE_NAME);
-
- if (!(new_state & SMSM_RESET))
- return;
-
- if (ss_restart_inprogress) {
- pr_err("%s: Ignoring smsm reset req, restart in progress\n",
- MODULE_NAME);
- return;
- }
-
- ss_restart_inprogress = true;
- restart_wcnss();
-}
-
-
-static irqreturn_t wcnss_wdog_bite_irq_hdlr(int irq, void *dev_id)
-{
- wcnss_crash = true;
-
- if (ss_restart_inprogress) {
- pr_err("%s: Ignoring wcnss bite irq, restart in progress\n",
- MODULE_NAME);
- return IRQ_HANDLED;
- }
-
- ss_restart_inprogress = true;
- restart_wcnss();
-
- return IRQ_HANDLED;
-}
-
-static void wcnss_post_bootup(struct work_struct *work)
-{
- struct platform_device *pdev = wcnss_get_platform_device();
- struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
-
- pr_debug(MODULE_NAME ": Cancel APPS vote for Iris & Pronto\n");
-
- wcnss_wlan_power(&pdev->dev, pwlanconfig,
- WCNSS_WLAN_SWITCH_OFF);
-}
-
-static int wcnss_shutdown(const struct subsys_desc *subsys)
-{
- pil_force_shutdown("wcnss");
- flush_delayed_work(&cancel_vote_work);
- wcnss_flush_delayed_boot_votes();
- disable_irq_nosync(WCNSS_APSS_WDOG_BITE_RESET_RDY_IRQ);
-
- return 0;
-}
-
-static int wcnss_powerup(const struct subsys_desc *subsys)
-{
- struct platform_device *pdev = wcnss_get_platform_device();
- struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
- int ret = -1;
-
- if (pdev && pwlanconfig)
- ret = wcnss_wlan_power(&pdev->dev, pwlanconfig,
- WCNSS_WLAN_SWITCH_ON);
- if (!ret) {
- msleep(1000);
- pil_force_boot("wcnss");
- }
- ss_restart_inprogress = false;
- enable_irq(WCNSS_APSS_WDOG_BITE_RESET_RDY_IRQ);
- schedule_delayed_work(&cancel_vote_work, msecs_to_jiffies(5000));
-
- return 0;
-}
-
-/* wcnss crash handler */
-static void wcnss_crash_shutdown(const struct subsys_desc *subsys)
-{
- pr_err("%s: crash shutdown : %d\n", MODULE_NAME, wcnss_crash);
- if (wcnss_crash != true)
- smsm_change_state(SMSM_APPS_STATE, SMSM_RESET, SMSM_RESET);
-}
-
-static int wcnss_ramdump(int enable,
- const struct subsys_desc *crashed_subsys)
-{
- return 0;
-}
-
-static struct subsys_desc wcnss_ssr = {
- .name = "wcnss",
- .shutdown = wcnss_shutdown,
- .powerup = wcnss_powerup,
- .ramdump = wcnss_ramdump,
- .crash_shutdown = wcnss_crash_shutdown
-};
-
-static int __init wcnss_ssr_init(void)
-{
- int ret;
-
- ret = smsm_state_cb_register(SMSM_WCNSS_STATE, SMSM_RESET,
- smsm_state_cb_hdlr, 0);
- if (ret < 0) {
- pr_err("%s: Unable to register smsm callback for wcnss Reset! %d\n",
- MODULE_NAME, ret);
- goto out;
- }
- ret = request_irq(WCNSS_APSS_WDOG_BITE_RESET_RDY_IRQ,
- wcnss_wdog_bite_irq_hdlr, IRQF_TRIGGER_HIGH,
- "wcnss_wdog", NULL);
-
- if (ret < 0) {
- pr_err("%s: Unable to register for wcnss bite interrupt (%d)\n",
- MODULE_NAME, ret);
- goto out;
- }
- wcnss_ssr_dev = subsys_register(&wcnss_ssr);
- if (IS_ERR(wcnss_ssr_dev))
- return PTR_ERR(wcnss_ssr_dev);
-
- INIT_DELAYED_WORK(&cancel_vote_work, wcnss_post_bootup);
-
- pr_info("%s: module initialized\n", MODULE_NAME);
-out:
- return ret;
-}
-
-arch_initcall(wcnss_ssr_init);
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index d283705..2b31f47 100644
--- a/arch/arm/tools/mach-types
+++ b/arch/arm/tools/mach-types
@@ -1196,3 +1196,4 @@
msm8625_qrd7 MACH_MSM8625_QRD7 MSM8625_QRD7 4095
msm8625_ffa MACH_MSM8625_FFA MSM8625_FFA 4166
msm8625_evt MACH_MSM8625_EVT MSM8625_EVT 4193
+qrd_skud_prime MACH_QRD_SKUD_PRIME QRD_SKUD_PRIME 4393
diff --git a/block/row-iosched.c b/block/row-iosched.c
index f24437c..b7965c6 100644
--- a/block/row-iosched.c
+++ b/block/row-iosched.c
@@ -387,12 +387,19 @@
if (list_empty(&rd->row_queues[currq].rqueue.fifo)) {
/* check idling */
if (delayed_work_pending(&rd->read_idle.idle_work)) {
- row_log_rowq(rd, currq,
- "Delayed work pending. Exiting");
- goto done;
+ if (force) {
+ (void)cancel_delayed_work(
+ &rd->read_idle.idle_work);
+ row_log_rowq(rd, currq,
+ "Canceled delayed work - forced dispatch");
+ } else {
+ row_log_rowq(rd, currq,
+ "Delayed work pending. Exiting");
+ goto done;
+ }
}
- if (queue_idling_enabled[currq] &&
+ if (!force && queue_idling_enabled[currq] &&
rd->row_queues[currq].rqueue.idle_data.begin_idling) {
if (!queue_delayed_work(rd->read_idle.idle_workqueue,
&rd->read_idle.idle_work,
@@ -603,29 +610,27 @@
return ret; \
}
STORE_FUNCTION(row_hp_read_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_HIGH_READ].disp_quantum, 0,
- INT_MAX, 0);
+&rowd->row_queues[ROWQ_PRIO_HIGH_READ].disp_quantum, 1, INT_MAX, 0);
STORE_FUNCTION(row_rp_read_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_REG_READ].disp_quantum, 0,
- INT_MAX, 0);
+ &rowd->row_queues[ROWQ_PRIO_REG_READ].disp_quantum,
+ 1, INT_MAX, 0);
STORE_FUNCTION(row_hp_swrite_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_HIGH_SWRITE].disp_quantum, 0,
- INT_MAX, 0);
+ &rowd->row_queues[ROWQ_PRIO_HIGH_SWRITE].disp_quantum,
+ 1, INT_MAX, 0);
STORE_FUNCTION(row_rp_swrite_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_REG_SWRITE].disp_quantum, 0,
- INT_MAX, 0);
+ &rowd->row_queues[ROWQ_PRIO_REG_SWRITE].disp_quantum,
+ 1, INT_MAX, 0);
STORE_FUNCTION(row_rp_write_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_REG_WRITE].disp_quantum, 0,
- INT_MAX, 0);
+ &rowd->row_queues[ROWQ_PRIO_REG_WRITE].disp_quantum,
+ 1, INT_MAX, 0);
STORE_FUNCTION(row_lp_read_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_LOW_READ].disp_quantum, 0,
- INT_MAX, 0);
+ &rowd->row_queues[ROWQ_PRIO_LOW_READ].disp_quantum,
+ 1, INT_MAX, 0);
STORE_FUNCTION(row_lp_swrite_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_LOW_SWRITE].disp_quantum, 0,
- INT_MAX, 1);
+ &rowd->row_queues[ROWQ_PRIO_LOW_SWRITE].disp_quantum,
+ 1, INT_MAX, 1);
STORE_FUNCTION(row_read_idle_store, &rowd->read_idle.idle_time, 1, INT_MAX, 1);
-STORE_FUNCTION(row_read_idle_freq_store, &rowd->read_idle.freq,
- 1, INT_MAX, 1);
+STORE_FUNCTION(row_read_idle_freq_store, &rowd->read_idle.freq, 1, INT_MAX, 1);
#undef STORE_FUNCTION
diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c
index a48e6b2..b3df752 100644
--- a/drivers/gpu/ion/ion.c
+++ b/drivers/gpu/ion/ion.c
@@ -261,6 +261,12 @@
mutex_unlock(&buffer->lock);
}
+static void ion_delayed_unsecure(struct ion_buffer *buffer)
+{
+ if (buffer->heap->ops->unsecure_buffer)
+ buffer->heap->ops->unsecure_buffer(buffer, 1);
+}
+
static void ion_buffer_destroy(struct kref *kref)
{
struct ion_buffer *buffer = container_of(kref, struct ion_buffer, ref);
@@ -271,6 +277,7 @@
buffer->heap->ops->unmap_dma(buffer->heap, buffer);
+ ion_delayed_unsecure(buffer);
ion_iommu_delayed_unmap(buffer);
buffer->heap->ops->free(buffer);
mutex_lock(&dev->lock);
@@ -1644,6 +1651,73 @@
mutex_unlock(&dev->lock);
}
+int ion_secure_handle(struct ion_client *client, struct ion_handle *handle,
+ int version, void *data, int flags)
+{
+ int ret = -EINVAL;
+ struct ion_heap *heap;
+ struct ion_buffer *buffer;
+
+ mutex_lock(&client->lock);
+ if (!ion_handle_validate(client, handle)) {
+ WARN(1, "%s: invalid handle passed to secure.\n", __func__);
+ goto out_unlock;
+ }
+
+ buffer = handle->buffer;
+ heap = buffer->heap;
+
+ if (heap->type != (enum ion_heap_type) ION_HEAP_TYPE_CP) {
+ pr_err("%s: cannot secure buffer from non secure heap\n",
+ __func__);
+ goto out_unlock;
+ }
+
+ BUG_ON(!buffer->heap->ops->secure_buffer);
+ /*
+ * Protect the handle via the client lock to ensure we aren't
+ * racing with free
+ */
+ ret = buffer->heap->ops->secure_buffer(buffer, version, data, flags);
+
+out_unlock:
+ mutex_unlock(&client->lock);
+ return ret;
+}
+
+int ion_unsecure_handle(struct ion_client *client, struct ion_handle *handle)
+{
+ int ret = -EINVAL;
+ struct ion_heap *heap;
+ struct ion_buffer *buffer;
+
+ mutex_lock(&client->lock);
+ if (!ion_handle_validate(client, handle)) {
+ WARN(1, "%s: invalid handle passed to secure.\n", __func__);
+ goto out_unlock;
+ }
+
+ buffer = handle->buffer;
+ heap = buffer->heap;
+
+ if (heap->type != (enum ion_heap_type) ION_HEAP_TYPE_CP) {
+ pr_err("%s: cannot secure buffer from non secure heap\n",
+ __func__);
+ goto out_unlock;
+ }
+
+ BUG_ON(!buffer->heap->ops->unsecure_buffer);
+ /*
+ * Protect the handle via the client lock to ensure we aren't
+ * racing with free
+ */
+ ret = buffer->heap->ops->unsecure_buffer(buffer, 0);
+
+out_unlock:
+ mutex_unlock(&client->lock);
+ return ret;
+}
+
int ion_secure_heap(struct ion_device *dev, int heap_id, int version,
void *data)
{
diff --git a/drivers/gpu/ion/ion_cp_heap.c b/drivers/gpu/ion/ion_cp_heap.c
index 2070abf..d0b1ed2 100644
--- a/drivers/gpu/ion/ion_cp_heap.c
+++ b/drivers/gpu/ion/ion_cp_heap.c
@@ -106,6 +106,26 @@
HEAP_PROTECTED = 1,
};
+struct ion_cp_buffer {
+ phys_addr_t buffer;
+ atomic_t secure_cnt;
+ int is_secure;
+ int want_delayed_unsecure;
+ /*
+ * Currently all user/kernel mapping is protected by the heap lock.
+ * This is sufficient to protect the map count as well. The lock
+ * should be used to protect map_cnt if the whole heap lock is
+ * ever removed.
+ */
+ atomic_t map_cnt;
+ /*
+ * protects secure_cnt for securing.
+ */
+ struct mutex lock;
+ int version;
+ void *data;
+};
+
static int ion_cp_protect_mem(unsigned int phy_base, unsigned int size,
unsigned int permission_type, int version,
void *data);
@@ -124,6 +144,145 @@
return cp_heap->kmap_cached_count + cp_heap->kmap_uncached_count;
}
+/* Must be protected by ion_cp_buffer lock */
+static int __ion_cp_protect_buffer(struct ion_buffer *buffer, int version,
+ void *data, int flags)
+{
+ struct ion_cp_buffer *buf = buffer->priv_virt;
+ int ret_value = 0;
+
+ if (atomic_inc_return(&buf->secure_cnt) == 1) {
+ ret_value = ion_cp_protect_mem(buf->buffer,
+ buffer->size, 0,
+ version, data);
+
+ if (ret_value) {
+ pr_err("Failed to secure buffer %p, error %d\n",
+ buffer, ret_value);
+ atomic_dec(&buf->secure_cnt);
+ } else {
+ pr_debug("Protected buffer %p from %x-%x\n",
+ buffer, buf->buffer,
+ buf->buffer + buffer->size);
+ buf->want_delayed_unsecure |=
+ flags & ION_UNSECURE_DELAYED ? 1 : 0;
+ buf->data = data;
+ buf->version = version;
+ }
+ }
+ pr_debug("buffer %p protect count %d\n", buffer,
+ atomic_read(&buf->secure_cnt));
+ BUG_ON(atomic_read(&buf->secure_cnt) < 0);
+ return ret_value;
+}
+
+/* Must be protected by ion_cp_buffer lock */
+static int __ion_cp_unprotect_buffer(struct ion_buffer *buffer, int version,
+ void *data, int force_unsecure)
+{
+ struct ion_cp_buffer *buf = buffer->priv_virt;
+ int ret_value = 0;
+
+ if (force_unsecure) {
+ if (!buf->is_secure || atomic_read(&buf->secure_cnt) == 0)
+ return 0;
+
+ if (atomic_read(&buf->secure_cnt) != 1) {
+ WARN(1, "Forcing unsecure of buffer with outstanding secure count %d!\n",
+ atomic_read(&buf->secure_cnt));
+ atomic_set(&buf->secure_cnt, 1);
+ }
+ }
+
+ if (atomic_dec_and_test(&buf->secure_cnt)) {
+ ret_value = ion_cp_unprotect_mem(
+ buf->buffer, buffer->size,
+ 0, version, data);
+
+ if (ret_value) {
+ pr_err("Failed to unsecure buffer %p, error %d\n",
+ buffer, ret_value);
+ /*
+ * If the force unsecure is happening, the buffer
+ * is being destroyed. We failed to unsecure the
+ * buffer even though the memory is given back.
+ * Just die now rather than discovering later what
+ * happens when trying to use the secured memory as
+ * unsecured...
+ */
+ BUG_ON(force_unsecure);
+ /* Bump the count back up one to try again later */
+ atomic_inc(&buf->secure_cnt);
+ } else {
+ buf->version = -1;
+ buf->data = NULL;
+ }
+ }
+ pr_debug("buffer %p unprotect count %d\n", buffer,
+ atomic_read(&buf->secure_cnt));
+ BUG_ON(atomic_read(&buf->secure_cnt) < 0);
+ return ret_value;
+}
+
+int ion_cp_secure_buffer(struct ion_buffer *buffer, int version, void *data,
+ int flags)
+{
+ int ret_value;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
+
+ mutex_lock(&buf->lock);
+ if (!buf->is_secure) {
+ pr_err("%s: buffer %p was not allocated as secure\n",
+ __func__, buffer);
+ ret_value = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (ION_IS_CACHED(buffer->flags)) {
+ pr_err("%s: buffer %p was allocated as cached\n",
+ __func__, buffer);
+ ret_value = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (atomic_read(&buf->map_cnt)) {
+ pr_err("%s: cannot secure buffer %p with outstanding mappings. Total count: %d",
+ __func__, buffer, atomic_read(&buf->map_cnt));
+ ret_value = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (atomic_read(&buf->secure_cnt)) {
+ if (buf->version != version || buf->data != data) {
+ pr_err("%s: Trying to re-secure buffer with different values",
+ __func__);
+ pr_err("Last secured version: %d Currrent %d\n",
+ buf->version, version);
+ pr_err("Last secured data: %p current %p\n",
+ buf->data, data);
+ ret_value = -EINVAL;
+ goto out_unlock;
+ }
+ }
+ ret_value = __ion_cp_protect_buffer(buffer, version, data, flags);
+
+out_unlock:
+ mutex_unlock(&buf->lock);
+ return ret_value;
+}
+
+int ion_cp_unsecure_buffer(struct ion_buffer *buffer, int force_unsecure)
+{
+ int ret_value = 0;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
+
+ mutex_lock(&buf->lock);
+ ret_value = __ion_cp_unprotect_buffer(buffer, buf->version, buf->data,
+ force_unsecure);
+ mutex_unlock(&buf->lock);
+ return ret_value;
+}
+
/**
* Protects memory if heap is unsecured heap. Also ensures that we are in
* the correct FMEM state if this heap is a reusable heap.
@@ -344,7 +503,9 @@
struct ion_buffer *buffer,
ion_phys_addr_t *addr, size_t *len)
{
- *addr = buffer->priv_phys;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
+
+ *addr = buf->buffer;
*len = buffer->size;
return 0;
}
@@ -354,34 +515,83 @@
unsigned long size, unsigned long align,
unsigned long flags)
{
- buffer->priv_phys = ion_cp_allocate(heap, size, align, flags);
- return buffer->priv_phys == ION_CP_ALLOCATE_FAIL ? -ENOMEM : 0;
+ struct ion_cp_buffer *buf;
+ phys_addr_t addr;
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return ION_CP_ALLOCATE_FAIL;
+
+ addr = ion_cp_allocate(heap, size, align, flags);
+ if (addr == ION_CP_ALLOCATE_FAIL)
+ return -ENOMEM;
+
+ buf->buffer = addr;
+ buf->want_delayed_unsecure = 0;
+ atomic_set(&buf->secure_cnt, 0);
+ mutex_init(&buf->lock);
+ buf->is_secure = flags & ION_SECURE ? 1 : 0;
+ buffer->priv_virt = buf;
+
+ return 0;
}
static void ion_cp_heap_free(struct ion_buffer *buffer)
{
struct ion_heap *heap = buffer->heap;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
- ion_cp_free(heap, buffer->priv_phys, buffer->size);
- buffer->priv_phys = ION_CP_ALLOCATE_FAIL;
+ ion_cp_free(heap, buf->buffer, buffer->size);
+ WARN_ON(atomic_read(&buf->secure_cnt));
+ WARN_ON(atomic_read(&buf->map_cnt));
+ kfree(buf);
+
+ buffer->priv_virt = NULL;
}
struct sg_table *ion_cp_heap_create_sg_table(struct ion_buffer *buffer)
{
struct sg_table *table;
int ret;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
table = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
if (!table)
return ERR_PTR(-ENOMEM);
- ret = sg_alloc_table(table, 1, GFP_KERNEL);
- if (ret)
- goto err0;
+ if (buf->is_secure) {
+ int n_chunks;
+ int i;
+ struct scatterlist *sg;
- table->sgl->length = buffer->size;
- table->sgl->offset = 0;
- table->sgl->dma_address = buffer->priv_phys;
+ if (!IS_ALIGNED(buffer->size, SZ_1M)) {
+ pr_err("%s: buffer is marked as secure but buffer size %x is not aligned to 1MB\n",
+ __func__, buffer->size);
+
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* Count number of 1MB chunks. Alignment is already checked. */
+ n_chunks = buffer->size >> 20;
+
+ ret = sg_alloc_table(table, n_chunks, GFP_KERNEL);
+ if (ret)
+ goto err0;
+
+ for_each_sg(table->sgl, sg, table->nents, i) {
+ sg_dma_address(sg) = buf->buffer + i * SZ_1M;
+ sg->length = SZ_1M;
+ sg->offset = 0;
+ }
+ } else {
+ ret = sg_alloc_table(table, 1, GFP_KERNEL);
+ if (ret)
+ goto err0;
+
+ table->sgl->length = buffer->size;
+ table->sgl->offset = 0;
+ table->sgl->dma_address = buf->buffer;
+ }
return table;
err0:
@@ -432,17 +642,18 @@
void *virt_base, unsigned long flags)
{
int ret;
- unsigned int offset = buffer->priv_phys - phys_base;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
+ unsigned int offset = buf->buffer - phys_base;
unsigned long start = ((unsigned long)virt_base) + offset;
const struct mem_type *type = ION_IS_CACHED(flags) ?
get_mem_type(MT_DEVICE_CACHED) :
get_mem_type(MT_DEVICE);
- if (phys_base > buffer->priv_phys)
+ if (phys_base > buf->buffer)
return NULL;
- ret = ioremap_pages(start, buffer->priv_phys, buffer->size, type);
+ ret = ioremap_pages(start, buf->buffer, buffer->size, type);
if (!ret)
return (void *)start;
@@ -455,6 +666,7 @@
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
void *ret_value = NULL;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
mutex_lock(&cp_heap->lock);
if ((cp_heap->heap_protected == HEAP_NOT_PROTECTED) ||
@@ -472,10 +684,10 @@
} else {
if (ION_IS_CACHED(buffer->flags))
- ret_value = ioremap_cached(buffer->priv_phys,
+ ret_value = ioremap_cached(buf->buffer,
buffer->size);
else
- ret_value = ioremap(buffer->priv_phys,
+ ret_value = ioremap(buf->buffer,
buffer->size);
}
@@ -486,6 +698,7 @@
++cp_heap->kmap_cached_count;
else
++cp_heap->kmap_uncached_count;
+ atomic_inc(&buf->map_cnt);
}
}
mutex_unlock(&cp_heap->lock);
@@ -497,6 +710,7 @@
{
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
+ struct ion_cp_buffer *buf = buffer->priv_virt;
if (cp_heap->reusable)
unmap_kernel_range((unsigned long)buffer->vaddr, buffer->size);
@@ -510,6 +724,8 @@
--cp_heap->kmap_cached_count;
else
--cp_heap->kmap_uncached_count;
+
+ atomic_dec(&buf->map_cnt);
ion_cp_release_region(cp_heap);
mutex_unlock(&cp_heap->lock);
@@ -522,6 +738,7 @@
int ret_value = -EAGAIN;
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
+ struct ion_cp_buffer *buf = buffer->priv_virt;
mutex_lock(&cp_heap->lock);
if (cp_heap->heap_protected == HEAP_NOT_PROTECTED) {
@@ -535,14 +752,17 @@
vma->vm_page_prot);
ret_value = remap_pfn_range(vma, vma->vm_start,
- __phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff,
+ __phys_to_pfn(buf->buffer) + vma->vm_pgoff,
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
- if (ret_value)
+ if (ret_value) {
ion_cp_release_region(cp_heap);
- else
+ } else {
+ atomic_inc(&buf->map_cnt);
++cp_heap->umap_count;
+ }
+
}
mutex_unlock(&cp_heap->lock);
return ret_value;
@@ -553,9 +773,11 @@
{
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
+ struct ion_cp_buffer *buf = buffer->priv_virt;
mutex_lock(&cp_heap->lock);
--cp_heap->umap_count;
+ atomic_dec(&buf->map_cnt);
ion_cp_release_region(cp_heap);
mutex_unlock(&cp_heap->lock);
}
@@ -567,6 +789,7 @@
void (*outer_cache_op)(phys_addr_t, phys_addr_t);
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
+ struct ion_cp_buffer *buf = buffer->priv_virt;
switch (cmd) {
case ION_IOC_CLEAN_CACHES:
@@ -586,7 +809,7 @@
}
if (cp_heap->has_outer_cache) {
- unsigned long pstart = buffer->priv_phys + offset;
+ unsigned long pstart = buf->buffer + offset;
outer_cache_op(pstart, pstart + length);
}
return 0;
@@ -774,25 +997,26 @@
struct ion_cp_heap *cp_heap =
container_of(buffer->heap, struct ion_cp_heap, heap);
int prot = IOMMU_WRITE | IOMMU_READ;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
prot |= ION_IS_CACHED(flags) ? IOMMU_CACHE : 0;
data->mapped_size = iova_length;
if (!msm_use_iommu()) {
- data->iova_addr = buffer->priv_phys;
+ data->iova_addr = buf->buffer;
return 0;
}
if (cp_heap->iommu_iova[domain_num]) {
/* Already mapped. */
- unsigned long offset = buffer->priv_phys - cp_heap->base;
+ unsigned long offset = buf->buffer - cp_heap->base;
data->iova_addr = cp_heap->iommu_iova[domain_num] + offset;
return 0;
} else if (cp_heap->iommu_map_all) {
ret = iommu_map_all(domain_num, cp_heap, partition_num, prot);
if (!ret) {
unsigned long offset =
- buffer->priv_phys - cp_heap->base;
+ buf->buffer - cp_heap->base;
data->iova_addr =
cp_heap->iommu_iova[domain_num] + offset;
cp_heap->iommu_partition[domain_num] = partition_num;
@@ -902,6 +1126,8 @@
.unsecure_heap = ion_cp_unsecure_heap,
.map_iommu = ion_cp_heap_map_iommu,
.unmap_iommu = ion_cp_heap_unmap_iommu,
+ .secure_buffer = ion_cp_secure_buffer,
+ .unsecure_buffer = ion_cp_unsecure_buffer,
};
struct ion_heap *ion_cp_heap_create(struct ion_platform_heap *heap_data)
diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h
index 273e57e..991a310 100644
--- a/drivers/gpu/ion/ion_priv.h
+++ b/drivers/gpu/ion/ion_priv.h
@@ -147,6 +147,9 @@
const struct rb_root *mem_map);
int (*secure_heap)(struct ion_heap *heap, int version, void *data);
int (*unsecure_heap)(struct ion_heap *heap, int version, void *data);
+ int (*secure_buffer)(struct ion_buffer *buffer, int version,
+ void *data, int flags);
+ int (*unsecure_buffer)(struct ion_buffer *buffer, int force_unsecure);
};
/**
@@ -307,4 +310,10 @@
void ion_mem_map_show(struct ion_heap *heap);
+
+
+int ion_secure_handle(struct ion_client *client, struct ion_handle *handle,
+ int version, void *data, int flags);
+
+int ion_unsecure_handle(struct ion_client *client, struct ion_handle *handle);
#endif /* _ION_PRIV_H */
diff --git a/drivers/gpu/ion/msm/ion_cp_common.c b/drivers/gpu/ion/msm/ion_cp_common.c
index b274ba2..803b04a 100644
--- a/drivers/gpu/ion/msm/ion_cp_common.c
+++ b/drivers/gpu/ion/msm/ion_cp_common.c
@@ -37,6 +37,7 @@
int lock)
{
struct cp2_lock_req request;
+ u32 resp;
request.mem_usage = usage;
request.lock = lock;
@@ -46,7 +47,7 @@
request.chunks.chunk_size = chunk_size;
return scm_call(SCM_SVC_CP, MEM_PROTECT_LOCK_ID,
- &request, sizeof(request), NULL, 0);
+ &request, sizeof(request), &resp, sizeof(resp));
}
diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c
index deff514..7fe47ee 100644
--- a/drivers/gpu/ion/msm/msm_ion.c
+++ b/drivers/gpu/ion/msm/msm_ion.c
@@ -130,6 +130,22 @@
}
EXPORT_SYMBOL(msm_ion_unsecure_heap_2_0);
+int msm_ion_secure_buffer(struct ion_client *client, struct ion_handle *handle,
+ enum cp_mem_usage usage,
+ int flags)
+{
+ return ion_secure_handle(client, handle, ION_CP_V2,
+ (void *)usage, flags);
+}
+EXPORT_SYMBOL(msm_ion_secure_buffer);
+
+int msm_ion_unsecure_buffer(struct ion_client *client,
+ struct ion_handle *handle)
+{
+ return ion_unsecure_handle(client, handle);
+}
+EXPORT_SYMBOL(msm_ion_unsecure_buffer);
+
int msm_ion_do_cache_op(struct ion_client *client, struct ion_handle *handle,
void *vaddr, unsigned long len, unsigned int cmd)
{
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 4eca776..41fd7aa 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -2263,7 +2263,7 @@
static uint io_cnt;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
- struct adreno_context *adreno_ctx = context->devctxt;
+ struct adreno_context *adreno_ctx = context ? context->devctxt : NULL;
int retries = 0;
unsigned int ts_issued;
unsigned int context_id = _get_context_id(context);
@@ -2286,7 +2286,8 @@
* again after 'retry_ts_cmp_msecs' milliseconds.
*/
if (timestamp_cmp(timestamp, ts_issued) > 0) {
- if (!(adreno_ctx->flags & CTXT_FLAGS_USER_GENERATED_TS)) {
+ if (adreno_ctx == NULL ||
+ !(adreno_ctx->flags & CTXT_FLAGS_USER_GENERATED_TS)) {
KGSL_DRV_ERR(device,
"Cannot wait for invalid ts <%d:0x%x>, "
"last issued ts <%d:0x%x>\n",
diff --git a/drivers/input/touchscreen/cyttsp-i2c-qc.c b/drivers/input/touchscreen/cyttsp-i2c-qc.c
index e54a24a..f96348e 100644
--- a/drivers/input/touchscreen/cyttsp-i2c-qc.c
+++ b/drivers/input/touchscreen/cyttsp-i2c-qc.c
@@ -3,7 +3,6 @@
* drivers/input/touchscreen/cyttsp-i2c.c
*
* Copyright (C) 2009, 2010 Cypress Semiconductor, Inc.
- * Copyright (c) 2012, Code Aurora Forum. 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
@@ -43,7 +42,6 @@
#include <linux/pm_runtime.h>
#include <linux/firmware.h>
#include <linux/mutex.h>
-#include <linux/completion.h>
#include <linux/regulator/consumer.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
@@ -59,30 +57,6 @@
#define FW_FNAME_LEN 40
#define TTSP_BUFF_SIZE 50
-enum cyttsp_powerstate {
- CY_IDLE = 0, /* IC cannot be reached */
- CY_READY, /* pre-operational; ready to go to ACTIVE */
- CY_ACTIVE, /* app is running, IC is scanning */
- CY_LOW_PWR, /* not currently used */
- CY_SLEEP, /* app is running, IC is idle */
- CY_BL, /* bootloader is running */
- CY_LDR, /* loader is running */
- CY_SYSINFO, /* switching to sysinfo mode */
- CY_INVALID, /* always last in the list */
-};
-static char *cyttsp_powerstate_string[] = {
- /* Order must match enum cyttsp_powerstate above */
- "IDLE",
- "READY",
- "ACTIVE",
- "LOW_PWR",
- "SLEEP",
- "BOOTLOADER",
- "LOADER",
- "SYSINFO",
- "INVALID",
-};
-
/* CY TTSP I2C Driver private data */
struct cyttsp {
struct i2c_client *client;
@@ -92,15 +66,12 @@
char phys[32];
struct cyttsp_platform_data *platform_data;
u8 num_prv_st_tch;
- u8 power_settings[3];
u16 fw_start_addr;
- enum cyttsp_powerstate power_state;
u16 act_trk[CY_NUM_TRK_ID];
u16 prv_st_tch[CY_NUM_ST_TCH_ID];
u16 prv_mt_tch[CY_NUM_MT_TCH_ID];
u16 prv_mt_pos[CY_NUM_TRK_ID][2];
atomic_t irq_enabled;
- struct completion si_int_running;
bool cyttsp_update_fw;
bool cyttsp_fwloader_mode;
bool is_suspended;
@@ -188,67 +159,6 @@
atomic_read(&ts->irq_enabled));
}
-static int cyttsp_hndshk(struct cyttsp *ts, u8 hst_mode)
-{
- int retval = 0;
- u8 mode = 0;
-
- mode = hst_mode & CY_HNDSHK_BIT ?
- hst_mode & ~CY_HNDSHK_BIT :
- hst_mode | CY_HNDSHK_BIT;
-
- retval = i2c_smbus_write_i2c_block_data(ts->client,
- CY_REG_BASE, sizeof(mode), &mode);
-
- if (retval < 0) {
- pr_err("%s: bus write fail on handshake r=%d\n",
- __func__, retval);
- }
-
- return retval;
-}
-
-static void cyttsp_change_state(struct cyttsp *ts,
- enum cyttsp_powerstate new_state)
-{
- ts->power_state = new_state;
- pr_info("%s: %s\n", __func__,
- (ts->power_state < CY_INVALID) ?
- cyttsp_powerstate_string[ts->power_state] :
- "INVALID");
-}
-
-static int cyttsp_wait_ready(struct cyttsp *ts, struct completion *complete,
- u8 *cmd, size_t cmd_size, unsigned long timeout_ms)
-{
- unsigned long timeout = 0;
- unsigned long uretval = 0;
- int retval = 0;
-
- timeout = msecs_to_jiffies(timeout_ms);
- INIT_COMPLETION(*complete);
- if ((cmd != NULL) && (cmd_size != 0)) {
- retval = i2c_smbus_write_i2c_block_data(ts->client,
- CY_REG_BASE, cmd_size, cmd);
- if (retval < 0) {
- pr_err("%s: bus write fail switch mode r=%d\n",
- __func__, retval);
- cyttsp_change_state(ts, CY_IDLE);
- goto _cyttsp_wait_ready_exit;
- }
- }
- uretval = wait_for_completion_interruptible_timeout(complete, timeout);
- if (uretval == 0) {
- pr_err("%s: Switch Mode Timeout waiting " \
- "for ready interrupt - try reading regs\n", __func__);
- /* continue anyway */
- retval = 0;
- }
-
-_cyttsp_wait_ready_exit:
- return retval;
-}
-
static ssize_t cyttsp_irq_enable(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
@@ -420,77 +330,39 @@
} while (tries++ < 10 && (retval < 0));
}
-static int cyttsp_set_sysinfo_mode(struct cyttsp *ts, u8 sleep)
-{
- int retval;
- u8 mode = CY_SYSINFO_MODE | sleep;
-
- cyttsp_change_state(ts, CY_SYSINFO);
-
- retval = cyttsp_wait_ready(ts, &ts->si_int_running,
- &mode, sizeof(mode), CY_HALF_SEC_TMO_MS);
-
- if (retval < 0) {
- pr_err("%s: fail wait ready r=%d\n", __func__, retval);
- goto cyttsp_set_sysinfo_mode_exit;
- }
-
- if (GET_HSTMODE(g_sysinfo_data.hst_mode) !=
- GET_HSTMODE(CY_SYSINFO_MODE)) {
- pr_err("%s: Fail enter Sysinfo mode hst_mode=0x%02X\n",
- __func__, g_sysinfo_data.hst_mode);
- retval = -EIO;
- } else {
- cyttsp_debug("%s: Enter Sysinfo mode hst_mode=0x%02X\n",
- __func__, g_sysinfo_data.hst_mode);
- }
-
-cyttsp_set_sysinfo_mode_exit:
- return retval;
-}
-
-static int cyttsp_set_opmode(struct cyttsp *ts, u8 sleep)
+static void cyttsp_set_sysinfo_mode(struct cyttsp *ts)
{
int retval, tries = 0;
- u8 host_reg = CY_OP_MODE | sleep;
+ u8 host_reg = CY_SYSINFO_MODE;
- cyttsp_change_state(ts, CY_ACTIVE);
+ do {
+ retval = i2c_smbus_write_i2c_block_data(ts->client,
+ CY_REG_BASE, sizeof(host_reg), &host_reg);
+ if (retval < 0)
+ msleep(20);
+ } while (tries++ < 10 && (retval < 0));
+
+ /* wait for TTSP Device to complete switch to SysInfo mode */
+ if (!(retval < 0)) {
+ retval = i2c_smbus_read_i2c_block_data(ts->client,
+ CY_REG_BASE,
+ sizeof(struct cyttsp_sysinfo_data_t),
+ (u8 *)&g_sysinfo_data);
+ } else
+ pr_err("%s: failed\n", __func__);
+}
+
+static void cyttsp_set_opmode(struct cyttsp *ts)
+{
+ int retval, tries = 0;
+ u8 host_reg = CY_OP_MODE;
+
do {
retval = i2c_smbus_write_i2c_block_data(ts->client,
CY_REG_BASE, sizeof(host_reg), &host_reg);
if (retval < 0)
msleep(20);
} while (tries++ < 10 && (retval < 0));
-
- return retval;
-}
-
-static int cyttsp_set_lp_mode(struct cyttsp *ts)
-{
- int retval = 0, tries = 0;
-
- retval = cyttsp_set_sysinfo_mode(ts, CY_LOW_PWR_MODE);
- if (retval < 0) {
- pr_err("%s: failed to enter sysinfo mode, retval =%x\n",
- __func__, retval);
- goto exit_low_power_mode;
- }
- do {
- retval = i2c_smbus_write_i2c_block_data(
- ts->client,
- CY_REG_ACT_INTRVL,
- sizeof(ts->power_settings), ts->power_settings);
- if (retval < 0)
- msleep(20);
- } while ((retval < 0) && (tries++ < 5));
- if (retval < 0)
- pr_err("%s: failed to write power_settings, retval =%x\n",
- __func__, retval);
- msleep(CY_DLY_SYSINFO);
-exit_low_power_mode:
- cyttsp_set_opmode(ts, CY_LOW_PWR_MODE);
- return retval;
-
}
static int str2uc(char *str, u8 *val)
@@ -869,20 +741,21 @@
pr_info("%s: firmware upgrade success\n", __func__);
}
- /* enable interrupts */
- if (ts->client->irq == 0)
- mod_timer(&ts->timer, jiffies + TOUCHSCREEN_TIMEOUT);
- else
- enable_irq(ts->client->irq);
-
/* enter bootloader idle mode */
cyttsp_soft_reset(ts);
/* exit bootloader mode */
cyttsp_exit_bl_mode(ts);
msleep(100);
- /* set low power mode and enter application mode*/
- if (ts->platform_data->use_sleep & CY_LOW_PWR_MODE)
- cyttsp_set_lp_mode(ts);
+ /* set sysinfo details */
+ cyttsp_set_sysinfo_mode(ts);
+ /* enter application mode */
+ cyttsp_set_opmode(ts);
+
+ /* enable interrupts */
+ if (ts->client->irq == 0)
+ mod_timer(&ts->timer, jiffies + TOUCHSCREEN_TIMEOUT);
+ else
+ enable_irq(ts->client->irq);
}
static void cyttspfw_upgrade_start(struct cyttsp *ts, const u8 *data,
@@ -1086,14 +959,15 @@
/* compare own irq counter with the device irq counter */
if (ts->client->irq) {
+ u8 host_reg;
u8 cur_cnt;
if (ts->platform_data->use_hndshk) {
- retval = cyttsp_hndshk(ts, g_xy_data.hst_mode);
- if (retval < 0) {
- pr_err("%s: Fail write handshake r=%d\n",
- __func__, retval);
- retval = 0;
- }
+
+ host_reg = g_xy_data.hst_mode & CY_HNDSHK_BIT ?
+ g_xy_data.hst_mode & ~CY_HNDSHK_BIT :
+ g_xy_data.hst_mode | CY_HNDSHK_BIT;
+ retval = i2c_smbus_write_i2c_block_data(ts->client,
+ CY_REG_BASE, sizeof(host_reg), &host_reg);
}
cur_cnt = g_xy_data.tt_undef[CY_IRQ_CNT_REG];
irq_cnt_total++;
@@ -1158,9 +1032,6 @@
tries++ < 100);
cyttsp_putbl(ts, 2, true, false, false);
}
- if (ts->platform_data->use_sleep & CY_LOW_PWR_MODE)
- cyttsp_set_lp_mode(ts);
-
goto exit_xy_handler;
} else {
cur_tch = GET_NUM_TOUCHES(g_xy_data.tt_stat);
@@ -2001,40 +1872,10 @@
static irqreturn_t cyttsp_irq(int irq, void *handle)
{
struct cyttsp *ts = (struct cyttsp *) handle;
- int retval = 0;
cyttsp_xdebug("%s: Got IRQ\n", CY_I2C_NAME);
- switch (ts->power_state) {
- case CY_SYSINFO:
- retval = i2c_smbus_read_i2c_block_data(ts->client,
- CY_REG_BASE,
- sizeof(struct cyttsp_sysinfo_data_t),
- (u8 *)&g_sysinfo_data);
- if (retval < 0) {
- pr_err("%s: Fail read status and version regs r=%d\n",
- __func__, retval);
- goto cyttsp_irq_sysinfo_exit;
- }
- if (ts->platform_data->use_hndshk) {
- retval = cyttsp_hndshk(ts, g_sysinfo_data.hst_mode);
- if (retval < 0) {
- pr_err("%s: Fail write handshake r=%d\n",
- __func__, retval);
- retval = 0;
- }
- }
- udelay(100); /* irq pulse: sysinfo mode switch=50us */
- complete(&ts->si_int_running);
-cyttsp_irq_sysinfo_exit:
- break;
- case CY_ACTIVE:
- cyttsp_xy_handler(ts);
- break;
- default:
- pr_err("%s: Unexpected power state with interrupt ps=%d\n",
- __func__, ts->power_state);
- break;
- }
+
+ cyttsp_xy_handler(ts);
return IRQ_HANDLED;
}
@@ -2400,6 +2241,106 @@
}
bypass:
+ /* switch to System Information mode to read versions
+ * and set interval registers */
+ if (!(retval < CY_OK)) {
+ cyttsp_debug("switch to sysinfo mode\n");
+ host_reg = CY_SYSINFO_MODE;
+ retval = i2c_smbus_write_i2c_block_data(ts->client,
+ CY_REG_BASE, sizeof(host_reg), &host_reg);
+ /* wait for TTSP Device to complete switch to SysInfo mode */
+ msleep(100);
+ if (!(retval < CY_OK)) {
+ retval = i2c_smbus_read_i2c_block_data(ts->client,
+ CY_REG_BASE,
+ sizeof(struct cyttsp_sysinfo_data_t),
+ (u8 *)&g_sysinfo_data);
+ cyttsp_debug("SI2: hst_mode=0x%02X mfg_cmd=0x%02X"\
+ "mfg_stat=0x%02X\n",
+ g_sysinfo_data.hst_mode,
+ g_sysinfo_data.mfg_cmd,
+ g_sysinfo_data.mfg_stat);
+ cyttsp_debug("SI2: bl_ver=0x%02X%02X\n",
+ g_sysinfo_data.bl_verh,
+ g_sysinfo_data.bl_verl);
+ pr_debug("SI2: sysinfo act_int=0x%02X tch_tmout=0x%02X lp_int=0x%02X\n",
+ g_sysinfo_data.act_intrvl,
+ g_sysinfo_data.tch_tmout,
+ g_sysinfo_data.lp_intrvl);
+ pr_info("SI%d: tver=%02X%02X a_id=%02X%02X aver=%02X%02X\n",
+ 102,
+ g_sysinfo_data.tts_verh,
+ g_sysinfo_data.tts_verl,
+ g_sysinfo_data.app_idh,
+ g_sysinfo_data.app_idl,
+ g_sysinfo_data.app_verh,
+ g_sysinfo_data.app_verl);
+ cyttsp_info("SI%d: c_id=%02X%02X%02X\n",
+ 103,
+ g_sysinfo_data.cid[0],
+ g_sysinfo_data.cid[1],
+ g_sysinfo_data.cid[2]);
+ if (!(retval < CY_OK) &&
+ (CY_DIFF(ts->platform_data->act_intrvl,
+ CY_ACT_INTRVL_DFLT) ||
+ CY_DIFF(ts->platform_data->tch_tmout,
+ CY_TCH_TMOUT_DFLT) ||
+ CY_DIFF(ts->platform_data->lp_intrvl,
+ CY_LP_INTRVL_DFLT))) {
+ if (!(retval < CY_OK)) {
+ u8 intrvl_ray[sizeof(\
+ ts->platform_data->act_intrvl) +
+ sizeof(\
+ ts->platform_data->tch_tmout) +
+ sizeof(\
+ ts->platform_data->lp_intrvl)];
+ u8 i = 0;
+
+ intrvl_ray[i++] =
+ ts->platform_data->act_intrvl;
+ intrvl_ray[i++] =
+ ts->platform_data->tch_tmout;
+ intrvl_ray[i++] =
+ ts->platform_data->lp_intrvl;
+
+ pr_debug("SI2: platinfo act_intrvl=0x%02X tch_tmout=0x%02X lp_intrvl=0x%02X\n",
+ ts->platform_data->act_intrvl,
+ ts->platform_data->tch_tmout,
+ ts->platform_data->lp_intrvl);
+ /* set intrvl registers */
+ retval = i2c_smbus_write_i2c_block_data(
+ ts->client,
+ CY_REG_ACT_INTRVL,
+ sizeof(intrvl_ray), intrvl_ray);
+ msleep(CY_DLY_SYSINFO);
+ }
+ }
+ }
+ /* switch back to Operational mode */
+ cyttsp_debug("switch back to operational mode\n");
+ if (!(retval < CY_OK)) {
+ host_reg = CY_OP_MODE/* + CY_LOW_PWR_MODE*/;
+ retval = i2c_smbus_write_i2c_block_data(ts->client,
+ CY_REG_BASE,
+ sizeof(host_reg), &host_reg);
+ /* wait for TTSP Device to complete
+ * switch to Operational mode */
+ msleep(100);
+ }
+ }
+ /* init gesture setup;
+ * this is required even if not using gestures
+ * in order to set the active distance */
+ if (!(retval < CY_OK)) {
+ u8 gesture_setup;
+ cyttsp_debug("init gesture setup\n");
+ gesture_setup = ts->platform_data->gest_set;
+ retval = i2c_smbus_write_i2c_block_data(ts->client,
+ CY_REG_GEST_SET,
+ sizeof(gesture_setup), &gesture_setup);
+ msleep(CY_DLY_DFLT);
+ }
+
if (!(retval < CY_OK))
ts->platform_data->power_state = CY_ACTIVE_STATE;
else
@@ -2498,98 +2439,6 @@
return rc;
}
-static void sysinfo_debug_msg(struct cyttsp *ts)
-{
- cyttsp_debug("SI2: hst_mode=0x%02X mfg_cmd=0x%02X " \
- "mfg_stat=0x%02X\n", \
- g_sysinfo_data.hst_mode, \
- g_sysinfo_data.mfg_cmd, \
- g_sysinfo_data.mfg_stat);
- cyttsp_debug("SI2: bl_ver=0x%02X%02X\n", \
- g_sysinfo_data.bl_verh, \
- g_sysinfo_data.bl_verl);
- cyttsp_debug("SI2: sysinfo act_int=0x%02X " \
- "tch_tmout=0x%02X lp_int=0x%02X\n", \
- g_sysinfo_data.act_intrvl, \
- g_sysinfo_data.tch_tmout, \
- g_sysinfo_data.lp_intrvl);
- cyttsp_info("SI%d: tver=%02X%02X a_id=%02X%02X " \
- "aver=%02X%02X\n", \
- 102, \
- g_sysinfo_data.tts_verh, \
- g_sysinfo_data.tts_verl, \
- g_sysinfo_data.app_idh, \
- g_sysinfo_data.app_idl, \
- g_sysinfo_data.app_verh, \
- g_sysinfo_data.app_verl);
- cyttsp_info("SI%d: c_id=%02X%02X%02X\n", \
- 103, \
- g_sysinfo_data.cid[0], \
- g_sysinfo_data.cid[1], \
- g_sysinfo_data.cid[2]);
- cyttsp_debug("SI2: platinfo " \
- "act_intrvl=0x%02X tch_tmout=0x%02X " \
- "lp_intrvl=0x%02X\n", \
- ts->platform_data->act_intrvl, \
- ts->platform_data->tch_tmout, \
- ts->platform_data->lp_intrvl);
-}
-
-static int set_bypass_modes(struct cyttsp *ts)
-{
- int retval = 0, tries = 0;
-
- /* switch to System Information mode to read versions
- * and set interval registers */
- if (ts->platform_data->use_sleep & CY_LOW_PWR_MODE)
- retval = cyttsp_set_sysinfo_mode(ts, CY_LOW_PWR_MODE);
- else
- retval = cyttsp_set_opmode(ts, CY_OP_MODE);
-
- if (!(retval < CY_OK)) {
- retval = i2c_smbus_read_i2c_block_data(ts->client,
- CY_REG_BASE,
- sizeof(struct cyttsp_sysinfo_data_t),
- (u8 *)&g_sysinfo_data);
- sysinfo_debug_msg(ts);
- /* set power settings registers */
- do {
- retval = i2c_smbus_write_i2c_block_data(ts->client,
- CY_REG_ACT_INTRVL, sizeof(ts->power_settings),
- ts->power_settings);
- if (retval < 0)
- msleep(20);
- } while ((retval < 0) && (tries++ < 5));
- if (retval < 0)
- pr_err("%s: failed to write power_settings, " \
- "retval =%x\n", __func__, retval);
- }
- /* switch back to Operational mode */
- cyttsp_debug("switch back to operational mode\n");
- if (!(retval < CY_OK)) {
- if (ts->platform_data->use_sleep & CY_LOW_PWR_MODE)
- cyttsp_set_opmode(ts, CY_LOW_PWR_MODE);
- else
- cyttsp_set_opmode(ts, CY_OP_MODE);
- /* wait for TTSP Device to complete
- * switch to Operational mode */
- msleep(100);
- }
- /* init gesture setup;
- * this is required even if not using gestures
- * in order to set the active distance */
- if (!(retval < CY_OK)) {
- u8 gesture_setup;
- cyttsp_debug("init gesture setup\n");
- gesture_setup = ts->platform_data->gest_set;
- retval = i2c_smbus_write_i2c_block_data(ts->client,
- CY_REG_GEST_SET,
- sizeof(gesture_setup), &gesture_setup);
- msleep(CY_DLY_DFLT);
- }
- return retval;
-}
-
/* cyttsp_initialize: Driver Initialization. This function takes
* care of the following tasks:
* 1. Create and register an input device with input layer
@@ -2627,21 +2476,6 @@
input_device->phys = ts->phys;
input_device->dev.parent = &client->dev;
- ts->power_state = CY_ACTIVE;
-
- if (ts->platform_data->act_intrvl)
- ts->power_settings[0] = ts->platform_data->act_intrvl;
- else
- ts->power_settings[0] = CY_ACT_INTRVL_DFLT;
- if (ts->platform_data->tch_tmout)
- ts->power_settings[1] = ts->platform_data->tch_tmout;
- else
- ts->power_settings[1] = CY_TCH_TMOUT_DFLT;
- if (ts->platform_data->lp_intrvl)
- ts->power_settings[2] = ts->platform_data->lp_intrvl;
- else
- ts->power_settings[2] = CY_LP_INTRVL_DFLT;
-
/* init the touch structures */
ts->num_prv_st_tch = CY_NTCH;
for (id = 0; id < CY_NUM_TRK_ID; id++) {
@@ -2859,7 +2693,6 @@
goto error_rm_dev_file_fupdate_fw;
}
- set_bypass_modes(ts);
cyttsp_info("%s: Successful registration\n", CY_I2C_NAME);
goto success;
@@ -2947,8 +2780,6 @@
i2c_set_clientdata(client, ts);
- init_completion(&ts->si_int_running);
-
error = cyttsp_initialize(client, ts);
if (error) {
cyttsp_xdebug1("err cyttsp_initialize\n");
@@ -2971,8 +2802,6 @@
#endif /* CONFIG_HAS_EARLYSUSPEND */
device_init_wakeup(&client->dev, ts->platform_data->wakeup);
mutex_init(&ts->mutex);
- if (ts->platform_data->use_sleep & CY_LOW_PWR_MODE)
- retval = cyttsp_set_lp_mode(ts);
cyttsp_info("Start Probe %s\n", \
(retval < CY_OK) ? "FAIL" : "PASS");
@@ -3109,9 +2938,6 @@
cyttsp_debug("Wake Up %s\n", \
(retval < CY_OK) ? "FAIL" : "PASS");
- if (ts->platform_data->use_sleep & CY_LOW_PWR_MODE)
- retval = cyttsp_set_lp_mode(ts);
-
return retval;
}
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index df66a3a..63a027b 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -1122,7 +1122,12 @@
}
SET_FSR(base, num, fsr);
- SET_RESUME(base, num, 1);
+ /*
+ * Only resume fetches if the registered fault handler
+ * allows it
+ */
+ if (ret != -EBUSY)
+ SET_RESUME(base, num, 1);
ret = IRQ_HANDLED;
} else
diff --git a/drivers/media/video/msm/vfe/msm_vfe40.c b/drivers/media/video/msm/vfe/msm_vfe40.c
index e958241..9ffa874 100644
--- a/drivers/media/video/msm/vfe/msm_vfe40.c
+++ b/drivers/media/video/msm/vfe/msm_vfe40.c
@@ -4615,19 +4615,19 @@
NOTIFY_VFE_IRQ,
(void *)VFE_IRQ_STATUS0_STATS_CS);
- if (qcmd->vfeInterruptStatus0 &
+ if (qcmd->vfeInterruptStatus1 &
VFE_IRQ_STATUS1_SYNC_TIMER0)
v4l2_subdev_notify(&vfe40_ctrl->subdev,
NOTIFY_VFE_IRQ,
(void *)VFE_IRQ_STATUS1_SYNC_TIMER0);
- if (qcmd->vfeInterruptStatus0 &
+ if (qcmd->vfeInterruptStatus1 &
VFE_IRQ_STATUS1_SYNC_TIMER1)
v4l2_subdev_notify(&vfe40_ctrl->subdev,
NOTIFY_VFE_IRQ,
(void *)VFE_IRQ_STATUS1_SYNC_TIMER1);
- if (qcmd->vfeInterruptStatus0 &
+ if (qcmd->vfeInterruptStatus1 &
VFE_IRQ_STATUS1_SYNC_TIMER2)
v4l2_subdev_notify(&vfe40_ctrl->subdev,
NOTIFY_VFE_IRQ,
diff --git a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
index fda03de..a608e1ab 100644
--- a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
@@ -486,7 +486,7 @@
|| CONTAINS(buff_off, size, temp->buff_off)
|| OVERLAPS(buff_off, size,
temp->buff_off, temp->size))) {
- dprintk(VIDC_WARN,
+ dprintk(VIDC_INFO,
"This memory region is already mapped\n");
ret = temp;
break;
@@ -509,7 +509,7 @@
if (!list_empty(list)) {
list_for_each_entry(temp, list, list) {
if (temp && temp->fd == fd) {
- dprintk(VIDC_ERR, "Found same fd buffer\n");
+ dprintk(VIDC_INFO, "Found same fd buffer\n");
ret = temp;
break;
}
@@ -709,7 +709,7 @@
b->m.planes[i].reserved[1],
b->m.planes[i].length);
if (binfo) {
- dprintk(VIDC_WARN,
+ dprintk(VIDC_INFO,
"This memory region has already been prepared\n");
rc = -EINVAL;
goto exit;
@@ -1153,7 +1153,7 @@
ocmem_notifier_register(OCMEM_VIDEO, &ocmem->vidc_ocmem_nb);
if (!ocmem->handle) {
dprintk(VIDC_WARN, "Failed to register OCMEM notifier.");
- dprintk(VIDC_WARN, " Performance will be impacted\n");
+ dprintk(VIDC_INFO, " Performance will be impacted\n");
}
return rc;
fail_register_domains:
diff --git a/drivers/media/video/msm_vidc/msm_vdec.c b/drivers/media/video/msm_vidc/msm_vdec.c
index e7b5caf..49b28ae 100644
--- a/drivers/media/video/msm_vidc/msm_vdec.c
+++ b/drivers/media/video/msm_vidc/msm_vdec.c
@@ -344,7 +344,7 @@
0, 0);
if (!inst->extradata_handle) {
dprintk(VIDC_ERR,
- "Failed to allocate extradta memory\n");
+ "Failed to allocate extradata memory\n");
rc = -ENOMEM;
break;
}
@@ -436,7 +436,7 @@
rc = vb2_dqbuf(&q->vb2_bufq, b, true);
mutex_unlock(&q->lock);
if (rc)
- dprintk(VIDC_WARN, "Failed to dqbuf, %d\n", rc);
+ dprintk(VIDC_DBG, "Failed to dqbuf, %d\n", rc);
return rc;
}
@@ -622,7 +622,7 @@
sizeof(f->description));
f->pixelformat = fmt->fourcc;
} else {
- dprintk(VIDC_WARN, "No more formats found\n");
+ dprintk(VIDC_INFO, "No more formats found\n");
rc = -EINVAL;
}
return rc;
diff --git a/drivers/media/video/msm_vidc/msm_venc.c b/drivers/media/video/msm_vidc/msm_venc.c
index 73a7f8b..4573018 100644
--- a/drivers/media/video/msm_vidc/msm_venc.c
+++ b/drivers/media/video/msm_vidc/msm_venc.c
@@ -574,7 +574,6 @@
{
int i, rc = 0;
struct msm_vidc_inst *inst;
- struct hal_frame_size frame_sz;
unsigned long flags;
if (!q || !q->drv_priv) {
dprintk(VIDC_ERR, "Invalid input, q = %p\n", q);
@@ -598,26 +597,6 @@
dprintk(VIDC_ERR, "Failed to open instance\n");
break;
}
- frame_sz.buffer_type = HAL_BUFFER_INPUT;
- frame_sz.width = inst->prop.width;
- frame_sz.height = inst->prop.height;
- dprintk(VIDC_DBG, "width = %d, height = %d\n",
- frame_sz.width, frame_sz.height);
- rc = vidc_hal_session_set_property((void *)inst->session,
- HAL_PARAM_FRAME_SIZE, &frame_sz);
- if (rc) {
- dprintk(VIDC_ERR,
- "Failed to set framesize for Output port\n");
- break;
- }
- frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
- rc = vidc_hal_session_set_property((void *)inst->session,
- HAL_PARAM_FRAME_SIZE, &frame_sz);
- if (rc) {
- dprintk(VIDC_ERR,
- "Failed to set hal property for framesize\n");
- break;
- }
rc = msm_comm_try_get_bufreqs(inst);
if (rc) {
dprintk(VIDC_ERR,
@@ -1346,6 +1325,7 @@
int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
{
const struct msm_vidc_format *fmt = NULL;
+ struct hal_frame_size frame_sz;
int rc = 0;
int i;
if (!inst || !f) {
@@ -1367,6 +1347,26 @@
} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
inst->prop.width = f->fmt.pix_mp.width;
inst->prop.height = f->fmt.pix_mp.height;
+ frame_sz.buffer_type = HAL_BUFFER_INPUT;
+ frame_sz.width = inst->prop.width;
+ frame_sz.height = inst->prop.height;
+ dprintk(VIDC_DBG, "width = %d, height = %d\n",
+ frame_sz.width, frame_sz.height);
+ rc = vidc_hal_session_set_property((void *)inst->session,
+ HAL_PARAM_FRAME_SIZE, &frame_sz);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to set framesize for Output port\n");
+ goto exit;
+ }
+ frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
+ rc = vidc_hal_session_set_property((void *)inst->session,
+ HAL_PARAM_FRAME_SIZE, &frame_sz);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to set hal property for framesize\n");
+ goto exit;
+ }
fmt = msm_comm_get_pixel_fmt_fourcc(venc_formats,
ARRAY_SIZE(venc_formats), f->fmt.pix_mp.pixelformat,
OUTPUT_PORT);
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.c b/drivers/media/video/msm_vidc/msm_vidc_common.c
index a6805af..d89ab9f 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -234,7 +234,7 @@
k++;
}
if (i == size) {
- dprintk(VIDC_WARN, "Format not found\n");
+ dprintk(VIDC_INFO, "Format not found\n");
return NULL;
}
return &fmt[i];
@@ -252,7 +252,7 @@
break;
}
if (i == size) {
- dprintk(VIDC_WARN, "Format not found\n");
+ dprintk(VIDC_INFO, "Format not found\n");
return NULL;
}
return &fmt[i];
@@ -521,6 +521,7 @@
dqevent.id = 0;
v4l2_event_queue_fh(&inst->event_handler, &dqevent);
wake_up(&inst->kernel_event_queue);
+ show_stats(inst);
} else {
dprintk(VIDC_ERR,
"Failed to get valid response for session close\n");
@@ -571,6 +572,7 @@
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
mutex_unlock(&inst->bufq[OUTPUT_PORT].lock);
wake_up(&inst->kernel_event_queue);
+ msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_EBD);
}
}
@@ -636,6 +638,7 @@
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
mutex_unlock(&inst->bufq[CAPTURE_PORT].lock);
wake_up(&inst->kernel_event_queue);
+ msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_FBD);
} else {
/*
* FIXME:
@@ -1094,7 +1097,7 @@
}
if (msm_comm_scale_clocks(core, inst->session_type)) {
dprintk(VIDC_WARN, "Failed to scale clocks while closing\n");
- dprintk(VIDC_WARN, "Power might be impacted\n");
+ dprintk(VIDC_INFO, "Power might be impacted\n");
}
if (list_empty(&core->instances)) {
msm_comm_unset_ocmem(core);
@@ -1258,7 +1261,7 @@
rc = vidc_hal_session_start((void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
- "Failed to send load resources\n");
+ "Failed to send start\n");
goto exit;
}
change_inst_state(inst, MSM_VIDC_START);
@@ -1304,7 +1307,7 @@
rc = vidc_hal_session_release_res((void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
- "Failed to send load resources\n");
+ "Failed to send release resources\n");
goto exit;
}
change_inst_state(inst, MSM_VIDC_RELEASE_RESOURCES);
@@ -1328,7 +1331,7 @@
rc = vidc_hal_session_end((void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
- "Failed to send load resources\n");
+ "Failed to send close\n");
goto exit;
}
change_inst_state(inst, MSM_VIDC_OPEN);
@@ -1499,6 +1502,9 @@
frame_data.alloc_len, frame_data.filled_len);
rc = vidc_hal_session_etb((void *) inst->session,
&frame_data);
+ if (!rc)
+ msm_vidc_debugfs_update(inst,
+ MSM_VIDC_DEBUGFS_EVENT_ETB);
dprintk(VIDC_DBG, "Sent etb to HAL\n");
} else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
struct vidc_seq_hdr seq_hdr;
@@ -1531,6 +1537,9 @@
} else {
rc = vidc_hal_session_ftb((void *)
inst->session, &frame_data);
+ if (!rc)
+ msm_vidc_debugfs_update(inst,
+ MSM_VIDC_DEBUGFS_EVENT_FTB);
}
inst->ftb_count++;
} else {
diff --git a/drivers/media/video/msm_vidc/msm_vidc_debug.c b/drivers/media/video/msm_vidc/msm_vidc_debug.c
index fa62988..7368136 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_debug.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_debug.c
@@ -181,7 +181,46 @@
dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
goto failed_create_dir;
}
-
+ inst->debug.pdata[FRAME_PROCESSING].sampling = true;
failed_create_dir:
return dir;
}
+
+void msm_vidc_debugfs_update(struct msm_vidc_inst *inst,
+ enum msm_vidc_debugfs_event e)
+{
+ struct msm_vidc_debug *d = &inst->debug;
+ char a[64] = "Frame processing";
+ switch (e) {
+ case MSM_VIDC_DEBUGFS_EVENT_ETB:
+ inst->count.etb++;
+ if (inst->count.ftb > inst->count.fbd) {
+ d->pdata[FRAME_PROCESSING].name[0] = '\0';
+ tic(inst, FRAME_PROCESSING, a);
+ }
+ break;
+ case MSM_VIDC_DEBUGFS_EVENT_EBD:
+ inst->count.ebd++;
+ if (inst->count.ebd == inst->count.etb)
+ toc(inst, FRAME_PROCESSING);
+ break;
+ case MSM_VIDC_DEBUGFS_EVENT_FTB: {
+ inst->count.ftb++;
+ if (inst->count.etb > inst->count.ebd) {
+ d->pdata[FRAME_PROCESSING].name[0] = '\0';
+ tic(inst, FRAME_PROCESSING, a);
+ }
+ }
+ break;
+ case MSM_VIDC_DEBUGFS_EVENT_FBD:
+ inst->count.fbd++;
+ inst->debug.counter++;
+ if (inst->count.fbd == inst->count.ftb)
+ toc(inst, FRAME_PROCESSING);
+ break;
+ default:
+ dprintk(VIDC_ERR, "Invalid state in debugfs: %d\n", e);
+ break;
+ }
+}
+
diff --git a/drivers/media/video/msm_vidc/msm_vidc_debug.h b/drivers/media/video/msm_vidc/msm_vidc_debug.h
index f7aa742..b641953 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_debug.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_debug.h
@@ -14,12 +14,16 @@
#ifndef __MSM_VIDC_DEBUG__
#define __MSM_VIDC_DEBUG__
#include <linux/debugfs.h>
+#include <linux/delay.h>
#include "msm_vidc_internal.h"
#define VIDC_DBG_TAG "msm_vidc: %d: "
-/*To enable messages OR these values and
-* echo the result to debugfs file*/
+/* To enable messages OR these values and
+ * echo the result to debugfs file.
+ *
+ * To enable all messages set debug_level = 0x101F
+ */
enum vidc_msg_prio {
VIDC_ERR = 0x0001,
@@ -30,17 +34,85 @@
VIDC_FW = 0x1000,
};
+enum msm_vidc_debugfs_event {
+ MSM_VIDC_DEBUGFS_EVENT_ETB,
+ MSM_VIDC_DEBUGFS_EVENT_EBD,
+ MSM_VIDC_DEBUGFS_EVENT_FTB,
+ MSM_VIDC_DEBUGFS_EVENT_FBD,
+};
+
extern int msm_vidc_debug;
#define dprintk(__level, __fmt, arg...) \
do { \
if (msm_vidc_debug & __level) \
- printk(KERN_DEBUG VIDC_DBG_TAG __fmt,\
- __level, ## arg); \
+ printk(KERN_DEBUG VIDC_DBG_TAG \
+ __fmt, __level, ## arg); \
} while (0)
struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core,
struct dentry *parent);
struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst,
struct dentry *parent);
+void msm_vidc_debugfs_update(struct msm_vidc_inst *inst,
+ enum msm_vidc_debugfs_event e);
+
+static inline void tic(struct msm_vidc_inst *i, enum profiling_points p,
+ char *b)
+{
+ struct timeval __ddl_tv;
+ if (!i->debug.pdata[p].name[0])
+ memcpy(i->debug.pdata[p].name, b, 64);
+ if ((msm_vidc_debug & VIDC_PROF) &&
+ i->debug.pdata[p].sampling) {
+ do_gettimeofday(&__ddl_tv);
+ i->debug.pdata[p].start =
+ (__ddl_tv.tv_sec * 1000) + (__ddl_tv.tv_usec / 1000);
+ i->debug.pdata[p].sampling = false;
+ }
+}
+
+static inline void toc(struct msm_vidc_inst *i, enum profiling_points p)
+{
+ struct timeval __ddl_tv;
+ if ((msm_vidc_debug & VIDC_PROF) &&
+ !i->debug.pdata[p].sampling) {
+ do_gettimeofday(&__ddl_tv);
+ i->debug.pdata[p].stop = (__ddl_tv.tv_sec * 1000)
+ + (__ddl_tv.tv_usec / 1000);
+ i->debug.pdata[p].cumulative =
+ (i->debug.pdata[p].stop - i->debug.pdata[p].start);
+ if (i->count.fbd) {
+ if (i->debug.pdata[p].average != 0) {
+ i->debug.pdata[p].average = ((i->debug.pdata[p].
+ average * (i->count.fbd -
+ i->debug.counter) +
+ i->debug.pdata[p].cumulative)
+ / i->count.fbd);
+ } else {
+ i->debug.pdata[p].average =
+ i->debug.pdata[p].cumulative
+ / i->count.fbd;
+ }
+ }
+ i->debug.counter = 0;
+ i->debug.pdata[p].cumulative = 0;
+ i->debug.pdata[p].sampling = true;
+ }
+}
+
+static inline void show_stats(struct msm_vidc_inst *i)
+{
+ int x;
+ for (x = 0; x < MAX_PROFILING_POINTS; x++) {
+ if ((i->debug.pdata[x].name[0]) &&
+ (msm_vidc_debug & VIDC_PROF)) {
+ dprintk(VIDC_PROF, "%s averaged %d ms/sample\n",
+ i->debug.pdata[x].name,
+ i->debug.pdata[x].average);
+ dprintk(VIDC_PROF, "%s Samples: %d",
+ i->debug.pdata[x].name, i->count.fbd);
+ }
+ }
+}
#endif
diff --git a/drivers/media/video/msm_vidc/msm_vidc_internal.h b/drivers/media/video/msm_vidc/msm_vidc_internal.h
index 1ea92fc..9806d771 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_internal.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_internal.h
@@ -193,6 +193,37 @@
struct mutex lock;
};
+enum profiling_points {
+ SYS_INIT = 0,
+ SESSION_INIT,
+ LOAD_RESOURCES,
+ FRAME_PROCESSING,
+ FW_IDLE,
+ MAX_PROFILING_POINTS,
+};
+
+struct buf_count {
+ int etb;
+ int ftb;
+ int fbd;
+ int ebd;
+};
+
+struct profile_data {
+ int start;
+ int stop;
+ int cumulative;
+ char name[64];
+ int sampling;
+ int average;
+};
+
+struct msm_vidc_debug {
+ struct profile_data pdata[MAX_PROFILING_POINTS];
+ int profile;
+ int counter;
+};
+
struct msm_vidc_core {
struct list_head list;
struct mutex sync_lock;
@@ -240,6 +271,8 @@
u32 ftb_count;
struct vb2_buffer *vb2_seq_hdr;
void *priv;
+ struct msm_vidc_debug debug;
+ struct buf_count count;
};
extern struct msm_vidc_drv *vidc_driver;
@@ -259,5 +292,4 @@
void handle_cmd_response(enum command_response cmd, void *data);
int msm_vidc_ocmem_notify_handler(struct notifier_block *this,
unsigned long event, void *data);
-
#endif
diff --git a/drivers/media/video/msm_vidc/vidc_hal.c b/drivers/media/video/msm_vidc/vidc_hal.c
index 8ac35d9..aa30644 100644
--- a/drivers/media/video/msm_vidc/vidc_hal.c
+++ b/drivers/media/video/msm_vidc/vidc_hal.c
@@ -248,7 +248,7 @@
struct vidc_iface_q_info *qinfo;
if (!info || !packet || !pb_tx_req_is_set) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
}
@@ -311,11 +311,11 @@
int rc = 0;
if (!mem || !clnt || !size) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
}
vmem = (struct vidc_mem_addr *)mem;
- dprintk(VIDC_WARN, "start to alloc: size:%d, Flags: %d", size, flags);
+ dprintk(VIDC_INFO, "start to alloc: size:%d, Flags: %d", size, flags);
alloc = msm_smem_alloc(clnt, size, align, flags, domain, 1, 1);
dprintk(VIDC_DBG, "Alloc done");
@@ -390,7 +390,7 @@
int result = -EPERM;
if (!device || !pkt) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
}
@@ -422,7 +422,7 @@
struct vidc_iface_q_info *q_info;
if (!pkt) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
}
spin_lock(&device->read_lock);
@@ -456,7 +456,7 @@
struct vidc_iface_q_info *q_info;
if (!pkt) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
}
spin_lock(&device->read_lock);
@@ -760,7 +760,7 @@
"times: %d interrupt_status: %d",
(u32) device, ++device->reg_count, intr_status);
} else {
- dprintk(VIDC_WARN, "SPURIOUS_INTR for device: 0x%x: "
+ dprintk(VIDC_INFO, "SPURIOUS_INTR for device: 0x%x: "
"times: %d interrupt_status: %d",
(u32) device, ++device->spur_count, intr_status);
}
@@ -1625,7 +1625,7 @@
if (device) {
dev = device;
} else {
- dprintk(VIDC_ERR, ":invalid device");
+ dprintk(VIDC_ERR, "invalid device");
return NULL;
}
@@ -1658,7 +1658,7 @@
if (session_id) {
session = session_id;
} else {
- dprintk(VIDC_ERR, ":invalid session");
+ dprintk(VIDC_ERR, "invalid session");
return -ENODEV;
}
@@ -1694,7 +1694,7 @@
struct hal_session *session;
if (!sess || !buffer_info) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
} else {
session = sess;
@@ -1758,7 +1758,7 @@
struct hal_session *session;
if (!sess || !buffer_info) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
} else {
session = sess;
@@ -1850,7 +1850,7 @@
struct hal_session *session;
if (!sess || !input_frame) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
} else {
session = sess;
@@ -1908,7 +1908,7 @@
struct hal_session *session;
if (!sess || !output_frame) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
} else {
session = sess;
@@ -1940,7 +1940,7 @@
struct hal_session *session;
if (!sess || !seq_hdr) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
} else {
session = sess;
@@ -1967,7 +1967,7 @@
struct hal_session *session;
if (!sess || !seq_hdr) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
} else {
session = sess;
@@ -1994,7 +1994,7 @@
if (sess) {
session = sess;
} else {
- dprintk(VIDC_ERR, ":invalid session");
+ dprintk(VIDC_ERR, "invalid session");
return -ENODEV;
}
@@ -2017,7 +2017,7 @@
if (sess) {
session = sess;
} else {
- dprintk(VIDC_ERR, ":invalid session");
+ dprintk(VIDC_ERR, "invalid session");
return -ENODEV;
}
diff --git a/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
index 795024d..a0dd93c 100644
--- a/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
+++ b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
@@ -530,7 +530,7 @@
struct hal_session *session;
if (!msg_hdr) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return;
}
diff --git a/drivers/media/video/msm_wfd/wfd-ioctl.c b/drivers/media/video/msm_wfd/wfd-ioctl.c
index 04b787a..d8080dd 100644
--- a/drivers/media/video/msm_wfd/wfd-ioctl.c
+++ b/drivers/media/video/msm_wfd/wfd-ioctl.c
@@ -91,6 +91,7 @@
u32 out_buf_size;
struct list_head input_mem_list;
struct wfd_stats stats;
+ struct completion stop_mdp_thread;
};
struct wfd_vid_buffer {
@@ -522,7 +523,7 @@
static int mdp_output_thread(void *data)
{
- int rc = 0;
+ int rc = 0, no_sig_wait = 0;
struct file *filp = (struct file *)data;
struct wfd_inst *inst = filp->private_data;
struct wfd_device *wfd_dev =
@@ -531,6 +532,14 @@
struct mem_region *mregion;
struct vsg_buf_info ibuf_vsg;
while (!kthread_should_stop()) {
+ if (rc) {
+ WFD_MSG_DBG("%s() error in output thread\n", __func__);
+ if (!no_sig_wait) {
+ wait_for_completion(&inst->stop_mdp_thread);
+ no_sig_wait = 1;
+ }
+ continue;
+ }
WFD_MSG_DBG("waiting for mdp output\n");
rc = v4l2_subdev_call(&wfd_dev->mdp_sdev,
core, ioctl, MDP_DQ_BUFFER, (void *)&obuf_mdp);
@@ -540,7 +549,7 @@
WFD_MSG_ERR("MDP reported err %d\n", rc);
WFD_MSG_ERR("Streamoff called\n");
- break;
+ continue;
} else {
wfd_stats_update(&inst->stats,
WFD_STAT_EVENT_MDP_DEQUEUE);
@@ -550,7 +559,7 @@
if (!mregion) {
WFD_MSG_ERR("mdp cookie is null\n");
rc = -EINVAL;
- break;
+ continue;
}
ibuf_vsg.mdp_buf_info = obuf_mdp;
@@ -565,7 +574,7 @@
if (rc) {
WFD_MSG_ERR("Failed to queue frame to vsg\n");
- break;
+ continue;
} else {
wfd_stats_update(&inst->stats,
WFD_STAT_EVENT_VSG_QUEUE);
@@ -599,7 +608,7 @@
WFD_MSG_ERR("Failed to start vsg\n");
goto subdev_start_fail;
}
-
+ init_completion(&inst->stop_mdp_thread);
inst->mdp_task = kthread_run(mdp_output_thread, priv_data,
"mdp_output_thread");
if (IS_ERR(inst->mdp_task)) {
@@ -634,6 +643,7 @@
if (rc)
WFD_MSG_ERR("Failed to stop VSG\n");
+ complete(&inst->stop_mdp_thread);
kthread_stop(inst->mdp_task);
rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
ENCODE_FLUSH, (void *)inst->venc_inst);
diff --git a/drivers/media/video/vcap_v4l2.c b/drivers/media/video/vcap_v4l2.c
index a160915..c552e2d 100644
--- a/drivers/media/video/vcap_v4l2.c
+++ b/drivers/media/video/vcap_v4l2.c
@@ -866,6 +866,7 @@
size = (c_data->vc_format.hactive_end -
c_data->vc_format.hactive_start);
+ size = VCAP_STRIDE_CALC(size);
if (c_data->vc_format.color_space)
size *= 3;
diff --git a/drivers/media/video/vcap_vc.c b/drivers/media/video/vcap_vc.c
index 3d81161..92b205e 100644
--- a/drivers/media/video/vcap_vc.c
+++ b/drivers/media/video/vcap_vc.c
@@ -35,10 +35,11 @@
if (c_data->vc_format.color_space == HAL_VCAP_RGB) {
writel_relaxed(buf->paddr, y_addr);
} else {
- int size = ((c_data->vc_format.hactive_end -
- c_data->vc_format.hactive_start) *
- (c_data->vc_format.vactive_end -
- c_data->vc_format.vactive_start));
+ int size = (c_data->vc_format.hactive_end -
+ c_data->vc_format.hactive_start);
+ size = VCAP_STRIDE_CALC(size);
+ size *= (c_data->vc_format.vactive_end -
+ c_data->vc_format.vactive_start);
writel_relaxed(buf->paddr, y_addr);
writel_relaxed(buf->paddr + size, c_addr);
}
@@ -449,6 +450,7 @@
writel_iowmb(0x000033FF, VCAP_VC_BUF_CTRL);
rc = vc_format->hactive_end - vc_format->hactive_start;
+ rc = VCAP_STRIDE_CALC(rc);
if (vc_format->color_space)
rc *= 3;
diff --git a/drivers/mfd/pm8038-core.c b/drivers/mfd/pm8038-core.c
index 9815f6e..48bc92d 100644
--- a/drivers/mfd/pm8038-core.c
+++ b/drivers/mfd/pm8038-core.c
@@ -33,6 +33,11 @@
#define REG_RTC_BASE 0x11D
#define REG_IRQ_BASE 0x1BB
+#define REG_BATT_ALARM_THRESH 0x023
+#define REG_BATT_ALARM_CTRL1 0x024
+#define REG_BATT_ALARM_CTRL2 0x021
+#define REG_BATT_ALARM_PWM_CTRL 0x020
+
#define REG_SPK_BASE 0x253
#define REG_SPK_REGISTERS 6
@@ -336,6 +341,27 @@
.pdata_size = sizeof(struct pm8xxx_tm_core_data),
};
+static const struct resource batt_alarm_cell_resources[] __devinitconst = {
+ SINGLE_IRQ_RESOURCE("pm8921_batt_alarm_irq", PM8038_BATT_ALARM_IRQ),
+};
+
+static struct pm8xxx_batt_alarm_core_data batt_alarm_cdata = {
+ .irq_name = "pm8921_batt_alarm_irq",
+ .reg_addr_threshold = REG_BATT_ALARM_THRESH,
+ .reg_addr_ctrl1 = REG_BATT_ALARM_CTRL1,
+ .reg_addr_ctrl2 = REG_BATT_ALARM_CTRL2,
+ .reg_addr_pwm_ctrl = REG_BATT_ALARM_PWM_CTRL,
+};
+
+static struct mfd_cell batt_alarm_cell __devinitdata = {
+ .name = PM8XXX_BATT_ALARM_DEV_NAME,
+ .id = -1,
+ .resources = batt_alarm_cell_resources,
+ .num_resources = ARRAY_SIZE(batt_alarm_cell_resources),
+ .platform_data = &batt_alarm_cdata,
+ .pdata_size = sizeof(struct pm8xxx_batt_alarm_core_data),
+};
+
static const struct resource ccadc_cell_resources[] __devinitconst = {
SINGLE_IRQ_RESOURCE("PM8921_BMS_CCADC_EOC", PM8921_BMS_CCADC_EOC),
};
@@ -661,6 +687,13 @@
goto bail;
}
+ ret = mfd_add_devices(pmic->dev, 0, &batt_alarm_cell, 1, NULL,
+ irq_base);
+ if (ret) {
+ pr_err("Failed to add battery alarm subdevice ret=%d\n", ret);
+ goto bail;
+ }
+
if (pdata->ccadc_pdata) {
ccadc_cell.platform_data = pdata->ccadc_pdata;
ccadc_cell.pdata_size =
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
index e7e11d0..62f1a93 100644
--- a/drivers/mfd/wcd9xxx-core.c
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/mfd/core.h>
#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
@@ -257,13 +258,20 @@
u8 byte[4];
struct mfd_cell *dev;
int size;
+ int num_irqs;
} wcd9xxx_codecs[] = {
- {{0x2, 0x0, 0x0, 0x1}, tabla_devs, ARRAY_SIZE(tabla_devs)},
- {{0x1, 0x0, 0x0, 0x1}, tabla1x_devs, ARRAY_SIZE(tabla1x_devs)},
- {{0x0, 0x0, 0x2, 0x1}, taiko_devs, ARRAY_SIZE(taiko_devs)},
- {{0x0, 0x0, 0x0, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs)},
- {{0x1, 0x0, 0x1, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs)},
- {{0x2, 0x0, 0x1, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs)},
+ {{0x2, 0x0, 0x0, 0x1}, tabla_devs, ARRAY_SIZE(tabla_devs),
+ TABLA_NUM_IRQS},
+ {{0x1, 0x0, 0x0, 0x1}, tabla1x_devs, ARRAY_SIZE(tabla1x_devs),
+ TABLA_NUM_IRQS},
+ {{0x0, 0x0, 0x2, 0x1}, taiko_devs, ARRAY_SIZE(taiko_devs),
+ TAIKO_NUM_IRQS},
+ {{0x0, 0x0, 0x0, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs),
+ SITAR_NUM_IRQS},
+ {{0x1, 0x0, 0x1, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs),
+ SITAR_NUM_IRQS},
+ {{0x2, 0x0, 0x1, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs),
+ SITAR_NUM_IRQS},
};
static void wcd9xxx_bring_up(struct wcd9xxx *wcd9xxx)
@@ -312,8 +320,9 @@
}
}
static int wcd9xxx_check_codec_type(struct wcd9xxx *wcd9xxx,
- struct mfd_cell **wcd9xxx_dev,
- int *wcd9xxx_dev_size)
+ struct mfd_cell **wcd9xxx_dev,
+ int *wcd9xxx_dev_size,
+ int *wcd9xxx_dev_num_irqs)
{
int i;
int ret;
@@ -343,6 +352,7 @@
wcd9xxx_codecs[i].dev->name);
*wcd9xxx_dev = wcd9xxx_codecs[i].dev;
*wcd9xxx_dev_size = wcd9xxx_codecs[i].size;
+ *wcd9xxx_dev_num_irqs = wcd9xxx_codecs[i].num_irqs;
break;
}
i++;
@@ -359,7 +369,7 @@
return ret;
}
-static int wcd9xxx_device_init(struct wcd9xxx *wcd9xxx, int irq)
+static int wcd9xxx_device_init(struct wcd9xxx *wcd9xxx)
{
int ret;
struct mfd_cell *wcd9xxx_dev = NULL;
@@ -379,6 +389,11 @@
wcd9xxx_bring_up(wcd9xxx);
+ ret = wcd9xxx_check_codec_type(wcd9xxx, &wcd9xxx_dev, &wcd9xxx_dev_size,
+ &wcd9xxx->num_irqs);
+ if (ret < 0)
+ goto err_irq;
+
if (wcd9xxx->irq != -1) {
ret = wcd9xxx_irq_init(wcd9xxx);
if (ret) {
@@ -386,11 +401,7 @@
goto err;
}
}
- ret = wcd9xxx_check_codec_type(wcd9xxx, &wcd9xxx_dev,
- &wcd9xxx_dev_size);
- if (ret < 0)
- goto err_irq;
ret = mfd_add_devices(wcd9xxx->dev, -1, wcd9xxx_dev, wcd9xxx_dev_size,
NULL, 0);
if (ret != 0) {
@@ -795,10 +806,12 @@
wcd9xxx->read_dev = wcd9xxx_i2c_read;
wcd9xxx->write_dev = wcd9xxx_i2c_write;
- wcd9xxx->irq = pdata->irq;
- wcd9xxx->irq_base = pdata->irq_base;
+ if (!wcd9xxx->dev->of_node) {
+ wcd9xxx->irq = pdata->irq;
+ wcd9xxx->irq_base = pdata->irq_base;
+ }
- ret = wcd9xxx_device_init(wcd9xxx, wcd9xxx->irq);
+ ret = wcd9xxx_device_init(wcd9xxx);
if (ret) {
pr_err("%s: error, initializing device failed\n", __func__);
goto err_device_init;
@@ -1072,7 +1085,6 @@
pdata->reset_gpio);
goto err;
}
- pdata->irq = -1;
ret = wcd9xxx_dt_parse_slim_interface_dev_info(dev,
&pdata->slimbus_slave_device);
@@ -1163,14 +1175,12 @@
}
wcd9xxx->read_dev = wcd9xxx_slim_read_device;
wcd9xxx->write_dev = wcd9xxx_slim_write_device;
- wcd9xxx->irq = pdata->irq;
- wcd9xxx->irq_base = pdata->irq_base;
wcd9xxx_pgd_la = wcd9xxx->slim->laddr;
-
- if (pdata->num_irqs < TABLA_NUM_IRQS)
- pr_warn("%s: Not enough interrupt lines allocated\n", __func__);
-
wcd9xxx->slim_slave = &pdata->slimbus_slave_device;
+ if (!wcd9xxx->dev->of_node) {
+ wcd9xxx->irq = pdata->irq;
+ wcd9xxx->irq_base = pdata->irq_base;
+ }
ret = slim_add_device(slim->ctrl, wcd9xxx->slim_slave);
if (ret) {
@@ -1190,14 +1200,11 @@
wcd9xxx_inf_la = wcd9xxx->slim_slave->laddr;
wcd9xxx_intf = WCD9XXX_INTERFACE_TYPE_SLIMBUS;
- ret = wcd9xxx_device_init(wcd9xxx, wcd9xxx->irq);
+ ret = wcd9xxx_device_init(wcd9xxx);
if (ret) {
pr_err("%s: error, initializing device failed\n", __func__);
goto err_slim_add;
}
-
- wcd9xxx_init_slimslave(wcd9xxx, wcd9xxx_pgd_la);
-
#ifdef CONFIG_DEBUG_FS
debugCodec = wcd9xxx;
diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c
index e9b2ef3..103c1a3 100644
--- a/drivers/mfd/wcd9xxx-irq.c
+++ b/drivers/mfd/wcd9xxx-irq.c
@@ -18,8 +18,13 @@
#include <linux/mfd/wcd9xxx/core.h>
#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
#include <linux/mfd/wcd9xxx/wcd9310_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+#include <linux/delay.h>
+#include <linux/irqdomain.h>
#include <linux/interrupt.h>
-
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
#include <mach/cpuidle.h>
#define BYTE_BIT_MASK(nr) (1UL << ((nr) % BITS_PER_BYTE))
@@ -27,19 +32,18 @@
#define WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS 100
-struct wcd9xxx_irq {
- bool level;
+#ifdef CONFIG_OF
+struct wcd9xxx_irq_drv_data {
+ struct irq_domain *domain;
+ int irq;
};
+#endif
-static struct wcd9xxx_irq wcd9xxx_irqs[TABLA_NUM_IRQS] = {
- [0] = { .level = 1},
-/* All other wcd9xxx interrupts are edge triggered */
-};
-
-static inline int irq_to_wcd9xxx_irq(struct wcd9xxx *wcd9xxx, int irq)
-{
- return irq - wcd9xxx->irq_base;
-}
+static int virq_to_phyirq(struct wcd9xxx *wcd9xxx, int virq);
+static int phyirq_to_virq(struct wcd9xxx *wcd9xxx, int irq);
+static unsigned int wcd9xxx_irq_get_upstream_irq(struct wcd9xxx *wcd9xxx);
+static void wcd9xxx_irq_put_upstream_irq(struct wcd9xxx *wcd9xxx);
+static int wcd9xxx_map_irq(struct wcd9xxx *wcd9xxx, int irq);
static void wcd9xxx_irq_lock(struct irq_data *data)
{
@@ -58,8 +62,9 @@
*/
if (wcd9xxx->irq_masks_cur[i] != wcd9xxx->irq_masks_cache[i]) {
wcd9xxx->irq_masks_cache[i] = wcd9xxx->irq_masks_cur[i];
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MASK0+i,
- wcd9xxx->irq_masks_cur[i]);
+ wcd9xxx_reg_write(wcd9xxx,
+ WCD9XXX_A_INTR_MASK0 + i,
+ wcd9xxx->irq_masks_cur[i]);
}
}
@@ -69,7 +74,7 @@
static void wcd9xxx_irq_enable(struct irq_data *data)
{
struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
- int wcd9xxx_irq = irq_to_wcd9xxx_irq(wcd9xxx, data->irq);
+ int wcd9xxx_irq = virq_to_phyirq(wcd9xxx, data->irq);
wcd9xxx->irq_masks_cur[BIT_BYTE(wcd9xxx_irq)] &=
~(BYTE_BIT_MASK(wcd9xxx_irq));
}
@@ -77,9 +82,14 @@
static void wcd9xxx_irq_disable(struct irq_data *data)
{
struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
- int wcd9xxx_irq = irq_to_wcd9xxx_irq(wcd9xxx, data->irq);
+ int wcd9xxx_irq = virq_to_phyirq(wcd9xxx, data->irq);
wcd9xxx->irq_masks_cur[BIT_BYTE(wcd9xxx_irq)]
- |= BYTE_BIT_MASK(wcd9xxx_irq);
+ |= BYTE_BIT_MASK(wcd9xxx_irq);
+}
+
+static void wcd9xxx_irq_mask(struct irq_data *d)
+{
+ /* do nothing but required as linux calls irq_mask without NULL check */
}
static struct irq_chip wcd9xxx_irq_chip = {
@@ -88,6 +98,7 @@
.irq_bus_sync_unlock = wcd9xxx_irq_sync_unlock,
.irq_disable = wcd9xxx_irq_disable,
.irq_enable = wcd9xxx_irq_enable,
+ .irq_mask = wcd9xxx_irq_mask,
};
enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg(struct wcd9xxx *wcd9xxx,
@@ -167,62 +178,72 @@
static void wcd9xxx_irq_dispatch(struct wcd9xxx *wcd9xxx, int irqbit)
{
- if ((irqbit <= TABLA_IRQ_MBHC_INSERTION) &&
- (irqbit >= TABLA_IRQ_MBHC_REMOVAL)) {
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_CLEAR0 +
- BIT_BYTE(irqbit), BYTE_BIT_MASK(irqbit));
+ if ((irqbit <= WCD9XXX_IRQ_MBHC_INSERTION) &&
+ (irqbit >= WCD9XXX_IRQ_MBHC_REMOVAL)) {
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_CLEAR0 +
+ BIT_BYTE(irqbit),
+ BYTE_BIT_MASK(irqbit));
if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MODE, 0x02);
- handle_nested_irq(wcd9xxx->irq_base + irqbit);
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MODE, 0x02);
+ handle_nested_irq(phyirq_to_virq(wcd9xxx, irqbit));
} else {
- handle_nested_irq(wcd9xxx->irq_base + irqbit);
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_CLEAR0 +
- BIT_BYTE(irqbit), BYTE_BIT_MASK(irqbit));
+ handle_nested_irq(phyirq_to_virq(wcd9xxx, irqbit));
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_CLEAR0 +
+ BIT_BYTE(irqbit),
+ BYTE_BIT_MASK(irqbit));
if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MODE, 0x02);
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MODE, 0x02);
}
}
+static int wcd9xxx_num_irq_regs(const struct wcd9xxx *wcd9xxx)
+{
+ return (wcd9xxx->num_irqs / 8) + ((wcd9xxx->num_irqs % 8) ? 1 : 0);
+}
+
static irqreturn_t wcd9xxx_irq_thread(int irq, void *data)
{
int ret;
- struct wcd9xxx *wcd9xxx = data;
- u8 status[WCD9XXX_NUM_IRQ_REGS];
int i;
+ struct wcd9xxx *wcd9xxx = data;
+ int num_irq_regs = wcd9xxx_num_irq_regs(wcd9xxx);
+ u8 status[num_irq_regs];
if (unlikely(wcd9xxx_lock_sleep(wcd9xxx) == false)) {
dev_err(wcd9xxx->dev, "Failed to hold suspend\n");
return IRQ_NONE;
}
- ret = wcd9xxx_bulk_read(wcd9xxx, TABLA_A_INTR_STATUS0,
- WCD9XXX_NUM_IRQ_REGS, status);
+ ret = wcd9xxx_bulk_read(wcd9xxx, WCD9XXX_A_INTR_STATUS0,
+ num_irq_regs, status);
if (ret < 0) {
dev_err(wcd9xxx->dev, "Failed to read interrupt status: %d\n",
ret);
wcd9xxx_unlock_sleep(wcd9xxx);
return IRQ_NONE;
}
+
/* Apply masking */
- for (i = 0; i < WCD9XXX_NUM_IRQ_REGS; i++)
+ for (i = 0; i < num_irq_regs; i++)
status[i] &= ~wcd9xxx->irq_masks_cur[i];
/* Find out which interrupt was triggered and call that interrupt's
* handler function
*/
- if (status[BIT_BYTE(TABLA_IRQ_SLIMBUS)] &
- BYTE_BIT_MASK(TABLA_IRQ_SLIMBUS))
- wcd9xxx_irq_dispatch(wcd9xxx, TABLA_IRQ_SLIMBUS);
+ if (status[BIT_BYTE(WCD9XXX_IRQ_SLIMBUS)] &
+ BYTE_BIT_MASK(WCD9XXX_IRQ_SLIMBUS))
+ wcd9xxx_irq_dispatch(wcd9xxx, WCD9XXX_IRQ_SLIMBUS);
/* Since codec has only one hardware irq line which is shared by
* codec's different internal interrupts, so it's possible master irq
* handler dispatches multiple nested irq handlers after breaking
* order. Dispatch MBHC interrupts order to follow MBHC state
* machine's order */
- for (i = TABLA_IRQ_MBHC_INSERTION; i >= TABLA_IRQ_MBHC_REMOVAL; i--) {
+ for (i = WCD9XXX_IRQ_MBHC_INSERTION;
+ i >= WCD9XXX_IRQ_MBHC_REMOVAL; i--) {
if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i))
wcd9xxx_irq_dispatch(wcd9xxx, i);
}
- for (i = TABLA_IRQ_BG_PRECHARGE; i < TABLA_NUM_IRQS; i++) {
+ for (i = WCD9XXX_IRQ_BG_PRECHARGE; i < wcd9xxx->num_irqs; i++) {
if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i))
wcd9xxx_irq_dispatch(wcd9xxx, i);
}
@@ -231,59 +252,105 @@
return IRQ_HANDLED;
}
+void wcd9xxx_free_irq(struct wcd9xxx *wcd9xxx, int irq, void *data)
+{
+ free_irq(phyirq_to_virq(wcd9xxx, irq), data);
+}
+
+void wcd9xxx_enable_irq(struct wcd9xxx *wcd9xxx, int irq)
+{
+ enable_irq(phyirq_to_virq(wcd9xxx, irq));
+}
+
+void wcd9xxx_disable_irq(struct wcd9xxx *wcd9xxx, int irq)
+{
+ disable_irq_nosync(phyirq_to_virq(wcd9xxx, irq));
+}
+
+void wcd9xxx_disable_irq_sync(struct wcd9xxx *wcd9xxx, int irq)
+{
+ disable_irq(phyirq_to_virq(wcd9xxx, irq));
+}
+
+static int wcd9xxx_irq_setup_downstream_irq(struct wcd9xxx *wcd9xxx)
+{
+ int irq, virq, ret;
+
+ pr_debug("%s: enter\n", __func__);
+
+ for (irq = 0; irq < wcd9xxx->num_irqs; irq++) {
+ /* Map OF irq */
+ virq = wcd9xxx_map_irq(wcd9xxx, irq);
+ pr_debug("%s: irq %d -> %d\n", __func__, irq, virq);
+ if (virq == NO_IRQ) {
+ pr_err("%s, No interrupt specifier for irq %d\n",
+ __func__, irq);
+ return NO_IRQ;
+ }
+
+ ret = irq_set_chip_data(virq, wcd9xxx);
+ if (ret) {
+ pr_err("%s: Failed to configure irq %d (%d)\n",
+ __func__, irq, ret);
+ return ret;
+ }
+
+ if (wcd9xxx->irq_level_high[irq])
+ irq_set_chip_and_handler(virq, &wcd9xxx_irq_chip,
+ handle_level_irq);
+ else
+ irq_set_chip_and_handler(virq, &wcd9xxx_irq_chip,
+ handle_edge_irq);
+
+ irq_set_nested_thread(virq, 1);
+ }
+
+ pr_debug("%s: leave\n", __func__);
+
+ return 0;
+}
+
int wcd9xxx_irq_init(struct wcd9xxx *wcd9xxx)
{
- int ret;
- unsigned int i, cur_irq;
+ int i, ret;
+ u8 irq_level[wcd9xxx_num_irq_regs(wcd9xxx)];
mutex_init(&wcd9xxx->irq_lock);
+ wcd9xxx->irq = wcd9xxx_irq_get_upstream_irq(wcd9xxx);
if (!wcd9xxx->irq) {
- dev_warn(wcd9xxx->dev,
- "No interrupt specified, no interrupts\n");
- wcd9xxx->irq_base = 0;
- return 0;
+ pr_warn("%s: irq driver is not yet initialized\n", __func__);
+ mutex_destroy(&wcd9xxx->irq_lock);
+ return -EPROBE_DEFER;
+ }
+ pr_debug("%s: probed irq %d\n", __func__, wcd9xxx->irq);
+
+ /* Setup downstream IRQs */
+ ret = wcd9xxx_irq_setup_downstream_irq(wcd9xxx);
+ if (ret) {
+ pr_err("%s: Failed to setup downstream IRQ\n", __func__);
+ wcd9xxx_irq_put_upstream_irq(wcd9xxx);
+ return ret;
}
- if (!wcd9xxx->irq_base) {
- dev_err(wcd9xxx->dev,
- "No interrupt base specified, no interrupts\n");
- return 0;
- }
- /* Mask the individual interrupt sources */
- for (i = 0, cur_irq = wcd9xxx->irq_base; i < TABLA_NUM_IRQS; i++,
- cur_irq++) {
+ /* All other wcd9xxx interrupts are edge triggered */
+ wcd9xxx->irq_level_high[0] = true;
- irq_set_chip_data(cur_irq, wcd9xxx);
-
- if (wcd9xxx_irqs[i].level)
- irq_set_chip_and_handler(cur_irq, &wcd9xxx_irq_chip,
- handle_level_irq);
- else
- irq_set_chip_and_handler(cur_irq, &wcd9xxx_irq_chip,
- handle_edge_irq);
-
- irq_set_nested_thread(cur_irq, 1);
-
- /* ARM needs us to explicitly flag the IRQ as valid
- * and will set them noprobe when we do so. */
-#ifdef CONFIG_ARM
- set_irq_flags(cur_irq, IRQF_VALID);
-#else
- set_irq_noprobe(cur_irq);
-#endif
-
+ /* mask all the interrupts */
+ memset(irq_level, 0, wcd9xxx_num_irq_regs(wcd9xxx));
+ for (i = 0; i < wcd9xxx->num_irqs; i++) {
wcd9xxx->irq_masks_cur[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
wcd9xxx->irq_masks_cache[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
- wcd9xxx->irq_level[BIT_BYTE(i)] |= wcd9xxx_irqs[i].level <<
- (i % BITS_PER_BYTE);
+ irq_level[BIT_BYTE(i)] |=
+ wcd9xxx->irq_level_high[i] << (i % BITS_PER_BYTE);
}
- for (i = 0; i < WCD9XXX_NUM_IRQ_REGS; i++) {
+
+ for (i = 0; i < wcd9xxx_num_irq_regs(wcd9xxx); i++) {
/* Initialize interrupt mask and level registers */
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_LEVEL0 + i,
- wcd9xxx->irq_level[i]);
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MASK0 + i,
- wcd9xxx->irq_masks_cur[i]);
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_LEVEL0 + i,
+ irq_level[i]);
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MASK0 + i,
+ wcd9xxx->irq_masks_cur[i]);
}
ret = request_threaded_irq(wcd9xxx->irq, NULL, wcd9xxx_irq_thread,
@@ -308,18 +375,226 @@
free_irq(wcd9xxx->irq, wcd9xxx);
}
- if (ret)
+ if (ret) {
+ pr_err("%s: Failed to init wcd9xxx irq\n", __func__);
+ wcd9xxx_irq_put_upstream_irq(wcd9xxx);
mutex_destroy(&wcd9xxx->irq_lock);
+ }
return ret;
}
+int wcd9xxx_request_irq(struct wcd9xxx *wcd9xxx, int irq, irq_handler_t handler,
+ const char *name, void *data)
+{
+ int virq;
+
+ virq = phyirq_to_virq(wcd9xxx, irq);
+
+ /*
+ * ARM needs us to explicitly flag the IRQ as valid
+ * and will set them noprobe when we do so.
+ */
+#ifdef CONFIG_ARM
+ set_irq_flags(virq, IRQF_VALID);
+#else
+ set_irq_noprobe(virq);
+#endif
+
+ return request_threaded_irq(virq, NULL, handler, IRQF_TRIGGER_RISING,
+ name, data);
+}
+
void wcd9xxx_irq_exit(struct wcd9xxx *wcd9xxx)
{
if (wcd9xxx->irq) {
disable_irq_wake(wcd9xxx->irq);
free_irq(wcd9xxx->irq, wcd9xxx);
+ /* Release parent's of node */
+ wcd9xxx_irq_put_upstream_irq(wcd9xxx);
device_init_wakeup(wcd9xxx->dev, 0);
}
mutex_destroy(&wcd9xxx->irq_lock);
}
+
+#ifndef CONFIG_OF
+static int phyirq_to_virq(struct wcd9xxx *wcd9xxx, int offset)
+{
+ return wcd9xxx->irq_base + offset;
+}
+
+static int virq_to_phyirq(struct wcd9xxx *wcd9xxx, int virq)
+{
+ return virq - wcd9xxx->irq_base;
+}
+
+static unsigned int wcd9xxx_irq_get_upstream_irq(struct wcd9xxx *wcd9xxx)
+{
+ return wcd9xxx->irq;
+}
+
+static void wcd9xxx_irq_put_upstream_irq(struct wcd9xxx *wcd9xxx)
+{
+ /* Do nothing */
+}
+
+static int wcd9xxx_map_irq(struct wcd9xxx *wcd9xxx, int irq)
+{
+ return phyirq_to_virq(wcd9xxx, irq);
+}
+#else
+int __init wcd9xxx_irq_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ struct wcd9xxx_irq_drv_data *data;
+
+ pr_debug("%s: node %s, node parent %s\n", __func__,
+ node->name, node->parent->name);
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /*
+ * wcd9xxx_intc interrupt controller supports N to N irq mapping with
+ * single cell binding with irq numbers(offsets) only.
+ * Use irq_domain_simple_ops that has irq_domain_simple_map and
+ * irq_domain_xlate_onetwocell.
+ */
+ data->domain = irq_domain_add_linear(node, WCD9XXX_MAX_NUM_IRQS,
+ &irq_domain_simple_ops, data);
+ if (!data->domain) {
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static struct wcd9xxx_irq_drv_data *
+wcd9xxx_get_irq_drv_d(const struct wcd9xxx *wcd9xxx)
+{
+ struct device_node *pnode;
+ struct irq_domain *domain;
+
+ pnode = of_irq_find_parent(wcd9xxx->dev->of_node);
+ /* Shouldn't happen */
+ if (unlikely(!pnode))
+ return NULL;
+
+ domain = irq_find_host(pnode);
+ return (struct wcd9xxx_irq_drv_data *)domain->host_data;
+}
+
+static int phyirq_to_virq(struct wcd9xxx *wcd9xxx, int offset)
+{
+ struct wcd9xxx_irq_drv_data *data;
+
+ data = wcd9xxx_get_irq_drv_d(wcd9xxx);
+ if (!data) {
+ pr_warn("%s: not registered to interrupt controller\n",
+ __func__);
+ return -EINVAL;
+ }
+ return irq_linear_revmap(data->domain, offset);
+}
+
+static int virq_to_phyirq(struct wcd9xxx *wcd9xxx, int virq)
+{
+ struct irq_data *irq_data = irq_get_irq_data(virq);
+ return irq_data->hwirq;
+}
+
+static unsigned int wcd9xxx_irq_get_upstream_irq(struct wcd9xxx *wcd9xxx)
+{
+ struct wcd9xxx_irq_drv_data *data;
+
+ /* Hold parent's of node */
+ if (!of_node_get(of_irq_find_parent(wcd9xxx->dev->of_node)))
+ return -EINVAL;
+
+ data = wcd9xxx_get_irq_drv_d(wcd9xxx);
+ if (!data) {
+ pr_err("%s: interrupt controller is not registerd\n", __func__);
+ return 0;
+ }
+
+ rmb();
+ return data->irq;
+}
+
+static void wcd9xxx_irq_put_upstream_irq(struct wcd9xxx *wcd9xxx)
+{
+ /* Hold parent's of node */
+ of_node_put(of_irq_find_parent(wcd9xxx->dev->of_node));
+}
+
+static int wcd9xxx_map_irq(struct wcd9xxx *wcd9xxx, int irq)
+{
+ return of_irq_to_resource(wcd9xxx->dev->of_node, irq, NULL);
+}
+
+static int __devinit wcd9xxx_irq_probe(struct platform_device *pdev)
+{
+ int irq;
+ struct irq_domain *domain;
+ struct wcd9xxx_irq_drv_data *data;
+ int ret = -EINVAL;
+
+ irq = platform_get_irq_byname(pdev, "cdc-int");
+ if (irq < 0) {
+ dev_err(&pdev->dev, "%s: Couldn't find cdc-int node(%d)\n",
+ __func__, irq);
+ return -EINVAL;
+ } else {
+ dev_dbg(&pdev->dev, "%s: virq = %d\n", __func__, irq);
+ domain = irq_find_host(pdev->dev.of_node);
+ data = (struct wcd9xxx_irq_drv_data *)domain->host_data;
+ data->irq = irq;
+ wmb();
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int wcd9xxx_irq_remove(struct platform_device *pdev)
+{
+ struct irq_domain *domain;
+ struct wcd9xxx_irq_drv_data *data;
+
+ domain = irq_find_host(pdev->dev.of_node);
+ data = (struct wcd9xxx_irq_drv_data *)domain->host_data;
+ data->irq = 0;
+ wmb();
+
+ return 0;
+}
+
+static const struct of_device_id of_match[] = {
+ { .compatible = "qcom,wcd9xxx-irq" },
+ { }
+};
+
+static struct platform_driver wcd9xxx_irq_driver = {
+ .probe = wcd9xxx_irq_probe,
+ .remove = wcd9xxx_irq_remove,
+ .driver = {
+ .name = "wcd9xxx_intc",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(of_match),
+ },
+};
+
+static int wcd9xxx_irq_drv_init(void)
+{
+ return platform_driver_register(&wcd9xxx_irq_driver);
+}
+subsys_initcall(wcd9xxx_irq_drv_init);
+
+static void wcd9xxx_irq_drv_exit(void)
+{
+ platform_driver_unregister(&wcd9xxx_irq_driver);
+}
+module_exit(wcd9xxx_irq_drv_exit);
+#endif /* CONFIG_OF */
diff --git a/drivers/mfd/wcd9xxx-slimslave.c b/drivers/mfd/wcd9xxx-slimslave.c
index b4cf435..6e6de37 100644
--- a/drivers/mfd/wcd9xxx-slimslave.c
+++ b/drivers/mfd/wcd9xxx-slimslave.c
@@ -16,44 +16,20 @@
#define WCD9XXX_CHIP_ID_TAIKO 0x00000201
-struct wcd9xxx_slim_sch_rx {
- u32 sph;
- u32 ch_num;
- u16 ch_h;
- u16 grph;
-};
-
-struct wcd9xxx_slim_sch_tx {
- u32 sph;
- u32 ch_num;
- u16 ch_h;
- u16 grph;
-};
-
struct wcd9xxx_slim_sch {
- struct wcd9xxx_slim_sch_rx rx[SLIM_MAX_RX_PORTS];
- struct wcd9xxx_slim_sch_tx tx[SLIM_MAX_TX_PORTS];
-
- u16 rx_port_start_offset;
- u16 num_rx_slave_port;
- u16 port_ch_0_start_port_id;
- u16 port_ch_0_end_port_id;
- u16 pgd_tx_port_ch_1_end_port_id;
u16 rx_port_ch_reg_base;
u16 port_tx_cfg_reg_base;
u16 port_rx_cfg_reg_base;
- int number_of_tx_slave_dev_ports;
- int number_of_rx_slave_dev_ports;
};
static struct wcd9xxx_slim_sch sh_ch;
-static int wcd9xxx_alloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx,
- u8 wcd9xxx_pgd_la);
-static int wcd9xxx_alloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx,
- u8 wcd9xxx_pgd_la);
-static int wcd9xxx_dealloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx);
-static int wcd9xxx_dealloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx);
+static int wcd9xxx_alloc_slim_sh_ch(struct wcd9xxx *wcd9xxx,
+ u8 wcd9xxx_pgd_la, u32 cnt,
+ struct wcd9xxx_ch *channels, u32 path);
+
+static int wcd9xxx_dealloc_slim_sh_ch(struct slim_device *slim,
+ u32 cnt, struct wcd9xxx_ch *channels);
static int wcd9xxx_configure_ports(struct wcd9xxx *wcd9xxx)
{
@@ -65,55 +41,27 @@
id = cpu_to_be32(id);
pr_debug("%s: chip id 0x%08x\n", __func__, id);
if (id != WCD9XXX_CHIP_ID_TAIKO) {
- sh_ch.rx_port_start_offset =
- TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS;
- sh_ch.num_rx_slave_port =
- TABLA_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS;
- sh_ch.port_ch_0_start_port_id =
- TABLA_SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID;
- sh_ch.port_ch_0_end_port_id =
- TABLA_SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID;
- sh_ch.pgd_tx_port_ch_1_end_port_id =
- TABLA_SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID;
-
- sh_ch.rx_port_ch_reg_base =
- 0x180 + (TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS * 4);
- sh_ch.port_rx_cfg_reg_base =
- 0x040 + (TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS);
- sh_ch.port_tx_cfg_reg_base = 0x040;
-
- sh_ch.number_of_tx_slave_dev_ports =
- TABLA_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS;
- sh_ch.number_of_rx_slave_dev_ports =
- TABLA_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS;
- } else {
- sh_ch.rx_port_start_offset =
- TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS;
- sh_ch.num_rx_slave_port =
- TAIKO_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS;
- sh_ch.port_ch_0_start_port_id =
- TAIKO_SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID;
- sh_ch.port_ch_0_end_port_id =
- TAIKO_SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID;
- sh_ch.pgd_tx_port_ch_1_end_port_id =
- TAIKO_SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID;
-
- sh_ch.rx_port_ch_reg_base = 0x180;
+ sh_ch.rx_port_ch_reg_base = 0x180 ;
sh_ch.port_rx_cfg_reg_base = 0x040;
+ sh_ch.port_tx_cfg_reg_base = 0x040;
+ } else {
+ sh_ch.rx_port_ch_reg_base =
+ 0x180 - (TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS * 4);
+ sh_ch.port_rx_cfg_reg_base =
+ 0x040 - TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS ;
sh_ch.port_tx_cfg_reg_base = 0x050;
-
- sh_ch.number_of_tx_slave_dev_ports =
- TAIKO_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS;
- sh_ch.number_of_rx_slave_dev_ports =
- TAIKO_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS;
}
return 0;
}
-int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la)
+
+int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
{
int ret = 0;
+ int i;
ret = wcd9xxx_configure_ports(wcd9xxx);
if (ret) {
@@ -122,125 +70,106 @@
goto err;
}
- ret = wcd9xxx_alloc_slim_sh_ch_rx(wcd9xxx, wcd9xxx_pgd_la);
- if (ret) {
- pr_err("%s: Failed to alloc rx slimbus shared channels\n",
- __func__);
- goto err;
+ if (wcd9xxx->rx_chs) {
+ wcd9xxx->num_rx_port = rx_num;
+ for (i = 0; i < rx_num; i++) {
+ wcd9xxx->rx_chs[i].ch_num = rx_slot[i];
+ INIT_LIST_HEAD(&wcd9xxx->rx_chs[i].list);
+ }
+ ret = wcd9xxx_alloc_slim_sh_ch(wcd9xxx, wcd9xxx_pgd_la,
+ wcd9xxx->num_rx_port,
+ wcd9xxx->rx_chs,
+ SLIM_SINK);
+ if (ret) {
+ pr_err("%s: Failed to alloc %d rx slimbus channels\n",
+ __func__, wcd9xxx->num_rx_port);
+ kfree(wcd9xxx->rx_chs);
+ wcd9xxx->rx_chs = NULL;
+ wcd9xxx->num_rx_port = 0;
+ }
+ } else {
+ pr_err("Not able to allocate memory for %d slimbus rx ports\n",
+ wcd9xxx->num_rx_port);
}
- ret = wcd9xxx_alloc_slim_sh_ch_tx(wcd9xxx, wcd9xxx_pgd_la);
- if (ret) {
- pr_err("%s: Failed to alloc tx slimbus shared channels\n",
- __func__);
- goto tx_err;
+
+ if (wcd9xxx->tx_chs) {
+ wcd9xxx->num_tx_port = tx_num;
+ for (i = 0; i < tx_num; i++) {
+ wcd9xxx->tx_chs[i].ch_num = tx_slot[i];
+ INIT_LIST_HEAD(&wcd9xxx->tx_chs[i].list);
+ }
+ ret = wcd9xxx_alloc_slim_sh_ch(wcd9xxx, wcd9xxx_pgd_la,
+ wcd9xxx->num_tx_port,
+ wcd9xxx->tx_chs,
+ SLIM_SRC);
+ if (ret) {
+ pr_err("%s: Failed to alloc %d tx slimbus channels\n",
+ __func__, wcd9xxx->num_tx_port);
+ kfree(wcd9xxx->tx_chs);
+ wcd9xxx->tx_chs = NULL;
+ wcd9xxx->num_tx_port = 0;
+ }
+ } else {
+ pr_err("Not able to allocate memory for %d slimbus tx ports\n",
+ wcd9xxx->num_tx_port);
}
+
return 0;
-tx_err:
- wcd9xxx_dealloc_slim_sh_ch_rx(wcd9xxx);
err:
return ret;
}
-
int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx)
{
- int ret = 0;
- ret = wcd9xxx_dealloc_slim_sh_ch_rx(wcd9xxx);
- if (ret < 0) {
- pr_err("%s: fail to dealloc rx slim ports\n", __func__);
- goto err;
+ if (wcd9xxx->num_rx_port) {
+ wcd9xxx_dealloc_slim_sh_ch(wcd9xxx->slim,
+ wcd9xxx->num_rx_port,
+ wcd9xxx->rx_chs);
+ wcd9xxx->num_rx_port = 0;
}
- ret = wcd9xxx_dealloc_slim_sh_ch_tx(wcd9xxx);
- if (ret < 0) {
- pr_err("%s: fail to dealloc tx slim ports\n", __func__);
- goto err;
+ if (wcd9xxx->num_tx_port) {
+ wcd9xxx_dealloc_slim_sh_ch(wcd9xxx->slim,
+ wcd9xxx->num_tx_port,
+ wcd9xxx->tx_chs);
+ wcd9xxx->num_tx_port = 0;
}
-err:
- return ret;
-}
-
-int wcd9xxx_get_channel(struct wcd9xxx *wcd9xxx, unsigned int *rx_ch,
- unsigned int *tx_ch)
-{
- int ch_idx = 0;
- struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
- struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
-
- for (ch_idx = 0; ch_idx < sh_ch.number_of_rx_slave_dev_ports; ch_idx++)
- rx_ch[ch_idx] = rx[ch_idx].ch_num;
- for (ch_idx = 0; ch_idx < sh_ch.number_of_tx_slave_dev_ports; ch_idx++)
- tx_ch[ch_idx] = tx[ch_idx].ch_num;
return 0;
}
-static int wcd9xxx_alloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx,
- u8 wcd9xxx_pgd_la)
+
+static int wcd9xxx_alloc_slim_sh_ch(struct wcd9xxx *wcd9xxx,
+ u8 wcd9xxx_pgd_la, u32 cnt,
+ struct wcd9xxx_ch *channels, u32 path)
{
int ret = 0;
- u8 ch_idx ;
- u16 slave_port_id = 0;
- struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+ u32 ch_idx ;
- /*
- * DSP requires channel number to be between 128 and 255.
+ /* The slimbus channel allocation seem take longer time
+ * so do the allocation up front to avoid delay in start of
+ * playback
*/
pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la);
- for (ch_idx = 0; ch_idx < sh_ch.number_of_rx_slave_dev_ports;
- ch_idx++) {
- slave_port_id = (ch_idx + sh_ch.rx_port_start_offset);
- rx[ch_idx].ch_num = slave_port_id + BASE_CH_NUM;
- ret = slim_get_slaveport(wcd9xxx_pgd_la, slave_port_id,
- &rx[ch_idx].sph, SLIM_SINK);
+ for (ch_idx = 0; ch_idx < cnt; ch_idx++) {
+ ret = slim_get_slaveport(wcd9xxx_pgd_la,
+ channels[ch_idx].port,
+ &channels[ch_idx].sph, path);
+ pr_debug("%s: pgd_la[%d] channels[%d].port[%d]\n"
+ "channels[%d].sph[%d] path[%d]\n",
+ __func__, wcd9xxx_pgd_la, ch_idx,
+ channels[ch_idx].port,
+ ch_idx, channels[ch_idx].sph, path);
if (ret < 0) {
pr_err("%s: slave port failure id[%d] ret[%d]\n",
- __func__, slave_port_id, ret);
+ __func__, channels[ch_idx].ch_num, ret);
goto err;
}
- ret = slim_query_ch(wcd9xxx->slim, rx[ch_idx].ch_num,
- &rx[ch_idx].ch_h);
+ ret = slim_query_ch(wcd9xxx->slim,
+ channels[ch_idx].ch_num,
+ &channels[ch_idx].ch_h);
if (ret < 0) {
pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n",
- __func__, rx[ch_idx].ch_num, ret);
- goto err;
- }
- pr_debug("%s:ch_num=%d ch_h=%d sph=%d la=%d slave_port_id %d\n",
- __func__, rx[ch_idx].ch_num, rx[ch_idx].ch_h,
- rx[ch_idx].sph, wcd9xxx_pgd_la, slave_port_id);
- }
-err:
- return ret;
-}
-
-static int wcd9xxx_alloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx,
- u8 wcd9xxx_pgd_la)
-{
- int ret = 0;
- u8 ch_idx;
- struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
- u16 slave_port_id = 0;
-
- pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la);
- /* DSP requires channel number to be between 128 and 255. For RX port
- * use channel numbers from 138 to 144, for TX port
- * use channel numbers from 128 to 137
- */
- for (ch_idx = 0; ch_idx < sh_ch.number_of_tx_slave_dev_ports;
- ch_idx++) {
- slave_port_id = ch_idx;
- tx[ch_idx].ch_num = slave_port_id + BASE_CH_NUM;
- ret = slim_get_slaveport(wcd9xxx_pgd_la, slave_port_id,
- &tx[ch_idx].sph, SLIM_SRC);
- if (ret < 0) {
- pr_err("%s: slave port failure id[%d] ret[%d]\n",
- __func__, slave_port_id, ret);
- goto err;
- }
- ret = slim_query_ch(wcd9xxx->slim, tx[ch_idx].ch_num,
- &tx[ch_idx].ch_h);
- if (ret < 0) {
- pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n",
- __func__, tx[ch_idx].ch_num, ret);
+ __func__, channels[ch_idx].ch_num, ret);
goto err;
}
}
@@ -248,116 +177,46 @@
return ret;
}
-static int wcd9xxx_dealloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx)
+static int wcd9xxx_dealloc_slim_sh_ch(struct slim_device *slim,
+ u32 cnt, struct wcd9xxx_ch *channels)
{
int idx = 0;
int ret = 0;
- struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
/* slim_dealloc_ch */
- for (idx = 0; idx < sh_ch.number_of_rx_slave_dev_ports; idx++) {
- ret = slim_dealloc_ch(wcd9xxx->slim, rx[idx].ch_h);
+ for (idx = 0; idx < cnt; idx++) {
+ ret = slim_dealloc_ch(slim, channels[idx].ch_h);
if (ret < 0) {
pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n",
- __func__, ret, rx[idx].ch_h);
+ __func__, ret, channels[idx].ch_h);
}
}
- memset(sh_ch.rx, 0, sizeof(sh_ch.rx));
- return ret;
-}
-
-static int wcd9xxx_dealloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx)
-{
- int idx = 0;
- int ret = 0;
- struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
- /* slim_dealloc_ch */
- for (idx = 0; idx < sh_ch.number_of_tx_slave_dev_ports; idx++) {
- ret = slim_dealloc_ch(wcd9xxx->slim, tx[idx].ch_h);
- if (ret < 0) {
- pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n",
- __func__, ret, tx[idx].ch_h);
- }
- }
- memset(sh_ch.tx, 0, sizeof(sh_ch.tx));
return ret;
}
/* Enable slimbus slave device for RX path */
-int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
- unsigned int ch_cnt, unsigned int rate)
+int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx,
+ struct list_head *wcd9xxx_ch_list,
+ unsigned int rate, unsigned int bit_width,
+ u16 *grph)
{
- u8 i;
- u16 grph;
- u32 sph[SLIM_MAX_RX_PORTS] = {0};
+ u8 ch_cnt = 0;
u16 ch_h[SLIM_MAX_RX_PORTS] = {0};
- u16 slave_port_id;
- u8 payload_rx = 0, wm_payload = 0;
- int ret, idx = 0;
- unsigned short multi_chan_cfg_reg_addr;
- struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+ u8 payload = 0;
+ u16 codec_port = 0;
+ int ret;
struct slim_ch prop;
+ struct wcd9xxx_ch *rx;
/* Configure slave interface device */
- pr_debug("%s: ch_cnt[%d] rate=%d\n", __func__, ch_cnt, rate);
- for (i = 0; i < ch_cnt; i++) {
- idx = (ch_num[i] - BASE_CH_NUM - sh_ch.rx_port_start_offset);
- ch_h[i] = rx[idx].ch_h;
- sph[i] = rx[idx].sph;
- slave_port_id = idx;
- pr_debug("%s: idx %d, ch_h %d, sph %d\n",
- __func__, idx, ch_h[i], sph[i]);
- if ((slave_port_id > sh_ch.num_rx_slave_port)) {
- pr_err("Slimbus: invalid slave port id: %d",
- slave_port_id);
- ret = -EINVAL;
- goto err;
- }
- slave_port_id += sh_ch.rx_port_start_offset;
- pr_debug("%s: slave_port_id %d\n", __func__, slave_port_id);
- /* look for the valid port range and chose the
- * payload accordingly
- */
- if ((slave_port_id > sh_ch.pgd_tx_port_ch_1_end_port_id) &&
- (slave_port_id <= sh_ch.port_ch_0_end_port_id)) {
- payload_rx = payload_rx |
- (1 << (slave_port_id -
- sh_ch.port_ch_0_start_port_id));
- } else {
- ret = -EINVAL;
- goto err;
- }
-
- multi_chan_cfg_reg_addr =
- SB_PGD_RX_PORT_MULTI_CHANNEL_0(sh_ch.rx_port_ch_reg_base,
- idx);
- pr_debug("%s: multi_chan_cfg_reg_addr 0x%x\n", __func__,
- multi_chan_cfg_reg_addr);
-
- /* write to interface device */
- ret = wcd9xxx_interface_reg_write(wcd9xxx,
- multi_chan_cfg_reg_addr,
- payload_rx);
- if (ret < 0) {
- pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
- __func__, multi_chan_cfg_reg_addr,
- payload_rx, ret);
- goto err;
- }
- /* configure the slave port for water mark and enable*/
- wm_payload = (SLAVE_PORT_WATER_MARK_VALUE <<
- SLAVE_PORT_WATER_MARK_SHIFT) + SLAVE_PORT_ENABLE;
- ret = wcd9xxx_interface_reg_write(
- wcd9xxx,
- SB_PGD_PORT_CFG_BYTE_ADDR(
- sh_ch.port_rx_cfg_reg_base, idx),
- wm_payload);
- if (ret < 0) {
- pr_err("%s:watermark set failure for port[%d] ret[%d]",
- __func__, slave_port_id, ret);
- }
+ list_for_each_entry(rx, wcd9xxx_ch_list, list) {
+ payload |= 1 << rx->shift;
+ ch_h[ch_cnt] = rx->ch_h;
+ ch_cnt++;
+ pr_debug("list ch->ch_h %d ch->sph %d\n", rx->ch_h, rx->sph);
}
-
+ pr_debug("%s: ch_cnt[%d] rate=%d WATER_MARK_VAL %d\n",
+ __func__, ch_cnt, rate, WATER_MARK_VAL);
/* slim_define_ch api */
prop.prot = SLIM_AUTO_ISO;
prop.baser = SLIM_RATE_4000HZ;
@@ -366,130 +225,97 @@
prop.ratem = (rate/4000);
prop.sampleszbits = 16;
- ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt, true, &grph);
+ pr_debug("Before slim_define_ch:\n"
+ "ch_cnt %d,ch_h[0] %d ch_h[1] %d, grph %d\n",
+ ch_cnt, ch_h[0], ch_h[1], *grph);
+ ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
+ true, grph);
if (ret < 0) {
pr_err("%s: slim_define_ch failed ret[%d]\n",
- __func__, ret);
+ __func__, ret);
goto err;
}
- for (i = 0; i < ch_cnt; i++) {
- ret = slim_connect_sink(wcd9xxx->slim, &sph[i], 1, ch_h[i]);
+
+ list_for_each_entry(rx, wcd9xxx_ch_list, list) {
+ codec_port = rx->port;
+ pr_debug("%s: codec_port %d rx 0x%x, payload %d\n"
+ "sh_ch.rx_port_ch_reg_base0 0x%x\n"
+ "sh_ch.port_rx_cfg_reg_base 0x%x\n",
+ __func__, codec_port, (u32)rx, payload,
+ sh_ch.rx_port_ch_reg_base,
+ sh_ch.port_rx_cfg_reg_base);
+
+ /* look for the valid port range and chose the
+ * payload accordingly
+ */
+ /* write to interface device */
+ ret = wcd9xxx_interface_reg_write(wcd9xxx,
+ SB_PGD_RX_PORT_MULTI_CHANNEL_0(
+ sh_ch.rx_port_ch_reg_base, codec_port),
+ payload);
+
+ if (ret < 0) {
+ pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+ __func__,
+ SB_PGD_RX_PORT_MULTI_CHANNEL_0(
+ sh_ch.rx_port_ch_reg_base, codec_port),
+ payload, ret);
+ goto err;
+ }
+ /* configure the slave port for water mark and enable*/
+ ret = wcd9xxx_interface_reg_write(wcd9xxx,
+ SB_PGD_PORT_CFG_BYTE_ADDR(
+ sh_ch.port_rx_cfg_reg_base, codec_port),
+ WATER_MARK_VAL);
+ if (ret < 0) {
+ pr_err("%s:watermark set failure for port[%d] ret[%d]",
+ __func__, codec_port, ret);
+ }
+
+ ret = slim_connect_sink(wcd9xxx->slim, &rx->sph, 1, rx->ch_h);
if (ret < 0) {
pr_err("%s: slim_connect_sink failed ret[%d]\n",
- __func__, ret);
+ __func__, ret);
goto err_close_slim_sch;
}
}
/* slim_control_ch */
- ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_ACTIVATE, true);
+ ret = slim_control_ch(wcd9xxx->slim, *grph, SLIM_CH_ACTIVATE,
+ true);
if (ret < 0) {
pr_err("%s: slim_control_ch failed ret[%d]\n",
- __func__, ret);
+ __func__, ret);
goto err_close_slim_sch;
}
- for (i = 0; i < ch_cnt; i++) {
- idx = (ch_num[i] - BASE_CH_NUM - sh_ch.rx_port_start_offset);
- rx[idx].grph = grph;
- }
return 0;
err_close_slim_sch:
/* release all acquired handles */
- wcd9xxx_close_slim_sch_rx(wcd9xxx, ch_num, ch_cnt);
+ wcd9xxx_close_slim_sch_rx(wcd9xxx, wcd9xxx_ch_list, *grph);
err:
return ret;
}
EXPORT_SYMBOL_GPL(wcd9xxx_cfg_slim_sch_rx);
/* Enable slimbus slave device for RX path */
-int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
- unsigned int ch_cnt, unsigned int rate)
+int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx,
+ struct list_head *wcd9xxx_ch_list,
+ unsigned int rate, unsigned int bit_width,
+ u16 *grph)
{
- u8 i = 0;
- u8 payload_tx_0 = 0, payload_tx_1 = 0, wm_payload = 0;
- u16 grph;
- u32 sph[SLIM_MAX_TX_PORTS] = {0};
+ u16 ch_cnt = 0;
+ u16 payload = 0;
u16 ch_h[SLIM_MAX_TX_PORTS] = {0};
- u16 idx = 0, slave_port_id;
+ u16 codec_port;
int ret = 0;
- unsigned short multi_chan_cfg_reg_addr;
+ struct wcd9xxx_ch *tx;
- struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
struct slim_ch prop;
- pr_debug("%s: ch_cnt[%d] rate[%d]\n", __func__, ch_cnt, rate);
- for (i = 0; i < ch_cnt; i++) {
- idx = (ch_num[i] - BASE_CH_NUM);
- ch_h[i] = tx[idx].ch_h;
- sph[i] = tx[idx].sph;
- slave_port_id = idx;
- pr_debug("%s: idx %d, ch_h %d, sph %d, slave_port_id %d\n",
- __func__, idx, ch_h[i], sph[i], slave_port_id);
- if (slave_port_id > sh_ch.number_of_tx_slave_dev_ports) {
- pr_err("SLIMbus: invalid slave port id: %d",
- slave_port_id);
- ret = -EINVAL;
- goto err;
- }
- /* look for the valid port range and chose the
- * payload accordingly
- */
- if (slave_port_id <=
- SB_PGD_TX_PORT_MULTI_CHANNEL_0_END_PORT_ID) {
- payload_tx_0 = payload_tx_0 | (1 << slave_port_id);
- } else if (slave_port_id <=
- sh_ch.pgd_tx_port_ch_1_end_port_id) {
- payload_tx_1 = payload_tx_1 |
- (1 << (slave_port_id -
- SB_PGD_TX_PORT_MULTI_CHANNEL_1_START_PORT_ID));
- } else {
- pr_err("%s: slave port id %d error\n", __func__,
- slave_port_id);
- ret = -EINVAL;
- goto err;
- }
- multi_chan_cfg_reg_addr =
- SB_PGD_TX_PORT_MULTI_CHANNEL_0(slave_port_id);
- pr_debug("%s: multi_chan_cfg_reg_addr 0x%x\n", __func__,
- multi_chan_cfg_reg_addr);
- /* write to interface device */
- ret = wcd9xxx_interface_reg_write(wcd9xxx,
- multi_chan_cfg_reg_addr,
- payload_tx_0);
- if (ret < 0) {
- pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
- __func__, multi_chan_cfg_reg_addr, payload_tx_0,
- ret);
- goto err;
- }
- multi_chan_cfg_reg_addr =
- SB_PGD_TX_PORT_MULTI_CHANNEL_1(slave_port_id);
- /* ports 8,9 */
- ret = wcd9xxx_interface_reg_write(wcd9xxx,
- multi_chan_cfg_reg_addr,
- payload_tx_1);
- if (ret < 0) {
- pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
- __func__, multi_chan_cfg_reg_addr,
- payload_tx_1, ret);
- goto err;
- }
- /* configure the slave port for water mark and enable*/
- wm_payload = (SLAVE_PORT_WATER_MARK_VALUE <<
- SLAVE_PORT_WATER_MARK_SHIFT) + SLAVE_PORT_ENABLE;
- pr_debug("%s: tx_cfg_reg 0x%x wm 0x%x\n", __func__,
- SB_PGD_PORT_CFG_BYTE_ADDR(sh_ch.port_tx_cfg_reg_base,
- slave_port_id), wm_payload);
- ret = wcd9xxx_interface_reg_write(
- wcd9xxx,
- SB_PGD_PORT_CFG_BYTE_ADDR(
- sh_ch.port_tx_cfg_reg_base,
- slave_port_id),
- wm_payload);
- if (ret < 0) {
- pr_err("%s: watermark set failure for port[%d] ret[%d]",
- __func__, slave_port_id, ret);
- }
+ list_for_each_entry(tx, wcd9xxx_ch_list, list) {
+ payload |= 1 << tx->shift;
+ ch_h[ch_cnt] = tx->ch_h;
+ ch_cnt++;
}
/* slim_define_ch api */
@@ -499,13 +325,53 @@
prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
prop.ratem = (rate/4000);
prop.sampleszbits = 16;
- ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt, true, &grph);
+ ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
+ true, grph);
if (ret < 0) {
- pr_err("%s: slim_define_ch failed ret[%d]\n", __func__, ret);
+ pr_err("%s: slim_define_ch failed ret[%d]\n",
+ __func__, ret);
goto err;
}
- for (i = 0; i < ch_cnt; i++) {
- ret = slim_connect_src(wcd9xxx->slim, sph[i], ch_h[i]);
+
+ pr_debug("%s: ch_cnt[%d] rate[%d]\n", __func__, ch_cnt, rate);
+ list_for_each_entry(tx, wcd9xxx_ch_list, list) {
+ codec_port = tx->port;
+ pr_debug("%s: codec_port %d rx 0x%x, payload 0x%x\n",
+ __func__, codec_port, (u32)tx, payload);
+ /* write to interface device */
+ ret = wcd9xxx_interface_reg_write(wcd9xxx,
+ SB_PGD_TX_PORT_MULTI_CHANNEL_0(codec_port),
+ payload & 0x00FF);
+ if (ret < 0) {
+ pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+ __func__,
+ SB_PGD_TX_PORT_MULTI_CHANNEL_0(codec_port),
+ payload, ret);
+ goto err;
+ }
+ /* ports 8,9 */
+ ret = wcd9xxx_interface_reg_write(wcd9xxx,
+ SB_PGD_TX_PORT_MULTI_CHANNEL_1(codec_port),
+ (payload & 0xFF00)>>8);
+ if (ret < 0) {
+ pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+ __func__,
+ SB_PGD_TX_PORT_MULTI_CHANNEL_1(codec_port),
+ payload, ret);
+ goto err;
+ }
+ /* configure the slave port for water mark and enable*/
+ ret = wcd9xxx_interface_reg_write(wcd9xxx,
+ SB_PGD_PORT_CFG_BYTE_ADDR(
+ sh_ch.port_tx_cfg_reg_base, codec_port),
+ WATER_MARK_VAL);
+ if (ret < 0) {
+ pr_err("%s:watermark set failure for port[%d] ret[%d]",
+ __func__, codec_port, ret);
+ }
+
+ ret = slim_connect_src(wcd9xxx->slim, tx->sph, tx->ch_h);
+
if (ret < 0) {
pr_err("%s: slim_connect_src failed ret[%d]\n",
__func__, ret);
@@ -513,91 +379,69 @@
}
}
/* slim_control_ch */
- ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_ACTIVATE, true);
+ ret = slim_control_ch(wcd9xxx->slim, *grph, SLIM_CH_ACTIVATE,
+ true);
if (ret < 0) {
pr_err("%s: slim_control_ch failed ret[%d]\n",
- __func__, ret);
+ __func__, ret);
goto err;
}
- for (i = 0; i < ch_cnt; i++) {
- idx = (ch_num[i] - BASE_CH_NUM);
- tx[idx].grph = grph;
- }
return 0;
err:
/* release all acquired handles */
- wcd9xxx_close_slim_sch_tx(wcd9xxx, ch_num, ch_cnt);
+ wcd9xxx_close_slim_sch_tx(wcd9xxx, wcd9xxx_ch_list, *grph);
return ret;
}
EXPORT_SYMBOL_GPL(wcd9xxx_cfg_slim_sch_tx);
-int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
- unsigned int ch_cnt)
+int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx,
+ struct list_head *wcd9xxx_ch_list, u16 grph)
{
- u16 grph = 0;
- int i = 0 , idx = 0;
+ u32 sph[SLIM_MAX_RX_PORTS] = {0};
+ int ch_cnt = 0 ;
int ret = 0;
- struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+ struct wcd9xxx_ch *rx;
- pr_debug("%s: ch_cnt[%d]\n", __func__, ch_cnt);
- for (i = 0; i < ch_cnt; i++) {
- idx = (ch_num[i] - BASE_CH_NUM - sh_ch.rx_port_start_offset);
- if (idx < 0) {
- pr_err("%s: Error:-Invalid index found = %d\n",
- __func__, idx);
- ret = -EINVAL;
- goto err;
- }
- grph = rx[idx].grph;
- pr_debug("%s: ch_num[%d] %d, idx %d, grph %x\n",
- __func__, i, ch_num[i], idx, grph);
- }
+ list_for_each_entry(rx, wcd9xxx_ch_list, list)
+ sph[ch_cnt++] = rx->sph;
+
+ pr_debug("%s ch_cht %d, sph[0] %d sph[1] %d\n", __func__, ch_cnt,
+ sph[0], sph[1]);
/* slim_control_ch (REMOVE) */
+ pr_debug("%s before slim_control_ch grph %d\n", __func__, grph);
ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
if (ret < 0) {
pr_err("%s: slim_control_ch failed ret[%d]\n", __func__, ret);
goto err;
}
- for (i = 0; i < ch_cnt; i++) {
- idx = (ch_num[i] - BASE_CH_NUM - sh_ch.rx_port_start_offset);
- rx[idx].grph = 0;
- }
err:
return ret;
}
EXPORT_SYMBOL_GPL(wcd9xxx_close_slim_sch_rx);
-int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
- unsigned int ch_cnt)
+int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx,
+ struct list_head *wcd9xxx_ch_list,
+ u16 grph)
{
- u16 grph = 0;
+ u32 sph[SLIM_MAX_TX_PORTS] = {0};
int ret = 0;
- int i = 0 , idx = 0;
- struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+ int ch_cnt = 0 ;
+ struct wcd9xxx_ch *tx;
- pr_debug("%s: ch_cnt[%d]\n", __func__, ch_cnt);
- for (i = 0; i < ch_cnt; i++) {
- idx = (ch_num[i] - BASE_CH_NUM);
- if (idx < 0) {
- pr_err("%s: Error:- Invalid index found = %d\n",
- __func__, idx);
- ret = -EINVAL;
- goto err;
- }
- grph = tx[idx].grph;
- }
+ pr_debug("%s\n", __func__);
+ list_for_each_entry(tx, wcd9xxx_ch_list, list)
+ sph[ch_cnt++] = tx->sph;
+
+ pr_debug("%s ch_cht %d, sph[0] %d sph[1] %d\n",
+ __func__, ch_cnt, sph[0], sph[1]);
/* slim_control_ch (REMOVE) */
ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
if (ret < 0) {
pr_err("%s: slim_control_ch failed ret[%d]\n",
- __func__, ret);
+ __func__, ret);
goto err;
}
- for (i = 0; i < ch_cnt; i++) {
- idx = (ch_num[i] - BASE_CH_NUM);
- tx[idx].grph = 0;
- }
err:
return ret;
}
@@ -607,8 +451,8 @@
{
int ret = 0;
- pr_debug("%s: ch_num[%d]\n", __func__, ch_num);
ret = (ch_num - BASE_CH_NUM);
+ pr_debug("%s: ch_num[%d] slave port[%d]\n", __func__, ch_num, ret);
if (ret < 0) {
pr_err("%s: Error:- Invalid slave port found = %d\n",
__func__, ret);
@@ -618,39 +462,16 @@
}
EXPORT_SYMBOL_GPL(wcd9xxx_get_slave_port);
-int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
- unsigned int ch_cnt, unsigned int rx_tx)
+int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx,
+ struct list_head *wcd9xxx_ch_list, u16 grph)
{
- u32 sph[SLIM_MAX_TX_PORTS] = {0};
- int i = 0 , idx = 0;
+ u32 sph[SLIM_MAX_TX_PORTS + SLIM_MAX_RX_PORTS] = {0};
+ int ch_cnt = 0 ;
int ret = 0;
- struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
- struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+ struct wcd9xxx_ch *slim_ch;
- pr_debug("%s: ch_cnt[%d], rx_tx flag = %d\n", __func__, ch_cnt, rx_tx);
- for (i = 0; i < ch_cnt; i++) {
- /* rx_tx will be 1 for rx, 0 for tx */
- if (rx_tx) {
- idx = (ch_num[i] - BASE_CH_NUM -
- sh_ch.rx_port_start_offset);
- if (idx < 0) {
- pr_err("%s: Invalid index found for RX = %d\n",
- __func__, idx);
- ret = -EINVAL;
- goto err;
- }
- sph[i] = rx[idx].sph;
- } else {
- idx = (ch_num[i] - BASE_CH_NUM);
- if (idx < 0) {
- pr_err("%s:Invalid index found for TX = %d\n",
- __func__, idx);
- ret = -EINVAL;
- goto err;
- }
- sph[i] = tx[idx].sph;
- }
- }
+ list_for_each_entry(slim_ch, wcd9xxx_ch_list, list)
+ sph[ch_cnt++] = slim_ch->sph;
/* slim_disconnect_port */
ret = slim_disconnect_ports(wcd9xxx->slim, sph, ch_cnt);
@@ -658,7 +479,6 @@
pr_err("%s: slim_disconnect_ports failed ret[%d]\n",
__func__, ret);
}
-err:
return ret;
}
EXPORT_SYMBOL_GPL(wcd9xxx_disconnect_port);
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index b9cd8ec..7387d9a 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -121,6 +121,7 @@
struct device_attribute force_ro;
struct device_attribute power_ro_lock;
struct device_attribute num_wr_reqs_to_start_packing;
+ struct device_attribute min_sectors_to_check_bkops_status;
int area_type;
};
@@ -306,6 +307,48 @@
return count;
}
+static ssize_t
+min_sectors_to_check_bkops_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+ unsigned int min_sectors_to_check_bkops_status;
+ struct mmc_card *card = md->queue.card;
+ int ret;
+
+ if (!card)
+ return -EINVAL;
+
+ min_sectors_to_check_bkops_status =
+ card->bkops_info.min_sectors_to_queue_delayed_work;
+
+ ret = snprintf(buf, PAGE_SIZE, "%d\n",
+ min_sectors_to_check_bkops_status);
+
+ mmc_blk_put(md);
+ return ret;
+}
+
+static ssize_t
+min_sectors_to_check_bkops_status_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int value;
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+ struct mmc_card *card = md->queue.card;
+
+ if (!card)
+ return -EINVAL;
+
+ sscanf(buf, "%d", &value);
+ if (value >= 0)
+ card->bkops_info.min_sectors_to_queue_delayed_work = value;
+
+ mmc_blk_put(md);
+ return count;
+}
+
static int mmc_blk_open(struct block_device *bdev, fmode_t mode)
{
struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk);
@@ -2316,6 +2359,19 @@
if (ret)
goto power_ro_lock_fail;
+ md->min_sectors_to_check_bkops_status.show =
+ min_sectors_to_check_bkops_status_show;
+ md->min_sectors_to_check_bkops_status.store =
+ min_sectors_to_check_bkops_status_store;
+ sysfs_attr_init(&md->min_sectors_to_check_bkops_status.attr);
+ md->min_sectors_to_check_bkops_status.attr.name =
+ "min_sectors_to_check_bkops_status";
+ md->min_sectors_to_check_bkops_status.attr.mode = S_IRUGO | S_IWUSR;
+ ret = device_create_file(disk_to_dev(md->disk),
+ &md->min_sectors_to_check_bkops_status);
+ if (ret)
+ goto power_ro_lock_fail;
+
return ret;
power_ro_lock_fail:
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 9b9e8cc..7e03e5a 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -291,7 +291,7 @@
return;
if (card->bkops_info.sectors_changed <
- BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK)
+ card->bkops_info.min_sectors_to_queue_delayed_work)
return;
pr_debug("%s: %s: queueing delayed_bkops_work\n",
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 457db7f..ca8b01c 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1336,6 +1336,8 @@
card->bkops_info.delay_ms,
card->bkops_info.host_suspend_tout_ms/2);
+ card->bkops_info.min_sectors_to_queue_delayed_work =
+ BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK;
}
}
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 4e92dd7..29413ab 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -3,7 +3,8 @@
*
* Copyright (C) 2007 Google Inc,
* Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
- * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2012, 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
@@ -1821,7 +1822,7 @@
*/
wake_lock(&host->sdio_wlock);
} else {
- if (!mmc->card || !mmc_card_sdio(mmc->card)) {
+ if (mmc->card && !mmc_card_sdio(mmc->card)) {
WARN(1, "%s: SDCC core interrupt received for non-SDIO cards when SDCC clocks are off\n",
mmc_hostname(mmc));
ret = 1;
@@ -1855,7 +1856,7 @@
#endif
if (status & MCI_SDIOINTROPE) {
- if (!mmc->card || mmc_card_sdio(mmc->card)) {
+ if (mmc->card && !mmc_card_sdio(mmc->card)) {
WARN(1, "%s: SDIO interrupt received for non-SDIO card\n",
mmc_hostname(mmc));
ret = 1;
@@ -5450,7 +5451,7 @@
struct mmc_platform_data *pdata;
struct device_node *np = dev->of_node;
u32 bus_width = 0, current_limit = 0;
- u32 *clk_table, *sup_voltages;
+ u32 *clk_table = NULL, *sup_voltages = NULL;
int clk_table_len, sup_volt_len, len;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 6de0a77..740c717 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1168,6 +1168,7 @@
usb_anchor_urb(urb, &dev->deferred);
/* no use to process more packets */
netif_stop_queue(net);
+ usb_put_urb(urb);
spin_unlock_irqrestore(&dev->txq.lock, flags);
netdev_dbg(dev->net, "Delaying transmission for resumption\n");
goto deferred;
@@ -1317,6 +1318,8 @@
cancel_work_sync(&dev->kevent);
+ usb_scuttle_anchored_urbs(&dev->deferred);
+
if (dev->driver_info->unbind)
dev->driver_info->unbind (dev, intf);
diff --git a/drivers/platform/msm/qpnp-pwm.c b/drivers/platform/msm/qpnp-pwm.c
index ebe3ad06..a938a45 100644
--- a/drivers/platform/msm/qpnp-pwm.c
+++ b/drivers/platform/msm/qpnp-pwm.c
@@ -107,8 +107,10 @@
/* LPG Control for RAMP_CONTROL */
#define QPNP_RAMP_START_MASK 0x01
-#define QPNP_ENABLE_LUT(value) (value |= QPNP_RAMP_START_MASK)
-#define QPNP_DISABLE_LUT(value) (value &= ~QPNP_RAMP_START_MASK)
+#define QPNP_ENABLE_LUT_V0(value) (value |= QPNP_RAMP_START_MASK)
+#define QPNP_DISABLE_LUT_V0(value) (value &= ~QPNP_RAMP_START_MASK)
+#define QPNP_ENABLE_LUT_V1(value, id) (value |= BIT(id))
+#define QPNP_DISABLE_LUT_V1(value, id) (value &= ~BIT(id))
/* LPG Control for RAMP_STEP_DURATION_LSB */
#define QPNP_RAMP_STEP_DURATION_LSB_MASK 0xFF
@@ -154,10 +156,18 @@
#define PRE_DIVIDE_5 5
#define PRE_DIVIDE_6 6
-#define SPMI_LPG_REG_ADDR_BASE 0x40
-#define SPMI_LPG_REG_ADDR(b, n) (b + SPMI_LPG_REG_ADDR_BASE + (n))
+#define SPMI_LPG_REG_BASE_OFFSET 0x40
+#define SPMI_LPG_REVISION2_OFFSET 0x1
+#define SPMI_LPG_REV1_RAMP_CONTROL_OFFSET 0x86
+#define SPMI_LPG_REG_ADDR(b, n) (b + SPMI_LPG_REG_BASE_OFFSET + (n))
#define SPMI_MAX_BUF_LEN 8
+/* LPG revisions */
+enum qpnp_lpg_revision {
+ QPNP_LPG_REVISION_0 = 0x0,
+ QPNP_LPG_REVISION_1 = 0x1,
+};
+
/* SPMI LPG registers */
enum qpnp_lpg_registers_list {
QPNP_LPG_PATTERN_CONFIG,
@@ -254,6 +264,7 @@
struct mutex lpg_mutex;
struct qpnp_lpg_config lpg_config;
u8 qpnp_lpg_registers[QPNP_TOTAL_LPG_SPMI_REGISTERS];
+ enum qpnp_lpg_revision revision;
};
/* Internal functions */
@@ -812,34 +823,68 @@
{
struct qpnp_lpg_config *lpg_config = &pwm->chip->lpg_config;
struct qpnp_lpg_chip *chip = pwm->chip;
- u8 value, mask;
+ u8 value, mask, *reg;
+ u16 addr;
value = pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
+ reg = &pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
- QPNP_ENABLE_LUT(value);
+ switch (chip->revision) {
+ case QPNP_LPG_REVISION_0:
+ QPNP_ENABLE_LUT_V0(value);
+ mask = QPNP_RAMP_START_MASK;
+ addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_RAMP_CONTROL);
+ break;
+ case QPNP_LPG_REVISION_1:
+ QPNP_ENABLE_LUT_V1(value, pwm->pwm_config.channel_id);
+ mask = BIT(pwm->pwm_config.channel_id);
+ addr = lpg_config->lut_base_addr +
+ SPMI_LPG_REV1_RAMP_CONTROL_OFFSET;
+ default:
+ pr_err("Invalid LPG revision\n");
+ return -EINVAL;
+ }
- mask = QPNP_RAMP_START_MASK;
+ qpnp_lpg_save(reg, mask, value);
- return qpnp_lpg_save_and_write(value, mask,
- &pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL],
- lpg_config->base_addr, QPNP_RAMP_CONTROL, 1, chip);
+ return spmi_ext_register_writel(chip->spmi_dev->ctrl,
+ chip->spmi_dev->sid, addr, reg, 1);
}
-static int qpnp_disable_lut(struct pwm_device *pwm)
+static int qpnp_lpg_disable_lut(struct pwm_device *pwm)
{
struct qpnp_lpg_config *lpg_config = &pwm->chip->lpg_config;
struct qpnp_lpg_chip *chip = pwm->chip;
- u8 value, mask;
+ u8 value, mask, *reg;
+ u16 addr;
value = pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
+ reg = &pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
- QPNP_DISABLE_LUT(value);
+ switch (chip->revision) {
+ case QPNP_LPG_REVISION_0:
+ QPNP_DISABLE_LUT_V0(value);
+ mask = QPNP_RAMP_START_MASK;
+ addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_RAMP_CONTROL);
+ break;
+ case QPNP_LPG_REVISION_1:
+ QPNP_DISABLE_LUT_V1(value, pwm->pwm_config.channel_id);
+ mask = BIT(pwm->pwm_config.channel_id);
+ addr = lpg_config->lut_base_addr +
+ SPMI_LPG_REV1_RAMP_CONTROL_OFFSET;
+ break;
+ default:
+ pr_err("Invalid LPG revision\n");
+ return -EINVAL;
+ break;
+ }
- mask = QPNP_RAMP_START_MASK;
+ qpnp_lpg_save(reg, mask, value);
- return qpnp_lpg_save_and_write(value, mask,
- &pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL],
- lpg_config->base_addr, QPNP_RAMP_CONTROL, 1, chip);
+ return spmi_ext_register_writel(chip->spmi_dev->ctrl,
+ chip->spmi_dev->sid, addr, reg, 1);
}
static int qpnp_lpg_enable_pwm(struct pwm_device *pwm)
@@ -847,6 +892,7 @@
struct qpnp_lpg_config *lpg_config = &pwm->chip->lpg_config;
struct qpnp_lpg_chip *chip = pwm->chip;
u8 value, mask;
+ int rc;
value = pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL];
@@ -854,12 +900,23 @@
mask = QPNP_EN_PWM_OUTPUT_MASK;
- return qpnp_lpg_save_and_write(value, mask,
+ rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],
lpg_config->base_addr, QPNP_ENABLE_CONTROL, 1, chip);
+ if (rc)
+ goto out;
+
+ /*
+ * Due to LPG hardware bug, in the PWM mode, having enabled PWM,
+ * We have to write PWM values one more time.
+ */
+ return qpnp_lpg_save_pwm_value(pwm);
+
+out:
+ return rc;
}
-static int qpnp_disable_pwm(struct pwm_device *pwm)
+static int qpnp_lpg_disable_pwm(struct pwm_device *pwm)
{
struct qpnp_lpg_config *lpg_config = &pwm->chip->lpg_config;
struct qpnp_lpg_chip *chip = pwm->chip;
@@ -1072,8 +1129,8 @@
pwm_config = &pwm->pwm_config;
if (pwm_config->in_use) {
- qpnp_disable_pwm(pwm);
- qpnp_disable_lut(pwm);
+ qpnp_lpg_disable_pwm(pwm);
+ qpnp_lpg_disable_lut(pwm);
pwm_config->in_use = 0;
pwm_config->lable = NULL;
}
@@ -1157,9 +1214,9 @@
if (pwm_config->in_use) {
if (QPNP_IS_PWM_CONFIG_SELECTED(
chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL]))
- qpnp_disable_pwm(pwm);
+ qpnp_lpg_disable_pwm(pwm);
else
- qpnp_disable_lut(pwm);
+ qpnp_lpg_disable_lut(pwm);
}
mutex_unlock(&pwm->chip->lpg_mutex);
@@ -1596,6 +1653,19 @@
id = chip->pwm_dev.pwm_config.channel_id;
+ spmi_ext_register_readl(chip->spmi_dev->ctrl,
+ chip->spmi_dev->sid,
+ chip->lpg_config.base_addr + SPMI_LPG_REVISION2_OFFSET,
+ (u8 *) &chip->revision, 1);
+
+ if (chip->revision < QPNP_LPG_REVISION_0 ||
+ chip->revision > QPNP_LPG_REVISION_1) {
+ pr_err("Unknown LPG revision detected, rev:%d\n",
+ chip->revision);
+ rc = -EINVAL;
+ goto failed_insert;
+ }
+
rc = radix_tree_insert(&lpg_dev_tree, id, chip);
if (rc) {
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index 352e60e..24918ca 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -51,6 +51,23 @@
* @psy: the power supply to control
* @enable: sets online property of power supply
*/
+int power_supply_set_present(struct power_supply *psy, bool enable)
+{
+ const union power_supply_propval ret = {enable,};
+
+ if (psy->set_property)
+ return psy->set_property(psy, POWER_SUPPLY_PROP_PRESENT,
+ &ret);
+
+ return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(power_supply_set_present);
+
+/**
+ * power_supply_set_online - set online state of the power supply
+ * @psy: the power supply to control
+ * @enable: sets online property of power supply
+ */
int power_supply_set_online(struct power_supply *psy, bool enable)
{
const union power_supply_propval ret = {enable,};
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index dda8976..0185a3e 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -20,6 +20,7 @@
#include <linux/of_device.h>
#include <linux/radix-tree.h>
#include <linux/interrupt.h>
+#include <linux/delay.h>
#include <linux/qpnp/qpnp-adc.h>
#include <linux/power_supply.h>
#include <linux/bitops.h>
@@ -69,14 +70,17 @@
#define CHGR_CHG_WDOG_PET 0x64
#define CHGR_CHG_WDOG_EN 0x65
#define CHGR_USB_IUSB_MAX 0x44
+#define CHGR_USB_USB_SUSP 0x47
#define CHGR_USB_ENUM_T_STOP 0x4E
#define CHGR_CHG_TEMP_THRESH 0x66
#define CHGR_BAT_IF_PRES_STATUS 0x08
-#define CHGR_BAT_TEMP_STATUS 0x09
+#define CHGR_STATUS 0x09
#define CHGR_BAT_IF_VCP 0x42
#define CHGR_BAT_IF_BATFET_CTRL1 0x90
#define CHGR_MISC_BOOT_DONE 0x42
+#define CHGR_BUCK_COMPARATOR_OVRIDE_3 0xED
#define MISC_REVISION2 0x01
+#define SEC_ACCESS 0xD0
/* SMBB peripheral subtype values */
#define REG_OFFSET_PERP_SUBTYPE 0x05
@@ -133,6 +137,9 @@
/* smbb_misc_interrupts */
#define TFTWDOG_IRQ BIT(0)
+/* Workaround flags */
+#define CHG_FLAGS_VCP_WA BIT(0)
+
/**
* struct qpnp_chg_chip - device information
* @dev: device pointer to access the parent
@@ -156,6 +163,8 @@
* @usb_psy power supply to export information to userspace
* @bms_psy power supply to export information to userspace
* @batt_psy: power supply to export information to userspace
+ * @flags: flags to activate specific workarounds
+ * throughout the driver
*
*/
struct qpnp_chg_chip {
@@ -185,6 +194,7 @@
struct power_supply *usb_psy;
struct power_supply *bms_psy;
struct power_supply batt_psy;
+ uint32_t flags;
};
static struct qpnp_chg_chip *the_chip;
@@ -258,6 +268,7 @@
return 0;
}
+#define USB_VALID_BIT BIT(7)
static int
qpnp_chg_is_usb_chg_plugged_in(struct qpnp_chg_chip *chip)
{
@@ -265,16 +276,16 @@
int rc;
rc = qpnp_chg_read(chip, &usbin_valid_rt_sts,
- INT_RT_STS(chip->usb_chgpth_base), 1);
+ chip->usb_chgpth_base + CHGR_STATUS , 1);
if (rc) {
pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->usb_chgpth_base), rc);
+ chip->usb_chgpth_base + CHGR_STATUS, rc);
return rc;
}
pr_debug("chgr usb sts 0x%x\n", usbin_valid_rt_sts);
- return (usbin_valid_rt_sts & USBIN_VALID_IRQ) ? 1 : 0;
+ return (usbin_valid_rt_sts & USB_VALID_BIT) ? 1 : 0;
}
static int
@@ -294,7 +305,6 @@
return (dcin_valid_rt_sts & DCIN_VALID_IRQ) ? 1 : 0;
}
-#define VCP_IUSBMAX_SETTING_MA 2000
#define QPNP_CHG_IUSB_MAX_MIN_100 100
#define QPNP_CHG_IUSB_MAX_MIN_150 150
#define QPNP_CHG_IUSB_MAX_MIN_MA 200
@@ -303,7 +313,8 @@
static int
qpnp_chg_iusbmax_set(struct qpnp_chg_chip *chip, int mA)
{
- u8 usb_reg = 0;
+ int rc = 0;
+ u8 usb_reg = 0, temp = 8;
if (mA == QPNP_CHG_IUSB_MAX_MIN_100) {
usb_reg = 0x00;
@@ -323,17 +334,42 @@
return -EINVAL;
}
- /* Hack for VCP issue make sure IUSBMAX setting
- * is at least 2 A to not brown out device */
- mA = VCP_IUSBMAX_SETTING_MA;
-
usb_reg = mA / QPNP_CHG_IUSB_MAX_STEP_MA;
+ if (chip->flags & CHG_FLAGS_VCP_WA) {
+ temp = 0xA5;
+ rc = qpnp_chg_write(chip, &temp,
+ chip->buck_base + SEC_ACCESS, 1);
+ rc = qpnp_chg_masked_write(chip,
+ chip->buck_base + CHGR_BUCK_COMPARATOR_OVRIDE_3,
+ 0x0C, 0x0C, 1);
+ }
+
pr_debug("current=%d setting 0x%x\n", mA, usb_reg);
- return qpnp_chg_write(chip, &usb_reg,
+ rc = qpnp_chg_write(chip, &usb_reg,
chip->usb_chgpth_base + CHGR_USB_IUSB_MAX, 1);
- pr_debug("done\n");
- return 0;
+
+ if (chip->flags & CHG_FLAGS_VCP_WA) {
+ temp = 0xA5;
+ udelay(200);
+ rc = qpnp_chg_write(chip, &temp,
+ chip->buck_base + SEC_ACCESS, 1);
+ rc = qpnp_chg_masked_write(chip,
+ chip->buck_base + CHGR_BUCK_COMPARATOR_OVRIDE_3,
+ 0x0C, 0x00, 1);
+ }
+
+ return rc;
+}
+
+#define USB_SUSPEND_BIT BIT(0)
+static int
+qpnp_chg_usb_suspend_enable(struct qpnp_chg_chip *chip, int enable)
+{
+ return qpnp_chg_masked_write(chip,
+ chip->usb_chgpth_base + CHGR_USB_USB_SUSP,
+ USB_SUSPEND_BIT,
+ enable ? USB_SUSPEND_BIT : 0, 1);
}
#define ENUM_T_STOP_BIT BIT(0)
@@ -350,11 +386,6 @@
chip->usb_present = usb_present;
power_supply_set_present(chip->usb_psy,
chip->usb_present);
- } else if (!(chip->usb_present && usb_present)) {
- qpnp_chg_masked_write(chip,
- chip->usb_chgpth_base + CHGR_USB_ENUM_T_STOP,
- ENUM_T_STOP_BIT,
- ENUM_T_STOP_BIT, 1);
}
return IRQ_HANDLED;
@@ -368,7 +399,7 @@
int rc, usb_present;
rc = qpnp_chg_masked_write(chip,
- chip->usb_chgpth_base + CHGR_CHG_FAILED,
+ chip->chgr_base + CHGR_CHG_FAILED,
CHGR_CHG_FAILED_BIT,
CHGR_CHG_FAILED_BIT, 1);
if (rc)
@@ -488,7 +519,7 @@
int rc;
rc = qpnp_chg_read(chip, &batt_health,
- chip->bat_if_base + CHGR_BAT_TEMP_STATUS, 1);
+ chip->bat_if_base + CHGR_STATUS, 1);
if (rc) {
pr_err("Couldn't read battery health read failed rc=%d\n", rc);
return POWER_SUPPLY_HEALTH_UNKNOWN;
@@ -647,8 +678,13 @@
chip->usb_psy->get_property(chip->usb_psy,
POWER_SUPPLY_PROP_CURRENT_MAX, &ret);
qpnp_chg_iusbmax_set(chip, ret.intval / 1000);
+ if ((ret.intval / 1000) <= QPNP_CHG_IUSB_MAX_MIN_MA)
+ qpnp_chg_usb_suspend_enable(chip, 1);
+ else
+ qpnp_chg_usb_suspend_enable(chip, 0);
} else {
- qpnp_chg_iusbmax_set(chip, QPNP_CHG_IUSB_MAX_MIN_MA);
+ qpnp_chg_iusbmax_set(chip, QPNP_CHG_IUSB_MAX_MIN_100);
+ qpnp_chg_usb_suspend_enable(chip, 0);
}
pr_debug("end of power supply changed\n");
@@ -863,6 +899,14 @@
chip->chgr_base + CHGR_VDD_MAX, 1);
}
+
+static void
+qpnp_chg_setup_flags(struct qpnp_chg_chip *chip)
+{
+ if (chip->revision > 0)
+ chip->flags |= CHG_FLAGS_VCP_WA;
+}
+
#define WDOG_EN_BIT BIT(7)
static int
qpnp_chg_hwinit(struct qpnp_chg_chip *chip, u8 subtype,
@@ -944,7 +988,7 @@
case SMBB_BAT_IF_SUBTYPE:
/* HACK: Unlock secure access to override temp comparator */
rc = qpnp_chg_masked_write(chip,
- chip->bat_if_base + 0xD0,
+ chip->bat_if_base + SEC_ACCESS,
0xA5, 0xA5, 1);
pr_debug("override hot cold\n");
rc = qpnp_chg_masked_write(chip,
@@ -980,6 +1024,12 @@
return -ENXIO;
}
}
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->usb_chgpth_base + CHGR_USB_ENUM_T_STOP,
+ ENUM_T_STOP_BIT,
+ ENUM_T_STOP_BIT, 1);
+
break;
case SMBB_DC_CHGPTH_SUBTYPE:
break;
@@ -1199,6 +1249,9 @@
goto unregister_dc;
}
+ /* Turn on appropriate workaround flags */
+ qpnp_chg_setup_flags(chip);
+
power_supply_set_present(chip->usb_psy,
qpnp_chg_is_usb_chg_plugged_in(chip));
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 817bfbb..f596411 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -52,6 +52,7 @@
#include "f_rmnet_sdio.c"
#include "f_rmnet_smd_sdio.c"
#include "f_rmnet.c"
+#include "f_audio_source.c"
#include "f_mass_storage.c"
#include "u_serial.c"
#include "u_sdio.c"
@@ -699,11 +700,19 @@
return mbim_bind_config(c, 0);
}
+static int mbim_function_ctrlrequest(struct android_usb_function *f,
+ struct usb_composite_dev *cdev,
+ const struct usb_ctrlrequest *c)
+{
+ return mbim_ctrlrequest(cdev, c);
+}
+
static struct android_usb_function mbim_function = {
.name = "usb_mbim",
.cleanup = mbim_function_cleanup,
.bind_config = mbim_function_bind_config,
.init = mbim_function_init,
+ .ctrlrequest = mbim_function_ctrlrequest,
};
#ifdef CONFIG_SND_PCM
@@ -1475,6 +1484,68 @@
.ctrlrequest = accessory_function_ctrlrequest,
};
+static int audio_source_function_init(struct android_usb_function *f,
+ struct usb_composite_dev *cdev)
+{
+ struct audio_source_config *config;
+
+ config = kzalloc(sizeof(struct audio_source_config), GFP_KERNEL);
+ if (!config)
+ return -ENOMEM;
+ config->card = -1;
+ config->device = -1;
+ f->config = config;
+ return 0;
+}
+
+static void audio_source_function_cleanup(struct android_usb_function *f)
+{
+ kfree(f->config);
+}
+
+static int audio_source_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ struct audio_source_config *config = f->config;
+
+ return audio_source_bind_config(c, config);
+}
+
+static void audio_source_function_unbind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ struct audio_source_config *config = f->config;
+
+ config->card = -1;
+ config->device = -1;
+}
+
+static ssize_t audio_source_pcm_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct audio_source_config *config = f->config;
+
+ /* print PCM card and device numbers */
+ return sprintf(buf, "%d %d\n", config->card, config->device);
+}
+
+static DEVICE_ATTR(pcm, S_IRUGO | S_IWUSR, audio_source_pcm_show, NULL);
+
+static struct device_attribute *audio_source_function_attributes[] = {
+ &dev_attr_pcm,
+ NULL
+};
+
+static struct android_usb_function audio_source_function = {
+ .name = "audio_source",
+ .init = audio_source_function_init,
+ .cleanup = audio_source_function_cleanup,
+ .bind_config = audio_source_function_bind_config,
+ .unbind_config = audio_source_function_unbind_config,
+ .attributes = audio_source_function_attributes,
+};
+
static int android_uasp_connect_cb(bool connect)
{
/*
@@ -1543,6 +1614,7 @@
&rndis_qc_function,
&mass_storage_function,
&accessory_function,
+ &audio_source_function,
&uasp_function,
NULL
};
@@ -2175,6 +2247,11 @@
unsigned long flags;
composite_disconnect(gadget);
+ /* accessory HID support can be active while the
+ accessory function is not actually enabled,
+ so we need to inform it when we are disconnected.
+ */
+ acc_disconnect();
spin_lock_irqsave(&cdev->lock, flags);
dev->connected = 0;
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 0b0ebe7..fa1bf43 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -76,6 +76,8 @@
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
#define ATDTW_SET_DELAY 100 /* 100msec delay */
+#define EP_PRIME_CHECK_DELAY (jiffies + msecs_to_jiffies(1000))
+#define MAX_PRIME_CHECK_RETRY 3 /*Wait for 3sec for EP prime failure */
/* ctrl register bank access */
static DEFINE_SPINLOCK(udc_lock);
@@ -498,8 +500,6 @@
hw_cwrite(CAP_ENDPTPRIME, BIT(n), BIT(n));
- while (hw_cread(CAP_ENDPTPRIME, BIT(n)))
- cpu_relax();
if (is_ctrl && dir == RX && hw_cread(CAP_ENDPTSETUPSTAT, BIT(num)))
return -EAGAIN;
@@ -1006,6 +1006,44 @@
}
/**
+ * dbg_prime_fail: prints a PRIME FAIL event
+ * @addr: endpoint address
+ * @mEp: endpoint structure
+ */
+static void dbg_prime_fail(u8 addr, const char *name,
+ const struct ci13xxx_ep *mEp)
+{
+ char msg[DBG_DATA_MSG];
+ struct ci13xxx_req *req;
+ struct list_head *ptr = NULL;
+
+ if (mEp != NULL) {
+ scnprintf(msg, sizeof(msg),
+ "PRIME fail EP%d%s QH:%08X",
+ mEp->num, mEp->dir ? "IN" : "OUT", mEp->qh.ptr->cap);
+ dbg_print(addr, name, 0, msg);
+ scnprintf(msg, sizeof(msg),
+ "cap:%08X %08X %08X\n",
+ mEp->qh.ptr->curr, mEp->qh.ptr->td.next,
+ mEp->qh.ptr->td.token);
+ dbg_print(addr, "QHEAD", 0, msg);
+
+ list_for_each(ptr, &mEp->qh.queue) {
+ req = list_entry(ptr, struct ci13xxx_req, queue);
+ scnprintf(msg, sizeof(msg),
+ "%08X:%08X:%08X\n",
+ req->dma, req->ptr->next,
+ req->ptr->token);
+ dbg_print(addr, "REQ", 0, msg);
+ scnprintf(msg, sizeof(msg), "%08X:%d\n",
+ req->ptr->page[0],
+ req->req.status);
+ dbg_print(addr, "REQPAGE", 0, msg);
+ }
+ }
+}
+
+/**
* show_events: displays the event buffer
*
* Check "device.h" for details
@@ -1468,12 +1506,14 @@
n = hw_ep_bit(mEp->num, mEp->dir);
pr_info("%s: prime:%08x stat:%08x ep#%d dir:%s"
"dTD_update_fail_count: %lu "
- "mEp->dTD_update_fail_count: %lu\n", __func__,
+ "mEp->dTD_update_fail_count: %lu"
+ "mEp->prime_fail_count: %lu\n", __func__,
hw_cread(CAP_ENDPTPRIME, ~0),
hw_cread(CAP_ENDPTSTAT, ~0),
mEp->num, mEp->dir ? "IN" : "OUT",
udc->dTD_update_fail_count,
- mEp->dTD_update_fail_count);
+ mEp->dTD_update_fail_count,
+ mEp->prime_fail_count);
pr_info("QH: cap:%08x cur:%08x next:%08x token:%08x\n",
mEp->qh.ptr->cap, mEp->qh.ptr->curr,
@@ -1661,6 +1701,57 @@
return ((ep->dir == TX) ? USB_ENDPOINT_DIR_MASK : 0) | ep->num;
}
+static void ep_prime_timer_func(unsigned long data)
+{
+ struct ci13xxx_ep *mEp = (struct ci13xxx_ep *)data;
+ struct ci13xxx_req *req;
+ struct list_head *ptr = NULL;
+ int n = hw_ep_bit(mEp->num, mEp->dir);
+ unsigned long flags;
+
+
+ spin_lock_irqsave(mEp->lock, flags);
+ if (!hw_cread(CAP_ENDPTPRIME, BIT(n)))
+ goto out;
+
+ if (list_empty(&mEp->qh.queue))
+ goto out;
+
+ req = list_entry(mEp->qh.queue.next, struct ci13xxx_req, queue);
+
+ mb();
+ if (!(TD_STATUS_ACTIVE & req->ptr->token))
+ goto out;
+
+ mEp->prime_timer_count++;
+ if (mEp->prime_timer_count == MAX_PRIME_CHECK_RETRY) {
+ mEp->prime_timer_count = 0;
+ pr_info("ep%d dir:%s QH:cap:%08x cur:%08x next:%08x tkn:%08x\n",
+ mEp->num, mEp->dir ? "IN" : "OUT",
+ mEp->qh.ptr->cap, mEp->qh.ptr->curr,
+ mEp->qh.ptr->td.next, mEp->qh.ptr->td.token);
+ list_for_each(ptr, &mEp->qh.queue) {
+ req = list_entry(ptr, struct ci13xxx_req, queue);
+ pr_info("\treq:%08xnext:%08xtkn:%08xpage0:%08xsts:%d\n",
+ req->dma, req->ptr->next,
+ req->ptr->token, req->ptr->page[0],
+ req->req.status);
+ }
+ dbg_prime_fail(0xFF, "PRIMEF", mEp);
+ mEp->prime_fail_count++;
+ } else {
+ mod_timer(&mEp->prime_timer, EP_PRIME_CHECK_DELAY);
+ }
+
+ spin_unlock_irqrestore(mEp->lock, flags);
+ return;
+
+out:
+ mEp->prime_timer_count = 0;
+ spin_unlock_irqrestore(mEp->lock, flags);
+
+}
+
/**
* _hardware_queue: configures a request at hardware level
* @gadget: gadget
@@ -1852,6 +1943,8 @@
ret = hw_ep_prime(mEp->num, mEp->dir,
mEp->type == USB_ENDPOINT_XFER_CONTROL);
+ if (!ret)
+ mod_timer(&mEp->prime_timer, EP_PRIME_CHECK_DELAY);
done:
return ret;
}
@@ -2300,6 +2393,8 @@
if (list_empty(&mEp->qh.queue))
return 0;
+ del_timer(&mEp->prime_timer);
+ mEp->prime_timer_count = 0;
list_for_each_entry_safe(mReq, mReqTemp, &mEp->qh.queue,
queue) {
dequeue:
@@ -2311,7 +2406,8 @@
* bits. This is a temporary workaround till
* HW designers come back on this.
*/
- if (retval == -EBUSY && req_dequeue && mEp->dir == 0) {
+ if (retval == -EBUSY && req_dequeue &&
+ (mEp->dir == 0 || mEp->num == 0)) {
req_dequeue = 0;
udc->dTD_update_fail_count++;
mEp->dTD_update_fail_count++;
@@ -2650,6 +2746,8 @@
/* only internal SW should disable ctrl endpts */
+ del_timer(&mEp->prime_timer);
+ mEp->prime_timer_count = 0;
direction = mEp->dir;
do {
dbg_event(_usb_addr(mEp), "DISABLE", 0);
@@ -2962,6 +3060,8 @@
spin_lock_irqsave(mEp->lock, flags);
+ del_timer(&mEp->prime_timer);
+ mEp->prime_timer_count = 0;
dbg_event(_usb_addr(mEp), "FFLUSH", 0);
hw_ep_flush(mEp->num, mEp->dir);
@@ -3434,6 +3534,8 @@
for (i = 0; i < hw_ep_max; i++) {
struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i];
INIT_LIST_HEAD(&mEp->ep.ep_list);
+ setup_timer(&mEp->prime_timer, ep_prime_timer_func,
+ (unsigned long) mEp);
}
if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) {
diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h
index d67ad7c..6b3cad8 100644
--- a/drivers/usb/gadget/ci13xxx_udc.h
+++ b/drivers/usb/gadget/ci13xxx_udc.h
@@ -108,6 +108,9 @@
struct device *device;
struct dma_pool *td_pool;
unsigned long dTD_update_fail_count;
+ unsigned long prime_fail_count;
+ int prime_timer_count;
+ struct timer_list prime_timer;
};
struct ci13xxx;
diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c
index 0353848..42a6c43 100644
--- a/drivers/usb/gadget/f_accessory.c
+++ b/drivers/usb/gadget/f_accessory.c
@@ -33,6 +33,8 @@
#include <linux/device.h>
#include <linux/miscdevice.h>
+#include <linux/hid.h>
+#include <linux/hiddev.h>
#include <linux/usb.h>
#include <linux/usb/ch9.h>
#include <linux/usb/f_accessory.h>
@@ -49,6 +51,20 @@
#define TX_REQ_MAX 4
#define RX_REQ_MAX 2
+struct acc_hid_dev {
+ struct list_head list;
+ struct hid_device *hid;
+ struct acc_dev *dev;
+ /* accessory defined ID */
+ int id;
+ /* HID report descriptor */
+ u8 *report_desc;
+ /* length of HID report descriptor */
+ int report_desc_len;
+ /* number of bytes of report_desc we have received so far */
+ int report_desc_offset;
+};
+
struct acc_dev {
struct usb_function function;
struct usb_composite_dev *cdev;
@@ -89,7 +105,21 @@
wait_queue_head_t write_wq;
struct usb_request *rx_req[RX_REQ_MAX];
int rx_done;
- struct delayed_work work;
+
+ /* delayed work for handling ACCESSORY_START */
+ struct delayed_work start_work;
+
+ /* worker for registering and unregistering hid devices */
+ struct work_struct hid_work;
+
+ /* list of active HID devices */
+ struct list_head hid_list;
+
+ /* list of new HID devices to register */
+ struct list_head new_hid_list;
+
+ /* list of dead HID devices to unregister */
+ struct list_head dead_hid_list;
};
static struct usb_interface_descriptor acc_interface_desc = {
@@ -298,6 +328,160 @@
}
}
+static void acc_complete_set_hid_report_desc(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ struct acc_hid_dev *hid = req->context;
+ struct acc_dev *dev = hid->dev;
+ int length = req->actual;
+
+ if (req->status != 0) {
+ pr_err("acc_complete_set_hid_report_desc, err %d\n",
+ req->status);
+ return;
+ }
+
+ memcpy(hid->report_desc + hid->report_desc_offset, req->buf, length);
+ hid->report_desc_offset += length;
+ if (hid->report_desc_offset == hid->report_desc_len) {
+ /* After we have received the entire report descriptor
+ * we schedule work to initialize the HID device
+ */
+ schedule_work(&dev->hid_work);
+ }
+}
+
+static void acc_complete_send_hid_event(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ struct acc_hid_dev *hid = req->context;
+ int length = req->actual;
+
+ if (req->status != 0) {
+ pr_err("acc_complete_send_hid_event, err %d\n", req->status);
+ return;
+ }
+
+ hid_report_raw_event(hid->hid, HID_INPUT_REPORT, req->buf, length, 1);
+}
+
+static int acc_hid_parse(struct hid_device *hid)
+{
+ struct acc_hid_dev *hdev = hid->driver_data;
+
+ hid_parse_report(hid, hdev->report_desc, hdev->report_desc_len);
+ return 0;
+}
+
+static int acc_hid_start(struct hid_device *hid)
+{
+ return 0;
+}
+
+static void acc_hid_stop(struct hid_device *hid)
+{
+}
+
+static int acc_hid_open(struct hid_device *hid)
+{
+ return 0;
+}
+
+static void acc_hid_close(struct hid_device *hid)
+{
+}
+
+static struct hid_ll_driver acc_hid_ll_driver = {
+ .parse = acc_hid_parse,
+ .start = acc_hid_start,
+ .stop = acc_hid_stop,
+ .open = acc_hid_open,
+ .close = acc_hid_close,
+};
+
+static struct acc_hid_dev *acc_hid_new(struct acc_dev *dev,
+ int id, int desc_len)
+{
+ struct acc_hid_dev *hdev;
+
+ hdev = kzalloc(sizeof(*hdev), GFP_ATOMIC);
+ if (!hdev)
+ return NULL;
+ hdev->report_desc = kzalloc(desc_len, GFP_ATOMIC);
+ if (!hdev->report_desc) {
+ kfree(hdev);
+ return NULL;
+ }
+ hdev->dev = dev;
+ hdev->id = id;
+ hdev->report_desc_len = desc_len;
+
+ return hdev;
+}
+
+static struct acc_hid_dev *acc_hid_get(struct list_head *list, int id)
+{
+ struct acc_hid_dev *hid;
+
+ list_for_each_entry(hid, list, list) {
+ if (hid->id == id)
+ return hid;
+ }
+ return NULL;
+}
+
+static int acc_register_hid(struct acc_dev *dev, int id, int desc_length)
+{
+ struct acc_hid_dev *hid;
+ unsigned long flags;
+
+ /* report descriptor length must be > 0 */
+ if (desc_length <= 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ /* replace HID if one already exists with this ID */
+ hid = acc_hid_get(&dev->hid_list, id);
+ if (!hid)
+ hid = acc_hid_get(&dev->new_hid_list, id);
+ if (hid)
+ list_move(&hid->list, &dev->dead_hid_list);
+
+ hid = acc_hid_new(dev, id, desc_length);
+ if (!hid) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -ENOMEM;
+ }
+
+ list_add(&hid->list, &dev->new_hid_list);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* schedule work to register the HID device */
+ schedule_work(&dev->hid_work);
+ return 0;
+}
+
+static int acc_unregister_hid(struct acc_dev *dev, int id)
+{
+ struct acc_hid_dev *hid;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ hid = acc_hid_get(&dev->hid_list, id);
+ if (!hid)
+ hid = acc_hid_get(&dev->new_hid_list, id);
+ if (!hid) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -EINVAL;
+ }
+
+ list_move(&hid->list, &dev->dead_hid_list);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ schedule_work(&dev->hid_work);
+ return 0;
+}
+
static int create_bulk_endpoints(struct acc_dev *dev,
struct usb_endpoint_descriptor *in_desc,
struct usb_endpoint_descriptor *out_desc)
@@ -355,7 +539,7 @@
return 0;
fail:
- printk(KERN_ERR "acc_bind() could not allocate requests\n");
+ pr_err("acc_bind() could not allocate requests\n");
while ((req = req_get(dev, &dev->tx_idle)))
acc_request_free(req, dev->ep_in);
for (i = 0; i < RX_REQ_MAX; i++)
@@ -544,7 +728,7 @@
return 0;
}
-/* file operations for /dev/acc_usb */
+/* file operations for /dev/usb_accessory */
static const struct file_operations acc_fops = {
.owner = THIS_MODULE,
.read = acc_read,
@@ -554,23 +738,47 @@
.release = acc_release,
};
+static int acc_hid_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ int ret;
+
+ ret = hid_parse(hdev);
+ if (ret)
+ return ret;
+ return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+}
+
static struct miscdevice acc_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "usb_accessory",
.fops = &acc_fops,
};
+static const struct hid_device_id acc_hid_table[] = {
+ { HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) },
+ { }
+};
+
+static struct hid_driver acc_hid_driver = {
+ .name = "USB accessory",
+ .id_table = acc_hid_table,
+ .probe = acc_hid_probe,
+};
static int acc_ctrlrequest(struct usb_composite_dev *cdev,
const struct usb_ctrlrequest *ctrl)
{
struct acc_dev *dev = _acc_dev;
int value = -EOPNOTSUPP;
+ struct acc_hid_dev *hid;
+ int offset;
u8 b_requestType = ctrl->bRequestType;
u8 b_request = ctrl->bRequest;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
+ unsigned long flags;
/*
printk(KERN_INFO "acc_ctrlrequest "
@@ -583,7 +791,7 @@
if (b_request == ACCESSORY_START) {
dev->start_requested = 1;
schedule_delayed_work(
- &dev->work, msecs_to_jiffies(10));
+ &dev->start_work, msecs_to_jiffies(10));
value = 0;
} else if (b_request == ACCESSORY_SEND_STRING) {
dev->string_index = w_index;
@@ -594,13 +802,45 @@
w_index == 0 && w_length == 0) {
dev->audio_mode = w_value;
value = 0;
+ } else if (b_request == ACCESSORY_REGISTER_HID) {
+ value = acc_register_hid(dev, w_value, w_index);
+ } else if (b_request == ACCESSORY_UNREGISTER_HID) {
+ value = acc_unregister_hid(dev, w_value);
+ } else if (b_request == ACCESSORY_SET_HID_REPORT_DESC) {
+ spin_lock_irqsave(&dev->lock, flags);
+ hid = acc_hid_get(&dev->new_hid_list, w_value);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ if (!hid) {
+ value = -EINVAL;
+ goto err;
+ }
+ offset = w_index;
+ if (offset != hid->report_desc_offset
+ || offset + w_length > hid->report_desc_len) {
+ value = -EINVAL;
+ goto err;
+ }
+ cdev->req->context = hid;
+ cdev->req->complete = acc_complete_set_hid_report_desc;
+ value = w_length;
+ } else if (b_request == ACCESSORY_SEND_HID_EVENT) {
+ spin_lock_irqsave(&dev->lock, flags);
+ hid = acc_hid_get(&dev->hid_list, w_value);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ if (!hid) {
+ value = -EINVAL;
+ goto err;
+ }
+ cdev->req->context = hid;
+ cdev->req->complete = acc_complete_send_hid_event;
+ value = w_length;
}
} else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) {
if (b_request == ACCESSORY_GET_PROTOCOL) {
*((u16 *)cdev->req->buf) = PROTOCOL_VERSION;
value = sizeof(u16);
- /* clear any string left over from a previous session */
+ /* clear strings left over from a previous session */
memset(dev->manufacturer, 0, sizeof(dev->manufacturer));
memset(dev->model, 0, sizeof(dev->model));
memset(dev->description, 0, sizeof(dev->description));
@@ -621,6 +861,7 @@
__func__);
}
+err:
if (value == -EOPNOTSUPP)
VDBG(cdev,
"unknown class-specific control req "
@@ -640,6 +881,10 @@
DBG(cdev, "acc_function_bind dev: %p\n", dev);
+ ret = hid_register_driver(&acc_hid_driver);
+ if (ret)
+ return ret;
+
dev->start_requested = 0;
/* allocate interface ID(s) */
@@ -669,6 +914,36 @@
}
static void
+kill_all_hid_devices(struct acc_dev *dev)
+{
+ struct acc_hid_dev *hid;
+ struct list_head *entry, *temp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ list_for_each_safe(entry, temp, &dev->hid_list) {
+ hid = list_entry(entry, struct acc_hid_dev, list);
+ list_del(&hid->list);
+ list_add(&hid->list, &dev->dead_hid_list);
+ }
+ list_for_each_safe(entry, temp, &dev->new_hid_list) {
+ hid = list_entry(entry, struct acc_hid_dev, list);
+ list_del(&hid->list);
+ list_add(&hid->list, &dev->dead_hid_list);
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ schedule_work(&dev->hid_work);
+}
+
+static void
+acc_hid_unbind(struct acc_dev *dev)
+{
+ hid_unregister_driver(&acc_hid_driver);
+ kill_all_hid_devices(dev);
+}
+
+static void
acc_function_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct acc_dev *dev = func_to_dev(f);
@@ -679,14 +954,104 @@
acc_request_free(req, dev->ep_in);
for (i = 0; i < RX_REQ_MAX; i++)
acc_request_free(dev->rx_req[i], dev->ep_out);
+
+ acc_hid_unbind(dev);
}
-static void acc_work(struct work_struct *data)
+static void acc_start_work(struct work_struct *data)
{
char *envp[2] = { "ACCESSORY=START", NULL };
kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp);
}
+static int acc_hid_init(struct acc_hid_dev *hdev)
+{
+ struct hid_device *hid;
+ int ret;
+
+ hid = hid_allocate_device();
+ if (IS_ERR(hid))
+ return PTR_ERR(hid);
+
+ hid->ll_driver = &acc_hid_ll_driver;
+ hid->dev.parent = acc_device.this_device;
+
+ hid->bus = BUS_USB;
+ hid->vendor = HID_ANY_ID;
+ hid->product = HID_ANY_ID;
+ hid->driver_data = hdev;
+ ret = hid_add_device(hid);
+ if (ret) {
+ pr_err("can't add hid device: %d\n", ret);
+ hid_destroy_device(hid);
+ return ret;
+ }
+
+ hdev->hid = hid;
+ return 0;
+}
+
+static void acc_hid_delete(struct acc_hid_dev *hid)
+{
+ kfree(hid->report_desc);
+ kfree(hid);
+}
+
+static void acc_hid_work(struct work_struct *data)
+{
+ struct acc_dev *dev = _acc_dev;
+ struct list_head *entry, *temp;
+ struct acc_hid_dev *hid;
+ struct list_head new_list, dead_list;
+ unsigned long flags;
+
+ INIT_LIST_HEAD(&new_list);
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ /* copy hids that are ready for initialization to new_list */
+ list_for_each_safe(entry, temp, &dev->new_hid_list) {
+ hid = list_entry(entry, struct acc_hid_dev, list);
+ if (hid->report_desc_offset == hid->report_desc_len)
+ list_move(&hid->list, &new_list);
+ }
+
+ if (list_empty(&dev->dead_hid_list)) {
+ INIT_LIST_HEAD(&dead_list);
+ } else {
+ /* move all of dev->dead_hid_list to dead_list */
+ dead_list.prev = dev->dead_hid_list.prev;
+ dead_list.next = dev->dead_hid_list.next;
+ dead_list.next->prev = &dead_list;
+ dead_list.prev->next = &dead_list;
+ INIT_LIST_HEAD(&dev->dead_hid_list);
+ }
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* register new HID devices */
+ list_for_each_safe(entry, temp, &new_list) {
+ hid = list_entry(entry, struct acc_hid_dev, list);
+ if (acc_hid_init(hid)) {
+ pr_err("can't add HID device %p\n", hid);
+ acc_hid_delete(hid);
+ } else {
+ spin_lock_irqsave(&dev->lock, flags);
+ list_move(&hid->list, &dev->hid_list);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ }
+ }
+
+ /* remove dead HID devices */
+ list_for_each_safe(entry, temp, &dead_list) {
+ hid = list_entry(entry, struct acc_hid_dev, list);
+ list_del(&hid->list);
+ if (hid->hid)
+ hid_destroy_device(hid->hid);
+ acc_hid_delete(hid);
+ }
+}
+
static int acc_function_set_alt(struct usb_function *f,
unsigned intf, unsigned alt)
{
@@ -792,7 +1157,11 @@
init_waitqueue_head(&dev->write_wq);
atomic_set(&dev->open_excl, 0);
INIT_LIST_HEAD(&dev->tx_idle);
- INIT_DELAYED_WORK(&dev->work, acc_work);
+ INIT_LIST_HEAD(&dev->hid_list);
+ INIT_LIST_HEAD(&dev->new_hid_list);
+ INIT_LIST_HEAD(&dev->dead_hid_list);
+ INIT_DELAYED_WORK(&dev->start_work, acc_start_work);
+ INIT_WORK(&dev->hid_work, acc_hid_work);
/* _acc_dev must be set before calling usb_gadget_register_driver */
_acc_dev = dev;
@@ -805,10 +1174,16 @@
err:
kfree(dev);
- printk(KERN_ERR "USB accessory gadget driver failed to initialize\n");
+ pr_err("USB accessory gadget driver failed to initialize\n");
return ret;
}
+static void acc_disconnect(void)
+{
+ /* unregister all HID devices if USB is disconnected */
+ kill_all_hid_devices(_acc_dev);
+}
+
static void acc_cleanup(void)
{
misc_deregister(&acc_device);
diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c
index 045fc6c..7966a79 100644
--- a/drivers/usb/gadget/f_adb.c
+++ b/drivers/usb/gadget/f_adb.c
@@ -55,6 +55,7 @@
wait_queue_head_t write_wq;
struct usb_request *rx_req;
int rx_done;
+ bool notify_close;
};
static struct usb_interface_descriptor adb_interface_desc = {
@@ -423,8 +424,10 @@
/* clear the error latch */
atomic_set(&_adb_dev->error, 0);
- adb_ready_callback();
+ if (_adb_dev->notify_close)
+ adb_ready_callback();
+ _adb_dev->notify_close = true;
return 0;
}
@@ -432,7 +435,16 @@
{
pr_info("adb_release\n");
- adb_closed_callback();
+ /*
+ * ADB daemon closes the device file after I/O error. The
+ * I/O error happen when Rx requests are flushed during
+ * cable disconnect or bus reset in configured state. Disabling
+ * USB configuration and pull-up during these scenarios are
+ * undesired. We want to force bus reset only for certain
+ * commands like "adb root" and "adb usb".
+ */
+ if (_adb_dev->notify_close)
+ adb_closed_callback();
adb_unlock(&_adb_dev->open_excl);
return 0;
@@ -561,6 +573,12 @@
struct usb_composite_dev *cdev = dev->cdev;
DBG(cdev, "adb_function_disable cdev %p\n", cdev);
+ /*
+ * Bus reset happened or cable disconnected. No
+ * need to disable the configuration now. We will
+ * set noify_close to true when device file is re-opened.
+ */
+ dev->notify_close = false;
atomic_set(&dev->online, 0);
atomic_set(&dev->error, 1);
usb_ep_disable(dev->ep_in);
@@ -607,6 +625,7 @@
atomic_set(&dev->open_excl, 0);
atomic_set(&dev->read_excl, 0);
atomic_set(&dev->write_excl, 0);
+ dev->notify_close = true;
INIT_LIST_HEAD(&dev->tx_idle);
diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c
new file mode 100644
index 0000000..aae941e
--- /dev/null
+++ b/drivers/usb/gadget/f_audio_source.c
@@ -0,0 +1,832 @@
+/*
+ * Gadget Function Driver for USB audio source device
+ *
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/usb/audio.h>
+#include <linux/wait.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+
+#define SAMPLE_RATE 44100
+#define FRAMES_PER_MSEC (SAMPLE_RATE / 1000)
+
+#define IN_EP_MAX_PACKET_SIZE 256
+
+/* Number of requests to allocate */
+#define IN_EP_REQ_COUNT 4
+
+#define AUDIO_AC_INTERFACE 0
+#define AUDIO_AS_INTERFACE 1
+#define AUDIO_NUM_INTERFACES 2
+
+/* B.3.1 Standard AC Interface Descriptor */
+static struct usb_interface_descriptor audio_source_ac_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+};
+
+
+#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(AUDIO_NUM_INTERFACES)
+/* 1 input terminal, 1 output terminal and 1 feature unit */
+#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \
+ + UAC_DT_INPUT_TERMINAL_SIZE + UAC_DT_OUTPUT_TERMINAL_SIZE \
+ + UAC_DT_FEATURE_UNIT_SIZE(0))
+/* B.3.2 Class-Specific AC Interface Descriptor */
+static struct uac1_ac_header_descriptor_2 audio_source_ac_header_desc = {
+ .bLength = UAC_DT_AC_HEADER_LENGTH,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_HEADER,
+ .bcdADC = __constant_cpu_to_le16(0x0100),
+ .wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH),
+ .bInCollection = AUDIO_NUM_INTERFACES,
+ .baInterfaceNr = {
+ [0] = AUDIO_AC_INTERFACE,
+ [1] = AUDIO_AS_INTERFACE,
+ }
+};
+
+#define INPUT_TERMINAL_ID 1
+static struct uac_input_terminal_descriptor input_terminal_desc = {
+ .bLength = UAC_DT_INPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_INPUT_TERMINAL,
+ .bTerminalID = INPUT_TERMINAL_ID,
+ .wTerminalType = UAC_INPUT_TERMINAL_MICROPHONE,
+ .bAssocTerminal = 0,
+ .wChannelConfig = 0x3,
+};
+
+DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0);
+
+#define FEATURE_UNIT_ID 2
+static struct uac_feature_unit_descriptor_0 feature_unit_desc = {
+ .bLength = UAC_DT_FEATURE_UNIT_SIZE(0),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_FEATURE_UNIT,
+ .bUnitID = FEATURE_UNIT_ID,
+ .bSourceID = INPUT_TERMINAL_ID,
+ .bControlSize = 2,
+};
+
+#define OUTPUT_TERMINAL_ID 3
+static struct uac1_output_terminal_descriptor output_terminal_desc = {
+ .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
+ .bTerminalID = OUTPUT_TERMINAL_ID,
+ .wTerminalType = UAC_TERMINAL_STREAMING,
+ .bAssocTerminal = FEATURE_UNIT_ID,
+ .bSourceID = FEATURE_UNIT_ID,
+};
+
+/* B.4.1 Standard AS Interface Descriptor */
+static struct usb_interface_descriptor as_interface_alt_0_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+static struct usb_interface_descriptor as_interface_alt_1_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bAlternateSetting = 1,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+/* B.4.2 Class-Specific AS Interface Descriptor */
+static struct uac1_as_header_descriptor as_header_desc = {
+ .bLength = UAC_DT_AS_HEADER_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_AS_GENERAL,
+ .bTerminalLink = INPUT_TERMINAL_ID,
+ .bDelay = 1,
+ .wFormatTag = UAC_FORMAT_TYPE_I_PCM,
+};
+
+static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = {
+ .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_FORMAT_TYPE,
+ .bFormatType = UAC_FORMAT_TYPE_I,
+ .bSubframeSize = 2,
+ .bBitResolution = 16,
+ .bSamFreqType = 1,
+};
+
+/* Standard ISO IN Endpoint Descriptor for highspeed */
+static struct usb_endpoint_descriptor hs_as_in_ep_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_SYNC_SYNC
+ | USB_ENDPOINT_XFER_ISOC,
+ .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE),
+ .bInterval = 4, /* poll 1 per millisecond */
+};
+
+/* Standard ISO IN Endpoint Descriptor for highspeed */
+static struct usb_endpoint_descriptor fs_as_in_ep_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_SYNC_SYNC
+ | USB_ENDPOINT_XFER_ISOC,
+ .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE),
+ .bInterval = 1, /* poll 1 per millisecond */
+};
+
+/* Class-specific AS ISO OUT Endpoint Descriptor */
+static struct uac_iso_endpoint_descriptor as_iso_in_desc = {
+ .bLength = UAC_ISO_ENDPOINT_DESC_SIZE,
+ .bDescriptorType = USB_DT_CS_ENDPOINT,
+ .bDescriptorSubtype = UAC_EP_GENERAL,
+ .bmAttributes = 1,
+ .bLockDelayUnits = 1,
+ .wLockDelay = __constant_cpu_to_le16(1),
+};
+
+static struct usb_descriptor_header *hs_audio_desc[] = {
+ (struct usb_descriptor_header *)&audio_source_ac_interface_desc,
+ (struct usb_descriptor_header *)&audio_source_ac_header_desc,
+
+ (struct usb_descriptor_header *)&input_terminal_desc,
+ (struct usb_descriptor_header *)&output_terminal_desc,
+ (struct usb_descriptor_header *)&feature_unit_desc,
+
+ (struct usb_descriptor_header *)&as_interface_alt_0_desc,
+ (struct usb_descriptor_header *)&as_interface_alt_1_desc,
+ (struct usb_descriptor_header *)&as_header_desc,
+
+ (struct usb_descriptor_header *)&as_type_i_desc,
+
+ (struct usb_descriptor_header *)&hs_as_in_ep_desc,
+ (struct usb_descriptor_header *)&as_iso_in_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *fs_audio_desc[] = {
+ (struct usb_descriptor_header *)&audio_source_ac_interface_desc,
+ (struct usb_descriptor_header *)&audio_source_ac_header_desc,
+
+ (struct usb_descriptor_header *)&input_terminal_desc,
+ (struct usb_descriptor_header *)&output_terminal_desc,
+ (struct usb_descriptor_header *)&feature_unit_desc,
+
+ (struct usb_descriptor_header *)&as_interface_alt_0_desc,
+ (struct usb_descriptor_header *)&as_interface_alt_1_desc,
+ (struct usb_descriptor_header *)&as_header_desc,
+
+ (struct usb_descriptor_header *)&as_type_i_desc,
+
+ (struct usb_descriptor_header *)&fs_as_in_ep_desc,
+ (struct usb_descriptor_header *)&as_iso_in_desc,
+ NULL,
+};
+
+static struct snd_pcm_hardware audio_hw_info = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = SAMPLE_RATE,
+ .rate_max = SAMPLE_RATE,
+
+ .buffer_bytes_max = 1024 * 1024,
+ .period_bytes_min = 64,
+ .period_bytes_max = 512 * 1024,
+ .periods_min = 2,
+ .periods_max = 1024,
+};
+
+/*-------------------------------------------------------------------------*/
+
+struct audio_source_config {
+ int card;
+ int device;
+};
+
+struct audio_dev {
+ struct usb_function func;
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ struct snd_pcm_substream *substream;
+
+ struct list_head idle_reqs;
+ struct usb_ep *in_ep;
+
+ spinlock_t lock;
+
+ /* beginning, end and current position in our buffer */
+ void *buffer_start;
+ void *buffer_end;
+ void *buffer_pos;
+
+ /* byte size of a "period" */
+ unsigned int period;
+ /* bytes sent since last call to snd_pcm_period_elapsed */
+ unsigned int period_offset;
+ /* time we started playing */
+ ktime_t start_time;
+ /* number of frames sent since start_time */
+ s64 frames_sent;
+};
+
+static inline struct audio_dev *func_to_audio_source(struct usb_function *f)
+{
+ return container_of(f, struct audio_dev, func);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size)
+{
+ struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!req)
+ return NULL;
+
+ req->buf = kmalloc(buffer_size, GFP_KERNEL);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ return NULL;
+ }
+ req->length = buffer_size;
+ return req;
+}
+
+static void audio_request_free(struct usb_request *req, struct usb_ep *ep)
+{
+ if (req) {
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+ }
+}
+
+static void audio_req_put(struct audio_dev *audio, struct usb_request *req)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->lock, flags);
+ list_add_tail(&req->list, &audio->idle_reqs);
+ spin_unlock_irqrestore(&audio->lock, flags);
+}
+
+static struct usb_request *audio_req_get(struct audio_dev *audio)
+{
+ unsigned long flags;
+ struct usb_request *req;
+
+ spin_lock_irqsave(&audio->lock, flags);
+ if (list_empty(&audio->idle_reqs)) {
+ req = 0;
+ } else {
+ req = list_first_entry(&audio->idle_reqs, struct usb_request,
+ list);
+ list_del(&req->list);
+ }
+ spin_unlock_irqrestore(&audio->lock, flags);
+ return req;
+}
+
+/* send the appropriate number of packets to match our bitrate */
+static void audio_send(struct audio_dev *audio)
+{
+ struct snd_pcm_runtime *runtime;
+ struct usb_request *req;
+ int length, length1, length2, ret;
+ s64 msecs;
+ s64 frames;
+ ktime_t now;
+
+ /* audio->substream will be null if we have been closed */
+ if (!audio->substream)
+ return;
+ /* audio->buffer_pos will be null if we have been stopped */
+ if (!audio->buffer_pos)
+ return;
+
+ runtime = audio->substream->runtime;
+
+ /* compute number of frames to send */
+ now = ktime_get();
+ msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time);
+ do_div(msecs, 1000000);
+ frames = msecs * SAMPLE_RATE;
+ do_div(frames, 1000);
+
+ /* Readjust our frames_sent if we fall too far behind.
+ * If we get too far behind it is better to drop some frames than
+ * to keep sending data too fast in an attempt to catch up.
+ */
+ if (frames - audio->frames_sent > 10 * FRAMES_PER_MSEC)
+ audio->frames_sent = frames - FRAMES_PER_MSEC;
+
+ frames -= audio->frames_sent;
+
+ /* We need to send something to keep the pipeline going */
+ if (frames <= 0)
+ frames = FRAMES_PER_MSEC;
+
+ while (frames > 0) {
+ req = audio_req_get(audio);
+ if (!req)
+ break;
+
+ length = frames_to_bytes(runtime, frames);
+ if (length > IN_EP_MAX_PACKET_SIZE)
+ length = IN_EP_MAX_PACKET_SIZE;
+
+ if (audio->buffer_pos + length > audio->buffer_end)
+ length1 = audio->buffer_end - audio->buffer_pos;
+ else
+ length1 = length;
+ memcpy(req->buf, audio->buffer_pos, length1);
+ if (length1 < length) {
+ /* Wrap around and copy remaining length
+ * at beginning of buffer.
+ */
+ length2 = length - length1;
+ memcpy(req->buf + length1, audio->buffer_start,
+ length2);
+ audio->buffer_pos = audio->buffer_start + length2;
+ } else {
+ audio->buffer_pos += length1;
+ if (audio->buffer_pos >= audio->buffer_end)
+ audio->buffer_pos = audio->buffer_start;
+ }
+
+ req->length = length;
+ ret = usb_ep_queue(audio->in_ep, req, GFP_ATOMIC);
+ if (ret < 0) {
+ pr_err("usb_ep_queue failed ret: %d\n", ret);
+ audio_req_put(audio, req);
+ break;
+ }
+
+ frames -= bytes_to_frames(runtime, length);
+ audio->frames_sent += bytes_to_frames(runtime, length);
+ }
+}
+
+static void audio_control_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ /* nothing to do here */
+}
+
+static void audio_data_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct audio_dev *audio = req->context;
+
+ pr_debug("audio_data_complete req->status %d req->actual %d\n",
+ req->status, req->actual);
+
+ audio_req_put(audio, req);
+
+ if (!audio->buffer_start || req->status)
+ return;
+
+ audio->period_offset += req->actual;
+ if (audio->period_offset >= audio->period) {
+ snd_pcm_period_elapsed(audio->substream);
+ audio->period_offset = 0;
+ }
+ audio_send(audio);
+}
+
+static int audio_source_set_endpoint_req(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ int value = -EOPNOTSUPP;
+ u16 ep = le16_to_cpu(ctrl->wIndex);
+ u16 len = le16_to_cpu(ctrl->wLength);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+
+ pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+ ctrl->bRequest, w_value, len, ep);
+
+ switch (ctrl->bRequest) {
+ case UAC_SET_CUR:
+ case UAC_SET_MIN:
+ case UAC_SET_MAX:
+ case UAC_SET_RES:
+ value = len;
+ break;
+ default:
+ break;
+ }
+
+ return value;
+}
+
+static int audio_source_get_endpoint_req(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_composite_dev *cdev = f->config->cdev;
+ int value = -EOPNOTSUPP;
+ u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+ u16 len = le16_to_cpu(ctrl->wLength);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u8 *buf = cdev->req->buf;
+
+ pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+ ctrl->bRequest, w_value, len, ep);
+
+ if (w_value == UAC_EP_CS_ATTR_SAMPLE_RATE << 8) {
+ switch (ctrl->bRequest) {
+ case UAC_GET_CUR:
+ case UAC_GET_MIN:
+ case UAC_GET_MAX:
+ case UAC_GET_RES:
+ /* return our sample rate */
+ buf[0] = (u8)SAMPLE_RATE;
+ buf[1] = (u8)(SAMPLE_RATE >> 8);
+ buf[2] = (u8)(SAMPLE_RATE >> 16);
+ value = 3;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return value;
+}
+
+static int
+audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = cdev->req;
+ int value = -EOPNOTSUPP;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+ /* composite driver infrastructure handles everything; interface
+ * activation uses set_alt().
+ */
+ switch (ctrl->bRequestType) {
+ case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
+ value = audio_source_set_endpoint_req(f, ctrl);
+ break;
+
+ case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
+ value = audio_source_get_endpoint_req(f, ctrl);
+ break;
+ }
+
+ /* respond with data transfer or status phase? */
+ if (value >= 0) {
+ pr_debug("audio req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ req->zero = 0;
+ req->length = value;
+ req->complete = audio_control_complete;
+ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0)
+ pr_err("audio response on err %d\n", value);
+ }
+
+ /* device either stalls (value < 0) or reports success */
+ return value;
+}
+
+static int audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct audio_dev *audio = func_to_audio_source(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ int ret;
+
+ pr_debug("audio_set_alt intf %d, alt %d\n", intf, alt);
+
+ ret = config_ep_by_speed(cdev->gadget, f, audio->in_ep);
+ if (ret) {
+ audio->in_ep->desc = NULL;
+ ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+ audio->in_ep->name, ret);
+ return ret;
+ }
+ ret = usb_ep_enable(audio->in_ep);
+ if (ret) {
+ ERROR(cdev, "failed to enable ep %s, result %d\n",
+ audio->in_ep->name, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static void audio_disable(struct usb_function *f)
+{
+ struct audio_dev *audio = func_to_audio_source(f);
+
+ pr_debug("audio_disable\n");
+ usb_ep_disable(audio->in_ep);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void audio_build_desc(struct audio_dev *audio)
+{
+ u8 *sam_freq;
+ int rate;
+
+ /* Set channel numbers */
+ input_terminal_desc.bNrChannels = 2;
+ as_type_i_desc.bNrChannels = 2;
+
+ /* Set sample rates */
+ rate = SAMPLE_RATE;
+ sam_freq = as_type_i_desc.tSamFreq[0];
+ memcpy(sam_freq, &rate, 3);
+}
+
+/* audio function driver setup/binding */
+static int
+audio_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct audio_dev *audio = func_to_audio_source(f);
+ int status;
+ struct usb_ep *ep;
+ struct usb_request *req;
+ int i;
+
+ audio_build_desc(audio);
+
+ /* allocate instance-specific interface IDs, and patch descriptors */
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ audio_source_ac_interface_desc.bInterfaceNumber = status;
+
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ as_interface_alt_0_desc.bInterfaceNumber = status;
+ as_interface_alt_1_desc.bInterfaceNumber = status;
+
+ status = -ENODEV;
+
+ /* allocate our endpoint */
+ ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_ep_desc);
+ if (!ep)
+ goto fail;
+ audio->in_ep = ep;
+ ep->driver_data = audio; /* claim */
+
+ if (gadget_is_dualspeed(c->cdev->gadget))
+ hs_as_in_ep_desc.bEndpointAddress =
+ fs_as_in_ep_desc.bEndpointAddress;
+
+ for (i = 0, status = 0; i < IN_EP_REQ_COUNT && status == 0; i++) {
+ req = audio_request_new(ep, IN_EP_MAX_PACKET_SIZE);
+ if (req) {
+ req->context = audio;
+ req->complete = audio_data_complete;
+ audio_req_put(audio, req);
+ } else
+ status = -ENOMEM;
+ }
+
+fail:
+ return status;
+}
+
+static void
+audio_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct audio_dev *audio = func_to_audio_source(f);
+ struct usb_request *req;
+
+ while ((req = audio_req_get(audio)))
+ audio_request_free(req, audio->in_ep);
+
+ snd_card_free_when_closed(audio->card);
+ audio->card = NULL;
+ audio->pcm = NULL;
+ audio->substream = NULL;
+ audio->in_ep = NULL;
+}
+
+static void audio_pcm_playback_start(struct audio_dev *audio)
+{
+ audio->start_time = ktime_get();
+ audio->frames_sent = 0;
+ audio_send(audio);
+}
+
+static void audio_pcm_playback_stop(struct audio_dev *audio)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->lock, flags);
+ audio->buffer_start = 0;
+ audio->buffer_end = 0;
+ audio->buffer_pos = 0;
+ spin_unlock_irqrestore(&audio->lock, flags);
+}
+
+static int audio_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_dev *audio = substream->private_data;
+
+ runtime->private_data = audio;
+ runtime->hw = audio_hw_info;
+ snd_pcm_limit_hw_rates(runtime);
+ runtime->hw.channels_max = 2;
+
+ audio->substream = substream;
+ return 0;
+}
+
+static int audio_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct audio_dev *audio = substream->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->lock, flags);
+ audio->substream = NULL;
+ spin_unlock_irqrestore(&audio->lock, flags);
+
+ return 0;
+}
+
+static int audio_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int channels = params_channels(params);
+ unsigned int rate = params_rate(params);
+
+ if (rate != SAMPLE_RATE)
+ return -EINVAL;
+ if (channels != 2)
+ return -EINVAL;
+
+ return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(params));
+}
+
+static int audio_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int audio_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_dev *audio = runtime->private_data;
+
+ audio->period = snd_pcm_lib_period_bytes(substream);
+ audio->period_offset = 0;
+ audio->buffer_start = runtime->dma_area;
+ audio->buffer_end = audio->buffer_start
+ + snd_pcm_lib_buffer_bytes(substream);
+ audio->buffer_pos = audio->buffer_start;
+
+ return 0;
+}
+
+static snd_pcm_uframes_t audio_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_dev *audio = runtime->private_data;
+ ssize_t bytes = audio->buffer_pos - audio->buffer_start;
+
+ /* return offset of next frame to fill in our buffer */
+ return bytes_to_frames(runtime, bytes);
+}
+
+static int audio_pcm_playback_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct audio_dev *audio = substream->runtime->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ audio_pcm_playback_start(audio);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ audio_pcm_playback_stop(audio);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static struct audio_dev _audio_dev = {
+ .func = {
+ .name = "audio_source",
+ .bind = audio_bind,
+ .unbind = audio_unbind,
+ .set_alt = audio_set_alt,
+ .setup = audio_setup,
+ .disable = audio_disable,
+ .descriptors = fs_audio_desc,
+ .hs_descriptors = hs_audio_desc,
+ },
+ .lock = __SPIN_LOCK_UNLOCKED(_audio_dev.lock),
+ .idle_reqs = LIST_HEAD_INIT(_audio_dev.idle_reqs),
+};
+
+static struct snd_pcm_ops audio_playback_ops = {
+ .open = audio_pcm_open,
+ .close = audio_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = audio_pcm_hw_params,
+ .hw_free = audio_pcm_hw_free,
+ .prepare = audio_pcm_prepare,
+ .trigger = audio_pcm_playback_trigger,
+ .pointer = audio_pcm_pointer,
+};
+
+int audio_source_bind_config(struct usb_configuration *c,
+ struct audio_source_config *config)
+{
+ struct audio_dev *audio;
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ int err;
+
+ config->card = -1;
+ config->device = -1;
+
+ audio = &_audio_dev;
+
+ err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, 0, &card);
+ if (err)
+ return err;
+
+ snd_card_set_dev(card, &c->cdev->gadget->dev);
+
+ err = snd_pcm_new(card, "USB audio source", 0, 1, 0, &pcm);
+ if (err)
+ goto pcm_fail;
+ pcm->private_data = audio;
+ pcm->info_flags = 0;
+ audio->pcm = pcm;
+
+ strlcpy(pcm->name, "USB gadget audio", sizeof(pcm->name));
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &audio_playback_ops);
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ NULL, 0, 64 * 1024);
+
+ strlcpy(card->driver, "audio_source", sizeof(card->driver));
+ strlcpy(card->shortname, card->driver, sizeof(card->shortname));
+ strlcpy(card->longname, "USB accessory audio source",
+ sizeof(card->longname));
+
+ err = snd_card_register(card);
+ if (err)
+ goto register_fail;
+
+ err = usb_add_function(c, &audio->func);
+ if (err)
+ goto add_fail;
+
+ config->card = pcm->card->number;
+ config->device = pcm->device;
+ audio->card = card;
+ return 0;
+
+add_fail:
+register_fail:
+pcm_fail:
+ snd_card_free(audio->card);
+ return err;
+}
diff --git a/drivers/usb/gadget/f_mbim.c b/drivers/usb/gadget/f_mbim.c
index 7a84ca3..681435a 100644
--- a/drivers/usb/gadget/f_mbim.c
+++ b/drivers/usb/gadget/f_mbim.c
@@ -19,7 +19,6 @@
#include <linux/usb/cdc.h>
#include <linux/usb/composite.h>
-#include <linux/usb/android_composite.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
@@ -41,6 +40,9 @@
#define NR_MBIM_PORTS 1
+/* ID for Microsoft OS String */
+#define MBIM_OS_STRING_ID 0xEE
+
struct ctrl_pkt {
void *buf;
int len;
@@ -356,6 +358,63 @@
NULL,
};
+/* Microsoft OS Descriptors */
+
+/*
+ * We specify our own bMS_VendorCode byte which Windows will use
+ * as the bRequest value in subsequent device get requests.
+ */
+#define MBIM_VENDOR_CODE 0xA5
+
+/* Microsoft OS String */
+static u8 mbim_os_string[] = {
+ 18, /* sizeof(mtp_os_string) */
+ USB_DT_STRING,
+ /* Signature field: "MSFT100" */
+ 'M', 0, 'S', 0, 'F', 0, 'T', 0, '1', 0, '0', 0, '0', 0,
+ /* vendor code */
+ MBIM_VENDOR_CODE,
+ /* padding */
+ 0
+};
+
+/* Microsoft Extended Configuration Descriptor Header Section */
+struct mbim_ext_config_desc_header {
+ __le32 dwLength;
+ __u16 bcdVersion;
+ __le16 wIndex;
+ __u8 bCount;
+ __u8 reserved[7];
+};
+
+/* Microsoft Extended Configuration Descriptor Function Section */
+struct mbim_ext_config_desc_function {
+ __u8 bFirstInterfaceNumber;
+ __u8 bInterfaceCount;
+ __u8 compatibleID[8];
+ __u8 subCompatibleID[8];
+ __u8 reserved[6];
+};
+
+/* Microsoft Extended Configuration Descriptor */
+static struct {
+ struct mbim_ext_config_desc_header header;
+ struct mbim_ext_config_desc_function function;
+} mbim_ext_config_desc = {
+ .header = {
+ .dwLength = __constant_cpu_to_le32(sizeof mbim_ext_config_desc),
+ .bcdVersion = __constant_cpu_to_le16(0x0100),
+ .wIndex = __constant_cpu_to_le16(4),
+ .bCount = 1,
+ },
+ .function = {
+ .bFirstInterfaceNumber = 0,
+ .bInterfaceCount = 1,
+ .compatibleID = { 'A', 'L', 'T', 'R', 'C', 'F', 'G' },
+ /* .subCompatibleID = DYNAMIC */
+ },
+};
+
/*
* Here are options for the Datagram Pointer table (NDP) parser.
* There are 2 different formats: NDP16 and NDP32 in the spec (ch. 3),
@@ -1108,6 +1167,63 @@
return value;
}
+/*
+ * This function handles the Microsoft-specific OS descriptor control
+ * requests that are issued by Windows host drivers to determine the
+ * configuration containing the MBIM function.
+ *
+ * Unlike mbim_setup() this function handles two specific device requests,
+ * and only when a configuration has not yet been selected.
+ */
+static int mbim_ctrlrequest(struct usb_composite_dev *cdev,
+ const struct usb_ctrlrequest *ctrl)
+{
+ int value = -EOPNOTSUPP;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+ /* only respond to OS desciptors when no configuration selected */
+ if (cdev->config || !mbim_ext_config_desc.function.subCompatibleID[0])
+ return value;
+
+ pr_debug("%02x.%02x v%04x i%04x l%u",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+
+ /* Handle MSFT OS string */
+ if (ctrl->bRequestType ==
+ (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE)
+ && ctrl->bRequest == USB_REQ_GET_DESCRIPTOR
+ && (w_value >> 8) == USB_DT_STRING
+ && (w_value & 0xFF) == MBIM_OS_STRING_ID) {
+
+ value = (w_length < sizeof(mbim_os_string) ?
+ w_length : sizeof(mbim_os_string));
+ memcpy(cdev->req->buf, mbim_os_string, value);
+
+ } else if (ctrl->bRequestType ==
+ (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE)
+ && ctrl->bRequest == MBIM_VENDOR_CODE && w_index == 4) {
+
+ /* Handle Extended OS descriptor */
+ value = (w_length < sizeof(mbim_ext_config_desc) ?
+ w_length : sizeof(mbim_ext_config_desc));
+ memcpy(cdev->req->buf, &mbim_ext_config_desc, value);
+ }
+
+ /* respond with data transfer or status phase? */
+ if (value >= 0) {
+ int rc;
+ cdev->req->zero = value < w_length;
+ cdev->req->length = value;
+ rc = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC);
+ if (rc < 0)
+ pr_err("response queue error: %d", rc);
+ }
+ return value;
+}
+
static int mbim_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_mbim *mbim = func_to_mbim(f);
@@ -1371,6 +1487,17 @@
goto fail;
}
+ /*
+ * If MBIM is bound in a config other than the first, tell Windows
+ * about it by returning the num as a string in the OS descriptor's
+ * subCompatibleID field. Windows only supports up to config #4.
+ */
+ if (c->bConfigurationValue >= 2 && c->bConfigurationValue <= 4) {
+ pr_debug("MBIM in configuration %d", c->bConfigurationValue);
+ mbim_ext_config_desc.function.subCompatibleID[0] =
+ c->bConfigurationValue + '0';
+ }
+
pr_info("mbim(%d): %s speed IN/%s OUT/%s NOTIFY/%s\n",
mbim->port_num,
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
@@ -1412,6 +1539,8 @@
kfree(mbim->not_port.notify_req->buf);
usb_ep_free_request(mbim->not_port.notify, mbim->not_port.notify_req);
+
+ mbim_ext_config_desc.function.subCompatibleID[0] = 0;
}
/**
diff --git a/drivers/usb/gadget/f_uac1.c b/drivers/usb/gadget/f_uac1.c
index b385592..8c74381 100644
--- a/drivers/usb/gadget/f_uac1.c
+++ b/drivers/usb/gadget/f_uac1.c
@@ -62,8 +62,6 @@
static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value);
static int generic_get_cmd(struct usb_audio_control *con, u8 cmd);
-DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
-DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
#define SPEAKER_INPUT_TERMINAL_ID 3
#define SPEAKER_OUTPUT_TERMINAL_ID 4
diff --git a/drivers/usb/misc/ks_bridge.c b/drivers/usb/misc/ks_bridge.c
index 61e6891..410b5c4 100644
--- a/drivers/usb/misc/ks_bridge.c
+++ b/drivers/usb/misc/ks_bridge.c
@@ -287,6 +287,9 @@
unsigned long flags;
struct ks_bridge *ksb = fp->private_data;
+ if (!test_bit(USB_DEV_CONNECTED, &ksb->flags))
+ return -ENODEV;
+
pkt = ksb_alloc_data_pkt(count, GFP_KERNEL, ksb);
if (IS_ERR(pkt)) {
pr_err("unable to allocate data packet");
@@ -570,6 +573,8 @@
struct usb_endpoint_descriptor *ep_desc;
int i;
struct ks_bridge *ksb;
+ unsigned long flags;
+ struct data_pkt *pkt;
ifc_num = ifc->cur_altsetting->desc.bInterfaceNumber;
@@ -625,6 +630,23 @@
dbg_log_event(ksb, "PID-ATT", id->idProduct, 0);
+ /*free up stale buffers if any from previous disconnect*/
+ spin_lock_irqsave(&ksb->lock, flags);
+ while (!list_empty(&ksb->to_ks_list)) {
+ pkt = list_first_entry(&ksb->to_ks_list,
+ struct data_pkt, list);
+ list_del_init(&pkt->list);
+ ksb_free_data_pkt(pkt);
+ ksb->alloced_read_pkts--;
+ }
+ while (!list_empty(&ksb->to_mdm_list)) {
+ pkt = list_first_entry(&ksb->to_mdm_list,
+ struct data_pkt, list);
+ list_del_init(&pkt->list);
+ ksb_free_data_pkt(pkt);
+ }
+ spin_unlock_irqrestore(&ksb->lock, flags);
+
ksb->fs_dev = (struct miscdevice *)id->driver_info;
misc_register(ksb->fs_dev);
@@ -674,6 +696,8 @@
cancel_work_sync(&ksb->to_mdm_work);
cancel_work_sync(&ksb->start_rx_work);
+ misc_deregister(ksb->fs_dev);
+
usb_kill_anchored_urbs(&ksb->submitted);
wait_event_interruptible_timeout(
@@ -688,6 +712,7 @@
struct data_pkt, list);
list_del_init(&pkt->list);
ksb_free_data_pkt(pkt);
+ ksb->alloced_read_pkts--;
}
while (!list_empty(&ksb->to_mdm_list)) {
pkt = list_first_entry(&ksb->to_mdm_list,
@@ -697,7 +722,6 @@
}
spin_unlock_irqrestore(&ksb->lock, flags);
- misc_deregister(ksb->fs_dev);
ifc->needs_remote_wakeup = 0;
usb_put_dev(ksb->udev);
ksb->ifc = NULL;
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index f27e0eb..26f1b84 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -39,9 +39,9 @@
#include <linux/regulator/consumer.h>
#include <linux/mfd/pm8xxx/pm8921-charger.h>
#include <linux/mfd/pm8xxx/misc.h>
-#include <linux/power_supply.h>
#include <linux/mhl_8334.h>
+#include <mach/scm.h>
#include <mach/clk.h>
#include <mach/mpm.h>
#include <mach/msm_xo.h>
@@ -96,6 +96,7 @@
static struct power_supply *psy;
static bool aca_id_turned_on;
+static bool legacy_power_supply;
static inline bool aca_enabled(void)
{
#ifdef CONFIG_USB_MSM_ACA
@@ -926,10 +927,15 @@
if (motg->caps & ALLOW_PHY_RETENTION && !host_bus_suspend &&
!device_bus_suspend && !dcp) {
phy_ctrl_val = readl_relaxed(USB_PHY_CTRL);
- if (motg->pdata->otg_control == OTG_PHY_CONTROL)
+ if (motg->pdata->otg_control == OTG_PHY_CONTROL) {
/* Enable PHY HV interrupts to wake MPM/Link */
- phy_ctrl_val |=
- (PHY_IDHV_INTEN | PHY_OTGSESSVLDHV_INTEN);
+ if ((motg->pdata->mode == USB_OTG) ||
+ (motg->pdata->mode == USB_HOST))
+ phy_ctrl_val |= (PHY_IDHV_INTEN |
+ PHY_OTGSESSVLDHV_INTEN);
+ else
+ phy_ctrl_val |= PHY_OTGSESSVLDHV_INTEN;
+ }
writel_relaxed(phy_ctrl_val & ~PHY_RETEN, USB_PHY_CTRL);
motg->lpm_flags |= PHY_RETENTIONED;
@@ -1110,24 +1116,32 @@
}
#endif
-static int msm_otg_notify_host_mode(struct msm_otg *motg, bool host_mode)
+static void msm_otg_notify_host_mode(struct msm_otg *motg, bool host_mode)
{
- if (!psy)
- goto psy_not_supported;
+ struct power_supply *usb = psy ? psy : &motg->usb_psy;
- if (host_mode)
- power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_SYSTEM);
- else
- power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_DEVICE);
+ if (!usb) {
+ pr_err("No USB power supply registered!\n");
+ return;
+ }
-psy_not_supported:
- dev_dbg(motg->phy.dev, "Power Supply doesn't support USB charger\n");
- return -ENXIO;
+ if (psy) {
+ /* legacy support */
+ if (host_mode)
+ power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_SYSTEM);
+ else
+ power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_DEVICE);
+ return;
+ } else {
+ motg->host_mode = host_mode;
+ power_supply_changed(usb);
+ }
}
static int msm_otg_notify_chg_type(struct msm_otg *motg)
{
static int charger_type;
+ struct power_supply *usb = psy ? psy : &motg->usb_psy;
/*
* TODO
@@ -1151,40 +1165,44 @@
else
charger_type = POWER_SUPPLY_TYPE_BATTERY;
- if (!psy) {
+ if (!usb) {
pr_err("No USB power supply registered!\n");
return -EINVAL;
}
pr_debug("setting usb power supply type %d\n", charger_type);
- power_supply_set_supply_type(psy, charger_type);
+ power_supply_set_supply_type(usb, charger_type);
return 0;
}
static int msm_otg_notify_power_supply(struct msm_otg *motg, unsigned mA)
{
+ struct power_supply *usb = psy ? psy : &motg->usb_psy;
- if (!psy)
- goto psy_not_supported;
-
- if (motg->cur_power == 0 && mA > 0) {
- /* Enable charging */
- if (power_supply_set_online(psy, true))
- goto psy_not_supported;
- } else if (motg->cur_power > 0 && mA == 0) {
- /* Disable charging */
- if (power_supply_set_online(psy, false))
- goto psy_not_supported;
- return 0;
+ if (!usb) {
+ dev_dbg(motg->phy.dev, "no usb power supply registered\n");
+ goto psy_error;
}
- /* Set max current limit */
- if (power_supply_set_current_limit(psy, 1000*mA))
- goto psy_not_supported;
+ if (motg->cur_power == 0 && mA > 2) {
+ /* Enable charging */
+ if (power_supply_set_online(usb, true))
+ goto psy_error;
+ if (power_supply_set_current_limit(usb, 1000*mA))
+ goto psy_error;
+ } else if (motg->cur_power > 0 && (mA == 0 || mA == 2)) {
+ /* Disable charging */
+ if (power_supply_set_online(usb, false))
+ goto psy_error;
+ /* Set max current limit */
+ if (power_supply_set_current_limit(usb, 0))
+ goto psy_error;
+ }
+ power_supply_changed(usb);
return 0;
-psy_not_supported:
- dev_dbg(motg->phy.dev, "Power Supply doesn't support USB charger\n");
+psy_error:
+ dev_dbg(motg->phy.dev, "power supply error when setting property\n");
return -ENXIO;
}
@@ -2315,9 +2333,13 @@
case OTG_STATE_UNDEFINED:
msm_otg_reset(otg->phy);
msm_otg_init_sm(motg);
- psy = power_supply_get_by_name("usb");
- if (!psy)
- pr_err("couldn't get usb power supply\n");
+ if (!psy && legacy_power_supply) {
+ psy = power_supply_get_by_name("usb");
+
+ if (!psy)
+ pr_err("couldn't get usb power supply\n");
+ }
+
otg->phy->state = OTG_STATE_B_IDLE;
if (!test_bit(B_SESS_VLD, &motg->inputs) &&
test_bit(ID, &motg->inputs)) {
@@ -3358,6 +3380,71 @@
return count;
}
+static int otg_power_get_property_usb(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct msm_otg *motg = container_of(psy, struct msm_otg,
+ usb_psy);
+ switch (psp) {
+ case POWER_SUPPLY_PROP_SCOPE:
+ if (motg->host_mode)
+ val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
+ else
+ val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ val->intval = motg->current_max;
+ break;
+ /* Reflect USB enumeration */
+ case POWER_SUPPLY_PROP_PRESENT:
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = motg->online;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int otg_power_set_property_usb(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct msm_otg *motg = container_of(psy, struct msm_otg,
+ usb_psy);
+
+ switch (psp) {
+ /* Process PMIC notification in PRESENT prop */
+ case POWER_SUPPLY_PROP_PRESENT:
+ msm_otg_set_vbus_state(val->intval);
+ break;
+ /* The ONLINE property reflects if usb has enumerated */
+ case POWER_SUPPLY_PROP_ONLINE:
+ motg->online = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ motg->current_max = val->intval;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ power_supply_changed(&motg->usb_psy);
+ return 0;
+}
+
+static char *otg_pm_power_supplied_to[] = {
+ "battery",
+};
+
+static enum power_supply_property otg_pm_power_props_usb[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_SCOPE,
+};
+
const struct file_operations msm_otg_bus_fops = {
.open = msm_otg_bus_open,
.read = seq_read,
@@ -3433,6 +3520,38 @@
debugfs_remove_recursive(msm_otg_dbg_root);
}
+#define MSM_OTG_CMD_ID 0x09
+#define MSM_OTG_DEVICE_ID 0x04
+#define MSM_OTG_VMID_IDX 0xFF
+#define MSM_OTG_MEM_TYPE 0x02
+struct msm_otg_scm_cmd_buf {
+ unsigned int device_id;
+ unsigned int vmid_idx;
+ unsigned int mem_type;
+} __attribute__ ((__packed__));
+
+static void msm_otg_pnoc_errata_fix(struct msm_otg *motg)
+{
+ int ret;
+ struct msm_otg_platform_data *pdata = motg->pdata;
+ struct msm_otg_scm_cmd_buf cmd_buf;
+
+ if (!pdata->pnoc_errata_fix)
+ return;
+
+ dev_dbg(motg->phy.dev, "applying fix for pnoc h/w issue\n");
+
+ cmd_buf.device_id = MSM_OTG_DEVICE_ID;
+ cmd_buf.vmid_idx = MSM_OTG_VMID_IDX;
+ cmd_buf.mem_type = MSM_OTG_MEM_TYPE;
+
+ ret = scm_call(SCM_SVC_CP, MSM_OTG_CMD_ID, &cmd_buf,
+ sizeof(cmd_buf), NULL, 0);
+
+ if (ret)
+ dev_err(motg->phy.dev, "scm command failed to update VMIDMT\n");
+}
+
static u64 msm_otg_dma_mask = DMA_BIT_MASK(64);
static struct platform_device *msm_otg_add_pdev(
struct platform_device *ofdev, const char *name)
@@ -3512,6 +3631,23 @@
return retval;
}
+static int msm_otg_register_power_supply(struct platform_device *pdev,
+ struct msm_otg *motg)
+{
+ int ret;
+
+ ret = power_supply_register(&pdev->dev, &motg->usb_psy);
+ if (ret < 0) {
+ dev_err(motg->phy.dev,
+ "%s:power_supply_register usb failed\n",
+ __func__);
+ return ret;
+ }
+
+ legacy_power_supply = false;
+ return 0;
+}
+
struct msm_otg_platform_data *msm_otg_dt_to_pdata(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
@@ -3546,6 +3682,8 @@
&pdata->pmic_id_irq);
pdata->disable_reset_on_disconnect = of_property_read_bool(node,
"qcom,hsusb-otg-disable-reset");
+ pdata->pnoc_errata_fix = of_property_read_bool(node,
+ "qcom,hsusb-otg-pnoc-errata-fix");
return pdata;
}
@@ -3744,6 +3882,9 @@
}
clk_prepare_enable(motg->core_clk);
+ /* Check if USB mem_type change is needed to workaround PNOC hw issue */
+ msm_otg_pnoc_errata_fix(motg);
+
writel(0, USB_USBINTR);
writel(0, USB_OTGSC);
/* Ensure that above STOREs are completed before enabling interrupts */
@@ -3770,8 +3911,8 @@
}
if (motg->async_irq) {
- ret = request_irq(motg->async_irq, msm_otg_irq, IRQF_SHARED,
- "msm_otg", motg);
+ ret = request_irq(motg->async_irq, msm_otg_irq,
+ IRQF_TRIGGER_RISING, "msm_otg", motg);
if (ret) {
dev_err(&pdev->dev, "request irq failed (ASYNC INT)\n");
goto free_irq;
@@ -3830,9 +3971,6 @@
dev_dbg(&pdev->dev, "mode debugfs file is"
"not available\n");
- if (motg->pdata->otg_control == OTG_PMIC_CONTROL)
- pm8921_charger_register_vbus_sn(&msm_otg_set_vbus_state);
-
if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY) {
if (motg->pdata->otg_control == OTG_PMIC_CONTROL &&
(!(motg->pdata->mode == USB_OTG) ||
@@ -3862,6 +4000,32 @@
debug_bus_voting_enabled = true;
}
+ motg->usb_psy.name = "usb";
+ motg->usb_psy.type = POWER_SUPPLY_TYPE_USB;
+ motg->usb_psy.supplied_to = otg_pm_power_supplied_to;
+ motg->usb_psy.num_supplicants = ARRAY_SIZE(otg_pm_power_supplied_to);
+ motg->usb_psy.properties = otg_pm_power_props_usb;
+ motg->usb_psy.num_properties = ARRAY_SIZE(otg_pm_power_props_usb);
+ motg->usb_psy.get_property = otg_power_get_property_usb;
+ motg->usb_psy.set_property = otg_power_set_property_usb;
+
+ if (motg->pdata->otg_control == OTG_PMIC_CONTROL) {
+ /* if pm8921 use legacy implementation */
+ if (!pm8921_charger_register_vbus_sn(&msm_otg_set_vbus_state)) {
+ dev_dbg(motg->phy.dev, "%s: legacy support\n",
+ __func__);
+ legacy_power_supply = true;
+ } else {
+ ret = msm_otg_register_power_supply(pdev, motg);
+ if (ret)
+ goto remove_phy;
+ }
+ } else {
+ ret = msm_otg_register_power_supply(pdev, motg);
+ if (ret)
+ goto remove_phy;
+ }
+
return 0;
remove_phy:
diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h
index 80ebc4f..25c39f6 100644
--- a/drivers/video/msm/mdss/mdss_fb.h
+++ b/drivers/video/msm/mdss/mdss_fb.h
@@ -96,6 +96,8 @@
struct mdss_mdp_ctl *ctl;
struct mdss_mdp_wb *wb;
struct list_head overlay_list;
+ struct list_head pipes_used;
+ struct list_head pipes_cleanup;
};
int mdss_fb_get_phys_info(unsigned long *start, unsigned long *len, int fb_num);
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index 6fd642f..33028cb 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -241,7 +241,9 @@
unsigned long smp[MAX_PLANES];
struct mdss_mdp_data buffers[2];
- struct list_head list;
+ struct list_head used_list;
+ struct list_head cleanup_list;
+
struct mdp_overlay_pp_params pp_cfg;
};
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index 5966989..d21a095 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -90,18 +90,34 @@
*bus_ib_quota = 0;
*clk_rate = 0;
- if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
- struct mdss_panel_info *pinfo = &mixer->ctl->mfd->panel_info;
- v_total = (pinfo->yres + pinfo->lcdc.v_back_porch +
- pinfo->lcdc.v_front_porch + pinfo->lcdc.v_pulse_width);
- v_active = pinfo->yres;
- } else if (mixer->rotator_mode) {
+ if (mixer->rotator_mode) {
pipe = mixer->stage_pipe[0]; /* rotator pipe */
v_total = pipe->flags & MDP_ROT_90 ? pipe->dst.w : pipe->dst.h;
v_active = v_total;
} else {
- v_total = mixer->height;
- v_active = v_total;
+ int is_writeback = false;
+ if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
+ struct mdss_panel_info *pinfo;
+ pinfo = &mixer->ctl->mfd->panel_info;
+ v_total = (pinfo->yres + pinfo->lcdc.v_back_porch +
+ pinfo->lcdc.v_front_porch +
+ pinfo->lcdc.v_pulse_width);
+ v_active = pinfo->yres;
+
+ if (pinfo->type == WRITEBACK_PANEL)
+ is_writeback = true;
+ } else {
+ v_total = mixer->height;
+ v_active = v_total;
+
+ is_writeback = true;
+ }
+ *clk_rate = mixer->width * v_total * fps;
+ if (is_writeback) {
+ /* perf for bus writeback */
+ *bus_ab_quota = fps * mixer->width * mixer->height * 3;
+ *bus_ib_quota = *bus_ab_quota;
+ }
}
for (i = 0; i < MDSS_MDP_MAX_STAGE; i++) {
@@ -118,7 +134,7 @@
if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF)
quota = (quota / v_active) * v_total;
- else
+ else if (mixer->rotator_mode)
quota *= 2; /* bus read + write */
rate = pipe->dst.w;
@@ -344,6 +360,8 @@
mdss_mdp_mixer_free(mixer);
mdss_mdp_ctl_free(ctl);
+ mdss_mdp_ctl_perf_commit(MDSS_MDP_PERF_UPDATE_ALL);
+
return 0;
}
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index f76b508..452ebdc 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -282,7 +282,7 @@
}
mutex_lock(&mfd->lock);
- list_add(&pipe->list, &mfd->overlay_list);
+ list_add(&pipe->used_list, &mfd->pipes_used);
mutex_unlock(&mfd->lock);
pipe->mixer = mixer;
pipe->mfd = mfd;
@@ -395,24 +395,31 @@
static int mdss_mdp_overlay_kickoff(struct mdss_mdp_ctl *ctl)
{
- int ret;
+ struct mdss_mdp_pipe *pipe, *tmp;
+ struct msm_fb_data_type *mfd = ctl->mfd;
+ int i, ret;
- if (ctl->mfd->kickoff_fnc)
- ret = ctl->mfd->kickoff_fnc(ctl);
+ if (mfd->kickoff_fnc)
+ ret = mfd->kickoff_fnc(ctl);
else
ret = mdss_mdp_display_commit(ctl, NULL);
if (IS_ERR_VALUE(ret))
return ret;
- pr_debug("freeing previous buffers\n");
+ mutex_lock(&mfd->lock);
+ list_for_each_entry_safe(pipe, tmp, &mfd->pipes_cleanup, cleanup_list) {
+ list_del(&pipe->cleanup_list);
+ for (i = 0; i < ARRAY_SIZE(pipe->buffers); i++)
+ mdss_mdp_overlay_free_buf(&pipe->buffers[i]);
- mutex_lock(&ctl->mfd->lock);
- if (!list_empty(&ctl->mfd->overlay_list)) {
- struct mdss_mdp_pipe *pipe;
+ mdss_mdp_pipe_destroy(pipe);
+ }
+
+ if (!list_empty(&mfd->pipes_used)) {
struct mdss_mdp_data *data;
int buf_ndx;
- list_for_each_entry(pipe, &ctl->mfd->overlay_list, list) {
+ list_for_each_entry(pipe, &mfd->pipes_used, used_list) {
buf_ndx = (pipe->play_cnt - 1) & 1; /* prev buffer */
data = &pipe->buffers[buf_ndx];
@@ -423,9 +430,7 @@
}
}
}
- mutex_unlock(&ctl->mfd->lock);
-
- pr_debug("done freeing previous buffers\n");
+ mutex_unlock(&mfd->lock);
return ret;
}
@@ -433,8 +438,7 @@
static int mdss_mdp_overlay_unset(struct msm_fb_data_type *mfd, int ndx)
{
struct mdss_mdp_pipe *pipe;
- struct mdss_mdp_pipe *cleanup_pipes[MDSS_MDP_MAX_SSPP];
- int i, ret = 0, clean_cnt = 0;
+ int i, ret = 0;
u32 pipe_ndx, unset_ndx = 0;
if (!mfd || !mfd->ctl)
@@ -460,28 +464,15 @@
if (pipe_ndx & ndx) {
unset_ndx |= pipe_ndx;
pipe = mdss_mdp_pipe_get_locked(pipe_ndx);
- if (pipe) {
- mutex_lock(&mfd->lock);
- list_del(&pipe->list);
- mutex_unlock(&mfd->lock);
- mdss_mdp_mixer_pipe_unstage(pipe);
- cleanup_pipes[clean_cnt++] = pipe;
- } else {
+ if (!pipe) {
pr_warn("unknown pipe ndx=%x\n", pipe_ndx);
+ continue;
}
- }
- }
-
- if (clean_cnt) {
- int j;
- ret = mdss_mdp_overlay_kickoff(mfd->ctl);
-
- for (i = 0; i < clean_cnt; i++) {
- pipe = cleanup_pipes[i];
- for (j = 0; j < ARRAY_SIZE(pipe->buffers); j++)
- mdss_mdp_overlay_free_buf(&pipe->buffers[i]);
-
- mdss_mdp_pipe_destroy(pipe);
+ mutex_lock(&mfd->lock);
+ list_del(&pipe->used_list);
+ list_add(&pipe->cleanup_list, &mfd->pipes_cleanup);
+ mutex_unlock(&mfd->lock);
+ mdss_mdp_mixer_pipe_unstage(pipe);
}
}
@@ -495,8 +486,8 @@
int cnt = 0;
mutex_lock(&mfd->lock);
- if (!list_empty(&mfd->overlay_list)) {
- list_for_each_entry(pipe, &mfd->overlay_list, list) {
+ if (!list_empty(&mfd->pipes_used)) {
+ list_for_each_entry(pipe, &mfd->pipes_used, used_list) {
if (pipe->ndx & MDSS_MDP_ROT_SESSION_MASK) {
struct mdss_mdp_rotator_session *rot;
rot = mdss_mdp_rotator_session_get(pipe->ndx);
@@ -513,6 +504,7 @@
if (unset_ndx) {
pr_debug("%d pipes need cleanup (%x)\n", cnt, unset_ndx);
mdss_mdp_overlay_unset(mfd, unset_ndx);
+ mdss_mdp_overlay_kickoff(mfd->ctl);
}
return 0;
@@ -603,7 +595,7 @@
ctl = pipe->mixer->ctl;
mdss_mdp_pipe_unlock(pipe);
- if (ret == 0 && !(pipe->flags & MDP_OV_PLAY_NOWAIT))
+ if ((ret == 0) && (mfd->panel_info.type == WRITEBACK_PANEL))
ret = mdss_mdp_overlay_kickoff(ctl);
return ret;
@@ -1039,7 +1031,9 @@
ret = -EFAULT;
}
break;
-
+ case MSMFB_OVERLAY_COMMIT:
+ ret = mdss_mdp_overlay_kickoff(mfd->ctl);
+ break;
default:
if (mfd->panel_info.type == WRITEBACK_PANEL)
ret = mdss_mdp_wb_ioctl_handler(mfd, cmd, argp);
@@ -1063,7 +1057,8 @@
if (mfd->panel_info.type == WRITEBACK_PANEL)
mfd->kickoff_fnc = mdss_mdp_wb_kickoff;
- INIT_LIST_HEAD(&mfd->overlay_list);
+ INIT_LIST_HEAD(&mfd->pipes_used);
+ INIT_LIST_HEAD(&mfd->pipes_cleanup);
return 0;
}
diff --git a/drivers/video/msm/mdss/mdss_mdp_rotator.h b/drivers/video/msm/mdss/mdss_mdp_rotator.h
index 1e4b81e0..eb5b47a 100644
--- a/drivers/video/msm/mdss/mdss_mdp_rotator.h
+++ b/drivers/video/msm/mdss/mdss_mdp_rotator.h
@@ -17,7 +17,7 @@
#include "mdss_mdp.h"
-#define MDSS_MDP_ROT_SESSION_MASK 0x80000000
+#define MDSS_MDP_ROT_SESSION_MASK 0x40000000
struct mdss_mdp_rotator_session {
u32 session_id;
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index abcdeab..9a66ff9 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -2088,7 +2088,7 @@
ext4_ext_in_cache(struct inode *inode, ext4_lblk_t block,
struct ext4_extent *ex)
{
- struct ext4_ext_cache cex;
+ struct ext4_ext_cache cex = {0, 0, 0};
int ret = 0;
if (ext4_ext_check_cache(inode, block, &cex)) {
diff --git a/include/linux/coresight-stm.h b/include/linux/coresight-stm.h
index 7c7c26e..bb3ebca 100644
--- a/include/linux/coresight-stm.h
+++ b/include/linux/coresight-stm.h
@@ -1,15 +1,3 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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 __MACH_STM_H
#define __MACH_STM_H
diff --git a/include/linux/cyttsp-qc.h b/include/linux/cyttsp-qc.h
index e1ab6fe..0e5cac7 100644
--- a/include/linux/cyttsp-qc.h
+++ b/include/linux/cyttsp-qc.h
@@ -356,7 +356,6 @@
#define CY_DLY_BL 300
#define CY_DLY_DNLOAD 100 /* ms */
#define CY_NUM_RETRY 4 /* max num touch data read */
-#define CY_HALF_SEC_TMO_MS 500
/* handshake bit in the hst_mode reg */
#define CY_HNDSHK_BIT 0x80
diff --git a/include/linux/mfd/pm8xxx/pm8038.h b/include/linux/mfd/pm8xxx/pm8038.h
index 682abc8..574dab6 100644
--- a/include/linux/mfd/pm8xxx/pm8038.h
+++ b/include/linux/mfd/pm8xxx/pm8038.h
@@ -54,6 +54,7 @@
/* PMIC Interrupts */
#define PM8038_RTC_ALARM_IRQ PM8038_IRQ_BLOCK_BIT(4, 7)
+#define PM8038_BATT_ALARM_IRQ PM8921_IRQ_BLOCK_BIT(5, 6)
#define PM8038_PWRKEY_REL_IRQ PM8038_IRQ_BLOCK_BIT(6, 2)
#define PM8038_PWRKEY_PRESS_IRQ PM8038_IRQ_BLOCK_BIT(6, 3)
#define PM8038_KEYPAD_IRQ PM8038_IRQ_BLOCK_BIT(9, 2)
diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h
index c306c75..8e8caf7 100644
--- a/include/linux/mfd/wcd9xxx/core.h
+++ b/include/linux/mfd/wcd9xxx/core.h
@@ -13,10 +13,13 @@
#ifndef __MFD_TABLA_CORE_H__
#define __MFD_TABLA_CORE_H__
+#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/pm_qos.h>
+#include <linux/platform_device.h>
+#include <linux/of_irq.h>
-#define WCD9XXX_NUM_IRQ_REGS 3
+#define WCD9XXX_NUM_IRQ_REGS 4
#define WCD9XXX_SLIM_NUM_PORT_REG 3
@@ -44,83 +47,52 @@
enum {
- TABLA_IRQ_SLIMBUS = 0,
- TABLA_IRQ_MBHC_REMOVAL,
- TABLA_IRQ_MBHC_SHORT_TERM,
- TABLA_IRQ_MBHC_PRESS,
- TABLA_IRQ_MBHC_RELEASE,
- TABLA_IRQ_MBHC_POTENTIAL,
- TABLA_IRQ_MBHC_INSERTION,
- TABLA_IRQ_BG_PRECHARGE,
- TABLA_IRQ_PA1_STARTUP,
- TABLA_IRQ_PA2_STARTUP,
- TABLA_IRQ_PA3_STARTUP,
- TABLA_IRQ_PA4_STARTUP,
- TABLA_IRQ_PA5_STARTUP,
- TABLA_IRQ_MICBIAS1_PRECHARGE,
- TABLA_IRQ_MICBIAS2_PRECHARGE,
- TABLA_IRQ_MICBIAS3_PRECHARGE,
- TABLA_IRQ_HPH_PA_OCPL_FAULT,
- TABLA_IRQ_HPH_PA_OCPR_FAULT,
- TABLA_IRQ_EAR_PA_OCPL_FAULT,
- TABLA_IRQ_HPH_L_PA_STARTUP,
- TABLA_IRQ_HPH_R_PA_STARTUP,
- TABLA_IRQ_EAR_PA_STARTUP,
- TABLA_NUM_IRQS,
+ /* INTR_REG 0 */
+ WCD9XXX_IRQ_SLIMBUS = 0,
+ WCD9XXX_IRQ_MBHC_REMOVAL,
+ WCD9XXX_IRQ_MBHC_SHORT_TERM,
+ WCD9XXX_IRQ_MBHC_PRESS,
+ WCD9XXX_IRQ_MBHC_RELEASE,
+ WCD9XXX_IRQ_MBHC_POTENTIAL,
+ WCD9XXX_IRQ_MBHC_INSERTION,
+ WCD9XXX_IRQ_BG_PRECHARGE,
+ /* INTR_REG 1 */
+ WCD9XXX_IRQ_PA1_STARTUP,
+ WCD9XXX_IRQ_PA2_STARTUP,
+ WCD9XXX_IRQ_PA3_STARTUP,
+ WCD9XXX_IRQ_PA4_STARTUP,
+ WCD9XXX_IRQ_PA5_STARTUP,
+ WCD9XXX_IRQ_MICBIAS1_PRECHARGE,
+ WCD9XXX_IRQ_MICBIAS2_PRECHARGE,
+ WCD9XXX_IRQ_MICBIAS3_PRECHARGE,
+ /* INTR_REG 2 */
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+ WCD9XXX_IRQ_EAR_PA_OCPL_FAULT,
+ WCD9XXX_IRQ_HPH_L_PA_STARTUP,
+ WCD9XXX_IRQ_HPH_R_PA_STARTUP,
+ WCD9XXX_IRQ_EAR_PA_STARTUP,
+ WCD9XXX_IRQ_RESERVED_0,
+ WCD9XXX_IRQ_RESERVED_1,
+ /* INTR_REG 3 */
+ WCD9XXX_IRQ_MAD_AUDIO,
+ WCD9XXX_IRQ_MAD_BEACON,
+ WCD9XXX_IRQ_MAD_ULTRASOUND,
+ WCD9XXX_IRQ_SPEAKER_CLIPPING,
+ WCD9XXX_IRQ_MBHC_JACK_SWITCH,
+ WCD9XXX_NUM_IRQS,
};
enum {
- SITAR_IRQ_SLIMBUS = 0,
- SITAR_IRQ_MBHC_REMOVAL,
- SITAR_IRQ_MBHC_SHORT_TERM,
- SITAR_IRQ_MBHC_PRESS,
- SITAR_IRQ_MBHC_RELEASE,
- SITAR_IRQ_MBHC_POTENTIAL,
- SITAR_IRQ_MBHC_INSERTION,
- SITAR_IRQ_BG_PRECHARGE,
- SITAR_IRQ_PA1_STARTUP,
- SITAR_IRQ_PA2_STARTUP,
- SITAR_IRQ_PA3_STARTUP,
- SITAR_IRQ_PA4_STARTUP,
- SITAR_IRQ_PA5_STARTUP,
- SITAR_IRQ_MICBIAS1_PRECHARGE,
- SITAR_IRQ_MICBIAS2_PRECHARGE,
- SITAR_IRQ_MICBIAS3_PRECHARGE,
- SITAR_IRQ_HPH_PA_OCPL_FAULT,
- SITAR_IRQ_HPH_PA_OCPR_FAULT,
- SITAR_IRQ_EAR_PA_OCPL_FAULT,
- SITAR_IRQ_HPH_L_PA_STARTUP,
- SITAR_IRQ_HPH_R_PA_STARTUP,
- SITAR_IRQ_EAR_PA_STARTUP,
- SITAR_NUM_IRQS,
+ TABLA_NUM_IRQS = WCD9XXX_NUM_IRQS,
+ SITAR_NUM_IRQS = WCD9XXX_NUM_IRQS,
+ TAIKO_NUM_IRQS = WCD9XXX_NUM_IRQS,
};
-enum {
- TAIKO_IRQ_SLIMBUS = 0,
- TAIKO_IRQ_MBHC_REMOVAL,
- TAIKO_IRQ_MBHC_SHORT_TERM,
- TAIKO_IRQ_MBHC_PRESS,
- TAIKO_IRQ_MBHC_RELEASE,
- TAIKO_IRQ_MBHC_POTENTIAL,
- TAIKO_IRQ_MBHC_INSERTION,
- TAIKO_IRQ_BG_PRECHARGE,
- TAIKO_IRQ_PA1_STARTUP,
- TAIKO_IRQ_PA2_STARTUP,
- TAIKO_IRQ_PA3_STARTUP,
- TAIKO_IRQ_PA4_STARTUP,
- TAIKO_IRQ_PA5_STARTUP,
- TAIKO_IRQ_MICBIAS1_PRECHARGE,
- TAIKO_IRQ_MICBIAS2_PRECHARGE,
- TAIKO_IRQ_MICBIAS3_PRECHARGE,
- TAIKO_IRQ_HPH_PA_OCPL_FAULT,
- TAIKO_IRQ_HPH_PA_OCPR_FAULT,
- TAIKO_IRQ_EAR_PA_OCPL_FAULT,
- TAIKO_IRQ_HPH_L_PA_STARTUP,
- TAIKO_IRQ_HPH_R_PA_STARTUP,
- TAIKO_IRQ_EAR_PA_STARTUP,
- TAIKO_NUM_IRQS,
-};
+#define MAX(X, Y) (((int)X) >= ((int)Y) ? (X) : (Y))
+#define WCD9XXX_MAX_NUM_IRQS (MAX(MAX(TABLA_NUM_IRQS, SITAR_NUM_IRQS), \
+ TAIKO_NUM_IRQS))
enum wcd9xxx_pm_state {
WCD9XXX_PM_SLEEPABLE,
@@ -128,6 +100,44 @@
WCD9XXX_PM_ASLEEP,
};
+/*
+ * data structure for Slimbus and I2S channel.
+ * Some of fields are only used in smilbus mode
+ */
+struct wcd9xxx_ch {
+ u32 sph; /* share channel handle - slimbus only */
+ u32 ch_num; /*
+ * vitrual channel number, such as 128 -144.
+ * apply for slimbus only
+ */
+ u16 ch_h; /* chanel handle - slimbus only */
+ u16 port; /*
+ * tabla port for RX and TX
+ * such as 0-9 for TX and 10 -16 for RX
+ * apply for both i2s and slimbus
+ */
+ u16 shift; /*
+ * shift bit for RX and TX
+ * apply for both i2s and slimbus
+ */
+ struct list_head list; /*
+ * channel link list
+ * apply for both i2s and slimbus
+ */
+};
+
+struct wcd9xxx_codec_dai_data {
+ u32 rate; /* sample rate */
+ u32 bit_width; /* sit width 16,24,32 */
+ struct list_head wcd9xxx_ch_list; /* channel list */
+ u16 grph; /* slimbus group handle */
+ u32 ch_mask;
+ wait_queue_head_t dai_wait;
+};
+
+#define WCD9XXX_CH(xport, xshift) \
+ {.port = xport, .shift = xshift}
+
struct wcd9xxx {
struct device *dev;
struct slim_device *slim;
@@ -137,18 +147,12 @@
struct mutex irq_lock;
u8 version;
- unsigned int irq_base;
- unsigned int irq;
- u8 irq_masks_cur[WCD9XXX_NUM_IRQ_REGS];
- u8 irq_masks_cache[WCD9XXX_NUM_IRQ_REGS];
- u8 irq_level[WCD9XXX_NUM_IRQ_REGS];
-
int reset_gpio;
int (*read_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg,
int bytes, void *dest, bool interface_reg);
int (*write_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg,
- int bytes, void *src, bool interface_reg);
+ int bytes, void *src, bool interface_reg);
u32 num_of_supplies;
struct regulator_bulk_data *supplies;
@@ -160,10 +164,19 @@
struct pm_qos_request pm_qos_req;
int wlock_holders;
- int num_rx_port;
- int num_tx_port;
-
u8 idbyte[4];
+
+ unsigned int irq_base;
+ unsigned int irq;
+ u8 irq_masks_cur[WCD9XXX_NUM_IRQ_REGS];
+ u8 irq_masks_cache[WCD9XXX_NUM_IRQ_REGS];
+ bool irq_level_high[WCD9XXX_MAX_NUM_IRQS];
+ int num_irqs;
+ /* Slimbus or I2S port */
+ u32 num_rx_port;
+ u32 num_tx_port;
+ struct wcd9xxx_ch *rx_chs;
+ struct wcd9xxx_ch *tx_chs;
};
int wcd9xxx_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg);
@@ -187,40 +200,15 @@
enum wcd9xxx_pm_state o,
enum wcd9xxx_pm_state n);
-static inline int wcd9xxx_request_irq(struct wcd9xxx *wcd9xxx, int irq,
- irq_handler_t handler, const char *name,
- void *data)
-{
- if (!wcd9xxx->irq_base)
- return -EINVAL;
- return request_threaded_irq(wcd9xxx->irq_base + irq, NULL, handler,
- IRQF_TRIGGER_RISING, name,
- data);
-}
-static inline void wcd9xxx_free_irq(struct wcd9xxx *wcd9xxx,
- int irq, void *data)
-{
- if (!wcd9xxx->irq_base)
- return;
- free_irq(wcd9xxx->irq_base + irq, data);
-}
-static inline void wcd9xxx_enable_irq(struct wcd9xxx *wcd9xxx, int irq)
-{
- if (!wcd9xxx->irq_base)
- return;
- enable_irq(wcd9xxx->irq_base + irq);
-}
-static inline void wcd9xxx_disable_irq(struct wcd9xxx *wcd9xxx, int irq)
-{
- if (!wcd9xxx->irq_base)
- return;
- disable_irq_nosync(wcd9xxx->irq_base + irq);
-}
-static inline void wcd9xxx_disable_irq_sync(struct wcd9xxx *wcd9xxx, int irq)
-{
- if (!wcd9xxx->irq_base)
- return;
- disable_irq(wcd9xxx->irq_base + irq);
-}
+int wcd9xxx_request_irq(struct wcd9xxx *wcd9xxx, int irq,
+ irq_handler_t handler, const char *name, void *data);
+void wcd9xxx_free_irq(struct wcd9xxx *wcd9xxx, int irq, void *data);
+void wcd9xxx_enable_irq(struct wcd9xxx *wcd9xxx, int irq);
+void wcd9xxx_disable_irq(struct wcd9xxx *wcd9xxx, int irq);
+void wcd9xxx_disable_irq_sync(struct wcd9xxx *wcd9xxx, int irq);
+#ifdef CONFIG_OF
+int __init wcd9xxx_irq_of_init(struct device_node *node,
+ struct device_node *parent);
+#endif /* CONFIG_OF */
#endif
diff --git a/include/linux/mfd/wcd9xxx/pdata.h b/include/linux/mfd/wcd9xxx/pdata.h
index e831f0b..a7ca417 100644
--- a/include/linux/mfd/wcd9xxx/pdata.h
+++ b/include/linux/mfd/wcd9xxx/pdata.h
@@ -28,6 +28,12 @@
#define SITAR_CFILT2_SEL 0x1
#define SITAR_CFILT3_SEL 0x2
+#define WCD9XXX_LDOH_1P95_V 0x0
+#define WCD9XXX_LDOH_2P35_V 0x1
+#define WCD9XXX_LDOH_2P75_V 0x2
+#define WCD9XXX_LDOH_2P85_V 0x3
+#define WCD9XXX_LDOH_3P0_V 0x3
+
#define TABLA_LDOH_1P95_V 0x0
#define TABLA_LDOH_2P35_V 0x1
#define TABLA_LDOH_2P75_V 0x2
@@ -37,16 +43,6 @@
#define TABLA_CFILT2_SEL 0x1
#define TABLA_CFILT3_SEL 0x2
-#define TAIKO_CFILT1_SEL 0x0
-#define TAIKO_CFILT2_SEL 0x1
-#define TAIKO_CFILT3_SEL 0x2
-
-#define TAIKO_LDOH_1P95_V 0x0
-#define TAIKO_LDOH_2P35_V 0x1
-#define TAIKO_LDOH_2P75_V 0x2
-#define TAIKO_LDOH_2P85_V 0x3
-
-
#define MAX_AMIC_CHANNEL 7
#define TABLA_OCP_300_MA 0x0
diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
index 0d5d058..45abd92 100644
--- a/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
+++ b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
@@ -16,27 +16,6 @@
#include <linux/slimbus/slimbus.h>
#include <linux/mfd/wcd9xxx/core.h>
-/* Channel numbers to be used for each port */
-enum {
- SLIM_TX_1 = 128,
- SLIM_TX_2 = 129,
- SLIM_TX_3 = 130,
- SLIM_TX_4 = 131,
- SLIM_TX_5 = 132,
- SLIM_TX_6 = 133,
- SLIM_TX_7 = 134,
- SLIM_TX_8 = 135,
- SLIM_TX_9 = 136,
- SLIM_TX_10 = 137,
- SLIM_RX_1 = 138,
- SLIM_RX_2 = 139,
- SLIM_RX_3 = 140,
- SLIM_RX_4 = 141,
- SLIM_RX_5 = 142,
- SLIM_RX_6 = 143,
- SLIM_RX_7 = 144,
- SLIM_MAX = 145
-};
/*
* client is expected to give port ids in the range of
@@ -92,30 +71,42 @@
/* slave port water mark level
* (0: 6bytes, 1: 9bytes, 2: 12 bytes, 3: 15 bytes)
*/
-#define SLAVE_PORT_WATER_MARK_VALUE 2
+#define SLAVE_PORT_WATER_MARK_6BYTES 0
+#define SLAVE_PORT_WATER_MARK_9BYTES 1
+#define SLAVE_PORT_WATER_MARK_12BYTES 2
+#define SLAVE_PORT_WATER_MARK_15BYTES 3
#define SLAVE_PORT_WATER_MARK_SHIFT 1
#define SLAVE_PORT_ENABLE 1
#define SLAVE_PORT_DISABLE 0
-
+#define WATER_MARK_VAL \
+ ((SLAVE_PORT_WATER_MARK_12BYTES << SLAVE_PORT_WATER_MARK_SHIFT) | \
+ (SLAVE_PORT_ENABLE))
#define BASE_CH_NUM 128
-int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la);
+int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx,
+ u8 wcd9xxx_pgd_la,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot);
int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx);
-int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
- unsigned int tot_ch, unsigned int rate);
-int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
- unsigned int tot_ch, unsigned int rate);
-int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
- unsigned int tot_ch);
-int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
- unsigned int tot_ch);
+int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx,
+ struct list_head *wcd9xxx_ch_list,
+ unsigned int rate, unsigned int bit_width,
+ u16 *grph);
+int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx,
+ struct list_head *wcd9xxx_ch_list,
+ unsigned int rate, unsigned int bit_width,
+ u16 *grph);
+int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx,
+ struct list_head *wcd9xxx_ch_list, u16 grph);
+int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx,
+ struct list_head *wcd9xxx_ch_list, u16 grph);
int wcd9xxx_get_channel(struct wcd9xxx *wcd9xxx,
unsigned int *rx_ch,
unsigned int *tx_ch);
int wcd9xxx_get_slave_port(unsigned int ch_num);
-int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
- unsigned int tot_ch, unsigned int rx_tx);
+int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx,
+ struct list_head *wcd9xxx_ch_list, u16 grph);
#endif /* __WCD9310_SLIMSLAVE_H_ */
diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h b/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
index c66e953..4b7a32c 100644
--- a/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
+++ b/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
@@ -16,27 +16,191 @@
#define WCD9XXX_A_CHIP_CTL (0x00)
#define WCD9XXX_A_CHIP_CTL__POR (0x00000000)
#define WCD9XXX_A_CHIP_STATUS (0x01)
-#define WCD9XXX_A_CHIP_STATUS__POR (0x00000000)
-#define WCD9XXX_A_CHIP_ID_BYTE_0 (0x04)
-#define WCD9XXX_A_CHIP_ID_BYTE_0__POR (0x00000000)
-#define WCD9XXX_A_CHIP_ID_BYTE_1 (0x05)
-#define WCD9XXX_A_CHIP_ID_BYTE_1__POR (0x00000000)
-#define WCD9XXX_A_CHIP_ID_BYTE_2 (0x06)
-#define WCD9XXX_A_CHIP_ID_BYTE_2__POR (0x00000000)
-#define WCD9XXX_A_CHIP_ID_BYTE_3 (0x07)
-#define WCD9XXX_A_CHIP_ID_BYTE_3__POR (0x00000001)
+#define WCD9XXX_A_CHIP_STATUS__POR (0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_0 (0x04)
+#define WCD9XXX_A_CHIP_ID_BYTE_0__POR (0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_1 (0x05)
+#define WCD9XXX_A_CHIP_ID_BYTE_1__POR (0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_2 (0x06)
+#define WCD9XXX_A_CHIP_ID_BYTE_2__POR (0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_3 (0x07)
+#define WCD9XXX_A_CHIP_ID_BYTE_3__POR (0x00000001)
#define WCD9XXX_A_CHIP_VERSION (0x08)
-#define WCD9XXX_A_CHIP_VERSION__POR (0x00000020)
+#define WCD9XXX_A_CHIP_VERSION__POR (0x00000020)
#define WCD9XXX_A_SB_VERSION (0x09)
-#define WCD9XXX_A_SB_VERSION__POR (0x00000010)
+#define WCD9XXX_A_SB_VERSION__POR (0x00000010)
#define WCD9XXX_A_SLAVE_ID_1 (0x0C)
-#define WCD9XXX_A_SLAVE_ID_1__POR (0x00000077)
+#define WCD9XXX_A_SLAVE_ID_1__POR (0x00000077)
#define WCD9XXX_A_SLAVE_ID_2 (0x0D)
-#define WCD9XXX_A_SLAVE_ID_2__POR (0x00000066)
+#define WCD9XXX_A_SLAVE_ID_2__POR (0x00000066)
#define WCD9XXX_A_SLAVE_ID_3 (0x0E)
-#define WCD9XXX_A_SLAVE_ID_3__POR (0x00000055)
+#define WCD9XXX_A_SLAVE_ID_3__POR (0x00000055)
#define WCD9XXX_A_CDC_CTL (0x80)
#define WCD9XXX_A_CDC_CTL__POR (0x00000000)
#define WCD9XXX_A_LEAKAGE_CTL (0x88)
-#define WCD9XXX_A_LEAKAGE_CTL__POR (0x00000004)
+#define WCD9XXX_A_LEAKAGE_CTL__POR (0x00000004)
+#define WCD9XXX_A_INTR_MODE (0x90)
+#define WCD9XXX_A_INTR_MASK0 (0x94)
+#define WCD9XXX_A_INTR_STATUS0 (0x98)
+#define WCD9XXX_A_INTR_CLEAR0 (0x9C)
+#define WCD9XXX_A_INTR_LEVEL0 (0xA0)
+#define WCD9XXX_A_INTR_LEVEL1 (0xA1)
+#define WCD9XXX_A_INTR_LEVEL2 (0xA2)
+#define WCD9XXX_A_RX_HPH_CNP_EN (0x1AB)
+#define WCD9XXX_A_RX_HPH_CNP_EN__POR (0x80)
+#define WCD9XXX_A_RX_HPH_CNP_EN (0x1AB)
+#define WCD9XXX_A_RX_HPH_CNP_EN__POR (0x80)
+#define WCD9XXX_A_BIAS_CENTRAL_BG_CTL (0x101)
+#define WCD9XXX_A_BIAS_CENTRAL_BG_CTL__POR (0x50)
+#define WCD9XXX_A_CLK_BUFF_EN1 (0x108)
+#define WCD9XXX_A_CLK_BUFF_EN1__POR (0x04)
+#define WCD9XXX_A_CLK_BUFF_EN2 (0x109)
+#define WCD9XXX_A_CLK_BUFF_EN2__POR (0x02)
+#define WCD9XXX_A_RX_COM_BIAS (0x1A2)
+#define WCD9XXX_A_RX_COM_BIAS__POR (0x00)
+#define WCD9XXX_A_RC_OSC_FREQ (0x1FA)
+#define WCD9XXX_A_RC_OSC_FREQ__POR (0x46)
+#define WCD9XXX_A_BIAS_OSC_BG_CTL (0x105)
+#define WCD9XXX_A_BIAS_OSC_BG_CTL__POR (0x16)
+#define WCD9XXX_A_RC_OSC_TEST (0x1FB)
+#define WCD9XXX_A_RC_OSC_TEST__POR (0x0A)
+#define WCD9XXX_A_CDC_CLK_MCLK_CTL (0x311)
+#define WCD9XXX_A_CDC_CLK_MCLK_CTL__POR (0x00)
+
+#define WCD9XXX_A_CDC_MBHC_EN_CTL (0x3C0)
+#define WCD9XXX_A_CDC_MBHC_EN_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_FIR_B1_CFG (0x3C1)
+#define WCD9XXX_A_CDC_MBHC_FIR_B1_CFG__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_FIR_B2_CFG (0x3C2)
+#define WCD9XXX_A_CDC_MBHC_FIR_B2_CFG__POR (0x06)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL (0x3C3)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL__POR (0x03)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL (0x3C4)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL__POR (0x09)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL (0x3C5)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL__POR (0x1E)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL (0x3C6)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL__POR (0x45)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL (0x3C7)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL__POR (0x04)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL (0x3C8)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL__POR (0x78)
+#define WCD9XXX_A_CDC_MBHC_B1_STATUS (0x3C9)
+#define WCD9XXX_A_CDC_MBHC_B1_STATUS__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_B2_STATUS (0x3CA)
+#define WCD9XXX_A_CDC_MBHC_B2_STATUS__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_B3_STATUS (0x3CB)
+#define WCD9XXX_A_CDC_MBHC_B3_STATUS__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_B4_STATUS (0x3CC)
+#define WCD9XXX_A_CDC_MBHC_B4_STATUS__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_B5_STATUS (0x3CD)
+#define WCD9XXX_A_CDC_MBHC_B5_STATUS__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_B1_CTL (0x3CE)
+#define WCD9XXX_A_CDC_MBHC_B1_CTL__POR (0xC0)
+#define WCD9XXX_A_CDC_MBHC_B2_CTL (0x3CF)
+#define WCD9XXX_A_CDC_MBHC_B2_CTL__POR (0x5D)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL (0x3D0)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL (0x3D1)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL (0x3D2)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL (0x3D3)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL (0x3D4)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL (0x3D5)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B7_CTL (0x3D6)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B7_CTL__POR (0xFF)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B8_CTL (0x3D7)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B8_CTL__POR (0x07)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL (0x3D8)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL__POR (0xFF)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL (0x3D9)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL__POR (0x7F)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL (0x3DA)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL (0x3DB)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL__POR (0x80)
+#define WCD9XXX_A_CDC_MBHC_CLK_CTL (0x3DC)
+#define WCD9XXX_A_CDC_MBHC_CLK_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_INT_CTL (0x3DD)
+#define WCD9XXX_A_CDC_MBHC_INT_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_DEBUG_CTL (0x3DE)
+#define WCD9XXX_A_CDC_MBHC_DEBUG_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_SPARE (0x3DF)
+#define WCD9XXX_A_CDC_MBHC_SPARE__POR (0x00)
+#define WCD9XXX_A_MBHC_SCALING_MUX_1 (0x14E)
+#define WCD9XXX_A_MBHC_SCALING_MUX_1__POR (0x00)
+#define WCD9XXX_A_RX_HPH_OCP_CTL (0x1AA)
+#define WCD9XXX_A_RX_HPH_OCP_CTL__POR (0x68)
+#define WCD9XXX_A_MICB_1_CTL (0x12B)
+#define WCD9XXX_A_MICB_1_CTL__POR (0x16)
+#define WCD9XXX_A_MICB_1_INT_RBIAS (0x12C)
+#define WCD9XXX_A_MICB_1_INT_RBIAS__POR (0x24)
+#define WCD9XXX_A_MICB_1_MBHC (0x12D)
+#define WCD9XXX_A_MICB_1_MBHC__POR (0x01)
+#define WCD9XXX_A_MICB_CFILT_2_CTL (0x12E)
+#define WCD9XXX_A_MICB_CFILT_2_CTL__POR (0x40)
+#define WCD9XXX_A_MICB_CFILT_2_VAL (0x12F)
+#define WCD9XXX_A_MICB_CFILT_2_VAL__POR (0x80)
+#define WCD9XXX_A_MICB_CFILT_2_PRECHRG (0x130)
+#define WCD9XXX_A_MICB_CFILT_2_PRECHRG__POR (0x38)
+#define WCD9XXX_A_MICB_2_CTL (0x131)
+#define WCD9XXX_A_MICB_2_CTL__POR (0x16)
+#define WCD9XXX_A_MICB_2_INT_RBIAS (0x132)
+#define WCD9XXX_A_MICB_2_INT_RBIAS__POR (0x24)
+#define WCD9XXX_A_MICB_2_MBHC (0x133)
+#define WCD9XXX_A_MICB_2_MBHC__POR (0x02)
+#define WCD9XXX_A_MICB_CFILT_3_CTL (0x134)
+#define WCD9XXX_A_MICB_CFILT_3_CTL__POR (0x40)
+#define WCD9XXX_A_MICB_CFILT_3_VAL (0x135)
+#define WCD9XXX_A_MICB_CFILT_3_VAL__POR (0x80)
+#define WCD9XXX_A_MICB_CFILT_3_PRECHRG (0x136)
+#define WCD9XXX_A_MICB_CFILT_3_PRECHRG__POR (0x38)
+#define WCD9XXX_A_MICB_3_CTL (0x137)
+#define WCD9XXX_A_MICB_3_CTL__POR (0x16)
+#define WCD9XXX_A_MICB_3_INT_RBIAS (0x138)
+#define WCD9XXX_A_MICB_3_INT_RBIAS__POR (0x24)
+#define WCD9XXX_A_MICB_3_MBHC (0x139)
+#define WCD9XXX_A_MICB_3_MBHC__POR (0x00)
+#define WCD9XXX_A_MICB_4_CTL (0x13D)
+#define WCD9XXX_A_MICB_4_CTL__POR (0x16)
+#define WCD9XXX_A_MICB_4_INT_RBIAS (0x13E)
+#define WCD9XXX_A_MICB_4_INT_RBIAS__POR (0x24)
+#define WCD9XXX_A_MICB_4_MBHC (0x13F)
+#define WCD9XXX_A_MICB_4_MBHC__POR (0x01)
+#define WCD9XXX_A_MICB_CFILT_1_VAL (0x129)
+#define WCD9XXX_A_MICB_CFILT_1_VAL__POR (0x80)
+#define WCD9XXX_A_MBHC_HPH (0x1FE)
+#define WCD9XXX_A_MBHC_HPH__POR (0x44)
+#define WCD9XXX_A_RX_HPH_CNP_WG_TIME (0x1AD)
+#define WCD9XXX_A_RX_HPH_CNP_WG_TIME__POR (0x2A)
+#define WCD9XXX_A_RX_HPH_R_DAC_CTL (0x1B7)
+#define WCD9XXX_A_RX_HPH_R_DAC_CTL__POR (0x00)
+#define WCD9XXX_A_RX_HPH_L_DAC_CTL (0x1B1)
+#define WCD9XXX_A_RX_HPH_L_DAC_CTL__POR (0x00)
+#define WCD9XXX_A_TX_7_MBHC_EN (0x171)
+#define WCD9XXX_A_TX_7_MBHC_EN__POR (0x0C)
+#define WCD9XXX_A_PIN_CTL_OE0 (0x010)
+#define WCD9XXX_A_PIN_CTL_OE0__POR (0x00)
+#define WCD9XXX_A_PIN_CTL_OE1 (0x011)
+#define WCD9XXX_A_PIN_CTL_OE1__POR (0x00)
+#define WCD9XXX_A_MICB_CFILT_1_CTL (0x128)
+#define WCD9XXX_A_LDO_H_MODE_1 (0x110)
+#define WCD9XXX_A_LDO_H_MODE_1__POR (0x65)
+#define WCD9XXX_A_MICB_CFILT_1_CTL__POR (0x40)
+#define WCD9XXX_A_TX_7_MBHC_TEST_CTL (0x174)
+#define WCD9XXX_A_TX_7_MBHC_TEST_CTL__POR (0x38)
+#define WCD9XXX_A_MBHC_SCALING_MUX_2 (0x14F)
+#define WCD9XXX_A_MBHC_SCALING_MUX_2__POR (0x80)
+#define WCD9XXX_A_TX_COM_BIAS (0x14C)
+#define WCD9XXX_A_TX_COM_BIAS__POR (0xF0)
+
+#define WCD9XXX_A_MBHC_INSERT_DETECT (0x14A) /* TAIKO and later */
+#define WCD9XXX_A_MBHC_INSERT_DETECT__POR (0x00)
+#define WCD9XXX_A_MBHC_INSERT_DET_STATUS (0x14B) /* TAIKO and later */
+#define WCD9XXX_A_MBHC_INSERT_DET_STATUS__POR (0x00)
+
#endif
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 257bcc0..477733c 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -248,6 +248,7 @@
struct delayed_work dw;
unsigned int host_suspend_tout_ms;
unsigned int delay_ms;
+ unsigned int min_sectors_to_queue_delayed_work;
/*
* A default time for checking the need for non urgent BKOPS once mmcqd
* is idle.
diff --git a/include/linux/msm_ion.h b/include/linux/msm_ion.h
index 21000f9..c1ea490 100644
--- a/include/linux/msm_ion.h
+++ b/include/linux/msm_ion.h
@@ -102,6 +102,12 @@
*/
#define ION_IOMMU_UNMAP_DELAYED 1
+/*
+ * This flag allows clients to defer unsecuring a buffer until the buffer
+ * is actually freed.
+ */
+#define ION_UNSECURE_DELAYED 1
+
/**
* struct ion_cp_heap_pdata - defines a content protection heap in the given
* platform
@@ -216,6 +222,26 @@
* Returns 0 on success
*/
int msm_ion_unsecure_heap_2_0(int heap_id, enum cp_mem_usage usage);
+
+/**
+ * msm_ion_secure_buffer - secure an individual buffer
+ *
+ * @client - client who has access to the buffer
+ * @handle - buffer to secure
+ * @usage - usage hint to TZ
+ * @flags - flags for the securing
+ */
+int msm_ion_secure_buffer(struct ion_client *client, struct ion_handle *handle,
+ enum cp_mem_usage usage, int flags);
+
+/**
+ * msm_ion_unsecure_buffer - unsecure an individual buffer
+ *
+ * @client - client who has access to the buffer
+ * @handle - buffer to secure
+ */
+int msm_ion_unsecure_buffer(struct ion_client *client,
+ struct ion_handle *handle);
#else
static inline int msm_ion_secure_heap(int heap_id)
{
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 03390b1..730c7b2 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -218,6 +218,7 @@
extern int power_supply_set_battery_charged(struct power_supply *psy);
extern int power_supply_set_current_limit(struct power_supply *psy, int limit);
extern int power_supply_set_online(struct power_supply *psy, bool enable);
+extern int power_supply_set_present(struct power_supply *psy, bool enable);
extern int power_supply_set_scope(struct power_supply *psy, int scope);
extern int power_supply_set_charge_type(struct power_supply *psy, int type);
extern int power_supply_set_supply_type(struct power_supply *psy,
@@ -241,6 +242,9 @@
static inline int power_supply_set_online(struct power_supply *psy,
bool enable)
{ return -ENOSYS; }
+static inline int power_supply_set_present(struct power_supply *psy,
+ bool enable)
+ { return -ENOSYS; }
static inline int power_supply_set_scope(struct power_supply *psy,
int scope)
{ return -ENOSYS; }
diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h
index a54b825..c3ea237 100644
--- a/include/linux/usb/audio.h
+++ b/include/linux/usb/audio.h
@@ -175,6 +175,7 @@
__u8 baInterfaceNr[n]; \
} __attribute__ ((packed))
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
/* 4.3.2.1 Input Terminal Descriptor */
struct uac_input_terminal_descriptor {
__u8 bLength; /* in bytes: 12 */
@@ -454,6 +455,7 @@
__u8 tSamFreq[n][3]; \
} __attribute__ ((packed))
+DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
#define UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(n) (8 + (n * 3))
struct uac_format_type_i_ext_descriptor {
diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h
index ddb2fd0..61ebe0a 100644
--- a/include/linux/usb/f_accessory.h
+++ b/include/linux/usb/f_accessory.h
@@ -44,7 +44,7 @@
* index: 0
* data version number (16 bits little endian)
* 1 for original accessory support
- * 2 adds device to host audio support
+ * 2 adds HID and device to host audio support
*/
#define ACCESSORY_GET_PROTOCOL 51
@@ -72,6 +72,54 @@
*/
#define ACCESSORY_START 53
+/* Control request for registering a HID device.
+ * Upon registering, a unique ID is sent by the accessory in the
+ * value parameter. This ID will be used for future commands for
+ * the device
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_REGISTER_HID_DEVICE
+ * value: Accessory assigned ID for the HID device
+ * index: total length of the HID report descriptor
+ * data none
+ */
+#define ACCESSORY_REGISTER_HID 54
+
+/* Control request for unregistering a HID device.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_REGISTER_HID
+ * value: Accessory assigned ID for the HID device
+ * index: 0
+ * data none
+ */
+#define ACCESSORY_UNREGISTER_HID 55
+
+/* Control request for sending the HID report descriptor.
+ * If the HID descriptor is longer than the endpoint zero max packet size,
+ * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC
+ * commands. The data for the descriptor must be sent sequentially
+ * if multiple packets are needed.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SET_HID_REPORT_DESC
+ * value: Accessory assigned ID for the HID device
+ * index: offset of data in descriptor
+ * (needed when HID descriptor is too big for one packet)
+ * data the HID report descriptor
+ */
+#define ACCESSORY_SET_HID_REPORT_DESC 56
+
+/* Control request for sending HID events.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SEND_HID_EVENT
+ * value: Accessory assigned ID for the HID device
+ * index: 0
+ * data the HID report for the event
+ */
+#define ACCESSORY_SEND_HID_EVENT 57
+
/* Control request for setting the audio mode.
*
* requestType: USB_DIR_OUT | USB_TYPE_VENDOR
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index d45889c..a998ac2 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -25,7 +25,7 @@
#include <linux/wakelock.h>
#include <linux/pm_qos.h>
#include <linux/hrtimer.h>
-
+#include <linux/power_supply.h>
/*
* The following are bit fields describing the usb_request.udc_priv word.
* These bit fields are set by function drivers that wish to queue
@@ -192,6 +192,8 @@
* @mhl_enable: indicates MHL connector or not.
* @disable_reset_on_disconnect: perform USB PHY and LINK reset
* on USB cable disconnection.
+ * @pnoc_errata_fix: workaround needed for PNOC hardware bug that
+ * affects USB performance.
* @enable_lpm_on_suspend: Enable the USB core to go into Low
* Power Mode, when USB bus is suspended but cable
* is connected.
@@ -213,6 +215,7 @@
unsigned int mpm_otgsessvld_int;
bool mhl_enable;
bool disable_reset_on_disconnect;
+ bool pnoc_errata_fix;
bool enable_lpm_on_dev_suspend;
bool core_clk_always_on_workaround;
struct msm_bus_scale_pdata *bus_scale_table;
@@ -380,6 +383,10 @@
u8 active_tmout;
struct hrtimer timer;
enum usb_vdd_type vdd_type;
+ struct power_supply usb_psy;
+ unsigned int online;
+ unsigned int host_mode;
+ unsigned int current_max;
};
struct msm_hsic_host_platform_data {
diff --git a/include/media/vcap_v4l2.h b/include/media/vcap_v4l2.h
index dd39124..3f0c862 100644
--- a/include/media/vcap_v4l2.h
+++ b/include/media/vcap_v4l2.h
@@ -38,6 +38,12 @@
} while (0)
#define VCAP_USEC (1000000)
+
+#define VCAP_STRIDE_ALIGN 0x10
+#define VCAP_STRIDE_CALC(x) (((x / VCAP_STRIDE_ALIGN) + \
+ (!(!(x % VCAP_STRIDE_ALIGN)))) * \
+ VCAP_STRIDE_ALIGN)
+
#define VCAP_BASE (dev->vcapbase)
#define VCAP_OFFSET(off) (VCAP_BASE + off)
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index d902881..07179e9 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -1627,7 +1627,7 @@
* Supported values: #AFE_API_VERSION_HDMI_CONFIG
*/
-u16 dataype;
+u16 datatype;
/* data type
* Supported values:
* - #LINEAR_PCM_DATA
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
index afbe7e0..8c35ada 100644
--- a/include/sound/apr_audio.h
+++ b/include/sound/apr_audio.h
@@ -1162,6 +1162,7 @@
#define EAC3_DECODER 0x00010C3C
#define DTS 0x00010D88
#define DTS_LBR 0x00010DBB
+#define MP2 0x00010DBE
#define ATRAC 0x00010D89
#define MAT 0x00010D8A
#define G711_ALAW_FS 0x00010BF7
diff --git a/include/sound/compress_params.h b/include/sound/compress_params.h
index 54af7d6a..02d69ea 100644
--- a/include/sound/compress_params.h
+++ b/include/sound/compress_params.h
@@ -87,7 +87,7 @@
#define SND_AUDIOCODEC_DTS_LBR ((__u32) 0x00000013)
#define SND_AUDIOCODEC_DTS_TRANSCODE_LOOPBACK ((__u32) 0x00000014)
#define SND_AUDIOCODEC_PASS_THROUGH ((__u32) 0x00000015)
-
+#define SND_AUDIOCODEC_MP2 ((__u32) 0x00000016)
/*
* Profile and modes are listed with bit masks. This allows for a
* more compact representation of fields that will not evolve
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 6cb456e..485e1c5 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -80,6 +80,7 @@
unsigned long offset);
int (*mmap)(struct snd_pcm_substream *substream, struct vm_area_struct *vma);
int (*ack)(struct snd_pcm_substream *substream);
+ int (*restart)(struct snd_pcm_substream *substream);
};
/*
@@ -110,6 +111,12 @@
#define SNDRV_PCM_POS_XRUN ((snd_pcm_uframes_t)-1)
+#define SNDRV_DMA_MODE (0)
+#define SNDRV_NON_DMA_MODE (1 << 0)
+#define SNDRV_RENDER_STOPPED (1 << 1)
+#define SNDRV_RENDER_RUNNING (1 << 2)
+
+
/* If you change this don't forget to change rates[] table in pcm_native.c */
#define SNDRV_PCM_RATE_5512 (1<<0) /* 5512Hz */
#define SNDRV_PCM_RATE_8000 (1<<1) /* 8000Hz */
@@ -299,6 +306,7 @@
unsigned int rate_num;
unsigned int rate_den;
unsigned int no_period_wakeup: 1;
+ unsigned int render_flag;
/* -- SW params -- */
int tstamp_mode; /* mmap timestamp is updated */
diff --git a/include/sound/q6asm.h b/include/sound/q6asm.h
index 9cc0de4..b0d74ba 100644
--- a/include/sound/q6asm.h
+++ b/include/sound/q6asm.h
@@ -50,6 +50,7 @@
#define FORMAT_AAC 0x0018
#define FORMAT_DTS_LBR 0x0019
#define FORMAT_PASS_THROUGH 0x0020
+#define FORMAT_MP2 0x0021
#define ENCDEC_SBCBITRATE 0x0001
#define ENCDEC_IMMEDIATE_DECODE 0x0002
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index b11118f..4636247 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -2477,6 +2477,7 @@
volatile struct snd_pcm_mmap_status *status;
volatile struct snd_pcm_mmap_control *control;
int err;
+ snd_pcm_uframes_t hw_avail;
memset(&sync_ptr, 0, sizeof(sync_ptr));
if (get_user(sync_ptr.flags, (unsigned __user *)&(_sync_ptr->flags)))
@@ -2499,6 +2500,16 @@
control->avail_min = sync_ptr.c.control.avail_min;
else
sync_ptr.c.control.avail_min = control->avail_min;
+
+ if (runtime->render_flag & SNDRV_NON_DMA_MODE) {
+ hw_avail = snd_pcm_playback_hw_avail(runtime);
+ if ((hw_avail >= runtime->start_threshold)
+ && (runtime->render_flag &
+ SNDRV_RENDER_STOPPED)) {
+ if (substream->ops->restart)
+ substream->ops->restart(substream);
+ }
+ }
sync_ptr.s.status.state = status->state;
sync_ptr.s.status.hw_ptr = status->hw_ptr;
sync_ptr.s.status.tstamp = status->tstamp;
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 5aabfee..1c9d86b 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -51,7 +51,7 @@
snd-soc-wcd9304-objs := wcd9304.o wcd9304-tables.o
snd-soc-wcd9310-objs := wcd9310.o wcd9310-tables.o
snd-soc-cs8427-objs := cs8427.o
-snd-soc-wcd9320-objs := wcd9320.o wcd9320-tables.o
+snd-soc-wcd9320-objs := wcd9xxx-resmgr.o wcd9320.o wcd9320-tables.o wcd9xxx-mbhc.o
snd-soc-wl1273-objs := wl1273.o
snd-soc-wm1250-ev1-objs := wm1250-ev1.o
snd-soc-wm2000-objs := wm2000.o
diff --git a/sound/soc/codecs/wcd9304.c b/sound/soc/codecs/wcd9304.c
index e9e950f..9f02134 100644
--- a/sound/soc/codecs/wcd9304.c
+++ b/sound/soc/codecs/wcd9304.c
@@ -45,31 +45,42 @@
#define NUM_DECIMATORS 4
#define NUM_INTERPOLATORS 3
#define BITS_PER_REG 8
+
+enum {
+ AIF1_PB = 0,
+ AIF1_CAP,
+ NUM_CODEC_DAIS,
+};
+
+struct wcd9xxx_ch sitar_rx_chs[SITAR_RX_MAX] = {
+ WCD9XXX_CH(10, 0),
+ WCD9XXX_CH(11, 1),
+ WCD9XXX_CH(12, 2),
+ WCD9XXX_CH(13, 3),
+ WCD9XXX_CH(14, 4)
+};
+
+struct wcd9xxx_ch sitar_tx_chs[SITAR_TX_MAX] = {
+ WCD9XXX_CH(0, 0),
+ WCD9XXX_CH(1, 1),
+ WCD9XXX_CH(2, 2),
+ WCD9XXX_CH(3, 3),
+ WCD9XXX_CH(4, 4),
+};
+
#define SITAR_CFILT_FAST_MODE 0x00
#define SITAR_CFILT_SLOW_MODE 0x40
#define MBHC_FW_READ_ATTEMPTS 15
#define MBHC_FW_READ_TIMEOUT 2000000
+#define SLIM_CLOSE_TIMEOUT 1000
+
#define SITAR_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | SND_JACK_OC_HPHR)
#define SITAR_I2S_MASTER_MODE_MASK 0x08
#define SITAR_OCP_ATTEMPT 1
-#define AIF1_PB 1
-#define AIF1_CAP 2
-#define NUM_CODEC_DAIS 2
-#define SLIM_CLOSE_TIMEOUT 1000
-
-struct sitar_codec_dai_data {
- u32 rate;
- u32 *ch_num;
- u32 ch_act;
- u32 ch_tot;
- u32 ch_mask;
- wait_queue_head_t dai_wait;
-};
-
#define SITAR_MCLK_RATE_12288KHZ 12288000
#define SITAR_MCLK_RATE_9600KHZ 9600000
@@ -178,6 +189,11 @@
MBHC_STATE_RELEASE,
};
+static const u32 vport_check_table[NUM_CODEC_DAIS] = {
+ 0, /* AIF1_PB */
+ 0, /* AIF1_CAP */
+};
+
struct sitar_priv {
struct snd_soc_codec *codec;
u32 mclk_freq;
@@ -240,7 +256,7 @@
const struct firmware *mbhc_fw;
/* num of slim ports required */
- struct sitar_codec_dai_data dai[NUM_CODEC_DAIS];
+ struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS];
/* Currently, only used for mbhc purpose, to protect
* concurrent execution of mbhc threaded irq handlers and
@@ -935,6 +951,201 @@
SOC_DAPM_SINGLE("Switch", SITAR_A_RX_EAR_EN, 5, 1, 0),
};
+static int slim_tx_vport_validation(u32 dai_id, u32 port_id,
+ struct sitar_priv *sitar_p)
+{
+ struct wcd9xxx_ch *ch;
+ int ret = 0;
+ int index = 0;
+ u32 vtable = vport_check_table[dai_id];
+ pr_debug("%s: dai_id %u vtable 0x%x port_id %u\n", __func__,
+ dai_id, vtable, port_id);
+ while (vtable) {
+ if (vtable & 1) {
+ list_for_each_entry(ch,
+ &sitar_p->dai[index].wcd9xxx_ch_list,
+ list) {
+ pr_debug("%s: index %u ch->port%u vtable 0x%x\n",
+ __func__, index, ch->port, vtable);
+ if (ch->port == port_id) {
+ pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
+ __func__, port_id + 1,
+ (index + 1)/2);
+ ret = -EINVAL;
+ break;
+ }
+ }
+ }
+ if (ret)
+ break;
+ index++;
+ vtable = vtable >> 1;
+ }
+ return ret;
+}
+
+static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+ ucontrol->value.integer.value[0] = widget->value;
+ return 0;
+}
+
+static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
+ struct sitar_priv *sitar_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+ struct soc_multi_mixer_control *mixer =
+ ((struct soc_multi_mixer_control *)kcontrol->private_value);
+ u32 dai_id = widget->shift;
+ u32 port_id = mixer->shift;
+ u32 enable = ucontrol->value.integer.value[0];
+
+ mutex_lock(&codec->mutex);
+
+ if (sitar_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ if (dai_id != AIF1_CAP) {
+ dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+ __func__);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ }
+
+ switch (dai_id) {
+ case AIF1_CAP:
+ if (enable && !(widget->value & 1 << port_id)) {
+ if (slim_tx_vport_validation(dai_id,
+ port_id, sitar_p)) {
+ pr_info("%s: TX%u is used by other virtual port\n",
+ __func__, port_id + 1);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ widget->value |= 1 << port_id;
+ list_add_tail(&core->tx_chs[port_id].list,
+ &sitar_p->dai[dai_id].wcd9xxx_ch_list);
+ } else if (!enable && (widget->value & 1 << port_id)) {
+ widget->value &= ~(1<<port_id);
+ list_del_init(&core->tx_chs[port_id].list);
+ } else {
+ if (enable)
+ pr_info("%s: TX%u port is used by this virtual port\n",
+ __func__, port_id + 1);
+ else
+ pr_info("%s: TX%u port is not used by this virtual port\n",
+ __func__, port_id + 1);
+ /* avoid update power function */
+ mutex_unlock(&codec->mutex);
+ return 0;
+ }
+ break;
+ default:
+ pr_err("Unknown AIF %d\n", dai_id);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__,
+ widget->name, widget->sname, widget->value, widget->shift);
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, enable);
+ mutex_unlock(&codec->mutex);
+ return 0;
+}
+
+static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+ ucontrol->value.enumerated.item[0] = widget->value;
+ return 0;
+}
+
+static const char * const slim_rx_mux_text[] = {
+ "ZERO", "AIF1_PB"
+};
+
+static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
+ struct sitar_priv *sitar_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ u32 port_id = widget->shift;
+
+ widget->value = ucontrol->value.enumerated.item[0];
+
+ mutex_lock(&codec->mutex);
+
+ if (sitar_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ if (widget->value > 1) {
+ dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+ __func__);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ }
+
+ switch (widget->value) {
+ case 0:
+ list_del_init(&core->rx_chs[port_id].list);
+ break;
+ case 1:
+ list_add_tail(&core->rx_chs[port_id].list,
+ &sitar_p->dai[AIF1_PB].wcd9xxx_ch_list);
+ break;
+ default:
+ pr_err("Unknown AIF %d\n", widget->value);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ snd_soc_dapm_mux_update_power(widget, kcontrol, 1, widget->value, e);
+ mutex_unlock(&codec->mutex);
+ return 0;
+}
+
+static const struct soc_enum slim_rx_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text);
+
+static const struct snd_kcontrol_new sitar_aif_pb_mux[SITAR_RX_MAX] = {
+ SOC_DAPM_ENUM_EXT("SLIM RX1 MUX", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX2 MUX", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX3 MUX", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX4 MUX", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX5 MUX", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put)
+};
+
+static const struct snd_kcontrol_new sitar_aif_cap_mixer[SITAR_TX_MAX] = {
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, SITAR_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, SITAR_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, SITAR_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, SITAR_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, SITAR_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+
static void sitar_codec_enable_adc_block(struct snd_soc_codec *codec,
int enable)
{
@@ -1713,7 +1924,7 @@
/* reset retry counter as PA is turned off signifying
* start of new OCP detection session
*/
- if (SITAR_IRQ_HPH_PA_OCPL_FAULT)
+ if (WCD9XXX_IRQ_HPH_PA_OCPL_FAULT)
sitar->hphlocp_cnt = 0;
else
sitar->hphrocp_cnt = 0;
@@ -1725,14 +1936,16 @@
{
struct sitar_priv *sitar = container_of(work, struct sitar_priv,
hphlocp_work);
- hphocp_off_report(sitar, SND_JACK_OC_HPHL, SITAR_IRQ_HPH_PA_OCPL_FAULT);
+ hphocp_off_report(sitar, SND_JACK_OC_HPHL,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
}
static void hphrocp_off_report(struct work_struct *work)
{
struct sitar_priv *sitar = container_of(work, struct sitar_priv,
hphrocp_work);
- hphocp_off_report(sitar, SND_JACK_OC_HPHR, SITAR_IRQ_HPH_PA_OCPR_FAULT);
+ hphocp_off_report(sitar, SND_JACK_OC_HPHR,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
}
static int sitar_hph_pa_event(struct snd_soc_dapm_widget *w,
@@ -1878,15 +2091,27 @@
SND_SOC_DAPM_MIXER("DAC1", SITAR_A_RX_EAR_EN, 6, 0, dac1_switch,
ARRAY_SIZE(dac1_switch)),
SND_SOC_DAPM_SUPPLY("EAR DRIVER", SITAR_A_RX_EAR_EN, 3, 0, NULL, 0),
- SND_SOC_DAPM_AIF_IN_E("SLIM RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
- 0, sitar_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_AIF_IN_E("SLIM RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
- 0, sitar_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_AIF_IN("SLIM RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_IN("SLIM RX4", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_IN("SLIM RX5", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
+ AIF1_PB, 0, sitar_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, SITAR_RX1, 0,
+ &sitar_aif_pb_mux[SITAR_RX1]),
+ SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, SITAR_RX2, 0,
+ &sitar_aif_pb_mux[SITAR_RX2]),
+ SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, SITAR_RX3, 0,
+ &sitar_aif_pb_mux[SITAR_RX3]),
+ SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, SITAR_RX4, 0,
+ &sitar_aif_pb_mux[SITAR_RX4]),
+ SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, SITAR_RX5, 0,
+ &sitar_aif_pb_mux[SITAR_RX5]),
+
+ SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
/* Headphone */
SND_SOC_DAPM_OUTPUT("HEADPHONE"),
@@ -2024,26 +2249,23 @@
SND_SOC_DAPM_MUX("ANC1 FB MUX", SND_SOC_NOPM, 0, 0, &anc1_fb_mux),
- SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, 0, 0, &sb_tx1_mux),
- SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, 0, 0, &sb_tx2_mux),
- SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, 0, 0, &sb_tx3_mux),
- SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, 0, 0, &sb_tx4_mux),
- SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, 0, 0, &sb_tx5_mux),
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX1", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
- 0, sitar_codec_enable_slimtx,
+ SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
+ AIF1_CAP, 0, sitar_codec_enable_slimtx,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX2", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
- 0, sitar_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
+ sitar_aif_cap_mixer, ARRAY_SIZE(sitar_aif_cap_mixer)),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX3", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
- 0, sitar_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX4", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
- 0, sitar_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, SITAR_TX1, 0,
+ &sb_tx1_mux),
+ SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, SITAR_TX2, 0,
+ &sb_tx2_mux),
+ SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, SITAR_TX3, 0,
+ &sb_tx3_mux),
+ SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, SITAR_TX3, 0,
+ &sb_tx4_mux),
+ SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, SITAR_TX3, 0,
+ &sb_tx5_mux),
SND_SOC_DAPM_AIF_OUT_E("SLIM TX5", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
0, sitar_codec_enable_slimtx,
@@ -2084,6 +2306,18 @@
{"SLIM TX3", NULL, "TX_I2S_CLK"},
{"SLIM TX4", NULL, "TX_I2S_CLK"},
};
+#define SLIM_MIXER(x) (\
+ {x, "SLIM TX1", "SLIM TX1 MUX"}, \
+ {x, "SLIM TX2", "SLIM TX2 MUX"}, \
+ {x, "SLIM TX3", "SLIM TX3 MUX"}, \
+ {x, "SLIM TX4", "SLIM TX4 MUX"})
+
+
+#define SLIM_MUX(x, y) (\
+ {"SLIM RX1 MUX", x, y}, \
+ {"SLIM RX2 MUX", x, y}, \
+ {"SLIM RX3 MUX", x, y}, \
+ {"SLIM RX4 MUX", x, y})
static const struct snd_soc_dapm_route audio_map[] = {
/* Earpiece (RX MIX1) */
@@ -2162,6 +2396,23 @@
{"RX3 MIX1", NULL, "ANC"},
/* SLIMBUS Connections */
+ {"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+
+ /* SLIM_MIXER("AIF1_CAP Mixer"),*/
+ {"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+ /* SLIM_MUX("AIF1_PB", "AIF1 PB"), */
+ {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"},
+
+ {"SLIM RX1", NULL, "SLIM RX1 MUX"},
+ {"SLIM RX2", NULL, "SLIM RX2 MUX"},
+ {"SLIM RX3", NULL, "SLIM RX3 MUX"},
+ {"SLIM RX4", NULL, "SLIM RX4 MUX"},
/* Slimbus port 5 is non functional in Sitar 1.0 */
{"RX1 MIX1 INP1", "RX1", "SLIM RX1"},
@@ -2203,12 +2454,6 @@
/* TX */
- {"SLIM TX1", NULL, "SLIM TX1 MUX"},
- {"SLIM TX2", NULL, "SLIM TX2 MUX"},
- {"SLIM TX3", NULL, "SLIM TX3 MUX"},
- {"SLIM TX4", NULL, "SLIM TX4 MUX"},
- {"SLIM TX5", NULL, "SLIM TX5 MUX"},
-
{"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX2 MUX", "DEC2", "DEC2 MUX"},
{"SLIM TX3 MUX", "DEC3", "DEC3 MUX"},
@@ -2319,6 +2564,9 @@
{
int ret;
+ if (reg == SND_SOC_NOPM)
+ return 0;
+
BUG_ON(reg > SITAR_MAX_REGISTER);
if (!sitar_volatile(codec, reg)) {
@@ -2336,6 +2584,9 @@
unsigned int val;
int ret;
+ if (reg == SND_SOC_NOPM)
+ return 0;
+
BUG_ON(reg > SITAR_MAX_REGISTER);
if (!sitar_volatile(codec, reg) && sitar_readable(codec, reg) &&
@@ -2583,11 +2834,11 @@
return;
if (dai->id <= NUM_CODEC_DAIS) {
- if (sitar->dai[dai->id-1].ch_mask) {
+ if (sitar->dai[dai->id].ch_mask) {
active = 1;
pr_debug("%s(): Codec DAI: chmask[%d] = 0x%x\n",
- __func__, dai->id-1,
- sitar->dai[dai->id-1].ch_mask);
+ __func__, dai->id,
+ sitar->dai[dai->id].ch_mask);
}
}
@@ -2701,38 +2952,16 @@
{
struct sitar_priv *sitar = snd_soc_codec_get_drvdata(dai->codec);
- u32 i = 0;
+ struct wcd9xxx *core = dev_get_drvdata(dai->codec->dev->parent);
if (!tx_slot && !rx_slot) {
pr_err("%s: Invalid\n", __func__);
return -EINVAL;
}
pr_debug("%s: DAI-ID %x %d %d\n", __func__, dai->id, tx_num, rx_num);
- if (dai->id == AIF1_PB) {
- for (i = 0; i < rx_num; i++) {
- sitar->dai[dai->id - 1].ch_num[i] = rx_slot[i];
- sitar->dai[dai->id - 1].ch_act = 0;
- sitar->dai[dai->id - 1].ch_tot = rx_num;
- }
- } else if (dai->id == AIF1_CAP) {
- sitar->dai[dai->id - 1].ch_tot = tx_num;
- /* If all channels are already active,
- * Do not reset ch_act flag
- */
- if ((sitar->dai[dai->id - 1].ch_tot != 0)
- && (sitar->dai[dai->id - 1].ch_act ==
- sitar->dai[dai->id - 1].ch_tot)) {
- pr_info("%s: ch_act = %d, ch_tot = %d\n", __func__,
- sitar->dai[dai->id - 1].ch_act,
- sitar->dai[dai->id - 1].ch_tot);
-
- return 0;
- }
- sitar->dai[dai->id - 1].ch_act = 0;
-
- for (i = 0; i < tx_num; i++)
- sitar->dai[dai->id - 1].ch_num[i] = tx_slot[i];
- }
+ if (sitar->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+ wcd9xxx_init_slimslave(core, core->slim->laddr,
+ tx_num, tx_slot, rx_num, rx_slot);
return 0;
}
@@ -2741,33 +2970,38 @@
unsigned int *rx_num, unsigned int *rx_slot)
{
- struct wcd9xxx *sitar = dev_get_drvdata(dai->codec->control_data);
+ struct sitar_priv *sitar_p = snd_soc_codec_get_drvdata(dai->codec);
+ u32 i = 0;
+ struct wcd9xxx_ch *ch;
- u32 cnt = 0;
- u32 tx_ch[SLIM_MAX_TX_PORTS];
- u32 rx_ch[SLIM_MAX_RX_PORTS];
-
- if (!rx_slot && !tx_slot) {
- pr_err("%s: Invalid\n", __func__);
- return -EINVAL;
- }
- pr_debug("%s: DAI-ID %x\n", __func__, dai->id);
- /* for virtual port, codec driver needs to do
- * housekeeping, for now should be ok
- */
- wcd9xxx_get_channel(sitar, rx_ch, tx_ch);
- if (dai->id == AIF1_PB) {
- *rx_num = sitar_dai[dai->id - 1].playback.channels_max;
- while (cnt < *rx_num) {
- rx_slot[cnt] = rx_ch[cnt];
- cnt++;
+ switch (dai->id) {
+ case AIF1_PB:
+ if (!rx_slot || !rx_num) {
+ pr_err("%s: Invalid rx_slot 0x%x or rx_num 0x%x\n",
+ __func__, (u32) rx_slot, (u32) rx_num);
+ return -EINVAL;
}
- } else if (dai->id == AIF1_CAP) {
- *tx_num = sitar_dai[dai->id - 1].capture.channels_max;
- tx_slot[0] = tx_ch[cnt];
- tx_slot[1] = tx_ch[4 + cnt];
- tx_slot[2] = tx_ch[2 + cnt];
- tx_slot[3] = tx_ch[3 + cnt];
+ list_for_each_entry(ch, &sitar_p->dai[dai->id].wcd9xxx_ch_list,
+ list) {
+ rx_slot[i++] = ch->ch_num;
+ }
+ *rx_num = i;
+ break;
+ case AIF1_CAP:
+ if (!tx_slot || !tx_num) {
+ pr_err("%s: Invalid tx_slot 0x%x or tx_num 0x%x\n",
+ __func__, (u32) tx_slot, (u32) tx_num);
+ return -EINVAL;
+ }
+ list_for_each_entry(ch, &sitar_p->dai[dai->id].wcd9xxx_ch_list,
+ list) {
+ tx_slot[i++] = ch->ch_num;
+ }
+ *tx_num = i;
+ break;
+ default:
+ pr_err("%s: Invalid dai %d", __func__, dai->id);
+ return -EINVAL;
}
return 0;
}
@@ -2803,7 +3037,7 @@
break;
default:
pr_err("%s: Invalid sampling rate %d\n", __func__,
- params_rate(params));
+ params_rate(params));
return -EINVAL;
}
@@ -2840,13 +3074,14 @@
0x20, 0x00);
break;
default:
- pr_err("invalid format\n");
- break;
+ pr_err("%s: Unsupport format %d\n", __func__,
+ params_format(params));
+ return -EINVAL;
}
snd_soc_update_bits(codec, SITAR_A_CDC_CLK_TX_I2S_CTL,
0x03, tx_fs_rate);
} else {
- sitar->dai[dai->id - 1].rate = params_rate(params);
+ sitar->dai[dai->id].rate = params_rate(params);
}
}
@@ -2887,13 +3122,14 @@
0x20, 0x00);
break;
default:
- pr_err("invalid format\n");
+ pr_err("%s: Unsupport format %d\n", __func__,
+ params_format(params));
break;
}
snd_soc_update_bits(codec, SITAR_A_CDC_CLK_RX_I2S_CTL,
0x03, (rx_fs_rate >> 0x05));
} else {
- sitar->dai[dai->id - 1].rate = params_rate(params);
+ sitar->dai[dai->id].rate = params_rate(params);
}
}
@@ -2975,30 +3211,30 @@
static int sitar_codec_enable_chmask(struct sitar_priv *sitar,
int event, int index)
{
- int ret = 0;
- u32 k = 0;
+ int ret = 0;
+ struct wcd9xxx_ch *ch;
+
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- for (k = 0; k < sitar->dai[index].ch_tot; k++) {
- ret = wcd9xxx_get_slave_port(
- sitar->dai[index].ch_num[k]);
+ list_for_each_entry(ch,
+ &sitar->dai[index].wcd9xxx_ch_list, list) {
+ ret = wcd9xxx_get_slave_port(ch->ch_num);
if (ret < 0) {
- pr_err("%s: Invalid Slave port ID: %d\n",
- __func__, ret);
+ pr_err("%s: Invalid slave port ID: %d\n",
+ __func__, ret);
ret = -EINVAL;
break;
}
sitar->dai[index].ch_mask |= 1 << ret;
}
- ret = 0;
break;
case SND_SOC_DAPM_POST_PMD:
ret = wait_event_timeout(sitar->dai[index].dai_wait,
(sitar->dai[index].ch_mask == 0),
- msecs_to_jiffies(SLIM_CLOSE_TIMEOUT));
+ msecs_to_jiffies(SLIM_CLOSE_TIMEOUT));
if (!ret) {
- pr_err("%s: slim close tx/rx timeout\n",
- __func__);
+ pr_err("%s: Slim close tx/rx wait timeout\n",
+ __func__);
ret = -EINVAL;
} else {
ret = 0;
@@ -3011,70 +3247,46 @@
static int sitar_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct wcd9xxx *sitar;
+ struct wcd9xxx *core;
struct snd_soc_codec *codec = w->codec;
struct sitar_priv *sitar_p = snd_soc_codec_get_drvdata(codec);
- u32 j = 0;
- int ret = 0;
- codec->control_data = dev_get_drvdata(codec->dev->parent);
- sitar = codec->control_data;
+ int ret = 0;
+ struct wcd9xxx_codec_dai_data *dai;
+
+ core = dev_get_drvdata(codec->dev->parent);
/* Execute the callback only if interface type is slimbus */
if (sitar_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
- if (event == SND_SOC_DAPM_POST_PMD && (sitar != NULL))
- sitar_codec_pm_runtime_put(sitar);
+ if (event == SND_SOC_DAPM_POST_PMD && (core != NULL))
+ sitar_codec_pm_runtime_put(core);
return 0;
}
+ dai = &sitar_p->dai[w->shift];
+
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- for (j = 0; j < ARRAY_SIZE(sitar_dai); j++) {
- if (sitar_dai[j].id == AIF1_CAP)
- continue;
- if (!strncmp(w->sname,
- sitar_dai[j].playback.stream_name, 13)) {
- ++sitar_p->dai[j].ch_act;
- break;
- }
- }
- if (sitar_p->dai[j].ch_act == sitar_p->dai[j].ch_tot) {
- ret = sitar_codec_enable_chmask(sitar_p, event, j);
- ret = wcd9xxx_cfg_slim_sch_rx(sitar,
- sitar_p->dai[j].ch_num,
- sitar_p->dai[j].ch_tot,
- sitar_p->dai[j].rate);
- }
+ ret = sitar_codec_enable_chmask(sitar_p, SND_SOC_DAPM_POST_PMU,
+ w->shift);
+ ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+ dai->rate, dai->bit_width,
+ &dai->grph);
break;
case SND_SOC_DAPM_POST_PMD:
- for (j = 0; j < ARRAY_SIZE(sitar_dai); j++) {
- if (sitar_dai[j].id == AIF1_CAP)
- continue;
- if (!strncmp(w->sname,
- sitar_dai[j].playback.stream_name, 13)) {
- --sitar_p->dai[j].ch_act;
- break;
- }
- }
- if (!sitar_p->dai[j].ch_act) {
- wcd9xxx_close_slim_sch_rx(sitar,
- sitar_p->dai[j].ch_num,
- sitar_p->dai[j].ch_tot);
- ret = sitar_codec_enable_chmask(sitar_p, event, j);
- if (ret < 0) {
- ret = wcd9xxx_disconnect_port(sitar,
- sitar_p->dai[j].ch_num,
- sitar_p->dai[j].ch_tot,
- 1);
+ ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+ dai->grph);
+ ret = sitar_codec_enable_chmask(sitar_p, SND_SOC_DAPM_POST_PMD,
+ w->shift);
+ if (ret < 0) {
+ ret = wcd9xxx_disconnect_port(core,
+ &dai->wcd9xxx_ch_list,
+ dai->grph);
pr_info("%s: Disconnect RX port ret = %d\n",
- __func__, ret);
- }
- sitar_p->dai[j].rate = 0;
- memset(sitar_p->dai[j].ch_num, 0, (sizeof(u32)*
- sitar_p->dai[j].ch_tot));
- sitar_p->dai[j].ch_tot = 0;
- if (sitar != NULL)
- sitar_codec_pm_runtime_put(sitar);
+ __func__, ret);
}
+ if (core != NULL)
+ sitar_codec_pm_runtime_put(core);
+ break;
}
return ret;
}
@@ -3082,72 +3294,45 @@
static int sitar_codec_enable_slimtx(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct wcd9xxx *sitar;
+ struct wcd9xxx *core;
struct snd_soc_codec *codec = w->codec;
struct sitar_priv *sitar_p = snd_soc_codec_get_drvdata(codec);
- /* index to the DAI ID, for now hardcoding */
- u32 j = 0;
+ struct wcd9xxx_codec_dai_data *dai;
int ret = 0;
- codec->control_data = dev_get_drvdata(codec->dev->parent);
- sitar = codec->control_data;
+ core = dev_get_drvdata(codec->dev->parent);
/* Execute the callback only if interface type is slimbus */
if (sitar_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
- if (event == SND_SOC_DAPM_POST_PMD && (sitar != NULL))
- sitar_codec_pm_runtime_put(sitar);
+ if (event == SND_SOC_DAPM_POST_PMD && (core != NULL))
+ sitar_codec_pm_runtime_put(core);
return 0;
}
+ dai = &sitar_p->dai[w->shift];
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- for (j = 0; j < ARRAY_SIZE(sitar_dai); j++) {
- if (sitar_dai[j].id == AIF1_PB)
- continue;
- if (!strncmp(w->sname,
- sitar_dai[j].capture.stream_name, 13)) {
- ++sitar_p->dai[j].ch_act;
- break;
- }
- }
- if (sitar_p->dai[j].ch_act == sitar_p->dai[j].ch_tot) {
- ret = sitar_codec_enable_chmask(sitar_p, event, j);
- ret = wcd9xxx_cfg_slim_sch_tx(sitar,
- sitar_p->dai[j].ch_num,
- sitar_p->dai[j].ch_tot,
- sitar_p->dai[j].rate);
- }
+ ret = sitar_codec_enable_chmask(sitar_p, SND_SOC_DAPM_POST_PMU,
+ w->shift);
+ ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->rate, dai->bit_width,
+ &dai->grph);
break;
case SND_SOC_DAPM_POST_PMD:
- for (j = 0; j < ARRAY_SIZE(sitar_dai); j++) {
- if (sitar_dai[j].id == AIF1_PB)
- continue;
- if (!strncmp(w->sname,
- sitar_dai[j].capture.stream_name, 13)) {
- --sitar_p->dai[j].ch_act;
- break;
- }
+ ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->grph);
+ ret = sitar_codec_enable_chmask(sitar_p, SND_SOC_DAPM_POST_PMD,
+ w->shift);
+ if (ret < 0) {
+ ret = wcd9xxx_disconnect_port(core,
+ &dai->wcd9xxx_ch_list,
+ dai->grph);
+ pr_info("%s: Disconnect RX port ret = %d\n",
+ __func__, ret);
}
- if (!sitar_p->dai[j].ch_act) {
- wcd9xxx_close_slim_sch_tx(sitar,
- sitar_p->dai[j].ch_num,
- sitar_p->dai[j].ch_tot);
- ret = sitar_codec_enable_chmask(sitar_p, event, j);
- if (ret < 0) {
- ret = wcd9xxx_disconnect_port(sitar,
- sitar_p->dai[j].ch_num,
- sitar_p->dai[j].ch_tot,
- 0);
- pr_info("%s: Disconnect TX port, ret = %d\n",
- __func__, ret);
- }
- sitar_p->dai[j].rate = 0;
- memset(sitar_p->dai[j].ch_num, 0, (sizeof(u32)*
- sitar_p->dai[j].ch_tot));
- sitar_p->dai[j].ch_tot = 0;
- if (sitar != NULL)
- sitar_codec_pm_runtime_put(sitar);
- }
+ if (core != NULL)
+ sitar_codec_pm_runtime_put(core);
+ break;
}
return ret;
}
@@ -3187,7 +3372,7 @@
short bias_value;
struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
- wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
if (noreldetection)
sitar_turn_onoff_rel_detection(codec, false);
@@ -3223,7 +3408,7 @@
if (noreldetection)
sitar_turn_onoff_rel_detection(codec, true);
- wcd9xxx_enable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
return bias_value;
}
@@ -3441,7 +3626,7 @@
sitar = snd_soc_codec_get_drvdata(codec);
calibration = sitar->mbhc_cfg.calibration;
- wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
sitar_turn_onoff_rel_detection(codec, false);
/* First compute the DCE / STA wait times
@@ -3524,7 +3709,7 @@
snd_soc_write(codec, SITAR_A_MBHC_SCALING_MUX_1, 0x84);
usleep_range(100, 100);
- wcd9xxx_enable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
sitar_turn_onoff_rel_detection(codec, true);
}
@@ -3808,9 +3993,9 @@
}
sitar_set_and_turnoff_hph_padac(codec);
hphocp_off_report(sitar, SND_JACK_OC_HPHR,
- SITAR_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
hphocp_off_report(sitar, SND_JACK_OC_HPHL,
- SITAR_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
sitar->current_plug = PLUG_TYPE_NONE;
sitar->mbhc_polling_active = false;
} else {
@@ -4232,9 +4417,9 @@
snd_soc_update_bits(codec, SITAR_A_RX_HPH_OCP_CTL,
0x10, 0x10);
wcd9xxx_enable_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
wcd9xxx_enable_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
/* Bootup time detection */
sitar_hs_gpio_handler(codec);
}
@@ -4624,7 +4809,7 @@
0x10);
} else {
wcd9xxx_disable_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
sitar->hphlocp_cnt = 0;
sitar->hph_status |= SND_JACK_OC_HPHL;
if (sitar->mbhc_cfg.headset_jack)
@@ -4657,7 +4842,7 @@
0x10);
} else {
wcd9xxx_disable_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
sitar->hphrocp_cnt = 0;
sitar->hph_status |= SND_JACK_OC_HPHR;
if (sitar->mbhc_cfg.headset_jack)
@@ -4680,7 +4865,7 @@
pr_debug("%s: enter\n", __func__);
SITAR_ACQUIRE_LOCK(priv->codec_resource_lock);
- wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_INSERTION);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
@@ -5061,16 +5246,16 @@
static int sitar_codec_probe(struct snd_soc_codec *codec)
{
- struct sitar *control;
+ struct wcd9xxx *core;
struct sitar_priv *sitar;
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret = 0;
int i;
u8 sitar_version;
- int ch_cnt;
+ void *ptr = NULL;
codec->control_data = dev_get_drvdata(codec->dev->parent);
- control = codec->control_data;
+ core = codec->control_data;
sitar = kzalloc(sizeof(struct sitar_priv), GFP_KERNEL);
if (!sitar) {
@@ -5119,12 +5304,35 @@
ARRAY_SIZE(sitar_snd_controls));
snd_soc_dapm_new_controls(dapm, sitar_dapm_widgets,
ARRAY_SIZE(sitar_dapm_widgets));
+
+ ptr = kmalloc((sizeof(sitar_rx_chs) +
+ sizeof(sitar_tx_chs)), GFP_KERNEL);
+ if (!ptr) {
+ pr_err("%s: no mem for slim chan ctl data\n", __func__);
+ ret = -ENOMEM;
+ goto err_nomem_slimch;
+ }
if (sitar->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
snd_soc_dapm_new_controls(dapm, sitar_dapm_i2s_widgets,
ARRAY_SIZE(sitar_dapm_i2s_widgets));
snd_soc_dapm_add_routes(dapm, audio_i2s_map,
ARRAY_SIZE(audio_i2s_map));
+ for (i = 0; i < ARRAY_SIZE(sitar_i2s_dai); i++)
+ INIT_LIST_HEAD(&sitar->dai[i].wcd9xxx_ch_list);
}
+ if (sitar->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ for (i = 0; i < NUM_CODEC_DAIS; i++) {
+ INIT_LIST_HEAD(&sitar->dai[i].wcd9xxx_ch_list);
+ init_waitqueue_head(&sitar->dai[i].dai_wait);
+ }
+ }
+ core->num_rx_port = SITAR_RX_MAX;
+ core->rx_chs = ptr;
+ memcpy(core->rx_chs, sitar_rx_chs, sizeof(sitar_rx_chs));
+ core->num_tx_port = SITAR_TX_MAX;
+ core->tx_chs = ptr + sizeof(sitar_rx_chs);
+ memcpy(core->tx_chs, sitar_tx_chs, sizeof(sitar_tx_chs));
+
snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
sitar_version = snd_soc_read(codec, WCD9XXX_A_CHIP_VERSION);
@@ -5136,44 +5344,49 @@
snd_soc_dapm_sync(dapm);
- ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_MBHC_INSERTION,
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_INSERTION,
sitar_hs_insert_irq, "Headset insert detect", sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_MBHC_INSERTION);
+ WCD9XXX_IRQ_MBHC_INSERTION);
goto err_insert_irq;
}
- wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_INSERTION);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
- ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_MBHC_REMOVAL,
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_REMOVAL,
sitar_hs_remove_irq, "Headset remove detect", sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_MBHC_REMOVAL);
+ WCD9XXX_IRQ_MBHC_REMOVAL);
goto err_remove_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL,
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_POTENTIAL,
sitar_dce_handler, "DC Estimation detect", sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_MBHC_POTENTIAL);
+ WCD9XXX_IRQ_MBHC_POTENTIAL);
goto err_potential_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_MBHC_RELEASE,
- sitar_release_handler, "Button Release detect", sitar);
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_RELEASE,
+ sitar_release_handler,
+ "Button Release detect", sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_MBHC_RELEASE);
+ WCD9XXX_IRQ_MBHC_RELEASE);
goto err_release_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_SLIMBUS,
- sitar_slimbus_irq, "SLIMBUS Slave", sitar);
+ ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
+ sitar_slimbus_irq, "SLIMBUS Slave", sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_SLIMBUS);
+ WCD9XXX_IRQ_SLIMBUS);
goto err_slimbus_irq;
}
@@ -5183,40 +5396,27 @@
ret = wcd9xxx_request_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPL_FAULT, sitar_hphl_ocp_irq,
- "HPH_L OCP detect", sitar);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+ sitar_hphl_ocp_irq,
+ "HPH_L OCP detect", sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
goto err_hphl_ocp_irq;
}
- wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_HPH_PA_OCPL_FAULT);
+ wcd9xxx_disable_irq(codec->control_data,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
ret = wcd9xxx_request_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPR_FAULT, sitar_hphr_ocp_irq,
- "HPH_R OCP detect", sitar);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+ sitar_hphr_ocp_irq, "HPH_R OCP detect",
+ sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
goto err_hphr_ocp_irq;
}
- wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_HPH_PA_OCPR_FAULT);
-
- for (i = 0; i < ARRAY_SIZE(sitar_dai); i++) {
- switch (sitar_dai[i].id) {
- case AIF1_PB:
- ch_cnt = sitar_dai[i].playback.channels_max;
- break;
- case AIF1_CAP:
- ch_cnt = sitar_dai[i].capture.channels_max;
- break;
- default:
- continue;
- }
- sitar->dai[i].ch_num = kzalloc((sizeof(unsigned int)*
- ch_cnt), GFP_KERNEL);
- init_waitqueue_head(&sitar->dai[i].dai_wait);
- }
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
codec->ignore_pmdown_time = 1;
@@ -5227,24 +5427,23 @@
return ret;
err_hphr_ocp_irq:
- wcd9xxx_free_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPL_FAULT, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+ sitar);
err_hphl_ocp_irq:
- wcd9xxx_free_irq(codec->control_data,
- SITAR_IRQ_SLIMBUS, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, sitar);
err_slimbus_irq:
- wcd9xxx_free_irq(codec->control_data,
- SITAR_IRQ_MBHC_RELEASE, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, sitar);
err_release_irq:
- wcd9xxx_free_irq(codec->control_data,
- SITAR_IRQ_MBHC_POTENTIAL, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL,
+ sitar);
err_potential_irq:
- wcd9xxx_free_irq(codec->control_data,
- SITAR_IRQ_MBHC_REMOVAL, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, sitar);
err_remove_irq:
- wcd9xxx_free_irq(codec->control_data,
- SITAR_IRQ_MBHC_INSERTION, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION,
+ sitar);
err_insert_irq:
+ kfree(ptr);
+err_nomem_slimch:
err_pdata:
mutex_destroy(&sitar->codec_resource_lock);
kfree(sitar);
@@ -5252,21 +5451,20 @@
}
static int sitar_codec_remove(struct snd_soc_codec *codec)
{
- int i;
struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
- wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_SLIMBUS, sitar);
- wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_MBHC_RELEASE, sitar);
- wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL, sitar);
- wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_MBHC_REMOVAL, sitar);
- wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_MBHC_INSERTION, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL,
+ sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION,
+ sitar);
SITAR_ACQUIRE_LOCK(sitar->codec_resource_lock);
sitar_codec_disable_clock_block(codec);
SITAR_RELEASE_LOCK(sitar->codec_resource_lock);
sitar_codec_enable_bandgap(codec, SITAR_BANDGAP_OFF);
if (sitar->mbhc_fw)
release_firmware(sitar->mbhc_fw);
- for (i = 0; i < ARRAY_SIZE(sitar_dai); i++)
- kfree(sitar->dai[i].ch_num);
mutex_destroy(&sitar->codec_resource_lock);
kfree(sitar);
return 0;
diff --git a/sound/soc/codecs/wcd9304.h b/sound/soc/codecs/wcd9304.h
index 70b3f0b..13336ef 100644
--- a/sound/soc/codecs/wcd9304.h
+++ b/sound/soc/codecs/wcd9304.h
@@ -191,6 +191,26 @@
extern int sitar_mclk_enable(struct snd_soc_codec *codec, int mclk_enable,
bool dapm);
+/* Number of input and output Slimbus ports
+ */
+enum {
+ SITAR_RX1 = 0,
+ SITAR_RX2,
+ SITAR_RX3,
+ SITAR_RX4,
+ SITAR_RX5,
+ SITAR_RX_MAX,
+};
+
+enum {
+ SITAR_TX1 = 0,
+ SITAR_TX2,
+ SITAR_TX3,
+ SITAR_TX4,
+ SITAR_TX5,
+ SITAR_TX_MAX,
+};
+
extern void *sitar_mbhc_cal_btn_det_mp(const struct sitar_mbhc_btn_detect_cfg
*btn_det,
const enum sitar_mbhc_btn_det_mem mem);
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 5a819c9..564cad6 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -74,25 +74,33 @@
#define TABLA_OCP_ATTEMPT 1
-#define AIF1_PB 1
-#define AIF1_CAP 2
-#define AIF2_PB 3
-#define AIF2_CAP 4
-#define AIF3_CAP 5
-#define AIF3_PB 6
-
-#define NUM_CODEC_DAIS 6
-#define TABLA_COMP_DIGITAL_GAIN_OFFSET 3
-
-struct tabla_codec_dai_data {
- u32 rate;
- u32 *ch_num;
- u32 ch_act;
- u32 ch_tot;
- u32 ch_mask;
- wait_queue_head_t dai_wait;
+enum {
+ AIF1_PB = 0,
+ AIF1_CAP,
+ AIF2_PB,
+ AIF2_CAP,
+ AIF3_PB,
+ AIF3_CAP,
+ NUM_CODEC_DAIS,
};
+enum {
+ RX_MIX1_INP_SEL_ZERO = 0,
+ RX_MIX1_INP_SEL_SRC1,
+ RX_MIX1_INP_SEL_SRC2,
+ RX_MIX1_INP_SEL_IIR1,
+ RX_MIX1_INP_SEL_IIR2,
+ RX_MIX1_INP_SEL_RX1,
+ RX_MIX1_INP_SEL_RX2,
+ RX_MIX1_INP_SEL_RX3,
+ RX_MIX1_INP_SEL_RX4,
+ RX_MIX1_INP_SEL_RX5,
+ RX_MIX1_INP_SEL_RX6,
+ RX_MIX1_INP_SEL_RX7,
+};
+
+#define TABLA_COMP_DIGITAL_GAIN_OFFSET 3
+
#define TABLA_MCLK_RATE_12288KHZ 12288000
#define TABLA_MCLK_RATE_9600KHZ 9600000
@@ -255,6 +263,38 @@
static struct hpf_work tx_hpf_work[NUM_DECIMATORS];
+static const struct wcd9xxx_ch tabla_rx_chs[TABLA_RX_MAX] = {
+ WCD9XXX_CH(10, 0),
+ WCD9XXX_CH(11, 1),
+ WCD9XXX_CH(12, 2),
+ WCD9XXX_CH(13, 3),
+ WCD9XXX_CH(14, 4),
+ WCD9XXX_CH(15, 5),
+ WCD9XXX_CH(16, 6)
+};
+
+static const struct wcd9xxx_ch tabla_tx_chs[TABLA_TX_MAX] = {
+ WCD9XXX_CH(0, 0),
+ WCD9XXX_CH(1, 1),
+ WCD9XXX_CH(2, 2),
+ WCD9XXX_CH(3, 3),
+ WCD9XXX_CH(4, 4),
+ WCD9XXX_CH(5, 5),
+ WCD9XXX_CH(6, 6),
+ WCD9XXX_CH(7, 7),
+ WCD9XXX_CH(8, 8),
+ WCD9XXX_CH(9, 9)
+};
+
+static const u32 vport_check_table[NUM_CODEC_DAIS] = {
+ 0, /* AIF1_PB */
+ (1 << AIF2_CAP) | (1 << AIF3_CAP), /* AIF1_CAP */
+ 0, /* AIF2_PB */
+ (1 << AIF1_CAP) | (1 << AIF3_CAP), /* AIF2_CAP */
+ 0, /* AIF2_PB */
+ (1 << AIF1_CAP) | (1 << AIF2_CAP), /* AIF2_CAP */
+};
+
struct tabla_priv {
struct snd_soc_codec *codec;
struct tabla_reg_address reg_addr;
@@ -310,7 +350,7 @@
const struct firmware *mbhc_fw;
/* num of slim ports required */
- struct tabla_codec_dai_data dai[NUM_CODEC_DAIS];
+ struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS];
/*compander*/
int comp_enabled[COMPANDER_MAX];
@@ -1682,6 +1722,238 @@
static const struct snd_kcontrol_new lineout4_ground_switch =
SOC_DAPM_SINGLE("Switch", TABLA_A_RX_LINE_4_DAC_CTL, 6, 1, 0);
+static int slim_tx_vport_validation(u32 dai_id, u32 port_id,
+ struct tabla_priv *tabla_p)
+{
+ struct wcd9xxx_ch *ch;
+ int ret = 0;
+ int index = 0;
+ u32 vtable = vport_check_table[dai_id];
+ pr_debug("%s: dai_id %u vtable 0x%x port_id %u\n", __func__,
+ dai_id, vtable, port_id);
+ while (vtable) {
+ if (vtable & 1) {
+ list_for_each_entry(ch,
+ &tabla_p->dai[index].wcd9xxx_ch_list,
+ list) {
+ pr_debug("%s: index %u ch->port %u vtable 0x%x\n",
+ __func__, index, ch->port, vtable);
+ if (ch->port == port_id) {
+ pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
+ __func__, port_id + 1,
+ (index + 1)/2);
+ ret = -EINVAL;
+ break;
+ }
+ }
+ }
+ if (ret)
+ break;
+ index++;
+ vtable = vtable >> 1;
+ }
+ return ret;
+}
+
+/* virtual port entries */
+static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+ ucontrol->value.integer.value[0] = widget->value;
+ return 0;
+}
+
+static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
+ struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+ struct soc_multi_mixer_control *mixer =
+ ((struct soc_multi_mixer_control *)kcontrol->private_value);
+ u32 dai_id = widget->shift;
+ u32 port_id = mixer->shift;
+ u32 enable = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__,
+ widget->name, ucontrol->id.name, widget->value, widget->shift,
+ ucontrol->value.integer.value[0]);
+
+ mutex_lock(&codec->mutex);
+ if (tabla_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ if (dai_id != AIF1_CAP) {
+ dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+ __func__);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ }
+ switch (dai_id) {
+ case AIF1_CAP:
+ case AIF2_CAP:
+ case AIF3_CAP:
+ /* only add to the list if value not set
+ */
+ if (enable && !(widget->value & 1 << port_id)) {
+ if (slim_tx_vport_validation(dai_id,
+ port_id, tabla_p)) {
+ pr_info("%s: TX%u is used by other virtual port\n",
+ __func__, port_id + 1);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ widget->value |= 1 << port_id;
+ list_add_tail(&core->tx_chs[port_id].list,
+ &tabla_p->dai[dai_id].wcd9xxx_ch_list
+ );
+ } else if (!enable && (widget->value & 1 << port_id)) {
+ widget->value &= ~(1 << port_id);
+ list_del_init(&core->tx_chs[port_id].list);
+ } else {
+ if (enable)
+ pr_info("%s: TX%u port is used by this virtual port\n",
+ __func__, port_id + 1);
+ else
+ pr_info("%s: TX%u port is not used by this virtual port\n",
+ __func__, port_id + 1);
+ /* avoid update power function */
+ mutex_unlock(&codec->mutex);
+ return 0;
+ }
+ break;
+ default:
+ pr_err("Unknown AIF %d\n", dai_id);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__,
+ widget->name, widget->sname, widget->value, widget->shift);
+
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, enable);
+
+ mutex_unlock(&codec->mutex);
+ return 0;
+}
+
+static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+ ucontrol->value.enumerated.item[0] = widget->value;
+ return 0;
+}
+
+static const char *const slim_rx_mux_text[] = {
+ "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB"
+};
+
+static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
+ struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ u32 port_id = widget->shift;
+
+ pr_debug("%s: wname %s cname %s value %u shift %d item %u\n", __func__,
+ widget->name, ucontrol->id.name, widget->value, widget->shift,
+ ucontrol->value.enumerated.item[0]);
+
+ widget->value = ucontrol->value.enumerated.item[0];
+
+ mutex_lock(&codec->mutex);
+
+ if (tabla_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ if (widget->value > 1) {
+ dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+ __func__);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ }
+ /* value need to match the Virtual port and AIF number
+ */
+ switch (widget->value) {
+ case 0:
+ list_del_init(&core->rx_chs[port_id].list);
+ break;
+ case 1:
+ list_add_tail(&core->rx_chs[port_id].list,
+ &tabla_p->dai[AIF1_PB].wcd9xxx_ch_list);
+ break;
+ case 2:
+ list_add_tail(&core->rx_chs[port_id].list,
+ &tabla_p->dai[AIF2_PB].wcd9xxx_ch_list);
+ break;
+ case 3:
+ list_add_tail(&core->rx_chs[port_id].list,
+ &tabla_p->dai[AIF3_PB].wcd9xxx_ch_list);
+ break;
+ default:
+ pr_err("Unknown AIF %d\n", widget->value);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+
+ snd_soc_dapm_mux_update_power(widget, kcontrol, 1, widget->value, e);
+
+ mutex_unlock(&codec->mutex);
+ return 0;
+}
+
+static const struct soc_enum slim_rx_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text);
+
+static const struct snd_kcontrol_new slim_rx_mux[TABLA_RX_MAX] = {
+ SOC_DAPM_ENUM_EXT("SLIM RX1 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX2 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX3 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX4 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX5 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX6 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX7 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+};
+
+static const struct snd_kcontrol_new aif_cap_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TABLA_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TABLA_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TABLA_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TABLA_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TABLA_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TABLA_TX6, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TABLA_TX7, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TABLA_TX8, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TABLA_TX9, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TABLA_TX10, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
static void tabla_codec_enable_adc_block(struct snd_soc_codec *codec,
int enable)
{
@@ -2911,7 +3183,7 @@
/* reset retry counter as PA is turned off signifying
* start of new OCP detection session
*/
- if (TABLA_IRQ_HPH_PA_OCPL_FAULT)
+ if (WCD9XXX_IRQ_HPH_PA_OCPL_FAULT)
tabla->hphlocp_cnt = 0;
else
tabla->hphrocp_cnt = 0;
@@ -2923,14 +3195,16 @@
{
struct tabla_priv *tabla = container_of(work, struct tabla_priv,
hphlocp_work);
- hphocp_off_report(tabla, SND_JACK_OC_HPHL, TABLA_IRQ_HPH_PA_OCPL_FAULT);
+ hphocp_off_report(tabla, SND_JACK_OC_HPHL,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
}
static void hphrocp_off_report(struct work_struct *work)
{
struct tabla_priv *tabla = container_of(work, struct tabla_priv,
hphrocp_work);
- hphocp_off_report(tabla, SND_JACK_OC_HPHR, TABLA_IRQ_HPH_PA_OCPR_FAULT);
+ hphocp_off_report(tabla, SND_JACK_OC_HPHR,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
}
static int tabla_hph_pa_event(struct snd_soc_dapm_widget *w,
@@ -3120,13 +3394,47 @@
static const struct snd_soc_dapm_route audio_map[] = {
/* SLIMBUS Connections */
- {"SLIM TX1", NULL, "SLIM TX1 MUX"},
- {"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
+ {"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+ {"AIF2 CAP", NULL, "AIF2_CAP Mixer"},
+ {"AIF3 CAP", NULL, "AIF3_CAP Mixer"},
- {"SLIM TX2", NULL, "SLIM TX2 MUX"},
+ /* SLIM_MIXER("AIF1_CAP Mixer"),*/
+ {"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+ /* SLIM_MIXER("AIF2_CAP Mixer"),*/
+ {"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+ /* SLIM_MIXER("AIF3_CAP Mixer"),*/
+ {"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+
+ {"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX2 MUX", "DEC2", "DEC2 MUX"},
- {"SLIM TX3", NULL, "SLIM TX3 MUX"},
{"SLIM TX3 MUX", "DEC3", "DEC3 MUX"},
{"SLIM TX3 MUX", "RMIX1", "RX1 MIX1"},
{"SLIM TX3 MUX", "RMIX2", "RX2 MIX1"},
@@ -3136,10 +3444,8 @@
{"SLIM TX3 MUX", "RMIX6", "RX6 MIX1"},
{"SLIM TX3 MUX", "RMIX7", "RX7 MIX1"},
- {"SLIM TX4", NULL, "SLIM TX4 MUX"},
{"SLIM TX4 MUX", "DEC4", "DEC4 MUX"},
- {"SLIM TX5", NULL, "SLIM TX5 MUX"},
{"SLIM TX5 MUX", "DEC5", "DEC5 MUX"},
{"SLIM TX5 MUX", "RMIX1", "RX1 MIX1"},
{"SLIM TX5 MUX", "RMIX2", "RX2 MIX1"},
@@ -3149,10 +3455,8 @@
{"SLIM TX5 MUX", "RMIX6", "RX6 MIX1"},
{"SLIM TX5 MUX", "RMIX7", "RX7 MIX1"},
- {"SLIM TX6", NULL, "SLIM TX6 MUX"},
{"SLIM TX6 MUX", "DEC6", "DEC6 MUX"},
- {"SLIM TX7", NULL, "SLIM TX7 MUX"},
{"SLIM TX7 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX7 MUX", "DEC2", "DEC2 MUX"},
{"SLIM TX7 MUX", "DEC3", "DEC3 MUX"},
@@ -3171,7 +3475,6 @@
{"SLIM TX7 MUX", "RMIX6", "RX6 MIX1"},
{"SLIM TX7 MUX", "RMIX7", "RX7 MIX1"},
- {"SLIM TX8", NULL, "SLIM TX8 MUX"},
{"SLIM TX8 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX8 MUX", "DEC2", "DEC2 MUX"},
{"SLIM TX8 MUX", "DEC3", "DEC3 MUX"},
@@ -3183,7 +3486,6 @@
{"SLIM TX8 MUX", "DEC9", "DEC9 MUX"},
{"SLIM TX8 MUX", "DEC10", "DEC10 MUX"},
- {"SLIM TX9", NULL, "SLIM TX9 MUX"},
{"SLIM TX9 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX9 MUX", "DEC2", "DEC2 MUX"},
{"SLIM TX9 MUX", "DEC3", "DEC3 MUX"},
@@ -3195,7 +3497,6 @@
{"SLIM TX9 MUX", "DEC9", "DEC9 MUX"},
{"SLIM TX9 MUX", "DEC10", "DEC10 MUX"},
- {"SLIM TX10", NULL, "SLIM TX10 MUX"},
{"SLIM TX10 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX10 MUX", "DEC2", "DEC2 MUX"},
{"SLIM TX10 MUX", "DEC3", "DEC3 MUX"},
@@ -3310,6 +3611,40 @@
{"RX3 MIX2", NULL, "RX3 MIX2 INP1"},
{"RX3 MIX2", NULL, "RX3 MIX2 INP2"},
+ /* SLIM_MUX("AIF1_PB", "AIF1 PB"),*/
+ {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"},
+ /* SLIM_MUX("AIF2_PB", "AIF2 PB"),*/
+ {"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"},
+ /* SLIM_MUX("AIF3_PB", "AIF3 PB"),*/
+ {"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"},
+
+ {"SLIM RX1", NULL, "SLIM RX1 MUX"},
+ {"SLIM RX2", NULL, "SLIM RX2 MUX"},
+ {"SLIM RX3", NULL, "SLIM RX3 MUX"},
+ {"SLIM RX4", NULL, "SLIM RX4 MUX"},
+ {"SLIM RX5", NULL, "SLIM RX5 MUX"},
+ {"SLIM RX6", NULL, "SLIM RX6 MUX"},
+ {"SLIM RX7", NULL, "SLIM RX7 MUX"},
+
+ /* Mixer control for output path */
{"RX1 MIX1 INP1", "RX1", "SLIM RX1"},
{"RX1 MIX1 INP1", "RX2", "SLIM RX2"},
{"RX1 MIX1 INP1", "RX3", "SLIM RX3"},
@@ -3659,6 +3994,10 @@
unsigned int value)
{
int ret;
+
+ if (reg == SND_SOC_NOPM)
+ return 0;
+
BUG_ON(reg > TABLA_MAX_REGISTER);
if (!tabla_volatile(codec, reg)) {
@@ -3676,6 +4015,9 @@
unsigned int val;
int ret;
+ if (reg == SND_SOC_NOPM)
+ return 0;
+
BUG_ON(reg > TABLA_MAX_REGISTER);
if (!tabla_volatile(codec, reg) && tabla_readable(codec, reg) &&
@@ -3799,10 +4141,10 @@
return;
if (dai->id <= NUM_CODEC_DAIS) {
- if (tabla->dai[dai->id-1].ch_mask) {
+ if (tabla->dai[dai->id].ch_mask) {
active = 1;
pr_debug("%s(): Codec DAI: chmask[%d] = 0x%x\n",
- __func__, dai->id-1, tabla->dai[dai->id-1].ch_mask);
+ __func__, dai->id, tabla->dai[dai->id].ch_mask);
}
}
@@ -3923,39 +4265,20 @@
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(dai->codec);
- u32 i = 0;
+ struct wcd9xxx *core = dev_get_drvdata(dai->codec->dev->parent);
+
if (!tx_slot && !rx_slot) {
pr_err("%s: Invalid\n", __func__);
return -EINVAL;
}
- pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n",
- __func__, dai->name, dai->id, tx_num, rx_num);
+ pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n"
+ "tabla->intf_type %d\n",
+ __func__, dai->name, dai->id, tx_num, rx_num,
+ tabla->intf_type);
- if (dai->id == AIF1_PB || dai->id == AIF2_PB || dai->id == AIF3_PB) {
- for (i = 0; i < rx_num; i++) {
- tabla->dai[dai->id - 1].ch_num[i] = rx_slot[i];
- tabla->dai[dai->id - 1].ch_act = 0;
- tabla->dai[dai->id - 1].ch_tot = rx_num;
- }
- } else if (dai->id == AIF1_CAP || dai->id == AIF2_CAP ||
- dai->id == AIF3_CAP) {
- tabla->dai[dai->id - 1].ch_tot = tx_num;
- /* All channels are already active.
- * do not reset ch_act flag
- */
- if ((tabla->dai[dai->id - 1].ch_tot != 0)
- && (tabla->dai[dai->id - 1].ch_act ==
- tabla->dai[dai->id - 1].ch_tot)) {
- pr_info("%s: ch_act = %d, ch_tot = %d\n", __func__,
- tabla->dai[dai->id - 1].ch_act,
- tabla->dai[dai->id - 1].ch_tot);
- return 0;
- }
-
- tabla->dai[dai->id - 1].ch_act = 0;
- for (i = 0; i < tx_num; i++)
- tabla->dai[dai->id - 1].ch_num[i] = tx_slot[i];
- }
+ if (tabla->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+ wcd9xxx_init_slimslave(core, core->slim->laddr,
+ tx_num, tx_slot, rx_num, rx_slot);
return 0;
}
@@ -3964,189 +4287,97 @@
unsigned int *rx_num, unsigned int *rx_slot)
{
- struct wcd9xxx *tabla = dev_get_drvdata(dai->codec->control_data);
+ struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(dai->codec);
+ u32 i = 0;
+ struct wcd9xxx_ch *ch;
- u32 cnt = 0;
- u32 tx_ch[SLIM_MAX_TX_PORTS];
- u32 rx_ch[SLIM_MAX_RX_PORTS];
+ switch (dai->id) {
+ case AIF1_PB:
+ case AIF2_PB:
+ case AIF3_PB:
+ if (!rx_slot || !rx_num) {
+ pr_err("%s: Invalid rx_slot %d or rx_num %d\n",
+ __func__, (u32) rx_slot, (u32) rx_num);
+ return -EINVAL;
+ }
+ list_for_each_entry(ch, &tabla_p->dai[dai->id].wcd9xxx_ch_list,
+ list) {
+ rx_slot[i++] = ch->ch_num;
+ }
+ *rx_num = i;
+ break;
+ case AIF1_CAP:
+ case AIF2_CAP:
+ case AIF3_CAP:
+ if (!tx_slot || !tx_num) {
+ pr_err("%s: Invalid tx_slot %d or tx_num %d\n",
+ __func__, (u32) tx_slot, (u32) tx_num);
+ return -EINVAL;
+ }
+ list_for_each_entry(ch, &tabla_p->dai[dai->id].wcd9xxx_ch_list,
+ list) {
+ tx_slot[i++] = ch->ch_num;
+ }
+ *tx_num = i;
+ break;
- if (!rx_slot && !tx_slot) {
- pr_err("%s: Invalid\n", __func__);
- return -EINVAL;
+ default:
+ pr_err("%s: Invalid DAI ID %x\n", __func__, dai->id);
+ break;
}
-
- /* for virtual port, codec driver needs to do
- * housekeeping, for now should be ok
- */
- wcd9xxx_get_channel(tabla, rx_ch, tx_ch);
- if (dai->id == AIF1_PB) {
- *rx_num = tabla_dai[dai->id - 1].playback.channels_max;
- while (cnt < *rx_num) {
- rx_slot[cnt] = rx_ch[cnt];
- cnt++;
- }
- } else if (dai->id == AIF1_CAP) {
- *tx_num = tabla_dai[dai->id - 1].capture.channels_max;
- while (cnt < *tx_num) {
- tx_slot[cnt] = tx_ch[6 + cnt];
- cnt++;
- }
- } else if (dai->id == AIF2_PB) {
- *rx_num = tabla_dai[dai->id - 1].playback.channels_max;
- while (cnt < *rx_num) {
- rx_slot[cnt] = rx_ch[5 + cnt];
- cnt++;
- }
- } else if (dai->id == AIF2_CAP) {
- *tx_num = tabla_dai[dai->id - 1].capture.channels_max;
- tx_slot[0] = tx_ch[cnt];
- tx_slot[1] = tx_ch[1 + cnt];
- tx_slot[2] = tx_ch[5 + cnt];
- tx_slot[3] = tx_ch[3 + cnt];
-
- } else if (dai->id == AIF3_PB) {
- *rx_num = tabla_dai[dai->id - 1].playback.channels_max;
- rx_slot[0] = rx_ch[3];
- rx_slot[1] = rx_ch[4];
-
- } else if (dai->id == AIF3_CAP) {
- *tx_num = tabla_dai[dai->id - 1].capture.channels_max;
- tx_slot[cnt] = tx_ch[2 + cnt];
- tx_slot[cnt + 1] = tx_ch[4 + cnt];
- }
- pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n",
- __func__, dai->name, dai->id, *tx_num, *rx_num);
-
-
return 0;
}
-static struct snd_soc_dapm_widget tabla_dapm_aif_in_widgets[] = {
-
- SND_SOC_DAPM_AIF_IN_E("SLIM RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 1,
- 0, tabla_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_IN_E("SLIM RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 2,
- 0, tabla_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_IN_E("SLIM RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 3,
- 0, tabla_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_IN_E("SLIM RX4", "AIF3 Playback", 0, SND_SOC_NOPM, 4,
- 0, tabla_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_IN_E("SLIM RX5", "AIF3 Playback", 0, SND_SOC_NOPM, 5,
- 0, tabla_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_IN_E("SLIM RX6", "AIF2 Playback", 0, SND_SOC_NOPM, 6,
- 0, tabla_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_IN_E("SLIM RX7", "AIF2 Playback", 0, SND_SOC_NOPM, 7,
- 0, tabla_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-};
-
-static struct snd_soc_dapm_widget tabla_dapm_aif_out_widgets[] = {
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX1", "AIF2 Capture", 0, SND_SOC_NOPM, 1,
- 0, tabla_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX2", "AIF2 Capture", 0, SND_SOC_NOPM, 2,
- 0, tabla_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX3", "AIF3 Capture", 0, SND_SOC_NOPM, 3,
- 0, tabla_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX4", "AIF2 Capture", 0, SND_SOC_NOPM, 4,
- 0, tabla_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX5", "AIF3 Capture", 0, SND_SOC_NOPM, 5,
- 0, tabla_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX6", "AIF2 Capture", 0, SND_SOC_NOPM, 6,
- 0, tabla_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX7", "AIF1 Capture", 0, SND_SOC_NOPM, 7,
- 0, tabla_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX8", "AIF1 Capture", 0, SND_SOC_NOPM, 8,
- 0, tabla_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX9", "AIF1 Capture", 0, SND_SOC_NOPM, 9,
- 0, tabla_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX10", "AIF1 Capture", 0, SND_SOC_NOPM, 10,
- 0, tabla_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-};
-
static int tabla_set_interpolator_rate(struct snd_soc_dai *dai,
- u8 rx_fs_rate_reg_val, u32 compander_fs, u32 sample_rate)
+ u8 rx_fs_rate_reg_val,
+ u32 compander_fs,
+ u32 sample_rate)
{
- u32 i, j;
+ u32 j;
u8 rx_mix1_inp;
u16 rx_mix_1_reg_1, rx_mix_1_reg_2;
u16 rx_fs_reg;
u8 rx_mix_1_reg_1_val, rx_mix_1_reg_2_val;
struct snd_soc_codec *codec = dai->codec;
+ struct wcd9xxx_ch *ch;
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_widget *w = tabla_dapm_aif_in_widgets;
- for (i = 0; i < ARRAY_SIZE(tabla_dapm_aif_in_widgets); i++) {
+ list_for_each_entry(ch, &tabla->dai[dai->id].wcd9xxx_ch_list, list) {
- if (strncmp(dai->driver->playback.stream_name, w[i].sname, 13))
- continue;
+ rx_mix1_inp = ch->port - RX_MIX1_INP_SEL_RX1;
- rx_mix1_inp = w[i].shift + 4;
-
- if ((rx_mix1_inp < 0x5) || (rx_mix1_inp > 0xB)) {
-
- pr_err("%s: Invalid SLIM RX%u port. widget = %s\n",
- __func__, rx_mix1_inp - 4 , w[i].name);
+ if ((rx_mix1_inp < RX_MIX1_INP_SEL_RX1) ||
+ (rx_mix1_inp > RX_MIX1_INP_SEL_RX7)) {
+ pr_err("%s: Invalid TABLA_RX%u port. Dai ID is %d\n",
+ __func__, rx_mix1_inp - 5 , dai->id);
return -EINVAL;
}
rx_mix_1_reg_1 = TABLA_A_CDC_CONN_RX1_B1_CTL;
for (j = 0; j < NUM_INTERPOLATORS; j++) {
-
rx_mix_1_reg_2 = rx_mix_1_reg_1 + 1;
rx_mix_1_reg_1_val = snd_soc_read(codec,
- rx_mix_1_reg_1);
+ rx_mix_1_reg_1);
rx_mix_1_reg_2_val = snd_soc_read(codec,
- rx_mix_1_reg_2);
+ rx_mix_1_reg_2);
if (((rx_mix_1_reg_1_val & 0x0F) == rx_mix1_inp) ||
- (((rx_mix_1_reg_1_val >> 4) & 0x0F) == rx_mix1_inp)
- || ((rx_mix_1_reg_2_val & 0x0F) == rx_mix1_inp)) {
+ (((rx_mix_1_reg_1_val >> 4) & 0x0F) == rx_mix1_inp) ||
+ ((rx_mix_1_reg_2_val & 0x0F) == rx_mix1_inp)) {
rx_fs_reg = TABLA_A_CDC_RX1_B5_CTL + 8 * j;
- pr_debug("%s: %s connected to RX%u\n", __func__,
- w[i].name, j + 1);
+ pr_debug("%s: AIF_PB DAI(%d) connected to RX%u\n",
+ __func__, dai->id, j + 1);
pr_debug("%s: set RX%u sample rate to %u\n",
__func__, j + 1, sample_rate);
snd_soc_update_bits(codec, rx_fs_reg,
- 0xE0, rx_fs_rate_reg_val);
+ 0xE0, rx_fs_rate_reg_val);
if (comp_rx_path[j] < COMPANDER_MAX)
tabla->comp_fs[comp_rx_path[j]]
@@ -4162,26 +4393,26 @@
}
static int tabla_set_decimator_rate(struct snd_soc_dai *dai,
- u8 tx_fs_rate_reg_val, u32 sample_rate)
+ u8 tx_fs_rate_reg_val,
+ u32 sample_rate)
{
struct snd_soc_codec *codec = dai->codec;
- struct snd_soc_dapm_widget *w = tabla_dapm_aif_out_widgets;
-
- u32 i, tx_port;
+ struct wcd9xxx_ch *ch;
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ u32 tx_port;
u16 tx_port_reg, tx_fs_reg;
u8 tx_port_reg_val;
s8 decimator;
- for (i = 0; i < ARRAY_SIZE(tabla_dapm_aif_out_widgets); i++) {
+ list_for_each_entry(ch, &tabla->dai[dai->id].wcd9xxx_ch_list, list) {
- if (strncmp(dai->driver->capture.stream_name, w[i].sname, 12))
- continue;
-
- tx_port = w[i].shift;
+ tx_port = ch->port + 1;
+ pr_debug("%s: dai->id = %d, tx_port = %d",
+ __func__, dai->id, tx_port);
if ((tx_port < 1) || (tx_port > NUM_DECIMATORS)) {
- pr_err("%s: Invalid SLIM TX%u port. widget = %s\n",
- __func__, tx_port, w[i].name);
+ pr_err("%s: Invalid SLIM TX%u port. DAI ID is %d\n",
+ __func__, tx_port, dai->id);
return -EINVAL;
}
@@ -4210,38 +4441,38 @@
if (decimator) { /* SLIM_TX port has a DEC as input */
tx_fs_reg = TABLA_A_CDC_TX1_CLK_FS_CTL +
- 8 * (decimator - 1);
+ 8 * (decimator - 1);
pr_debug("%s: set DEC%u (-> SLIM_TX%u) rate to %u\n",
__func__, decimator, tx_port, sample_rate);
snd_soc_update_bits(codec, tx_fs_reg, 0x07,
- tx_fs_rate_reg_val);
+ tx_fs_rate_reg_val);
} else {
if ((tx_port_reg_val >= 0x1) &&
- (tx_port_reg_val <= 0x7)) {
+ (tx_port_reg_val <= 0x7)) {
pr_debug("%s: RMIX%u going to SLIM TX%u\n",
__func__, tx_port_reg_val, tx_port);
} else if ((tx_port_reg_val >= 0x8) &&
- (tx_port_reg_val <= 0x11)) {
+ (tx_port_reg_val <= 0x11)) {
pr_err("%s: ERROR: Should not be here\n",
- __func__);
- pr_err("%s: ERROR: DEC connected to SLIM TX%u\n"
- , __func__, tx_port);
+ __func__);
+ pr_err("%s: ERROR: DEC connected to SLIM TX%u\n",
+ __func__, tx_port);
return -EINVAL;
} else if (tx_port_reg_val == 0) {
pr_debug("%s: no signal to SLIM TX%u\n",
- __func__, tx_port);
+ __func__, tx_port);
} else {
- pr_err("%s: ERROR: wrong signal to SLIM TX%u\n"
- , __func__, tx_port);
- pr_err("%s: ERROR: wrong signal = %u\n"
- , __func__, tx_port_reg_val);
+ pr_err("%s: ERROR: wrong signal to SLIM TX%u\n",
+ __func__, tx_port);
+ pr_err("%s: ERROR: wrong signal = %u\n",
+ __func__, tx_port_reg_val);
return -EINVAL;
}
}
@@ -4250,8 +4481,8 @@
}
static int tabla_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(dai->codec);
@@ -4260,8 +4491,8 @@
int ret;
pr_debug("%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__,
- dai->name, dai->id, params_rate(params),
- params_channels(params));
+ dai->name, dai->id, params_rate(params),
+ params_channels(params));
switch (params_rate(params)) {
case 8000:
@@ -4296,7 +4527,7 @@
break;
default:
pr_err("%s: Invalid sampling rate %d\n", __func__,
- params_rate(params));
+ params_rate(params));
return -EINVAL;
}
@@ -4304,10 +4535,10 @@
case SNDRV_PCM_STREAM_CAPTURE:
ret = tabla_set_decimator_rate(dai, tx_fs_rate_reg_val,
- params_rate(params));
+ params_rate(params));
if (ret < 0) {
pr_err("%s: set decimator rate failed %d\n", __func__,
- ret);
+ ret);
return ret;
}
@@ -4322,24 +4553,34 @@
TABLA_A_CDC_CLK_TX_I2S_CTL, 0x20, 0x00);
break;
default:
- pr_err("%s: invalid TX format %u\n", __func__,
- params_format(params));
+ pr_err("%s: Invalid format %d\n", __func__,
+ params_format(params));
return -EINVAL;
}
snd_soc_update_bits(codec, TABLA_A_CDC_CLK_TX_I2S_CTL,
- 0x07, tx_fs_rate_reg_val);
+ 0x07, tx_fs_rate_reg_val);
} else {
- tabla->dai[dai->id - 1].rate = params_rate(params);
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ tabla->dai[dai->id].bit_width = 16;
+ break;
+ default:
+ pr_err("%s: Invalid TX format %d\n", __func__,
+ params_format(params));
+ return -EINVAL;
+ }
+ tabla->dai[dai->id].rate = params_rate(params);
}
break;
case SNDRV_PCM_STREAM_PLAYBACK:
ret = tabla_set_interpolator_rate(dai, rx_fs_rate_reg_val,
- compander_fs, params_rate(params));
+ compander_fs,
+ params_rate(params));
if (ret < 0) {
pr_err("%s: set decimator rate failed %d\n", __func__,
- ret);
+ ret);
return ret;
}
@@ -4354,20 +4595,29 @@
TABLA_A_CDC_CLK_RX_I2S_CTL, 0x20, 0x00);
break;
default:
- pr_err("%s: invalid RX format %u\n", __func__,
- params_format(params));
+ pr_err("%s: Invalid RX format %d\n", __func__,
+ params_format(params));
return -EINVAL;
}
snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_I2S_CTL,
0x03, (rx_fs_rate_reg_val >> 0x05));
} else {
- tabla->dai[dai->id - 1].rate = params_rate(params);
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ tabla->dai[dai->id].bit_width = 16;
+ break;
+ default:
+ pr_err("%s: Invalid format %d\n", __func__,
+ params_format(params));
+ return -EINVAL;
+ }
+ tabla->dai[dai->id].rate = params_rate(params);
}
break;
default:
pr_err("%s: Invalid stream type %d\n", __func__,
- substream->stream);
+ substream->stream);
return -EINVAL;
}
return 0;
@@ -4473,7 +4723,7 @@
static struct snd_soc_dai_driver tabla_i2s_dai[] = {
{
.name = "tabla_i2s_rx1",
- .id = 1,
+ .id = AIF1_PB,
.playback = {
.stream_name = "AIF1 Playback",
.rates = WCD9310_RATES,
@@ -4487,7 +4737,7 @@
},
{
.name = "tabla_i2s_tx1",
- .id = 2,
+ .id = AIF1_CAP,
.capture = {
.stream_name = "AIF1 Capture",
.rates = WCD9310_RATES,
@@ -4502,15 +4752,16 @@
};
static int tabla_codec_enable_chmask(struct tabla_priv *tabla_p,
- int event, int index)
+ int event, int index)
{
int ret = 0;
- u32 k = 0;
+ struct wcd9xxx_ch *ch;
+
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- for (k = 0; k < tabla_p->dai[index].ch_tot; k++) {
- ret = wcd9xxx_get_slave_port(
- tabla_p->dai[index].ch_num[k]);
+ list_for_each_entry(ch,
+ &tabla_p->dai[index].wcd9xxx_ch_list, list) {
+ ret = wcd9xxx_get_slave_port(ch->ch_num);
if (ret < 0) {
pr_err("%s: Invalid slave port ID: %d\n",
__func__, ret);
@@ -4519,7 +4770,6 @@
}
tabla_p->dai[index].ch_mask |= 1 << ret;
}
- ret = 0;
break;
case SND_SOC_DAPM_POST_PMD:
ret = wait_event_timeout(tabla_p->dai[index].dai_wait,
@@ -4529,191 +4779,134 @@
pr_err("%s: Slim close tx/rx wait timeout\n",
__func__);
ret = -EINVAL;
- } else
- ret = 0;
+ }
break;
}
return ret;
}
static int tabla_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol,
+ int event)
{
- struct wcd9xxx *tabla;
+ struct wcd9xxx *core;
struct snd_soc_codec *codec = w->codec;
struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(codec);
- u32 j = 0;
- int ret = 0;
- codec->control_data = dev_get_drvdata(codec->dev->parent);
- tabla = codec->control_data;
+ u32 ret = 0;
+ struct wcd9xxx_codec_dai_data *dai;
+
+ core = dev_get_drvdata(codec->dev->parent);
+
+ pr_debug("%s: event called! codec name %s num_dai %d\n"
+ "stream name %s event %d\n",
+ __func__, w->codec->name, w->codec->num_dai,
+ w->sname, event);
/* Execute the callback only if interface type is slimbus */
if (tabla_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
- if (event == SND_SOC_DAPM_POST_PMD && (tabla != NULL) &&
- (tabla->dev != NULL) &&
- (tabla->dev->parent != NULL)) {
- pm_runtime_mark_last_busy(tabla->dev->parent);
- pm_runtime_put(tabla->dev->parent);
+ if (event == SND_SOC_DAPM_POST_PMD && (core != NULL) &&
+ (core->dev != NULL) &&
+ (core->dev->parent != NULL)) {
+ pm_runtime_mark_last_busy(core->dev->parent);
+ pm_runtime_put(core->dev->parent);
}
return 0;
}
-
- pr_debug("%s: %s %d\n", __func__, w->name, event);
+ pr_debug("%s: w->name %s w->shift %d event %d\n",
+ __func__, w->name, w->shift, event);
+ dai = &tabla_p->dai[w->shift];
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- for (j = 0; j < ARRAY_SIZE(tabla_dai); j++) {
- if ((tabla_dai[j].id == AIF1_CAP) ||
- (tabla_dai[j].id == AIF2_CAP) ||
- (tabla_dai[j].id == AIF3_CAP))
- continue;
- if (!strncmp(w->sname,
- tabla_dai[j].playback.stream_name, 13)) {
- ++tabla_p->dai[j].ch_act;
- break;
- }
- }
- if (tabla_p->dai[j].ch_act == tabla_p->dai[j].ch_tot) {
- ret = tabla_codec_enable_chmask(tabla_p,
- SND_SOC_DAPM_POST_PMU,
- j);
- ret = wcd9xxx_cfg_slim_sch_rx(tabla,
- tabla_p->dai[j].ch_num,
- tabla_p->dai[j].ch_tot,
- tabla_p->dai[j].rate);
- }
+ ret = tabla_codec_enable_chmask(tabla_p, SND_SOC_DAPM_POST_PMU,
+ w->shift);
+ ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+ dai->rate, dai->bit_width,
+ &dai->grph);
break;
case SND_SOC_DAPM_POST_PMD:
- for (j = 0; j < ARRAY_SIZE(tabla_dai); j++) {
- if ((tabla_dai[j].id == AIF1_CAP) ||
- (tabla_dai[j].id == AIF2_CAP) ||
- (tabla_dai[j].id == AIF3_CAP))
- continue;
- if (!strncmp(w->sname,
- tabla_dai[j].playback.stream_name, 13)) {
- if (tabla_p->dai[j].ch_act)
- --tabla_p->dai[j].ch_act;
- break;
- }
+ ret = wcd9xxx_close_slim_sch_rx(core,
+ &dai->wcd9xxx_ch_list,
+ dai->grph);
+ ret = tabla_codec_enable_chmask(tabla_p, SND_SOC_DAPM_POST_PMD,
+ w->shift);
+ if (ret < 0) {
+ ret = wcd9xxx_disconnect_port(core,
+ &dai->wcd9xxx_ch_list,
+ dai->grph);
+ pr_info("%s: Disconnect RX port, ret = %d\n",
+ __func__, ret);
}
- if (!tabla_p->dai[j].ch_act) {
- ret = wcd9xxx_close_slim_sch_rx(tabla,
- tabla_p->dai[j].ch_num,
- tabla_p->dai[j].ch_tot);
- ret = tabla_codec_enable_chmask(tabla_p,
- SND_SOC_DAPM_POST_PMD,
- j);
- if (ret < 0) {
- ret = wcd9xxx_disconnect_port(tabla,
- tabla_p->dai[j].ch_num,
- tabla_p->dai[j].ch_tot,
- 1);
- pr_info("%s: Disconnect RX port ret = %d\n",
- __func__, ret);
- }
- tabla_p->dai[j].rate = 0;
- memset(tabla_p->dai[j].ch_num, 0, (sizeof(u32)*
- tabla_p->dai[j].ch_tot));
- tabla_p->dai[j].ch_tot = 0;
-
- if ((tabla != NULL) &&
- (tabla->dev != NULL) &&
- (tabla->dev->parent != NULL)) {
- pm_runtime_mark_last_busy(tabla->dev->parent);
- pm_runtime_put(tabla->dev->parent);
- }
+ if ((core != NULL) &&
+ (core->dev != NULL) &&
+ (core->dev->parent != NULL)) {
+ pm_runtime_mark_last_busy(core->dev->parent);
+ pm_runtime_put(core->dev->parent);
}
+ break;
}
+
return ret;
}
static int tabla_codec_enable_slimtx(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol,
+ int event)
{
- struct wcd9xxx *tabla;
+ struct wcd9xxx *core;
struct snd_soc_codec *codec = w->codec;
struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(codec);
- /* index to the DAI ID, for now hardcoding */
- u32 j = 0;
- int ret = 0;
+ u32 ret = 0;
+ struct wcd9xxx_codec_dai_data *dai;
- codec->control_data = dev_get_drvdata(codec->dev->parent);
- tabla = codec->control_data;
+ core = dev_get_drvdata(codec->dev->parent);
+
+ pr_debug("%s: event called! codec name %s num_dai %d\n"
+ "stream name %s\n", __func__, w->codec->name,
+ w->codec->num_dai, w->sname);
/* Execute the callback only if interface type is slimbus */
if (tabla_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
- if (event == SND_SOC_DAPM_POST_PMD && (tabla != NULL) &&
- (tabla->dev != NULL) &&
- (tabla->dev->parent != NULL)) {
- pm_runtime_mark_last_busy(tabla->dev->parent);
- pm_runtime_put(tabla->dev->parent);
+ if (event == SND_SOC_DAPM_POST_PMD && (core != NULL) &&
+ (core->dev != NULL) &&
+ (core->dev->parent != NULL)) {
+ pm_runtime_mark_last_busy(core->dev->parent);
+ pm_runtime_put(core->dev->parent);
}
return 0;
}
pr_debug("%s(): %s %d\n", __func__, w->name, event);
+ dai = &tabla_p->dai[w->shift];
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- for (j = 0; j < ARRAY_SIZE(tabla_dai); j++) {
- if (tabla_dai[j].id == AIF1_PB ||
- tabla_dai[j].id == AIF2_PB ||
- tabla_dai[j].id == AIF3_PB)
- continue;
- if (!strncmp(w->sname,
- tabla_dai[j].capture.stream_name, 13)) {
- ++tabla_p->dai[j].ch_act;
- break;
- }
- }
- if (tabla_p->dai[j].ch_act == tabla_p->dai[j].ch_tot) {
- ret = tabla_codec_enable_chmask(tabla_p,
- SND_SOC_DAPM_POST_PMU,
- j);
- ret = wcd9xxx_cfg_slim_sch_tx(tabla,
- tabla_p->dai[j].ch_num,
- tabla_p->dai[j].ch_tot,
- tabla_p->dai[j].rate);
- }
+ ret = tabla_codec_enable_chmask(tabla_p, SND_SOC_DAPM_POST_PMU,
+ w->shift);
+ ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->rate,
+ dai->bit_width,
+ &dai->grph);
break;
case SND_SOC_DAPM_POST_PMD:
- for (j = 0; j < ARRAY_SIZE(tabla_dai); j++) {
- if (tabla_dai[j].id == AIF1_PB ||
- tabla_dai[j].id == AIF2_PB ||
- tabla_dai[j].id == AIF3_PB)
- continue;
- if (!strncmp(w->sname,
- tabla_dai[j].capture.stream_name, 13)) {
- --tabla_p->dai[j].ch_act;
- break;
- }
+ ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->grph);
+ ret = tabla_codec_enable_chmask(tabla_p, SND_SOC_DAPM_POST_PMD,
+ w->shift);
+ if (ret < 0) {
+ ret = wcd9xxx_disconnect_port(core,
+ &dai->wcd9xxx_ch_list,
+ dai->grph);
+ pr_info("%s: Disconnect TX port, ret = %d\n",
+ __func__, ret);
}
- if (!tabla_p->dai[j].ch_act) {
- ret = wcd9xxx_close_slim_sch_tx(tabla,
- tabla_p->dai[j].ch_num,
- tabla_p->dai[j].ch_tot);
- ret = tabla_codec_enable_chmask(tabla_p,
- SND_SOC_DAPM_POST_PMD,
- j);
- if (ret < 0) {
- ret = wcd9xxx_disconnect_port(tabla,
- tabla_p->dai[j].ch_num,
- tabla_p->dai[j].ch_tot, 0);
- pr_info("%s: Disconnect TX port, ret = %d\n",
- __func__, ret);
- }
-
- tabla_p->dai[j].rate = 0;
- memset(tabla_p->dai[j].ch_num, 0, (sizeof(u32)*
- tabla_p->dai[j].ch_tot));
- tabla_p->dai[j].ch_tot = 0;
- if ((tabla != NULL) &&
- (tabla->dev != NULL) &&
- (tabla->dev->parent != NULL)) {
- pm_runtime_mark_last_busy(tabla->dev->parent);
- pm_runtime_put(tabla->dev->parent);
- }
+ if ((core != NULL) &&
+ (core->dev != NULL) &&
+ (core->dev->parent != NULL)) {
+ pm_runtime_mark_last_busy(core->dev->parent);
+ pm_runtime_put(core->dev->parent);
}
+ break;
}
return ret;
}
@@ -4732,6 +4925,38 @@
SND_SOC_DAPM_MIXER("DAC1", SND_SOC_NOPM, 0, 0, dac1_switch,
ARRAY_SIZE(dac1_switch)),
+ SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
+ AIF1_PB, 0, tabla_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM,
+ AIF2_PB, 0, tabla_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM,
+ AIF3_PB, 0, tabla_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, TABLA_RX1, 0,
+ &slim_rx_mux[TABLA_RX1]),
+ SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, TABLA_RX2, 0,
+ &slim_rx_mux[TABLA_RX2]),
+ SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, TABLA_RX3, 0,
+ &slim_rx_mux[TABLA_RX3]),
+ SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, TABLA_RX4, 0,
+ &slim_rx_mux[TABLA_RX4]),
+ SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, TABLA_RX5, 0,
+ &slim_rx_mux[TABLA_RX5]),
+ SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, TABLA_RX6, 0,
+ &slim_rx_mux[TABLA_RX6]),
+ SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, TABLA_RX7, 0,
+ &slim_rx_mux[TABLA_RX7]),
+
+ SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0),
/* Headphone */
SND_SOC_DAPM_OUTPUT("HEADPHONE"),
SND_SOC_DAPM_PGA_E("HPHL", TABLA_A_RX_HPH_CNP_EN, 5, 0, NULL, 0,
@@ -5011,16 +5236,47 @@
tabla_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, 0, 0, &sb_tx1_mux),
- SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, 0, 0, &sb_tx2_mux),
- SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, 0, 0, &sb_tx3_mux),
- SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, 0, 0, &sb_tx4_mux),
- SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, 0, 0, &sb_tx5_mux),
- SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, 0, 0, &sb_tx6_mux),
- SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, 0, 0, &sb_tx7_mux),
- SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, 0, 0, &sb_tx8_mux),
- SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, 0, 0, &sb_tx9_mux),
- SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, 0, 0, &sb_tx10_mux),
+ SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
+ AIF1_CAP, 0, tabla_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM,
+ AIF2_CAP, 0, tabla_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM,
+ AIF3_CAP, 0, tabla_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
+ aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0,
+ aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0,
+ aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
+
+ SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, TABLA_TX1, 0,
+ &sb_tx1_mux),
+ SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, TABLA_TX2, 0,
+ &sb_tx2_mux),
+ SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, TABLA_TX3, 0,
+ &sb_tx3_mux),
+ SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, TABLA_TX4, 0,
+ &sb_tx4_mux),
+ SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, TABLA_TX5, 0,
+ &sb_tx5_mux),
+ SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, TABLA_TX6, 0,
+ &sb_tx6_mux),
+ SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, TABLA_TX7, 0,
+ &sb_tx7_mux),
+ SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, TABLA_TX8, 0,
+ &sb_tx8_mux),
+ SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, TABLA_TX9, 0,
+ &sb_tx9_mux),
+ SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, TABLA_TX10, 0,
+ &sb_tx10_mux),
/* Digital Mic Inputs */
SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
@@ -5120,7 +5376,7 @@
short bias_value;
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
if (noreldetection)
tabla_turn_onoff_rel_detection(codec, false);
@@ -5156,7 +5412,7 @@
if (noreldetection)
tabla_turn_onoff_rel_detection(codec, true);
- wcd9xxx_enable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
return bias_value;
}
@@ -5345,9 +5601,9 @@
}
tabla_set_and_turnoff_hph_padac(codec);
hphocp_off_report(tabla, SND_JACK_OC_HPHR,
- TABLA_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
hphocp_off_report(tabla, SND_JACK_OC_HPHL,
- TABLA_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
tabla->current_plug = PLUG_TYPE_NONE;
tabla->mbhc_polling_active = false;
} else {
@@ -5510,7 +5766,7 @@
snd_soc_update_bits(codec, tabla->reg_addr.micb_4_mbhc, 0x3,
tabla->mbhc_cfg.micbias);
- wcd9xxx_enable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
pr_debug("%s: leave\n", __func__);
return 0;
@@ -5640,7 +5896,7 @@
tabla = snd_soc_codec_get_drvdata(codec);
calibration = tabla->mbhc_cfg.calibration;
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
tabla_turn_onoff_rel_detection(codec, false);
/* First compute the DCE / STA wait times
@@ -5746,7 +6002,7 @@
snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x84);
usleep_range(100, 100);
- wcd9xxx_enable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
tabla_turn_onoff_rel_detection(codec, true);
}
@@ -6334,7 +6590,7 @@
0x10);
} else {
wcd9xxx_disable_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
tabla->hph_status |= SND_JACK_OC_HPHL;
if (tabla->mbhc_cfg.headset_jack)
tabla_snd_soc_jack_report(tabla,
@@ -6368,7 +6624,7 @@
0x10);
} else {
wcd9xxx_disable_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
tabla->hph_status |= SND_JACK_OC_HPHR;
if (tabla->mbhc_cfg.headset_jack)
tabla_snd_soc_jack_report(tabla,
@@ -6977,7 +7233,7 @@
wcd9xxx_unlock_sleep(core);
} else {
wcd9xxx_enable_irq(codec->control_data,
- TABLA_IRQ_MBHC_INSERTION);
+ WCD9XXX_IRQ_MBHC_INSERTION);
pr_err("%s: Error detecting plug insertion\n",
__func__);
}
@@ -7015,7 +7271,7 @@
pr_debug("%s: enter\n", __func__);
TABLA_ACQUIRE_LOCK(priv->codec_resource_lock);
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
is_mb_trigger = !!(snd_soc_read(codec, priv->mbhc_bias_regs.mbhc_reg) &
0x10);
@@ -7262,7 +7518,8 @@
snd_soc_update_bits(codec, tabla->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x13, 0x00);
snd_soc_update_bits(codec, tabla->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
- wcd9xxx_disable_irq_sync(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+ wcd9xxx_disable_irq_sync(codec->control_data,
+ WCD9XXX_IRQ_MBHC_INSERTION);
tabla_codec_detect_plug_type(codec);
wcd9xxx_unlock_sleep(tabla_core);
}
@@ -7485,9 +7742,9 @@
if (!IS_ERR_VALUE(ret)) {
snd_soc_update_bits(codec, TABLA_A_RX_HPH_OCP_CTL, 0x10, 0x10);
wcd9xxx_enable_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
wcd9xxx_enable_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
if (tabla->mbhc_cfg.gpio) {
ret = request_threaded_irq(tabla->mbhc_cfg.gpio_irq,
@@ -7605,7 +7862,6 @@
int i, j, port_id, k, ch_mask_temp;
unsigned long slimbus_value;
u8 val;
-
for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++) {
slimbus_value = wcd9xxx_interface_reg_read(codec->control_data,
TABLA_SLIM_PGD_PORT_INT_STATUS0 + i);
@@ -7619,17 +7875,18 @@
pr_err_ratelimited("underflow error on port %x,"
" value %x\n", i*8 + j, val);
if (val & 0x4) {
- pr_debug("%s: port %x disconnect value %x\n",
- __func__, i*8 + j, val);
port_id = i*8 + j;
for (k = 0; k < ARRAY_SIZE(tabla_dai); k++) {
ch_mask_temp = 1 << port_id;
+ pr_debug("%s: tabla_p->dai[%d].ch_mask = 0x%x\n",
+ __func__, k,
+ tabla_p->dai[k].ch_mask);
if (ch_mask_temp &
tabla_p->dai[k].ch_mask) {
tabla_p->dai[k].ch_mask &=
- ~ch_mask_temp;
+ ~ch_mask_temp;
if (!tabla_p->dai[k].ch_mask)
- wake_up(
+ wake_up(
&tabla_p->dai[k].dai_wait);
}
}
@@ -8111,7 +8368,7 @@
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret = 0;
int i;
- int ch_cnt;
+ void *ptr = NULL;
codec->control_data = dev_get_drvdata(codec->dev->parent);
control = codec->control_data;
@@ -8171,8 +8428,6 @@
goto err_pdata;
}
-// snd_soc_add_codec_controls(codec, tabla_snd_controls,
-// ARRAY_SIZE(tabla_snd_controls));
if (TABLA_IS_1_X(control->version))
snd_soc_add_codec_controls(codec, tabla_1_x_snd_controls,
ARRAY_SIZE(tabla_1_x_snd_controls));
@@ -8180,15 +8435,6 @@
snd_soc_add_codec_controls(codec, tabla_2_higher_snd_controls,
ARRAY_SIZE(tabla_2_higher_snd_controls));
-// snd_soc_dapm_new_controls(dapm, tabla_dapm_widgets,
-// ARRAY_SIZE(tabla_dapm_widgets));
-
- snd_soc_dapm_new_controls(dapm, tabla_dapm_aif_in_widgets,
- ARRAY_SIZE(tabla_dapm_aif_in_widgets));
-
- snd_soc_dapm_new_controls(dapm, tabla_dapm_aif_out_widgets,
- ARRAY_SIZE(tabla_dapm_aif_out_widgets));
-
if (TABLA_IS_1_X(control->version))
snd_soc_dapm_new_controls(dapm, tabla_1_x_dapm_widgets,
ARRAY_SIZE(tabla_1_x_dapm_widgets));
@@ -8196,13 +8442,35 @@
snd_soc_dapm_new_controls(dapm, tabla_2_higher_dapm_widgets,
ARRAY_SIZE(tabla_2_higher_dapm_widgets));
+
+ ptr = kmalloc((sizeof(tabla_rx_chs) +
+ sizeof(tabla_tx_chs)), GFP_KERNEL);
+ if (!ptr) {
+ pr_err("%s: no mem for slim chan ctl data\n", __func__);
+ ret = -ENOMEM;
+ goto err_nomem_slimch;
+ }
if (tabla->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
snd_soc_dapm_new_controls(dapm, tabla_dapm_i2s_widgets,
ARRAY_SIZE(tabla_dapm_i2s_widgets));
snd_soc_dapm_add_routes(dapm, audio_i2s_map,
ARRAY_SIZE(audio_i2s_map));
+ for (i = 0; i < ARRAY_SIZE(tabla_i2s_dai); i++)
+ INIT_LIST_HEAD(&tabla->dai[i].wcd9xxx_ch_list);
+ } else if (tabla->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ for (i = 0; i < NUM_CODEC_DAIS; i++) {
+ INIT_LIST_HEAD(&tabla->dai[i].wcd9xxx_ch_list);
+ init_waitqueue_head(&tabla->dai[i].dai_wait);
+ }
}
-// snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
+
+ control->num_rx_port = TABLA_RX_MAX;
+ control->rx_chs = ptr;
+ memcpy(control->rx_chs, tabla_rx_chs, sizeof(tabla_rx_chs));
+ control->num_tx_port = TABLA_TX_MAX;
+ control->tx_chs = ptr + sizeof(tabla_rx_chs);
+ memcpy(control->tx_chs, tabla_tx_chs, sizeof(tabla_tx_chs));
+
if (TABLA_IS_1_X(control->version)) {
snd_soc_dapm_add_routes(dapm, tabla_1_x_lineout_2_to_4_map,
@@ -8218,44 +8486,50 @@
snd_soc_dapm_sync(dapm);
- ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION,
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_INSERTION,
tabla_hs_insert_irq, "Headset insert detect", tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_MBHC_INSERTION);
+ WCD9XXX_IRQ_MBHC_INSERTION);
goto err_insert_irq;
}
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
- ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL,
- tabla_hs_remove_irq, "Headset remove detect", tabla);
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_REMOVAL,
+ tabla_hs_remove_irq,
+ "Headset remove detect", tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_MBHC_REMOVAL);
+ WCD9XXX_IRQ_MBHC_REMOVAL);
goto err_remove_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL,
- tabla_dce_handler, "DC Estimation detect", tabla);
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_POTENTIAL,
+ tabla_dce_handler, "DC Estimation detect",
+ tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_MBHC_POTENTIAL);
+ WCD9XXX_IRQ_MBHC_POTENTIAL);
goto err_potential_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE,
- tabla_release_handler, "Button Release detect", tabla);
+ ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE,
+ tabla_release_handler,
+ "Button Release detect", tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_MBHC_RELEASE);
+ WCD9XXX_IRQ_MBHC_RELEASE);
goto err_release_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_SLIMBUS,
- tabla_slimbus_irq, "SLIMBUS Slave", tabla);
+ ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
+ tabla_slimbus_irq, "SLIMBUS Slave", tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_SLIMBUS);
+ WCD9XXX_IRQ_SLIMBUS);
goto err_slimbus_irq;
}
@@ -8264,24 +8538,26 @@
TABLA_SLIM_PGD_PORT_INT_EN0 + i, 0xFF);
ret = wcd9xxx_request_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPL_FAULT, tabla_hphl_ocp_irq,
- "HPH_L OCP detect", tabla);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+ tabla_hphl_ocp_irq,
+ "HPH_L OCP detect", tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
goto err_hphl_ocp_irq;
}
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_HPH_PA_OCPL_FAULT);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
ret = wcd9xxx_request_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPR_FAULT, tabla_hphr_ocp_irq,
- "HPH_R OCP detect", tabla);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+ tabla_hphr_ocp_irq,
+ "HPH_R OCP detect", tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
goto err_hphr_ocp_irq;
}
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_HPH_PA_OCPR_FAULT);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
/*
* Register suspend lock and notifier to resend edge triggered
@@ -8291,33 +8567,6 @@
"tabla_gpio_irq_resend");
tabla->gpio_irq_resend = false;
- for (i = 0; i < ARRAY_SIZE(tabla_dai); i++) {
- switch (tabla_dai[i].id) {
- case AIF1_PB:
- ch_cnt = tabla_dai[i].playback.channels_max;
- break;
- case AIF1_CAP:
- ch_cnt = tabla_dai[i].capture.channels_max;
- break;
- case AIF2_PB:
- ch_cnt = tabla_dai[i].playback.channels_max;
- break;
- case AIF2_CAP:
- ch_cnt = tabla_dai[i].capture.channels_max;
- break;
- case AIF3_PB:
- ch_cnt = tabla_dai[i].playback.channels_max;
- break;
- case AIF3_CAP:
- ch_cnt = tabla_dai[i].capture.channels_max;
- break;
- default:
- continue;
- }
- tabla->dai[i].ch_num = kzalloc((sizeof(unsigned int)*
- ch_cnt), GFP_KERNEL);
- init_waitqueue_head(&tabla->dai[i].dai_wait);
- }
#ifdef CONFIG_DEBUG_FS
if (ret == 0) {
@@ -8334,43 +8583,46 @@
err_hphr_ocp_irq:
wcd9xxx_free_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPL_FAULT, tabla);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, tabla);
err_hphl_ocp_irq:
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_SLIMBUS, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, tabla);
err_slimbus_irq:
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, tabla);
err_release_irq:
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL,
+ tabla);
err_potential_irq:
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, tabla);
err_remove_irq:
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION,
+ tabla);
err_insert_irq:
err_pdata:
+ kfree(ptr);
+err_nomem_slimch:
mutex_destroy(&tabla->codec_resource_lock);
kfree(tabla);
return ret;
}
static int tabla_codec_remove(struct snd_soc_codec *codec)
{
- int i;
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
wake_lock_destroy(&tabla->irq_resend_wlock);
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_SLIMBUS, tabla);
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE, tabla);
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL, tabla);
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL, tabla);
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL,
+ tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION,
+ tabla);
TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
tabla_codec_disable_clock_block(codec);
TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
tabla_codec_enable_bandgap(codec, TABLA_BANDGAP_OFF);
if (tabla->mbhc_fw)
release_firmware(tabla->mbhc_fw);
- for (i = 0; i < ARRAY_SIZE(tabla_dai); i++)
- kfree(tabla->dai[i].ch_num);
mutex_destroy(&tabla->codec_resource_lock);
#ifdef CONFIG_DEBUG_FS
debugfs_remove(tabla->debugfs_poke);
diff --git a/sound/soc/codecs/wcd9310.h b/sound/soc/codecs/wcd9310.h
index 4c9f8b4..98c1835 100644
--- a/sound/soc/codecs/wcd9310.h
+++ b/sound/soc/codecs/wcd9310.h
@@ -252,3 +252,29 @@
sizeof(cfg_ptr->_alpha[0]))))
+/* Number of input and output Slimbus port */
+enum {
+ TABLA_RX1 = 0,
+ TABLA_RX2,
+ TABLA_RX3,
+ TABLA_RX4,
+ TABLA_RX5,
+ TABLA_RX6,
+ TABLA_RX7,
+ TABLA_RX_MAX,
+};
+
+enum {
+ TABLA_TX1 = 0,
+ TABLA_TX2,
+ TABLA_TX3,
+ TABLA_TX4,
+ TABLA_TX5,
+ TABLA_TX6,
+ TABLA_TX7,
+ TABLA_TX8,
+ TABLA_TX9,
+ TABLA_TX10,
+ TABLA_TX_MAX,
+};
+
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index a0a272f..886e4d3 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -33,81 +33,46 @@
#include <linux/kernel.h>
#include <linux/gpio.h>
#include "wcd9320.h"
+#include "wcd9xxx-resmgr.h"
#define WCD9320_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
-
#define NUM_DECIMATORS 10
#define NUM_INTERPOLATORS 7
#define BITS_PER_REG 8
-#define TAIKO_CFILT_FAST_MODE 0x00
-#define TAIKO_CFILT_SLOW_MODE 0x40
-#define MBHC_FW_READ_ATTEMPTS 15
-#define MBHC_FW_READ_TIMEOUT 2000000
-
-enum {
- MBHC_USE_HPHL_TRIGGER = 1,
- MBHC_USE_MB_TRIGGER = 2
-};
-
-#define MBHC_NUM_DCE_PLUG_DETECT 3
-#define NUM_ATTEMPTS_INSERT_DETECT 25
-#define NUM_ATTEMPTS_TO_REPORT 5
-
-#define TAIKO_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
- SND_JACK_OC_HPHR | SND_JACK_UNSUPPORTED)
+#define TAIKO_TX_PORT_NUMBER 16
#define TAIKO_I2S_MASTER_MODE_MASK 0x08
-#define TAIKO_OCP_ATTEMPT 1
-
-#define AIF1_PB 1
-#define AIF1_CAP 2
-#define AIF2_PB 3
-#define AIF2_CAP 4
-#define AIF3_CAP 5
-#define AIF3_PB 6
-
-#define NUM_CODEC_DAIS 6
-#define TAIKO_COMP_DIGITAL_GAIN_OFFSET 3
-
-struct taiko_codec_dai_data {
- u32 rate;
- u32 *ch_num;
- u32 ch_act;
- u32 ch_tot;
+enum {
+ AIF1_PB = 0,
+ AIF1_CAP,
+ AIF2_PB,
+ AIF2_CAP,
+ AIF3_PB,
+ AIF3_CAP,
+ NUM_CODEC_DAIS,
};
-#define TAIKO_MCLK_RATE_12288KHZ 12288000
-#define TAIKO_MCLK_RATE_9600KHZ 9600000
+enum {
+ RX_MIX1_INP_SEL_ZERO = 0,
+ RX_MIX1_INP_SEL_SRC1,
+ RX_MIX1_INP_SEL_SRC2,
+ RX_MIX1_INP_SEL_IIR1,
+ RX_MIX1_INP_SEL_IIR2,
+ RX_MIX1_INP_SEL_RX1,
+ RX_MIX1_INP_SEL_RX2,
+ RX_MIX1_INP_SEL_RX3,
+ RX_MIX1_INP_SEL_RX4,
+ RX_MIX1_INP_SEL_RX5,
+ RX_MIX1_INP_SEL_RX6,
+ RX_MIX1_INP_SEL_RX7,
+ RX_MIX1_INP_SEL_AUXRX,
+};
-#define TAIKO_FAKE_INS_THRESHOLD_MS 2500
-#define TAIKO_FAKE_REMOVAL_MIN_PERIOD_MS 50
-
-#define TAIKO_MBHC_BUTTON_MIN 0x8000
-
-#define TAIKO_MBHC_FAKE_INSERT_LOW 10
-#define TAIKO_MBHC_FAKE_INSERT_HIGH 80
-#define TAIKO_MBHC_FAKE_INS_HIGH_NO_GPIO 150
-
-#define TAIKO_MBHC_STATUS_REL_DETECTION 0x0C
-
-#define TAIKO_MBHC_GPIO_REL_DEBOUNCE_TIME_MS 200
-
-#define TAIKO_MBHC_FAKE_INS_DELTA_MV 200
-#define TAIKO_MBHC_FAKE_INS_DELTA_SCALED_MV 300
-
-#define TAIKO_HS_DETECT_PLUG_TIME_MS (5 * 1000)
-#define TAIKO_HS_DETECT_PLUG_INERVAL_MS 100
-
-#define TAIKO_GPIO_IRQ_DEBOUNCE_TIME_US 5000
-
-#define TAIKO_MBHC_GND_MIC_SWAP_THRESHOLD 2
-
-#define TAIKO_ACQUIRE_LOCK(x) do { mutex_lock(&x); } while (0)
-#define TAIKO_RELEASE_LOCK(x) do { mutex_unlock(&x); } while (0)
+#define TAIKO_COMP_DIGITAL_GAIN_OFFSET 3
static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
@@ -115,21 +80,6 @@
static struct snd_soc_dai_driver taiko_dai[];
static const DECLARE_TLV_DB_SCALE(aux_pga_gain, 0, 2, 0);
-enum taiko_bandgap_type {
- TAIKO_BANDGAP_OFF = 0,
- TAIKO_BANDGAP_AUDIO_MODE,
- TAIKO_BANDGAP_MBHC_MODE,
-};
-
-struct mbhc_micbias_regs {
- u16 cfilt_val;
- u16 cfilt_ctl;
- u16 mbhc_reg;
- u16 int_rbias;
- u16 ctl_reg;
- u8 cfilt_sel;
-};
-
/* Codec supports 2 IIR filters */
enum {
IIR1 = 0,
@@ -162,72 +112,12 @@
COMPANDER_FS_MAX,
};
-/* Flags to track of PA and DAC state.
- * PA and DAC should be tracked separately as AUXPGA loopback requires
- * only PA to be turned on without DAC being on. */
-enum taiko_priv_ack_flags {
- TAIKO_HPHL_PA_OFF_ACK = 0,
- TAIKO_HPHR_PA_OFF_ACK,
- TAIKO_HPHL_DAC_OFF_ACK,
- TAIKO_HPHR_DAC_OFF_ACK
-};
-
-
struct comp_sample_dependent_params {
u32 peak_det_timeout;
u32 rms_meter_div_fact;
u32 rms_meter_resamp_fact;
};
-/* Data used by MBHC */
-struct mbhc_internal_cal_data {
- u16 dce_z;
- u16 dce_mb;
- u16 sta_z;
- u16 sta_mb;
- u32 t_sta_dce;
- u32 t_dce;
- u32 t_sta;
- u32 micb_mv;
- u16 v_ins_hu;
- u16 v_ins_h;
- u16 v_b1_hu;
- u16 v_b1_h;
- u16 v_b1_huc;
- u16 v_brh;
- u16 v_brl;
- u16 v_no_mic;
- u8 npoll;
- u8 nbounce_wait;
- s16 adj_v_hs_max;
- u16 adj_v_ins_hu;
- u16 adj_v_ins_h;
- s16 v_inval_ins_low;
- s16 v_inval_ins_high;
-};
-
-struct taiko_reg_address {
- u16 micb_4_ctl;
- u16 micb_4_int_rbias;
- u16 micb_4_mbhc;
-};
-
-enum taiko_mbhc_plug_type {
- PLUG_TYPE_INVALID = -1,
- PLUG_TYPE_NONE,
- PLUG_TYPE_HEADSET,
- PLUG_TYPE_HEADPHONE,
- PLUG_TYPE_HIGH_HPH,
- PLUG_TYPE_GND_MIC_SWAP,
-};
-
-enum taiko_mbhc_state {
- MBHC_STATE_NONE = -1,
- MBHC_STATE_POTENTIAL,
- MBHC_STATE_POTENTIAL_RECOVERY,
- MBHC_STATE_RELEASE,
-};
-
struct hpf_work {
struct taiko_priv *taiko;
u32 decimator;
@@ -237,62 +127,65 @@
static struct hpf_work tx_hpf_work[NUM_DECIMATORS];
+static const struct wcd9xxx_ch taiko_rx_chs[TAIKO_RX_MAX] = {
+ WCD9XXX_CH(16, 0),
+ WCD9XXX_CH(17, 1),
+ WCD9XXX_CH(18, 2),
+ WCD9XXX_CH(19, 3),
+ WCD9XXX_CH(20, 4),
+ WCD9XXX_CH(21, 5),
+ WCD9XXX_CH(22, 6),
+ WCD9XXX_CH(23, 7),
+ WCD9XXX_CH(24, 8),
+ WCD9XXX_CH(25, 9),
+ WCD9XXX_CH(26, 10),
+ WCD9XXX_CH(27, 11),
+ WCD9XXX_CH(28, 12),
+};
+
+static const struct wcd9xxx_ch taiko_tx_chs[TAIKO_TX_MAX] = {
+ WCD9XXX_CH(0, 0),
+ WCD9XXX_CH(1, 1),
+ WCD9XXX_CH(2, 2),
+ WCD9XXX_CH(3, 3),
+ WCD9XXX_CH(4, 4),
+ WCD9XXX_CH(5, 5),
+ WCD9XXX_CH(6, 6),
+ WCD9XXX_CH(7, 7),
+ WCD9XXX_CH(8, 8),
+ WCD9XXX_CH(9, 9),
+ WCD9XXX_CH(10, 10),
+ WCD9XXX_CH(11, 11),
+ WCD9XXX_CH(12, 12),
+ WCD9XXX_CH(13, 13),
+ WCD9XXX_CH(14, 14),
+ WCD9XXX_CH(15, 15),
+};
+
+static const u32 vport_check_table[NUM_CODEC_DAIS] = {
+ 0, /* AIF1_PB */
+ (1 << AIF2_CAP) | (1 << AIF3_CAP), /* AIF1_CAP */
+ 0, /* AIF2_PB */
+ (1 << AIF1_CAP) | (1 << AIF3_CAP), /* AIF2_CAP */
+ 0, /* AIF2_PB */
+ (1 << AIF1_CAP) | (1 << AIF2_CAP), /* AIF2_CAP */
+};
+
struct taiko_priv {
struct snd_soc_codec *codec;
- struct taiko_reg_address reg_addr;
u32 adc_count;
- u32 cfilt1_cnt;
- u32 cfilt2_cnt;
- u32 cfilt3_cnt;
u32 rx_bias_count;
s32 dmic_1_2_clk_cnt;
s32 dmic_3_4_clk_cnt;
s32 dmic_5_6_clk_cnt;
- enum taiko_bandgap_type bandgap_type;
- bool mclk_enabled;
- bool clock_active;
- bool config_mode_active;
- bool mbhc_polling_active;
- unsigned long mbhc_fake_ins_start;
- int buttons_pressed;
- enum taiko_mbhc_state mbhc_state;
- struct taiko_mbhc_config mbhc_cfg;
- struct mbhc_internal_cal_data mbhc_data;
-
- struct wcd9xxx_pdata *pdata;
u32 anc_slot;
- bool no_mic_headset_override;
- /* Delayed work to report long button press */
- struct delayed_work mbhc_btn_dwork;
-
- struct mbhc_micbias_regs mbhc_bias_regs;
- bool mbhc_micbias_switched;
-
- /* track PA/DAC state */
- unsigned long hph_pa_dac_state;
-
/*track taiko interface type*/
u8 intf_type;
- u32 hph_status; /* track headhpone status */
- /* define separate work for left and right headphone OCP to avoid
- * additional checking on which OCP event to report so no locking
- * to ensure synchronization is required
- */
- struct work_struct hphlocp_work; /* reporting left hph ocp off */
- struct work_struct hphrocp_work; /* reporting right hph ocp off */
-
- u8 hphlocp_cnt; /* headphone left ocp retry */
- u8 hphrocp_cnt; /* headphone right ocp retry */
-
- /* Work to perform MBHC Firmware Read */
- struct delayed_work mbhc_firmware_dwork;
- const struct firmware *mbhc_fw;
-
/* num of slim ports required */
- struct taiko_codec_dai_data dai[NUM_CODEC_DAIS];
+ struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS];
/*compander*/
int comp_enabled[COMPANDER_MAX];
@@ -303,29 +196,12 @@
u8 aux_l_gain;
u8 aux_r_gain;
- struct delayed_work mbhc_insert_dwork;
- unsigned long mbhc_last_resume; /* in jiffies */
-
- u8 current_plug;
- struct work_struct hs_correct_plug_work;
- bool hs_detect_work_stop;
- bool hs_polling_irq_prepared;
- bool lpi_enabled; /* low power insertion detection */
- bool in_gpio_handler;
- /* Currently, only used for mbhc purpose, to protect
- * concurrent execution of mbhc threaded irq handlers and
- * kill race between DAPM and MBHC.But can serve as a
- * general lock to protect codec resource
- */
- struct mutex codec_resource_lock;
-
-#ifdef CONFIG_DEBUG_FS
- struct dentry *debugfs_poke;
- struct dentry *debugfs_mbhc;
-#endif
+ /* resmgr module */
+ struct wcd9xxx_resmgr resmgr;
+ /* mbhc module */
+ struct wcd9xxx_mbhc mbhc;
};
-
static const u32 comp_shift[] = {
0,
2,
@@ -1604,6 +1480,240 @@
static const struct snd_kcontrol_new lineout4_ground_switch =
SOC_DAPM_SINGLE("Switch", TAIKO_A_RX_LINE_4_DAC_CTL, 6, 1, 0);
+static int slim_tx_vport_validation(u32 dai_id, u32 port_id,
+ struct taiko_priv *taiko_p)
+{
+ struct wcd9xxx_ch *ch;
+ int ret = 0;
+ int index = 0;
+ u32 vtable = vport_check_table[dai_id];
+ pr_debug("%s: dai_id %u vtable 0x%x port_id %u\n", __func__,
+ dai_id, vtable, port_id);
+ while (vtable) {
+ if (vtable & 1) {
+ list_for_each_entry(ch,
+ &taiko_p->dai[index].wcd9xxx_ch_list,
+ list) {
+ pr_debug("%s: index %u ch->port %u vtable 0x%x\n",
+ __func__, index, ch->port, vtable);
+ if (ch->port == port_id) {
+ pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
+ __func__, port_id + 1,
+ (index + 1)/2);
+ ret = -EINVAL;
+ break;
+ }
+ }
+ }
+ if (ret)
+ break;
+ index++;
+ vtable = vtable >> 1;
+ }
+ return ret;
+}
+
+/* virtual port entries */
+static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+ ucontrol->value.integer.value[0] = widget->value;
+ return 0;
+}
+
+static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
+ struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+ struct soc_multi_mixer_control *mixer =
+ ((struct soc_multi_mixer_control *)kcontrol->private_value);
+ u32 dai_id = widget->shift;
+ u32 port_id = mixer->shift;
+ u32 enable = ucontrol->value.integer.value[0];
+
+
+ pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__,
+ widget->name, ucontrol->id.name, widget->value, widget->shift,
+ ucontrol->value.integer.value[0]);
+
+ mutex_lock(&codec->mutex);
+
+ if (taiko_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ if (dai_id != AIF1_CAP) {
+ dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+ __func__);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ }
+ switch (dai_id) {
+ case AIF1_CAP:
+ case AIF2_CAP:
+ case AIF3_CAP:
+ /* only add to the list if value not set
+ */
+ if (enable && !(widget->value & 1 << port_id)) {
+ if (slim_tx_vport_validation(dai_id,
+ port_id, taiko_p)) {
+ pr_info("%s: TX%u is used by other virtual port\n",
+ __func__, port_id + 1);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ widget->value |= 1 << port_id;
+ list_add_tail(&core->tx_chs[port_id].list,
+ &taiko_p->dai[dai_id].wcd9xxx_ch_list
+ );
+ } else if (!enable && (widget->value & 1 << port_id)) {
+ widget->value &= ~(1 << port_id);
+ list_del_init(&core->tx_chs[port_id].list);
+ } else {
+ if (enable)
+ pr_info("%s: TX%u port is used by this virtual port\n",
+ __func__, port_id + 1);
+ else
+ pr_info("%s: TX%u port is not used by this virtual port\n",
+ __func__, port_id + 1);
+ /* avoid update power function */
+ mutex_unlock(&codec->mutex);
+ return 0;
+ }
+ break;
+ default:
+ pr_err("Unknown AIF %d\n", dai_id);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__,
+ widget->name, widget->sname, widget->value, widget->shift);
+
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, enable);
+
+ mutex_unlock(&codec->mutex);
+ return 0;
+}
+
+static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+ ucontrol->value.enumerated.item[0] = widget->value;
+ return 0;
+}
+
+static const char *const slim_rx_mux_text[] = {
+ "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB"
+};
+
+static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
+ struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ u32 port_id = widget->shift;
+
+ pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__,
+ widget->name, ucontrol->id.name, widget->value, widget->shift,
+ ucontrol->value.integer.value[0]);
+
+ widget->value = ucontrol->value.enumerated.item[0];
+
+ mutex_lock(&codec->mutex);
+
+ if (taiko_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ if (widget->value > 1) {
+ dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+ __func__);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ }
+ /* value need to match the Virtual port and AIF number
+ */
+ switch (widget->value) {
+ case 0:
+ list_del_init(&core->rx_chs[port_id].list);
+ break;
+ case 1:
+ list_add_tail(&core->rx_chs[port_id].list,
+ &taiko_p->dai[AIF1_PB].wcd9xxx_ch_list);
+ break;
+ case 2:
+ list_add_tail(&core->rx_chs[port_id].list,
+ &taiko_p->dai[AIF2_PB].wcd9xxx_ch_list);
+ break;
+ case 3:
+ list_add_tail(&core->rx_chs[port_id].list,
+ &taiko_p->dai[AIF3_PB].wcd9xxx_ch_list);
+ break;
+ default:
+ pr_err("Unknown AIF %d\n", widget->value);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+
+ snd_soc_dapm_mux_update_power(widget, kcontrol, 1, widget->value, e);
+
+ mutex_unlock(&codec->mutex);
+ return 0;
+}
+
+static const struct soc_enum slim_rx_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text);
+
+static const struct snd_kcontrol_new slim_rx_mux[TAIKO_RX_MAX] = {
+ SOC_DAPM_ENUM_EXT("SLIM RX1 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX2 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX3 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX4 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX5 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX6 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX7 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+};
+
+static const struct snd_kcontrol_new aif_cap_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TAIKO_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TAIKO_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TAIKO_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TAIKO_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TAIKO_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TAIKO_TX6, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TAIKO_TX7, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TAIKO_TX8, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TAIKO_TX9, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TAIKO_TX10, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
static void taiko_codec_enable_adc_block(struct snd_soc_codec *codec,
int enable)
{
@@ -1670,173 +1780,6 @@
return 0;
}
-static void taiko_codec_enable_audio_mode_bandgap(struct snd_soc_codec *codec)
-{
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x80,
- 0x80);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x04,
- 0x04);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x01,
- 0x01);
- usleep_range(1000, 1000);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x80,
- 0x00);
-}
-
-static void taiko_codec_enable_bandgap(struct snd_soc_codec *codec,
- enum taiko_bandgap_type choice)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- /* TODO lock resources accessed by audio streams and threaded
- * interrupt handlers
- */
-
- pr_debug("%s, choice is %d, current is %d\n", __func__, choice,
- taiko->bandgap_type);
-
- if (taiko->bandgap_type == choice)
- return;
-
- if ((taiko->bandgap_type == TAIKO_BANDGAP_OFF) &&
- (choice == TAIKO_BANDGAP_AUDIO_MODE)) {
- taiko_codec_enable_audio_mode_bandgap(codec);
- } else if (choice == TAIKO_BANDGAP_MBHC_MODE) {
- /* bandgap mode becomes fast,
- * mclk should be off or clk buff source souldn't be VBG
- * Let's turn off mclk always */
- WARN_ON(snd_soc_read(codec, TAIKO_A_CLK_BUFF_EN2) & (1 << 2));
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x2,
- 0x2);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x80,
- 0x80);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x4,
- 0x4);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x01,
- 0x01);
- usleep_range(1000, 1000);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x80,
- 0x00);
- } else if ((taiko->bandgap_type == TAIKO_BANDGAP_MBHC_MODE) &&
- (choice == TAIKO_BANDGAP_AUDIO_MODE)) {
- snd_soc_write(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x00);
- usleep_range(100, 100);
- taiko_codec_enable_audio_mode_bandgap(codec);
- } else if (choice == TAIKO_BANDGAP_OFF) {
- snd_soc_write(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x50);
- } else {
- pr_err("%s: Error, Invalid bandgap settings\n", __func__);
- }
- taiko->bandgap_type = choice;
-}
-
-static void taiko_codec_disable_clock_block(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- pr_debug("%s\n", __func__);
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN2, 0x04, 0x00);
- usleep_range(50, 50);
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN2, 0x02, 0x02);
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x01, 0x00);
- usleep_range(50, 50);
- taiko->clock_active = false;
-}
-
-static int taiko_codec_mclk_index(const struct taiko_priv *taiko)
-{
- if (taiko->mbhc_cfg.mclk_rate == TAIKO_MCLK_RATE_12288KHZ)
- return 0;
- else if (taiko->mbhc_cfg.mclk_rate == TAIKO_MCLK_RATE_9600KHZ)
- return 1;
- else {
- BUG_ON(1);
- return -EINVAL;
- }
-}
-
-static void taiko_enable_rx_bias(struct snd_soc_codec *codec, u32 enable)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- if (enable) {
- taiko->rx_bias_count++;
- if (taiko->rx_bias_count == 1)
- snd_soc_update_bits(codec, TAIKO_A_RX_COM_BIAS,
- 0x80, 0x80);
- } else {
- taiko->rx_bias_count--;
- if (!taiko->rx_bias_count)
- snd_soc_update_bits(codec, TAIKO_A_RX_COM_BIAS,
- 0x80, 0x00);
- }
-}
-
-static int taiko_codec_enable_config_mode(struct snd_soc_codec *codec,
- int enable)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- pr_debug("%s: enable = %d\n", __func__, enable);
- if (enable) {
-
- snd_soc_update_bits(codec, TAIKO_A_RC_OSC_FREQ, 0x10, 0);
- /* bandgap mode to fast */
- snd_soc_write(codec, TAIKO_A_BIAS_OSC_BG_CTL, 0x17);
- usleep_range(5, 5);
- snd_soc_update_bits(codec, TAIKO_A_RC_OSC_FREQ, 0x80,
- 0x80);
- snd_soc_update_bits(codec, TAIKO_A_RC_OSC_TEST, 0x80,
- 0x80);
- usleep_range(10, 10);
- snd_soc_update_bits(codec, TAIKO_A_RC_OSC_TEST, 0x80, 0);
- usleep_range(10000, 10000);
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x08, 0x08);
-
- } else {
- snd_soc_update_bits(codec, TAIKO_A_BIAS_OSC_BG_CTL, 0x1,
- 0);
- snd_soc_update_bits(codec, TAIKO_A_RC_OSC_FREQ, 0x80, 0);
- /* clk source to ext clk and clk buff ref to VBG */
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x0C, 0x04);
- }
- taiko->config_mode_active = enable ? true : false;
-
- return 0;
-}
-
-static int taiko_codec_enable_clock_block(struct snd_soc_codec *codec,
- int config_mode)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- pr_debug("%s: config_mode = %d\n", __func__, config_mode);
-
- /* transit to RCO requires mclk off */
- WARN_ON(snd_soc_read(codec, TAIKO_A_CLK_BUFF_EN2) & (1 << 2));
- if (config_mode) {
- /* enable RCO and switch to it */
- taiko_codec_enable_config_mode(codec, 1);
- snd_soc_write(codec, TAIKO_A_CLK_BUFF_EN2, 0x02);
- usleep_range(1000, 1000);
- } else {
- /* switch to MCLK */
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x08, 0x00);
-
- if (taiko->mbhc_polling_active)
- snd_soc_write(codec, TAIKO_A_CLK_BUFF_EN2, 0x02);
- taiko_codec_enable_config_mode(codec, 0);
- }
-
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x01, 0x01);
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN2, 0x02, 0x00);
- /* on MCLK */
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN2, 0x04, 0x04);
- snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_MCLK_CTL, 0x01, 0x01);
- usleep_range(50, 50);
- taiko->clock_active = true;
- return 0;
-}
-
static int taiko_codec_enable_aux_pga(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -1847,32 +1790,22 @@
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- taiko_codec_enable_bandgap(codec, TAIKO_BANDGAP_AUDIO_MODE);
- taiko_enable_rx_bias(codec, 1);
-
- if (taiko->aux_pga_cnt++ == 1
- && !taiko->mclk_enabled) {
- taiko_codec_enable_clock_block(codec, 1);
- pr_debug("AUX PGA enabled RC osc\n");
- }
+ WCD9XXX_BCL_LOCK(&taiko->resmgr);
+ wcd9xxx_resmgr_get_bandgap(&taiko->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ /* AUX PGA requires RCO or MCLK */
+ wcd9xxx_resmgr_get_clk_block(&taiko->resmgr, WCD9XXX_CLK_RCO);
+ wcd9xxx_resmgr_enable_rx_bias(&taiko->resmgr, 1);
+ WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
break;
case SND_SOC_DAPM_POST_PMD:
- taiko_enable_rx_bias(codec, 0);
-
- if (taiko->aux_pga_cnt-- == 0) {
- if (taiko->mbhc_polling_active)
- taiko_codec_enable_bandgap(codec,
- TAIKO_BANDGAP_MBHC_MODE);
- else
- taiko_codec_enable_bandgap(codec,
- TAIKO_BANDGAP_OFF);
-
- if (!taiko->mclk_enabled &&
- !taiko->mbhc_polling_active) {
- taiko_codec_enable_clock_block(codec, 0);
- }
- }
+ WCD9XXX_BCL_LOCK(&taiko->resmgr);
+ wcd9xxx_resmgr_enable_rx_bias(&taiko->resmgr, 0);
+ wcd9xxx_resmgr_put_bandgap(&taiko->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ wcd9xxx_resmgr_put_clk_block(&taiko->resmgr, WCD9XXX_CLK_RCO);
+ WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
break;
}
return 0;
@@ -2099,358 +2032,47 @@
return 0;
}
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_start_hs_polling(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- int mbhc_state = taiko->mbhc_state;
-
- pr_debug("%s: enter\n", __func__);
- if (!taiko->mbhc_polling_active) {
- pr_debug("Polling is not active, do not start polling\n");
- return;
- }
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x84);
-
- if (!taiko->no_mic_headset_override) {
- if (mbhc_state == MBHC_STATE_POTENTIAL) {
- pr_debug("%s recovering MBHC state macine\n", __func__);
- taiko->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
- /* set to max button press threshold */
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B2_CTL,
- 0x7F);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B1_CTL,
- 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B4_CTL,
- 0x7F);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B3_CTL,
- 0xFF);
- /* set to max */
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B6_CTL,
- 0x7F);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B5_CTL,
- 0xFF);
- }
- }
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x1);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x1);
- pr_debug("%s: leave\n", __func__);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_pause_hs_polling(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- pr_debug("%s: enter\n", __func__);
- if (!taiko->mbhc_polling_active) {
- pr_debug("polling not active, nothing to pause\n");
- return;
- }
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
- pr_debug("%s: leave\n", __func__);
-}
-
-static void taiko_codec_switch_cfilt_mode(struct snd_soc_codec *codec, int mode)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- u8 reg_mode_val, cur_mode_val;
- bool mbhc_was_polling = false;
-
- if (mode)
- reg_mode_val = TAIKO_CFILT_FAST_MODE;
- else
- reg_mode_val = TAIKO_CFILT_SLOW_MODE;
-
- cur_mode_val = snd_soc_read(codec,
- taiko->mbhc_bias_regs.cfilt_ctl) & 0x40;
-
- if (cur_mode_val != reg_mode_val) {
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- if (taiko->mbhc_polling_active) {
- taiko_codec_pause_hs_polling(codec);
- mbhc_was_polling = true;
- }
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.cfilt_ctl, 0x40, reg_mode_val);
- if (mbhc_was_polling)
- taiko_codec_start_hs_polling(codec);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
- cur_mode_val, reg_mode_val);
- } else {
- pr_debug("%s: CFILT Value is already %x\n",
- __func__, cur_mode_val);
- }
-}
-
-static void taiko_codec_update_cfilt_usage(struct snd_soc_codec *codec,
- u8 cfilt_sel, int inc)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- u32 *cfilt_cnt_ptr = NULL;
- u16 micb_cfilt_reg;
-
- switch (cfilt_sel) {
- case TAIKO_CFILT1_SEL:
- cfilt_cnt_ptr = &taiko->cfilt1_cnt;
- micb_cfilt_reg = TAIKO_A_MICB_CFILT_1_CTL;
- break;
- case TAIKO_CFILT2_SEL:
- cfilt_cnt_ptr = &taiko->cfilt2_cnt;
- micb_cfilt_reg = TAIKO_A_MICB_CFILT_2_CTL;
- break;
- case TAIKO_CFILT3_SEL:
- cfilt_cnt_ptr = &taiko->cfilt3_cnt;
- micb_cfilt_reg = TAIKO_A_MICB_CFILT_3_CTL;
- break;
- default:
- return; /* should not happen */
- }
-
- if (inc) {
- if (!(*cfilt_cnt_ptr)++) {
- /* Switch CFILT to slow mode if MBHC CFILT being used */
- if (cfilt_sel == taiko->mbhc_bias_regs.cfilt_sel)
- taiko_codec_switch_cfilt_mode(codec, 0);
-
- snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0x80);
- }
- } else {
- /* check if count not zero, decrement
- * then check if zero, go ahead disable cfilter
- */
- if ((*cfilt_cnt_ptr) && !--(*cfilt_cnt_ptr)) {
- snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0);
-
- /* Switch CFILT to fast mode if MBHC CFILT being used */
- if (cfilt_sel == taiko->mbhc_bias_regs.cfilt_sel)
- taiko_codec_switch_cfilt_mode(codec, 1);
- }
- }
-}
-
-static int taiko_find_k_value(unsigned int ldoh_v, unsigned int cfilt_mv)
-{
- int rc = -EINVAL;
- unsigned min_mv, max_mv;
-
- switch (ldoh_v) {
- case TAIKO_LDOH_1P95_V:
- min_mv = 160;
- max_mv = 1800;
- break;
- case TAIKO_LDOH_2P35_V:
- min_mv = 200;
- max_mv = 2200;
- break;
- case TAIKO_LDOH_2P75_V:
- min_mv = 240;
- max_mv = 2600;
- break;
- case TAIKO_LDOH_2P85_V:
- min_mv = 250;
- max_mv = 2700;
- break;
- default:
- goto done;
- }
-
- if (cfilt_mv < min_mv || cfilt_mv > max_mv)
- goto done;
-
- for (rc = 4; rc <= 44; rc++) {
- min_mv = max_mv * (rc) / 44;
- if (min_mv >= cfilt_mv) {
- rc -= 4;
- break;
- }
- }
-done:
- return rc;
-}
-
-static bool taiko_is_hph_pa_on(struct snd_soc_codec *codec)
-{
- u8 hph_reg_val = 0;
- hph_reg_val = snd_soc_read(codec, TAIKO_A_RX_HPH_CNP_EN);
-
- return (hph_reg_val & 0x30) ? true : false;
-}
-
-static bool taiko_is_hph_dac_on(struct snd_soc_codec *codec, int left)
-{
- u8 hph_reg_val = 0;
- if (left)
- hph_reg_val = snd_soc_read(codec,
- TAIKO_A_RX_HPH_L_DAC_CTL);
- else
- hph_reg_val = snd_soc_read(codec,
- TAIKO_A_RX_HPH_R_DAC_CTL);
-
- return (hph_reg_val & 0xC0) ? true : false;
-}
-
-static void taiko_turn_onoff_override(struct snd_soc_codec *codec, bool on)
-{
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x04, on << 2);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_drive_v_to_micbias(struct snd_soc_codec *codec,
- int usec)
-{
- int cfilt_k_val;
- bool set = true;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- if (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
- taiko->mbhc_micbias_switched) {
- pr_debug("%s: set mic V to micbias V\n", __func__);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
- taiko_turn_onoff_override(codec, true);
- while (1) {
- cfilt_k_val = taiko_find_k_value(
- taiko->pdata->micbias.ldoh_v,
- set ? taiko->mbhc_data.micb_mv :
- VDDIO_MICBIAS_MV);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.cfilt_val,
- 0xFC, (cfilt_k_val << 2));
- if (!set)
- break;
- usleep_range(usec, usec);
- set = false;
- }
- taiko_turn_onoff_override(codec, false);
- }
-}
-
-/* called under codec_resource_lock acquisition */
-static void __taiko_codec_switch_micbias(struct snd_soc_codec *codec,
- int vddio_switch, bool restartpolling,
- bool checkpolling)
-{
- int cfilt_k_val;
- bool override;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- if (vddio_switch && !taiko->mbhc_micbias_switched &&
- (!checkpolling || taiko->mbhc_polling_active)) {
- if (restartpolling)
- taiko_codec_pause_hs_polling(codec);
- override = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B1_CTL) & 0x04;
- if (!override)
- taiko_turn_onoff_override(codec, true);
- /* Adjust threshold if Mic Bias voltage changes */
- if (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
- cfilt_k_val = taiko_find_k_value(
- taiko->pdata->micbias.ldoh_v,
- VDDIO_MICBIAS_MV);
- usleep_range(10000, 10000);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.cfilt_val,
- 0xFC, (cfilt_k_val << 2));
- usleep_range(10000, 10000);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B1_CTL,
- taiko->mbhc_data.adj_v_ins_hu & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B2_CTL,
- (taiko->mbhc_data.adj_v_ins_hu >> 8) &
- 0xFF);
- pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
- __func__);
- }
-
- /* enable MIC BIAS Switch to VDDIO */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
- 0x80, 0x80);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
- 0x10, 0x00);
- if (!override)
- taiko_turn_onoff_override(codec, false);
- if (restartpolling)
- taiko_codec_start_hs_polling(codec);
-
- taiko->mbhc_micbias_switched = true;
- pr_debug("%s: VDDIO switch enabled\n", __func__);
- } else if (!vddio_switch && taiko->mbhc_micbias_switched) {
- if ((!checkpolling || taiko->mbhc_polling_active) &&
- restartpolling)
- taiko_codec_pause_hs_polling(codec);
- /* Reprogram thresholds */
- if (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
- cfilt_k_val = taiko_find_k_value(
- taiko->pdata->micbias.ldoh_v,
- taiko->mbhc_data.micb_mv);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.cfilt_val,
- 0xFC, (cfilt_k_val << 2));
- usleep_range(10000, 10000);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B1_CTL,
- taiko->mbhc_data.v_ins_hu & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B2_CTL,
- (taiko->mbhc_data.v_ins_hu >> 8) & 0xFF);
- pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
- __func__);
- }
-
- /* Disable MIC BIAS Switch to VDDIO */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
- 0x80, 0x00);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
- 0x10, 0x00);
-
- if ((!checkpolling || taiko->mbhc_polling_active) &&
- restartpolling)
- taiko_codec_start_hs_polling(codec);
-
- taiko->mbhc_micbias_switched = false;
- pr_debug("%s: VDDIO switch disabled\n", __func__);
- }
-}
-
-static void taiko_codec_switch_micbias(struct snd_soc_codec *codec,
- int vddio_switch)
-{
- return __taiko_codec_switch_micbias(codec, vddio_switch, true, true);
-}
-
static int taiko_codec_enable_micbias(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
u16 micb_int_reg;
- int micb_line;
u8 cfilt_sel_val = 0;
char *internal1_text = "Internal1";
char *internal2_text = "Internal2";
char *internal3_text = "Internal3";
+ enum wcd9xxx_notify_event e_post_off, e_pre_on, e_post_on;
pr_debug("%s %d\n", __func__, event);
switch (w->reg) {
case TAIKO_A_MICB_1_CTL:
micb_int_reg = TAIKO_A_MICB_1_INT_RBIAS;
- cfilt_sel_val = taiko->pdata->micbias.bias1_cfilt_sel;
- micb_line = TAIKO_MICBIAS1;
+ cfilt_sel_val = taiko->resmgr.pdata->micbias.bias1_cfilt_sel;
+ e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_1_ON;
+ e_post_on = WCD9XXX_EVENT_POST_MICBIAS_1_ON;
+ e_post_off = WCD9XXX_EVENT_POST_MICBIAS_1_OFF;
break;
case TAIKO_A_MICB_2_CTL:
micb_int_reg = TAIKO_A_MICB_2_INT_RBIAS;
- cfilt_sel_val = taiko->pdata->micbias.bias2_cfilt_sel;
- micb_line = TAIKO_MICBIAS2;
+ cfilt_sel_val = taiko->resmgr.pdata->micbias.bias2_cfilt_sel;
+ e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_2_ON;
+ e_post_on = WCD9XXX_EVENT_POST_MICBIAS_2_ON;
+ e_post_off = WCD9XXX_EVENT_POST_MICBIAS_2_OFF;
break;
case TAIKO_A_MICB_3_CTL:
micb_int_reg = TAIKO_A_MICB_3_INT_RBIAS;
- cfilt_sel_val = taiko->pdata->micbias.bias3_cfilt_sel;
- micb_line = TAIKO_MICBIAS3;
+ cfilt_sel_val = taiko->resmgr.pdata->micbias.bias3_cfilt_sel;
+ e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_3_ON;
+ e_post_on = WCD9XXX_EVENT_POST_MICBIAS_3_ON;
+ e_post_off = WCD9XXX_EVENT_POST_MICBIAS_3_OFF;
break;
case TAIKO_A_MICB_4_CTL:
- micb_int_reg = taiko->reg_addr.micb_4_int_rbias;
- cfilt_sel_val = taiko->pdata->micbias.bias4_cfilt_sel;
- micb_line = TAIKO_MICBIAS4;
+ micb_int_reg = taiko->resmgr.reg_addr->micb_4_int_rbias;
+ cfilt_sel_val = taiko->resmgr.pdata->micbias.bias4_cfilt_sel;
+ e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_4_ON;
+ e_post_on = WCD9XXX_EVENT_POST_MICBIAS_4_ON;
+ e_post_off = WCD9XXX_EVENT_POST_MICBIAS_4_OFF;
break;
default:
pr_err("%s: Error, invalid micbias register\n", __func__);
@@ -2459,15 +2081,12 @@
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- /* Decide whether to switch the micbias for MBHC */
- if (w->reg == taiko->mbhc_bias_regs.ctl_reg) {
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- taiko_codec_switch_micbias(codec, 0);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- }
+ /* Let MBHC module know so micbias switch to be off */
+ wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_pre_on);
snd_soc_update_bits(codec, w->reg, 0x0E, 0x0A);
- taiko_codec_update_cfilt_usage(codec, cfilt_sel_val, 1);
+ /* Get cfilt */
+ wcd9xxx_resmgr_cfilt_get(&taiko->resmgr, cfilt_sel_val);
if (strnstr(w->name, internal1_text, 30))
snd_soc_update_bits(codec, micb_int_reg, 0xE0, 0xE0);
@@ -2478,25 +2097,13 @@
break;
case SND_SOC_DAPM_POST_PMU:
-
usleep_range(20000, 20000);
-
- if (taiko->mbhc_polling_active &&
- taiko->mbhc_cfg.micbias == micb_line) {
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- taiko_codec_pause_hs_polling(codec);
- taiko_codec_start_hs_polling(codec);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- }
+ /* Let MBHC module know so micbias is on */
+ wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_post_on);
break;
-
case SND_SOC_DAPM_POST_PMD:
- if ((w->reg == taiko->mbhc_bias_regs.ctl_reg) &&
- taiko_is_hph_pa_on(codec)) {
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- taiko_codec_switch_micbias(codec, 1);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- }
+ /* Let MBHC module know so micbias switch to be off */
+ wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_post_off);
if (strnstr(w->name, internal1_text, 30))
snd_soc_update_bits(codec, micb_int_reg, 0x80, 0x00);
@@ -2505,7 +2112,8 @@
else if (strnstr(w->name, internal3_text, 30))
snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x0);
- taiko_codec_update_cfilt_usage(codec, cfilt_sel_val, 0);
+ /* Put cfilt */
+ wcd9xxx_resmgr_cfilt_put(&taiko->resmgr, cfilt_sel_val);
break;
}
@@ -2707,15 +2315,16 @@
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
+ struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
pr_debug("%s %d\n", __func__, event);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- taiko_enable_rx_bias(codec, 1);
+ wcd9xxx_resmgr_enable_rx_bias(&taiko->resmgr, 1);
break;
case SND_SOC_DAPM_POST_PMD:
- taiko_enable_rx_bias(codec, 0);
+ wcd9xxx_resmgr_enable_rx_bias(&taiko->resmgr, 0);
break;
}
return 0;
@@ -2738,81 +2347,32 @@
return 0;
}
-static void taiko_snd_soc_jack_report(struct taiko_priv *taiko,
- struct snd_soc_jack *jack, int status,
- int mask)
-{
- /* XXX: wake_lock_timeout()? */
- snd_soc_jack_report_no_dapm(jack, status, mask);
-}
-
-static void hphocp_off_report(struct taiko_priv *taiko,
- u32 jack_status, int irq)
-{
- struct snd_soc_codec *codec;
- if (!taiko) {
- pr_err("%s: Bad taiko private data\n", __func__);
- return;
- }
-
- pr_debug("%s: clear ocp status %x\n", __func__, jack_status);
- codec = taiko->codec;
- if (taiko->hph_status & jack_status) {
- taiko->hph_status &= ~jack_status;
- if (taiko->mbhc_cfg.headset_jack)
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.headset_jack,
- taiko->hph_status,
- TAIKO_JACK_MASK);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10, 0x10);
- /* reset retry counter as PA is turned off signifying
- * start of new OCP detection session
- */
- if (TAIKO_IRQ_HPH_PA_OCPL_FAULT)
- taiko->hphlocp_cnt = 0;
- else
- taiko->hphrocp_cnt = 0;
- wcd9xxx_enable_irq(codec->control_data, irq);
- }
-}
-
-static void hphlocp_off_report(struct work_struct *work)
-{
- struct taiko_priv *taiko = container_of(work, struct taiko_priv,
- hphlocp_work);
- hphocp_off_report(taiko, SND_JACK_OC_HPHL, TAIKO_IRQ_HPH_PA_OCPL_FAULT);
-}
-
-static void hphrocp_off_report(struct work_struct *work)
-{
- struct taiko_priv *taiko = container_of(work, struct taiko_priv,
- hphrocp_work);
- hphocp_off_report(taiko, SND_JACK_OC_HPHR, TAIKO_IRQ_HPH_PA_OCPR_FAULT);
-}
-
static int taiko_hph_pa_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- u8 mbhc_micb_ctl_val;
+ enum wcd9xxx_notify_event e_pre_on, e_post_off;
+
pr_debug("%s: %s event = %d\n", __func__, w->name, event);
+ if (w->shift == 5) {
+ e_pre_on = WCD9XXX_EVENT_PRE_HPHR_PA_ON;
+ e_post_off = WCD9XXX_EVENT_POST_HPHR_PA_OFF;
+ } else if (w->shift == 4) {
+ e_pre_on = WCD9XXX_EVENT_PRE_HPHL_PA_ON;
+ e_post_off = WCD9XXX_EVENT_POST_HPHL_PA_OFF;
+ } else {
+ pr_err("%s: Invalid w->shift %d\n", __func__, w->shift);
+ return -EINVAL;
+ }
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- mbhc_micb_ctl_val = snd_soc_read(codec,
- taiko->mbhc_bias_regs.ctl_reg);
-
- if (!(mbhc_micb_ctl_val & 0x80)) {
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- taiko_codec_switch_micbias(codec, 1);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- }
+ /* Let MBHC module know PA is turning on */
+ wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_pre_on);
break;
case SND_SOC_DAPM_POST_PMU:
-
usleep_range(10000, 10000);
snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_5, 0x02, 0x00);
@@ -2821,100 +2381,26 @@
snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_3, 0x08, 0x00);
usleep_range(10, 10);
-
break;
case SND_SOC_DAPM_POST_PMD:
- /* schedule work is required because at the time HPH PA DAPM
+ /* Let MBHC module know PA turned off */
+ wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_post_off);
+
+ /*
+ * schedule work is required because at the time HPH PA DAPM
* event callback is called by DAPM framework, CODEC dapm mutex
* would have been locked while snd_soc_jack_report also
* attempts to acquire same lock.
*/
- if (w->shift == 5) {
- clear_bit(TAIKO_HPHL_PA_OFF_ACK,
- &taiko->hph_pa_dac_state);
- clear_bit(TAIKO_HPHL_DAC_OFF_ACK,
- &taiko->hph_pa_dac_state);
- if (taiko->hph_status & SND_JACK_OC_HPHL)
- schedule_work(&taiko->hphlocp_work);
- } else if (w->shift == 4) {
- clear_bit(TAIKO_HPHR_PA_OFF_ACK,
- &taiko->hph_pa_dac_state);
- clear_bit(TAIKO_HPHR_DAC_OFF_ACK,
- &taiko->hph_pa_dac_state);
- if (taiko->hph_status & SND_JACK_OC_HPHR)
- schedule_work(&taiko->hphrocp_work);
- }
-
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- taiko_codec_switch_micbias(codec, 0);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-
pr_debug("%s: sleep 10 ms after %s PA disable.\n", __func__,
- w->name);
+ w->name);
usleep_range(10000, 10000);
break;
}
return 0;
}
-static void taiko_get_mbhc_micbias_regs(struct snd_soc_codec *codec,
- struct mbhc_micbias_regs *micbias_regs)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- unsigned int cfilt;
-
- switch (taiko->mbhc_cfg.micbias) {
- case TAIKO_MICBIAS1:
- cfilt = taiko->pdata->micbias.bias1_cfilt_sel;
- micbias_regs->mbhc_reg = TAIKO_A_MICB_1_MBHC;
- micbias_regs->int_rbias = TAIKO_A_MICB_1_INT_RBIAS;
- micbias_regs->ctl_reg = TAIKO_A_MICB_1_CTL;
- break;
- case TAIKO_MICBIAS2:
- cfilt = taiko->pdata->micbias.bias2_cfilt_sel;
- micbias_regs->mbhc_reg = TAIKO_A_MICB_2_MBHC;
- micbias_regs->int_rbias = TAIKO_A_MICB_2_INT_RBIAS;
- micbias_regs->ctl_reg = TAIKO_A_MICB_2_CTL;
- break;
- case TAIKO_MICBIAS3:
- cfilt = taiko->pdata->micbias.bias3_cfilt_sel;
- micbias_regs->mbhc_reg = TAIKO_A_MICB_3_MBHC;
- micbias_regs->int_rbias = TAIKO_A_MICB_3_INT_RBIAS;
- micbias_regs->ctl_reg = TAIKO_A_MICB_3_CTL;
- break;
- case TAIKO_MICBIAS4:
- cfilt = taiko->pdata->micbias.bias4_cfilt_sel;
- micbias_regs->mbhc_reg = taiko->reg_addr.micb_4_mbhc;
- micbias_regs->int_rbias = taiko->reg_addr.micb_4_int_rbias;
- micbias_regs->ctl_reg = taiko->reg_addr.micb_4_ctl;
- break;
- default:
- /* Should never reach here */
- pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__);
- return;
- }
-
- micbias_regs->cfilt_sel = cfilt;
-
- switch (cfilt) {
- case TAIKO_CFILT1_SEL:
- micbias_regs->cfilt_val = TAIKO_A_MICB_CFILT_1_VAL;
- micbias_regs->cfilt_ctl = TAIKO_A_MICB_CFILT_1_CTL;
- taiko->mbhc_data.micb_mv = taiko->pdata->micbias.cfilt1_mv;
- break;
- case TAIKO_CFILT2_SEL:
- micbias_regs->cfilt_val = TAIKO_A_MICB_CFILT_2_VAL;
- micbias_regs->cfilt_ctl = TAIKO_A_MICB_CFILT_2_CTL;
- taiko->mbhc_data.micb_mv = taiko->pdata->micbias.cfilt2_mv;
- break;
- case TAIKO_CFILT3_SEL:
- micbias_regs->cfilt_val = TAIKO_A_MICB_CFILT_3_VAL;
- micbias_regs->cfilt_ctl = TAIKO_A_MICB_CFILT_3_CTL;
- taiko->mbhc_data.micb_mv = taiko->pdata->micbias.cfilt3_mv;
- break;
- }
-}
static const struct snd_soc_dapm_widget taiko_dapm_i2s_widgets[] = {
SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", TAIKO_A_CDC_CLK_RX_I2S_CTL,
4, 0, NULL, 0),
@@ -2963,14 +2449,48 @@
static const struct snd_soc_dapm_route audio_map[] = {
/* SLIMBUS Connections */
+ {"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+ {"AIF2 CAP", NULL, "AIF2_CAP Mixer"},
+ {"AIF3 CAP", NULL, "AIF3_CAP Mixer"},
- {"SLIM TX1", NULL, "SLIM TX1 MUX"},
+ /* SLIM_MIXER("AIF1_CAP Mixer"),*/
+ {"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+ /* SLIM_MIXER("AIF2_CAP Mixer"),*/
+ {"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+ /* SLIM_MIXER("AIF3_CAP Mixer"),*/
+ {"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+
{"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
- {"SLIM TX2", NULL, "SLIM TX2 MUX"},
{"SLIM TX2 MUX", "DEC2", "DEC2 MUX"},
- {"SLIM TX3", NULL, "SLIM TX3 MUX"},
{"SLIM TX3 MUX", "DEC3", "DEC3 MUX"},
{"SLIM TX3 MUX", "RMIX1", "RX1 MIX1"},
{"SLIM TX3 MUX", "RMIX2", "RX2 MIX1"},
@@ -2980,10 +2500,8 @@
{"SLIM TX3 MUX", "RMIX6", "RX6 MIX1"},
{"SLIM TX3 MUX", "RMIX7", "RX7 MIX1"},
- {"SLIM TX4", NULL, "SLIM TX4 MUX"},
{"SLIM TX4 MUX", "DEC4", "DEC4 MUX"},
- {"SLIM TX5", NULL, "SLIM TX5 MUX"},
{"SLIM TX5 MUX", "DEC5", "DEC5 MUX"},
{"SLIM TX5 MUX", "RMIX1", "RX1 MIX1"},
{"SLIM TX5 MUX", "RMIX2", "RX2 MIX1"},
@@ -2993,10 +2511,8 @@
{"SLIM TX5 MUX", "RMIX6", "RX6 MIX1"},
{"SLIM TX5 MUX", "RMIX7", "RX7 MIX1"},
- {"SLIM TX6", NULL, "SLIM TX6 MUX"},
{"SLIM TX6 MUX", "DEC6", "DEC6 MUX"},
- {"SLIM TX7", NULL, "SLIM TX7 MUX"},
{"SLIM TX7 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX7 MUX", "DEC2", "DEC2 MUX"},
{"SLIM TX7 MUX", "DEC3", "DEC3 MUX"},
@@ -3015,7 +2531,6 @@
{"SLIM TX7 MUX", "RMIX6", "RX6 MIX1"},
{"SLIM TX7 MUX", "RMIX7", "RX7 MIX1"},
- {"SLIM TX8", NULL, "SLIM TX8 MUX"},
{"SLIM TX8 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX8 MUX", "DEC2", "DEC2 MUX"},
{"SLIM TX8 MUX", "DEC3", "DEC3 MUX"},
@@ -3027,7 +2542,6 @@
{"SLIM TX8 MUX", "DEC9", "DEC9 MUX"},
{"SLIM TX8 MUX", "DEC10", "DEC10 MUX"},
- {"SLIM TX9", NULL, "SLIM TX9 MUX"},
{"SLIM TX9 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX9 MUX", "DEC2", "DEC2 MUX"},
{"SLIM TX9 MUX", "DEC3", "DEC3 MUX"},
@@ -3039,7 +2553,6 @@
{"SLIM TX9 MUX", "DEC9", "DEC9 MUX"},
{"SLIM TX9 MUX", "DEC10", "DEC10 MUX"},
- {"SLIM TX10", NULL, "SLIM TX10 MUX"},
{"SLIM TX10 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX10 MUX", "DEC2", "DEC2 MUX"},
{"SLIM TX10 MUX", "DEC3", "DEC3 MUX"},
@@ -3170,6 +2683,39 @@
{"RX7 MIX2", NULL, "RX7 MIX2 INP1"},
{"RX7 MIX2", NULL, "RX7 MIX2 INP2"},
+ /* SLIM_MUX("AIF1_PB", "AIF1 PB"),*/
+ {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"},
+ /* SLIM_MUX("AIF2_PB", "AIF2 PB"),*/
+ {"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"},
+ /* SLIM_MUX("AIF3_PB", "AIF3 PB"),*/
+ {"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"},
+
+ {"SLIM RX1", NULL, "SLIM RX1 MUX"},
+ {"SLIM RX2", NULL, "SLIM RX2 MUX"},
+ {"SLIM RX3", NULL, "SLIM RX3 MUX"},
+ {"SLIM RX4", NULL, "SLIM RX4 MUX"},
+ {"SLIM RX5", NULL, "SLIM RX5 MUX"},
+ {"SLIM RX6", NULL, "SLIM RX6 MUX"},
+ {"SLIM RX7", NULL, "SLIM RX7 MUX"},
+
{"RX1 MIX1 INP1", "RX1", "SLIM RX1"},
{"RX1 MIX1 INP1", "RX2", "SLIM RX2"},
{"RX1 MIX1 INP1", "RX3", "SLIM RX3"},
@@ -3438,6 +2984,9 @@
if (reg == TAIKO_A_RX_HPH_L_STATUS || reg == TAIKO_A_RX_HPH_R_STATUS)
return 1;
+ if (reg == TAIKO_A_MBHC_INSERT_DET_STATUS)
+ return 1;
+
return 0;
}
@@ -3446,6 +2995,10 @@
unsigned int value)
{
int ret;
+
+ if (reg == SND_SOC_NOPM)
+ return 0;
+
BUG_ON(reg > TAIKO_MAX_REGISTER);
if (!taiko_volatile(codec, reg)) {
@@ -3463,6 +3016,9 @@
unsigned int val;
int ret;
+ if (reg == SND_SOC_NOPM)
+ return 0;
+
BUG_ON(reg > TAIKO_MAX_REGISTER);
if (!taiko_volatile(codec, reg) && taiko_readable(codec, reg) &&
@@ -3479,79 +3035,6 @@
return val;
}
-static s16 taiko_get_current_v_ins(struct taiko_priv *taiko, bool hu)
-{
- s16 v_ins;
- if ((taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
- taiko->mbhc_micbias_switched)
- v_ins = hu ? (s16)taiko->mbhc_data.adj_v_ins_hu :
- (s16)taiko->mbhc_data.adj_v_ins_h;
- else
- v_ins = hu ? (s16)taiko->mbhc_data.v_ins_hu :
- (s16)taiko->mbhc_data.v_ins_h;
- return v_ins;
-}
-
-static s16 taiko_get_current_v_hs_max(struct taiko_priv *taiko)
-{
- s16 v_hs_max;
- struct taiko_mbhc_plug_type_cfg *plug_type;
-
- plug_type = TAIKO_MBHC_CAL_PLUG_TYPE_PTR(taiko->mbhc_cfg.calibration);
- if ((taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
- taiko->mbhc_micbias_switched)
- v_hs_max = taiko->mbhc_data.adj_v_hs_max;
- else
- v_hs_max = plug_type->v_hs_max;
- return v_hs_max;
-}
-
-static void taiko_codec_calibrate_hs_polling(struct snd_soc_codec *codec)
-{
- u8 *n_ready, *n_cic;
- struct taiko_mbhc_btn_detect_cfg *btn_det;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- const s16 v_ins_hu = taiko_get_current_v_ins(taiko, true);
-
- btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(taiko->mbhc_cfg.calibration);
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B1_CTL,
- v_ins_hu & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B2_CTL,
- (v_ins_hu >> 8) & 0xFF);
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B3_CTL,
- taiko->mbhc_data.v_b1_hu & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B4_CTL,
- (taiko->mbhc_data.v_b1_hu >> 8) & 0xFF);
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B5_CTL,
- taiko->mbhc_data.v_b1_h & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B6_CTL,
- (taiko->mbhc_data.v_b1_h >> 8) & 0xFF);
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B9_CTL,
- taiko->mbhc_data.v_brh & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B10_CTL,
- (taiko->mbhc_data.v_brh >> 8) & 0xFF);
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B11_CTL,
- taiko->mbhc_data.v_brl & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B12_CTL,
- (taiko->mbhc_data.v_brl >> 8) & 0xFF);
-
- n_ready = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_READY);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B1_CTL,
- n_ready[taiko_codec_mclk_index(taiko)]);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B2_CTL,
- taiko->mbhc_data.npoll);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B3_CTL,
- taiko->mbhc_data.nbounce_wait);
- n_cic = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_CIC);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B6_CTL,
- n_cic[taiko_codec_mclk_index(taiko)]);
-}
-
static int taiko_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -3586,54 +3069,20 @@
pr_debug("%s: mclk_enable = %u, dapm = %d\n", __func__, mclk_enable,
dapm);
- if (dapm)
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
+
+ WCD9XXX_BCL_LOCK(&taiko->resmgr);
if (mclk_enable) {
- taiko->mclk_enabled = true;
-
- if (taiko->mbhc_polling_active) {
- taiko_codec_pause_hs_polling(codec);
- taiko_codec_disable_clock_block(codec);
- taiko_codec_enable_bandgap(codec,
- TAIKO_BANDGAP_AUDIO_MODE);
- taiko_codec_enable_clock_block(codec, 0);
- taiko_codec_calibrate_hs_polling(codec);
- taiko_codec_start_hs_polling(codec);
- } else {
- taiko_codec_disable_clock_block(codec);
- taiko_codec_enable_bandgap(codec,
- TAIKO_BANDGAP_AUDIO_MODE);
- taiko_codec_enable_clock_block(codec, 0);
- }
+ wcd9xxx_resmgr_get_bandgap(&taiko->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ wcd9xxx_resmgr_get_clk_block(&taiko->resmgr, WCD9XXX_CLK_MCLK);
} else {
-
- if (!taiko->mclk_enabled) {
- if (dapm)
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- pr_err("Error, MCLK already diabled\n");
- return -EINVAL;
- }
- taiko->mclk_enabled = false;
-
- if (taiko->mbhc_polling_active) {
- taiko_codec_pause_hs_polling(codec);
- taiko_codec_disable_clock_block(codec);
- taiko_codec_enable_bandgap(codec,
- TAIKO_BANDGAP_MBHC_MODE);
- taiko_enable_rx_bias(codec, 1);
- taiko_codec_enable_clock_block(codec, 1);
- taiko_codec_calibrate_hs_polling(codec);
- taiko_codec_start_hs_polling(codec);
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1,
- 0x05, 0x01);
- } else {
- taiko_codec_disable_clock_block(codec);
- taiko_codec_enable_bandgap(codec,
- TAIKO_BANDGAP_OFF);
- }
+ /* Put clock and BG */
+ wcd9xxx_resmgr_put_clk_block(&taiko->resmgr, WCD9XXX_CLK_MCLK);
+ wcd9xxx_resmgr_put_bandgap(&taiko->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
}
- if (dapm)
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
+ WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
+
return 0;
}
@@ -3688,90 +3137,221 @@
{
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(dai->codec);
- u32 i = 0;
+ struct wcd9xxx *core = dev_get_drvdata(dai->codec->dev->parent);
if (!tx_slot && !rx_slot) {
pr_err("%s: Invalid\n", __func__);
return -EINVAL;
}
- pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n",
- __func__, dai->name, dai->id, tx_num, rx_num);
+ pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n"
+ "taiko->intf_type %d\n",
+ __func__, dai->name, dai->id, tx_num, rx_num,
+ taiko->intf_type);
- if (dai->id == AIF1_PB || dai->id == AIF2_PB || dai->id == AIF3_PB) {
- for (i = 0; i < rx_num; i++) {
- taiko->dai[dai->id - 1].ch_num[i] = rx_slot[i];
- taiko->dai[dai->id - 1].ch_act = 0;
- taiko->dai[dai->id - 1].ch_tot = rx_num;
+ if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+ wcd9xxx_init_slimslave(core, core->slim->laddr,
+ tx_num, tx_slot, rx_num, rx_slot);
+ return 0;
+}
+
+static int taiko_get_channel_map(struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+
+{
+ struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(dai->codec);
+ u32 i = 0;
+ struct wcd9xxx_ch *ch;
+
+ switch (dai->id) {
+ case AIF1_PB:
+ case AIF2_PB:
+ case AIF3_PB:
+ if (!rx_slot || !rx_num) {
+ pr_err("%s: Invalid rx_slot %d or rx_num %d\n",
+ __func__, (u32) rx_slot, (u32) rx_num);
+ return -EINVAL;
}
- } else if (dai->id == AIF1_CAP || dai->id == AIF2_CAP ||
- dai->id == AIF3_CAP) {
- for (i = 0; i < tx_num; i++) {
- taiko->dai[dai->id - 1].ch_num[i] = tx_slot[i];
- taiko->dai[dai->id - 1].ch_act = 0;
- taiko->dai[dai->id - 1].ch_tot = tx_num;
+ list_for_each_entry(ch, &taiko_p->dai[dai->id].wcd9xxx_ch_list,
+ list) {
+ pr_debug("%s: tx_slot[%d] %d, ch->ch_num %d\n",
+ __func__, i, tx_slot[i], ch->ch_num);
+ rx_slot[i++] = ch->ch_num;
+ }
+ pr_debug("%s: rx_num %d\n", __func__, i);
+ *rx_num = i;
+ break;
+ case AIF1_CAP:
+ case AIF2_CAP:
+ case AIF3_CAP:
+ if (!tx_slot || !tx_num) {
+ pr_err("%s: Invalid tx_slot %d or tx_num %d\n",
+ __func__, (u32) tx_slot, (u32) tx_num);
+ return -EINVAL;
+ }
+ list_for_each_entry(ch, &taiko_p->dai[dai->id].wcd9xxx_ch_list,
+ list) {
+ pr_debug("%s: tx_slot[%d] %d, ch->ch_num %d\n",
+ __func__, i, tx_slot[i], ch->ch_num);
+ tx_slot[i++] = ch->ch_num;
+ }
+ pr_debug("%s: tx_num %d\n", __func__, i);
+ *tx_num = i;
+ break;
+
+ default:
+ pr_err("%s: Invalid DAI ID %x\n", __func__, dai->id);
+ break;
+ }
+
+ return 0;
+}
+
+static int taiko_set_interpolator_rate(struct snd_soc_dai *dai,
+ u8 rx_fs_rate_reg_val, u32 compander_fs, u32 sample_rate)
+{
+ u32 j;
+ u8 rx_mix1_inp;
+ u16 rx_mix_1_reg_1, rx_mix_1_reg_2;
+ u16 rx_fs_reg;
+ u8 rx_mix_1_reg_1_val, rx_mix_1_reg_2_val;
+ struct snd_soc_codec *codec = dai->codec;
+ struct wcd9xxx_ch *ch;
+ struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
+
+ list_for_each_entry(ch, &taiko->dai[dai->id].wcd9xxx_ch_list, list) {
+ /* for RX port starting from 16 instead of 10 like tabla */
+ rx_mix1_inp = ch->port + RX_MIX1_INP_SEL_RX1 -
+ TAIKO_TX_PORT_NUMBER;
+ if ((rx_mix1_inp < RX_MIX1_INP_SEL_RX1) ||
+ (rx_mix1_inp > RX_MIX1_INP_SEL_RX7)) {
+ pr_err("%s: Invalid TAIKO_RX%u port. Dai ID is %d\n",
+ __func__, rx_mix1_inp - 5 , dai->id);
+ return -EINVAL;
+ }
+
+ rx_mix_1_reg_1 = TAIKO_A_CDC_CONN_RX1_B1_CTL;
+
+ for (j = 0; j < NUM_INTERPOLATORS; j++) {
+ rx_mix_1_reg_2 = rx_mix_1_reg_1 + 1;
+
+ rx_mix_1_reg_1_val = snd_soc_read(codec,
+ rx_mix_1_reg_1);
+ rx_mix_1_reg_2_val = snd_soc_read(codec,
+ rx_mix_1_reg_2);
+
+ if (((rx_mix_1_reg_1_val & 0x0F) == rx_mix1_inp) ||
+ (((rx_mix_1_reg_1_val >> 4) & 0x0F)
+ == rx_mix1_inp) ||
+ ((rx_mix_1_reg_2_val & 0x0F) == rx_mix1_inp)) {
+
+ rx_fs_reg = TAIKO_A_CDC_RX1_B5_CTL + 8 * j;
+
+ pr_debug("%s: AIF_PB DAI(%d) connected to RX%u\n",
+ __func__, dai->id, j + 1);
+
+ pr_debug("%s: set RX%u sample rate to %u\n",
+ __func__, j + 1, sample_rate);
+
+ snd_soc_update_bits(codec, rx_fs_reg,
+ 0xE0, rx_fs_rate_reg_val);
+
+ if (comp_rx_path[j] < COMPANDER_MAX)
+ taiko->comp_fs[comp_rx_path[j]]
+ = compander_fs;
+ }
+ if (j <= 2)
+ rx_mix_1_reg_1 += 3;
+ else
+ rx_mix_1_reg_1 += 2;
}
}
return 0;
}
-static int taiko_get_channel_map(struct snd_soc_dai *dai,
- unsigned int *tx_num, unsigned int *tx_slot,
- unsigned int *rx_num, unsigned int *rx_slot)
-
+static int taiko_set_decimator_rate(struct snd_soc_dai *dai,
+ u8 tx_fs_rate_reg_val, u32 sample_rate)
{
- struct wcd9xxx *taiko = dev_get_drvdata(dai->codec->control_data);
+ struct snd_soc_codec *codec = dai->codec;
+ struct wcd9xxx_ch *ch;
+ struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
+ u32 tx_port;
+ u16 tx_port_reg, tx_fs_reg;
+ u8 tx_port_reg_val;
+ s8 decimator;
- u32 cnt = 0;
- u32 tx_ch[SLIM_MAX_TX_PORTS];
- u32 rx_ch[SLIM_MAX_RX_PORTS];
+ list_for_each_entry(ch, &taiko->dai[dai->id].wcd9xxx_ch_list, list) {
- if (!rx_slot && !tx_slot) {
- pr_err("%s: Invalid\n", __func__);
- return -EINVAL;
+ tx_port = ch->port + 1;
+ pr_debug("%s: dai->id = %d, tx_port = %d",
+ __func__, dai->id, tx_port);
+
+ if ((tx_port < 1) || (tx_port > NUM_DECIMATORS)) {
+ pr_err("%s: Invalid SLIM TX%u port. DAI ID is %d\n",
+ __func__, tx_port, dai->id);
+ return -EINVAL;
+ }
+
+ tx_port_reg = TAIKO_A_CDC_CONN_TX_SB_B1_CTL + (tx_port - 1);
+ tx_port_reg_val = snd_soc_read(codec, tx_port_reg);
+
+ decimator = 0;
+
+ if ((tx_port >= 1) && (tx_port <= 6)) {
+
+ tx_port_reg_val = tx_port_reg_val & 0x0F;
+ if (tx_port_reg_val == 0x8)
+ decimator = tx_port;
+
+ } else if ((tx_port >= 7) && (tx_port <= NUM_DECIMATORS)) {
+
+ tx_port_reg_val = tx_port_reg_val & 0x1F;
+
+ if ((tx_port_reg_val >= 0x8) &&
+ (tx_port_reg_val <= 0x11)) {
+
+ decimator = (tx_port_reg_val - 0x8) + 1;
+ }
+ }
+
+ if (decimator) { /* SLIM_TX port has a DEC as input */
+
+ tx_fs_reg = TAIKO_A_CDC_TX1_CLK_FS_CTL +
+ 8 * (decimator - 1);
+
+ pr_debug("%s: set DEC%u (-> SLIM_TX%u) rate to %u\n",
+ __func__, decimator, tx_port, sample_rate);
+
+ snd_soc_update_bits(codec, tx_fs_reg, 0x07,
+ tx_fs_rate_reg_val);
+
+ } else {
+ if ((tx_port_reg_val >= 0x1) &&
+ (tx_port_reg_val <= 0x7)) {
+
+ pr_debug("%s: RMIX%u going to SLIM TX%u\n",
+ __func__, tx_port_reg_val, tx_port);
+
+ } else if ((tx_port_reg_val >= 0x8) &&
+ (tx_port_reg_val <= 0x11)) {
+
+ pr_err("%s: ERROR: Should not be here\n",
+ __func__);
+ pr_err("%s: ERROR: DEC connected to SLIM TX%u\n",
+ __func__, tx_port);
+ return -EINVAL;
+
+ } else if (tx_port_reg_val == 0) {
+ pr_debug("%s: no signal to SLIM TX%u\n",
+ __func__, tx_port);
+ } else {
+ pr_err("%s: ERROR: wrong signal to SLIM TX%u\n",
+ __func__, tx_port);
+ pr_err("%s: ERROR: wrong signal = %u\n",
+ __func__, tx_port_reg_val);
+ return -EINVAL;
+ }
+ }
}
-
- /* for virtual port, codec driver needs to do
- * housekeeping, for now should be ok
- */
- wcd9xxx_get_channel(taiko, rx_ch, tx_ch);
- if (dai->id == AIF1_PB) {
- *rx_num = taiko_dai[dai->id - 1].playback.channels_max;
- while (cnt < *rx_num) {
- rx_slot[cnt] = rx_ch[cnt];
- cnt++;
- }
- } else if (dai->id == AIF1_CAP) {
- *tx_num = taiko_dai[dai->id - 1].capture.channels_max;
- while (cnt < *tx_num) {
- tx_slot[cnt] = tx_ch[6 + cnt];
- cnt++;
- }
- } else if (dai->id == AIF2_PB) {
- *rx_num = taiko_dai[dai->id - 1].playback.channels_max;
- while (cnt < *rx_num) {
- rx_slot[cnt] = rx_ch[5 + cnt];
- cnt++;
- }
- } else if (dai->id == AIF2_CAP) {
- *tx_num = taiko_dai[dai->id - 1].capture.channels_max;
- tx_slot[0] = tx_ch[cnt];
- tx_slot[1] = tx_ch[1 + cnt];
- tx_slot[2] = tx_ch[5 + cnt];
- tx_slot[3] = tx_ch[3 + cnt];
-
- } else if (dai->id == AIF3_PB) {
- *rx_num = taiko_dai[dai->id - 1].playback.channels_max;
- rx_slot[0] = rx_ch[3];
- rx_slot[1] = rx_ch[4];
-
- } else if (dai->id == AIF3_CAP) {
- *tx_num = taiko_dai[dai->id - 1].capture.channels_max;
- tx_slot[cnt] = tx_ch[2 + cnt];
- tx_slot[cnt + 1] = tx_ch[4 + cnt];
- }
- pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n",
- __func__, dai->name, dai->id, *tx_num, *rx_num);
-
-
return 0;
}
@@ -3781,10 +3361,9 @@
{
struct snd_soc_codec *codec = dai->codec;
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(dai->codec);
- u8 path, shift;
- u16 tx_fs_reg, rx_fs_reg;
- u8 tx_fs_rate, rx_fs_rate, rx_state, tx_state;
+ u8 tx_fs_rate, rx_fs_rate;
u32 compander_fs;
+ int ret;
pr_debug("%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__,
dai->name, dai->id, params_rate(params),
@@ -3823,37 +3402,20 @@
break;
default:
pr_err("%s: Invalid sampling rate %d\n", __func__,
- params_rate(params));
+ params_rate(params));
return -EINVAL;
}
-
- /**
- * If current dai is a tx dai, set sample rate to
- * all the txfe paths that are currently not active
- */
- if ((dai->id == AIF1_CAP) || (dai->id == AIF2_CAP) ||
- (dai->id == AIF3_CAP)) {
-
- tx_state = snd_soc_read(codec,
- TAIKO_A_CDC_CLK_TX_CLK_EN_B1_CTL);
-
- for (path = 1, shift = 0;
- path <= NUM_DECIMATORS; path++, shift++) {
-
- if (path == BITS_PER_REG + 1) {
- shift = 0;
- tx_state = snd_soc_read(codec,
- TAIKO_A_CDC_CLK_TX_CLK_EN_B2_CTL);
- }
-
- if (!(tx_state & (1 << shift))) {
- tx_fs_reg = TAIKO_A_CDC_TX1_CLK_FS_CTL
- + (BITS_PER_REG*(path-1));
- snd_soc_update_bits(codec, tx_fs_reg,
- 0x07, tx_fs_rate);
- }
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_CAPTURE:
+ ret = taiko_set_decimator_rate(dai, tx_fs_rate,
+ params_rate(params));
+ if (ret < 0) {
+ pr_err("%s: set decimator rate failed %d\n", __func__,
+ ret);
+ return ret;
}
+
if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
@@ -3871,37 +3433,20 @@
break;
}
snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_TX_I2S_CTL,
- 0x07, tx_fs_rate);
+ 0x07, tx_fs_rate);
} else {
- taiko->dai[dai->id - 1].rate = params_rate(params);
+ taiko->dai[dai->id].rate = params_rate(params);
}
- }
- /**
- * TODO: Need to handle case where same RX chain takes 2 or more inputs
- * with varying sample rates
- */
+ break;
- /**
- * If current dai is a rx dai, set sample rate to
- * all the rx paths that are currently not active
- */
- if (dai->id == AIF1_PB || dai->id == AIF2_PB || dai->id == AIF3_PB) {
-
- rx_state = snd_soc_read(codec,
- TAIKO_A_CDC_CLK_RX_B1_CTL);
-
- for (path = 1, shift = 0;
- path <= NUM_INTERPOLATORS; path++, shift++) {
-
- if (!(rx_state & (1 << shift))) {
- rx_fs_reg = TAIKO_A_CDC_RX1_B5_CTL
- + (BITS_PER_REG*(path-1));
- snd_soc_update_bits(codec, rx_fs_reg,
- 0xE0, rx_fs_rate);
- if (comp_rx_path[shift] < COMPANDER_MAX)
- taiko->comp_fs[comp_rx_path[shift]]
- = compander_fs;
- }
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ ret = taiko_set_interpolator_rate(dai, rx_fs_rate,
+ compander_fs,
+ params_rate(params));
+ if (ret < 0) {
+ pr_err("%s: set decimator rate failed %d\n", __func__,
+ ret);
+ return ret;
}
if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
switch (params_format(params)) {
@@ -3920,10 +3465,15 @@
break;
}
snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_RX_I2S_CTL,
- 0x03, (rx_fs_rate >> 0x05));
+ 0x03, (rx_fs_rate >> 0x05));
} else {
- taiko->dai[dai->id - 1].rate = params_rate(params);
+ taiko->dai[dai->id].rate = params_rate(params);
}
+ break;
+ default:
+ pr_err("%s: Invalid stream type %d\n", __func__,
+ substream->stream);
+ return -EINVAL;
}
return 0;
@@ -4029,7 +3579,7 @@
static struct snd_soc_dai_driver taiko_i2s_dai[] = {
{
.name = "taiko_i2s_rx1",
- .id = 1,
+ .id = AIF1_PB,
.playback = {
.stream_name = "AIF1 Playback",
.rates = WCD9320_RATES,
@@ -4043,7 +3593,7 @@
},
{
.name = "taiko_i2s_tx1",
- .id = 2,
+ .id = AIF1_CAP,
.capture = {
.stream_name = "AIF1 Capture",
.rates = WCD9320_RATES,
@@ -4058,125 +3608,77 @@
};
static int taiko_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol,
+ int event)
{
- struct wcd9xxx *taiko;
+ struct wcd9xxx *core;
struct snd_soc_codec *codec = w->codec;
struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
- u32 j = 0;
u32 ret = 0;
- codec->control_data = dev_get_drvdata(codec->dev->parent);
- taiko = codec->control_data;
+ struct wcd9xxx_codec_dai_data *dai;
+
+ core = dev_get_drvdata(codec->dev->parent);
+
+ pr_debug("%s: event called! codec name %s num_dai %d\n"
+ "stream name %s event %d\n",
+ __func__, w->codec->name, w->codec->num_dai, w->sname, event);
+
/* Execute the callback only if interface type is slimbus */
if (taiko_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS)
return 0;
- pr_debug("%s: %s %d\n", __func__, w->name, event);
+ dai = &taiko_p->dai[w->shift];
+ pr_debug("%s: w->name %s w->shift %d event %d\n",
+ __func__, w->name, w->shift, event);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- for (j = 0; j < ARRAY_SIZE(taiko_dai); j++) {
- if ((taiko_dai[j].id == AIF1_CAP) ||
- (taiko_dai[j].id == AIF2_CAP) ||
- (taiko_dai[j].id == AIF3_CAP))
- continue;
- if (!strncmp(w->sname,
- taiko_dai[j].playback.stream_name, 13)) {
- ++taiko_p->dai[j].ch_act;
- break;
- }
- }
- if (taiko_p->dai[j].ch_act == taiko_p->dai[j].ch_tot)
- ret = wcd9xxx_cfg_slim_sch_rx(taiko,
- taiko_p->dai[j].ch_num,
- taiko_p->dai[j].ch_tot,
- taiko_p->dai[j].rate);
+ ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+ dai->rate, dai->bit_width,
+ &dai->grph);
break;
case SND_SOC_DAPM_POST_PMD:
- for (j = 0; j < ARRAY_SIZE(taiko_dai); j++) {
- if ((taiko_dai[j].id == AIF1_CAP) ||
- (taiko_dai[j].id == AIF2_CAP) ||
- (taiko_dai[j].id == AIF3_CAP))
- continue;
- if (!strncmp(w->sname,
- taiko_dai[j].playback.stream_name, 13)) {
- --taiko_p->dai[j].ch_act;
- break;
- }
- }
- if (!taiko_p->dai[j].ch_act) {
- ret = wcd9xxx_close_slim_sch_rx(taiko,
- taiko_p->dai[j].ch_num,
- taiko_p->dai[j].ch_tot);
- usleep_range(15000, 15000);
- taiko_p->dai[j].rate = 0;
- memset(taiko_p->dai[j].ch_num, 0, (sizeof(u32)*
- taiko_p->dai[j].ch_tot));
- taiko_p->dai[j].ch_tot = 0;
- }
+ ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+ dai->grph);
+ usleep_range(15000, 15000);
+ break;
}
return ret;
}
static int taiko_codec_enable_slimtx(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol,
+ int event)
{
- struct wcd9xxx *taiko;
+ struct wcd9xxx *core;
struct snd_soc_codec *codec = w->codec;
struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
- /* index to the DAI ID, for now hardcoding */
- u32 j = 0;
u32 ret = 0;
+ struct wcd9xxx_codec_dai_data *dai;
- codec->control_data = dev_get_drvdata(codec->dev->parent);
- taiko = codec->control_data;
+ core = dev_get_drvdata(codec->dev->parent);
+
+ pr_debug("%s: event called! codec name %s num_dai %d stream name %s\n",
+ __func__, w->codec->name, w->codec->num_dai, w->sname);
/* Execute the callback only if interface type is slimbus */
if (taiko_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS)
return 0;
- pr_debug("%s(): %s %d\n", __func__, w->name, event);
+ pr_debug("%s(): w->name %s event %d w->shift %d\n",
+ __func__, w->name, event, w->shift);
+ dai = &taiko_p->dai[w->shift];
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- for (j = 0; j < ARRAY_SIZE(taiko_dai); j++) {
- if (taiko_dai[j].id == AIF1_PB ||
- taiko_dai[j].id == AIF2_PB ||
- taiko_dai[j].id == AIF3_PB)
- continue;
- if (!strncmp(w->sname,
- taiko_dai[j].capture.stream_name, 13)) {
- ++taiko_p->dai[j].ch_act;
- break;
- }
- }
- if (taiko_p->dai[j].ch_act == taiko_p->dai[j].ch_tot)
- ret = wcd9xxx_cfg_slim_sch_tx(taiko,
- taiko_p->dai[j].ch_num,
- taiko_p->dai[j].ch_tot,
- taiko_p->dai[j].rate);
+ ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->rate, dai->bit_width,
+ &dai->grph);
break;
case SND_SOC_DAPM_POST_PMD:
- for (j = 0; j < ARRAY_SIZE(taiko_dai); j++) {
- if (taiko_dai[j].id == AIF1_PB ||
- taiko_dai[j].id == AIF2_PB ||
- taiko_dai[j].id == AIF3_PB)
- continue;
- if (!strncmp(w->sname,
- taiko_dai[j].capture.stream_name, 13)) {
- --taiko_p->dai[j].ch_act;
- break;
- }
- }
- if (!taiko_p->dai[j].ch_act) {
- ret = wcd9xxx_close_slim_sch_tx(taiko,
- taiko_p->dai[j].ch_num,
- taiko_p->dai[j].ch_tot);
- taiko_p->dai[j].rate = 0;
- memset(taiko_p->dai[j].ch_num, 0, (sizeof(u32)*
- taiko_p->dai[j].ch_tot));
- taiko_p->dai[j].ch_tot = 0;
- }
+ ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->grph);
+ break;
}
return ret;
}
@@ -4216,29 +3718,38 @@
SND_SOC_DAPM_MIXER("DAC1", TAIKO_A_RX_EAR_EN, 6, 0, dac1_switch,
ARRAY_SIZE(dac1_switch)),
- SND_SOC_DAPM_AIF_IN_E("SLIM RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimrx,
+ SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
+ AIF1_PB, 0, taiko_codec_enable_slimrx,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_AIF_IN_E("SLIM RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimrx,
+ SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM,
+ AIF2_PB, 0, taiko_codec_enable_slimrx,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_AIF_IN_E("SLIM RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimrx,
+ SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM,
+ AIF3_PB, 0, taiko_codec_enable_slimrx,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_AIF_IN_E("SLIM RX4", "AIF3 Playback", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_AIF_IN_E("SLIM RX5", "AIF3 Playback", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, TAIKO_RX1, 0,
+ &slim_rx_mux[TAIKO_RX1]),
+ SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, TAIKO_RX2, 0,
+ &slim_rx_mux[TAIKO_RX2]),
+ SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, TAIKO_RX3, 0,
+ &slim_rx_mux[TAIKO_RX3]),
+ SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, TAIKO_RX4, 0,
+ &slim_rx_mux[TAIKO_RX4]),
+ SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, TAIKO_RX5, 0,
+ &slim_rx_mux[TAIKO_RX5]),
+ SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, TAIKO_RX6, 0,
+ &slim_rx_mux[TAIKO_RX6]),
+ SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, TAIKO_RX7, 0,
+ &slim_rx_mux[TAIKO_RX7]),
- SND_SOC_DAPM_AIF_IN_E("SLIM RX6", "AIF2 Playback", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_AIF_IN_E("SLIM RX7", "AIF2 Playback", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0),
/* Headphone */
SND_SOC_DAPM_OUTPUT("HEADPHONE"),
@@ -4539,55 +4050,47 @@
taiko_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, 0, 0, &sb_tx1_mux),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX1", "AIF2 Capture", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
+ AIF1_CAP, 0, taiko_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, 0, 0, &sb_tx2_mux),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX2", "AIF2 Capture", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM,
+ AIF2_CAP, 0, taiko_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, 0, 0, &sb_tx3_mux),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX3", "AIF3 Capture", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM,
+ AIF3_CAP, 0, taiko_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, 0, 0, &sb_tx4_mux),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX4", "AIF2 Capture", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
+ aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
- SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, 0, 0, &sb_tx5_mux),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX5", "AIF3 Capture", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0,
+ aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
- SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, 0, 0, &sb_tx6_mux),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX6", "AIF2 Capture", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0,
+ aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
- SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, 0, 0, &sb_tx7_mux),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX7", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, 0, 0, &sb_tx8_mux),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX8", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, 0, 0, &sb_tx9_mux),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX9", "AIF1 Capture", NULL, SND_SOC_NOPM,
- 0, 0, taiko_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, 0, 0, &sb_tx10_mux),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX10", "AIF1 Capture", NULL, SND_SOC_NOPM,
- 0, 0, taiko_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, TAIKO_TX1, 0,
+ &sb_tx1_mux),
+ SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, TAIKO_TX2, 0,
+ &sb_tx2_mux),
+ SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, TAIKO_TX3, 0,
+ &sb_tx3_mux),
+ SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, TAIKO_TX4, 0,
+ &sb_tx4_mux),
+ SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, TAIKO_TX5, 0,
+ &sb_tx5_mux),
+ SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, TAIKO_TX6, 0,
+ &sb_tx6_mux),
+ SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, TAIKO_TX7, 0,
+ &sb_tx7_mux),
+ SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, TAIKO_TX8, 0,
+ &sb_tx8_mux),
+ SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, TAIKO_TX9, 0,
+ &sb_tx9_mux),
+ SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, TAIKO_TX10, 0,
+ &sb_tx10_mux),
/* Digital Mic Inputs */
SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
@@ -4651,2200 +4154,6 @@
};
-static short taiko_codec_read_sta_result(struct snd_soc_codec *codec)
-{
- u8 bias_msb, bias_lsb;
- short bias_value;
-
- bias_msb = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B3_STATUS);
- bias_lsb = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B2_STATUS);
- bias_value = (bias_msb << 8) | bias_lsb;
- return bias_value;
-}
-
-static short taiko_codec_read_dce_result(struct snd_soc_codec *codec)
-{
- u8 bias_msb, bias_lsb;
- short bias_value;
-
- bias_msb = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B5_STATUS);
- bias_lsb = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B4_STATUS);
- bias_value = (bias_msb << 8) | bias_lsb;
- return bias_value;
-}
-
-static void taiko_turn_onoff_rel_detection(struct snd_soc_codec *codec, bool on)
-{
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
-}
-
-static short __taiko_codec_sta_dce(struct snd_soc_codec *codec, int dce,
- bool override_bypass, bool noreldetection)
-{
- short bias_value;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL);
- if (noreldetection)
- taiko_turn_onoff_rel_detection(codec, false);
-
- /* Turn on the override */
- if (!override_bypass)
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
- if (dce) {
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x4);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
- usleep_range(taiko->mbhc_data.t_sta_dce,
- taiko->mbhc_data.t_sta_dce);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x4);
- usleep_range(taiko->mbhc_data.t_dce,
- taiko->mbhc_data.t_dce);
- bias_value = taiko_codec_read_dce_result(codec);
- } else {
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x2);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
- usleep_range(taiko->mbhc_data.t_sta_dce,
- taiko->mbhc_data.t_sta_dce);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x2);
- usleep_range(taiko->mbhc_data.t_sta,
- taiko->mbhc_data.t_sta);
- bias_value = taiko_codec_read_sta_result(codec);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x0);
- }
- /* Turn off the override after measuring mic voltage */
- if (!override_bypass)
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
-
- if (noreldetection)
- taiko_turn_onoff_rel_detection(codec, true);
- wcd9xxx_enable_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL);
-
- return bias_value;
-}
-
-static short taiko_codec_sta_dce(struct snd_soc_codec *codec, int dce,
- bool norel)
-{
- return __taiko_codec_sta_dce(codec, dce, false, norel);
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static short taiko_codec_setup_hs_polling(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- short bias_value;
- u8 cfilt_mode;
-
- pr_debug("%s: enter, mclk_enabled %d\n", __func__, taiko->mclk_enabled);
- if (!taiko->mbhc_cfg.calibration) {
- pr_err("Error, no taiko calibration\n");
- return -ENODEV;
- }
-
- if (!taiko->mclk_enabled) {
- taiko_codec_disable_clock_block(codec);
- taiko_codec_enable_bandgap(codec, TAIKO_BANDGAP_MBHC_MODE);
- taiko_enable_rx_bias(codec, 1);
- taiko_codec_enable_clock_block(codec, 1);
- }
-
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x05, 0x01);
-
- /* Make sure CFILT is in fast mode, save current mode */
- cfilt_mode = snd_soc_read(codec, taiko->mbhc_bias_regs.cfilt_ctl);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl, 0x70, 0x00);
-
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x1F, 0x16);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x84);
-
- snd_soc_update_bits(codec, TAIKO_A_TX_7_MBHC_EN, 0x80, 0x80);
- snd_soc_update_bits(codec, TAIKO_A_TX_7_MBHC_EN, 0x1F, 0x1C);
- snd_soc_update_bits(codec, TAIKO_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
-
- snd_soc_update_bits(codec, TAIKO_A_TX_7_MBHC_EN, 0x80, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
-
- taiko_codec_calibrate_hs_polling(codec);
-
- /* don't flip override */
- bias_value = __taiko_codec_sta_dce(codec, 1, true, true);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl, 0x40,
- cfilt_mode);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x13, 0x00);
-
- return bias_value;
-}
-
-static int taiko_cancel_btn_work(struct taiko_priv *taiko)
-{
- int r = 0;
- struct wcd9xxx *core = dev_get_drvdata(taiko->codec->dev->parent);
-
- if (cancel_delayed_work_sync(&taiko->mbhc_btn_dwork)) {
- /* if scheduled mbhc_btn_dwork is canceled from here,
- * we have to unlock from here instead btn_work */
- wcd9xxx_unlock_sleep(core);
- r = 1;
- }
- return r;
-}
-
-/* called under codec_resource_lock acquisition */
-void taiko_set_and_turnoff_hph_padac(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- u8 wg_time;
-
- wg_time = snd_soc_read(codec, TAIKO_A_RX_HPH_CNP_WG_TIME) ;
- wg_time += 1;
-
- /* If headphone PA is on, check if userspace receives
- * removal event to sync-up PA's state */
- if (taiko_is_hph_pa_on(codec)) {
- pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
- set_bit(TAIKO_HPHL_PA_OFF_ACK, &taiko->hph_pa_dac_state);
- set_bit(TAIKO_HPHR_PA_OFF_ACK, &taiko->hph_pa_dac_state);
- } else {
- pr_debug("%s PA is off\n", __func__);
- }
-
- if (taiko_is_hph_dac_on(codec, 1))
- set_bit(TAIKO_HPHL_DAC_OFF_ACK, &taiko->hph_pa_dac_state);
- if (taiko_is_hph_dac_on(codec, 0))
- set_bit(TAIKO_HPHR_DAC_OFF_ACK, &taiko->hph_pa_dac_state);
-
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_CNP_EN, 0x30, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_L_DAC_CTL,
- 0xC0, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_R_DAC_CTL,
- 0xC0, 0x00);
- usleep_range(wg_time * 1000, wg_time * 1000);
-}
-
-static void taiko_clr_and_turnon_hph_padac(struct taiko_priv *taiko)
-{
- bool pa_turned_on = false;
- struct snd_soc_codec *codec = taiko->codec;
- u8 wg_time;
-
- wg_time = snd_soc_read(codec, TAIKO_A_RX_HPH_CNP_WG_TIME) ;
- wg_time += 1;
-
- if (test_and_clear_bit(TAIKO_HPHR_DAC_OFF_ACK,
- &taiko->hph_pa_dac_state)) {
- pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
- snd_soc_update_bits(taiko->codec, TAIKO_A_RX_HPH_R_DAC_CTL,
- 0xC0, 0xC0);
- }
- if (test_and_clear_bit(TAIKO_HPHL_DAC_OFF_ACK,
- &taiko->hph_pa_dac_state)) {
- pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
- snd_soc_update_bits(taiko->codec, TAIKO_A_RX_HPH_L_DAC_CTL,
- 0xC0, 0xC0);
- }
-
- if (test_and_clear_bit(TAIKO_HPHR_PA_OFF_ACK,
- &taiko->hph_pa_dac_state)) {
- pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
- snd_soc_update_bits(taiko->codec, TAIKO_A_RX_HPH_CNP_EN, 0x10,
- 1 << 4);
- pa_turned_on = true;
- }
- if (test_and_clear_bit(TAIKO_HPHL_PA_OFF_ACK,
- &taiko->hph_pa_dac_state)) {
- pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
- snd_soc_update_bits(taiko->codec, TAIKO_A_RX_HPH_CNP_EN, 0x20,
- 1 << 5);
- pa_turned_on = true;
- }
-
- if (pa_turned_on) {
- pr_debug("%s: PA was turned off by MBHC and not by DAPM\n",
- __func__);
- usleep_range(wg_time * 1000, wg_time * 1000);
- }
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_report_plug(struct snd_soc_codec *codec, int insertion,
- enum snd_jack_types jack_type)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- if (!insertion) {
- /* Report removal */
- taiko->hph_status &= ~jack_type;
- if (taiko->mbhc_cfg.headset_jack) {
- /* cancel possibly scheduled btn work and
- * report release if we reported button press */
- if (taiko_cancel_btn_work(taiko)) {
- pr_debug("%s: button press is canceled\n",
- __func__);
- } else if (taiko->buttons_pressed) {
- pr_debug("%s: release of button press%d\n",
- __func__, jack_type);
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.button_jack, 0,
- taiko->buttons_pressed);
- taiko->buttons_pressed &=
- ~TAIKO_JACK_BUTTON_MASK;
- }
- pr_debug("%s: Reporting removal %d(%x)\n", __func__,
- jack_type, taiko->hph_status);
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.headset_jack,
- taiko->hph_status,
- TAIKO_JACK_MASK);
- }
- taiko_set_and_turnoff_hph_padac(codec);
- hphocp_off_report(taiko, SND_JACK_OC_HPHR,
- TAIKO_IRQ_HPH_PA_OCPR_FAULT);
- hphocp_off_report(taiko, SND_JACK_OC_HPHL,
- TAIKO_IRQ_HPH_PA_OCPL_FAULT);
- taiko->current_plug = PLUG_TYPE_NONE;
- taiko->mbhc_polling_active = false;
- } else {
- /* Report insertion */
- taiko->hph_status |= jack_type;
-
- if (jack_type == SND_JACK_HEADPHONE)
- taiko->current_plug = PLUG_TYPE_HEADPHONE;
- else if (jack_type == SND_JACK_UNSUPPORTED)
- taiko->current_plug = PLUG_TYPE_GND_MIC_SWAP;
- else if (jack_type == SND_JACK_HEADSET) {
- taiko->mbhc_polling_active = true;
- taiko->current_plug = PLUG_TYPE_HEADSET;
- }
- if (taiko->mbhc_cfg.headset_jack) {
- pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
- jack_type, taiko->hph_status);
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.headset_jack,
- taiko->hph_status,
- TAIKO_JACK_MASK);
- }
- taiko_clr_and_turnon_hph_padac(taiko);
- }
-}
-
-static int taiko_codec_enable_hs_detect(struct snd_soc_codec *codec,
- int insertion, int trigger,
- bool padac_off)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- int central_bias_enabled = 0;
- const struct taiko_mbhc_general_cfg *generic =
- TAIKO_MBHC_CAL_GENERAL_PTR(taiko->mbhc_cfg.calibration);
- const struct taiko_mbhc_plug_detect_cfg *plug_det =
- TAIKO_MBHC_CAL_PLUG_DET_PTR(taiko->mbhc_cfg.calibration);
-
- if (!taiko->mbhc_cfg.calibration) {
- pr_err("Error, no taiko calibration\n");
- return -EINVAL;
- }
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x1, 0);
-
- /* Make sure mic bias and Mic line schmitt trigger
- * are turned OFF
- */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
-
- if (insertion) {
- taiko_codec_switch_micbias(codec, 0);
-
- /* DAPM can manipulate PA/DAC bits concurrently */
- if (padac_off == true)
- taiko_set_and_turnoff_hph_padac(codec);
-
- if (trigger & MBHC_USE_HPHL_TRIGGER) {
- /* Enable HPH Schmitt Trigger */
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x11,
- 0x11);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x0C,
- plug_det->hph_current << 2);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x02,
- 0x02);
- }
- if (trigger & MBHC_USE_MB_TRIGGER) {
- /* enable the mic line schmitt trigger */
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.mbhc_reg,
- 0x60, plug_det->mic_current << 5);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.mbhc_reg,
- 0x80, 0x80);
- usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.ctl_reg, 0x01,
- 0x00);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.mbhc_reg,
- 0x10, 0x10);
- }
-
- /* setup for insetion detection */
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x2, 0);
- } else {
- pr_debug("setup for removal detection\n");
- /* Make sure the HPH schmitt trigger is OFF */
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x12, 0x00);
-
- /* enable the mic line schmitt trigger */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg,
- 0x01, 0x00);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x60,
- plug_det->mic_current << 5);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
- 0x80, 0x80);
- usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
- 0x10, 0x10);
-
- /* Setup for low power removal detection */
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x2, 0x2);
- }
-
- if (snd_soc_read(codec, TAIKO_A_CDC_MBHC_B1_CTL) & 0x4) {
- /* called called by interrupt */
- if (!(taiko->clock_active)) {
- taiko_codec_enable_config_mode(codec, 1);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL,
- 0x06, 0);
- usleep_range(generic->t_shutdown_plug_rem,
- generic->t_shutdown_plug_rem);
- taiko_codec_enable_config_mode(codec, 0);
- } else
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL,
- 0x06, 0);
- }
-
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.int_rbias, 0x80, 0);
-
- /* If central bandgap disabled */
- if (!(snd_soc_read(codec, TAIKO_A_PIN_CTL_OE1) & 1)) {
- snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE1, 0x3, 0x3);
- usleep_range(generic->t_bg_fast_settle,
- generic->t_bg_fast_settle);
- central_bias_enabled = 1;
- }
-
- /* If LDO_H disabled */
- if (snd_soc_read(codec, TAIKO_A_PIN_CTL_OE0) & 0x80) {
- snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE0, 0x10, 0);
- snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE0, 0x80, 0x80);
- usleep_range(generic->t_ldoh, generic->t_ldoh);
- snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE0, 0x80, 0);
-
- if (central_bias_enabled)
- snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE1, 0x1, 0);
- }
-
- snd_soc_update_bits(codec, taiko->reg_addr.micb_4_mbhc, 0x3,
- taiko->mbhc_cfg.micbias);
-
- wcd9xxx_enable_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
- return 0;
-}
-
-static u16 taiko_codec_v_sta_dce(struct snd_soc_codec *codec, bool dce,
- s16 vin_mv)
-{
- struct taiko_priv *taiko;
- s16 diff, zero;
- u32 mb_mv, in;
- u16 value;
-
- taiko = snd_soc_codec_get_drvdata(codec);
- mb_mv = taiko->mbhc_data.micb_mv;
-
- if (mb_mv == 0) {
- pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
- return -EINVAL;
- }
-
- if (dce) {
- diff = (taiko->mbhc_data.dce_mb) - (taiko->mbhc_data.dce_z);
- zero = (taiko->mbhc_data.dce_z);
- } else {
- diff = (taiko->mbhc_data.sta_mb) - (taiko->mbhc_data.sta_z);
- zero = (taiko->mbhc_data.sta_z);
- }
- in = (u32) diff * vin_mv;
-
- value = (u16) (in / mb_mv) + zero;
- return value;
-}
-
-static s32 taiko_codec_sta_dce_v(struct snd_soc_codec *codec, s8 dce,
- u16 bias_value)
-{
- struct taiko_priv *taiko;
- s16 value, z, mb;
- s32 mv;
-
- taiko = snd_soc_codec_get_drvdata(codec);
- value = bias_value;
- if (dce) {
- z = (taiko->mbhc_data.dce_z);
- mb = (taiko->mbhc_data.dce_mb);
- mv = (value - z) * (s32)taiko->mbhc_data.micb_mv / (mb - z);
- } else {
- z = (taiko->mbhc_data.sta_z);
- mb = (taiko->mbhc_data.sta_mb);
- mv = (value - z) * (s32)taiko->mbhc_data.micb_mv / (mb - z);
- }
-
- return mv;
-}
-
-static void btn_lpress_fn(struct work_struct *work)
-{
- struct delayed_work *delayed_work;
- struct taiko_priv *taiko;
- short bias_value;
- int dce_mv, sta_mv;
- struct wcd9xxx *core;
-
- pr_debug("%s:\n", __func__);
-
- delayed_work = to_delayed_work(work);
- taiko = container_of(delayed_work, struct taiko_priv, mbhc_btn_dwork);
- core = dev_get_drvdata(taiko->codec->dev->parent);
-
- if (taiko) {
- if (taiko->mbhc_cfg.button_jack) {
- bias_value = taiko_codec_read_sta_result(taiko->codec);
- sta_mv = taiko_codec_sta_dce_v(taiko->codec, 0,
- bias_value);
- bias_value = taiko_codec_read_dce_result(taiko->codec);
- dce_mv = taiko_codec_sta_dce_v(taiko->codec, 1,
- bias_value);
- pr_debug("%s: Reporting long button press event\n",
- __func__);
- pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv,
- dce_mv);
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.button_jack,
- taiko->buttons_pressed,
- taiko->buttons_pressed);
- }
- } else {
- pr_err("%s: Bad taiko private data\n", __func__);
- }
-
- pr_debug("%s: leave\n", __func__);
- wcd9xxx_unlock_sleep(core);
-}
-
-void taiko_mbhc_cal(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko;
- struct taiko_mbhc_btn_detect_cfg *btn_det;
- u8 cfilt_mode, bg_mode;
- u8 ncic, nmeas, navg;
- u32 mclk_rate;
- u32 dce_wait, sta_wait;
- u8 *n_cic;
- void *calibration;
-
- taiko = snd_soc_codec_get_drvdata(codec);
- calibration = taiko->mbhc_cfg.calibration;
-
- wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL);
- taiko_turn_onoff_rel_detection(codec, false);
-
- /* First compute the DCE / STA wait times
- * depending on tunable parameters.
- * The value is computed in microseconds
- */
- btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(calibration);
- n_cic = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_CIC);
- ncic = n_cic[taiko_codec_mclk_index(taiko)];
- nmeas = TAIKO_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
- navg = TAIKO_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
- mclk_rate = taiko->mbhc_cfg.mclk_rate;
- dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (mclk_rate / 1000);
- sta_wait = (1000 * 128 * (navg + 1)) / (mclk_rate / 1000);
-
- taiko->mbhc_data.t_dce = dce_wait;
- taiko->mbhc_data.t_sta = sta_wait;
-
- /* LDOH and CFILT are already configured during pdata handling.
- * Only need to make sure CFILT and bandgap are in Fast mode.
- * Need to restore defaults once calculation is done.
- */
- cfilt_mode = snd_soc_read(codec, taiko->mbhc_bias_regs.cfilt_ctl);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl, 0x40, 0x00);
- bg_mode = snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x02,
- 0x02);
-
- /* Micbias, CFILT, LDOH, MBHC MUX mode settings
- * to perform ADC calibration
- */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x60,
- taiko->mbhc_cfg.micbias << 5);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_LDO_H_MODE_1, 0x60, 0x60);
- snd_soc_write(codec, TAIKO_A_TX_7_MBHC_TEST_CTL, 0x78);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x04, 0x04);
-
- /* DCE measurement for 0 volts */
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x0A);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x04);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x02);
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x81);
- usleep_range(100, 100);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x04);
- usleep_range(taiko->mbhc_data.t_dce, taiko->mbhc_data.t_dce);
- taiko->mbhc_data.dce_z = taiko_codec_read_dce_result(codec);
-
- /* DCE measurment for MB voltage */
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x0A);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x02);
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x82);
- usleep_range(100, 100);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x04);
- usleep_range(taiko->mbhc_data.t_dce, taiko->mbhc_data.t_dce);
- taiko->mbhc_data.dce_mb = taiko_codec_read_dce_result(codec);
-
- /* Sta measuremnt for 0 volts */
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x0A);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x02);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x02);
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x81);
- usleep_range(100, 100);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x02);
- usleep_range(taiko->mbhc_data.t_sta, taiko->mbhc_data.t_sta);
- taiko->mbhc_data.sta_z = taiko_codec_read_sta_result(codec);
-
- /* STA Measurement for MB Voltage */
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x82);
- usleep_range(100, 100);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x02);
- usleep_range(taiko->mbhc_data.t_sta, taiko->mbhc_data.t_sta);
- taiko->mbhc_data.sta_mb = taiko_codec_read_sta_result(codec);
-
- /* Restore default settings. */
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl, 0x40,
- cfilt_mode);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x02, bg_mode);
-
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x84);
- usleep_range(100, 100);
-
- wcd9xxx_enable_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL);
- taiko_turn_onoff_rel_detection(codec, true);
-}
-
-void *taiko_mbhc_cal_btn_det_mp(const struct taiko_mbhc_btn_detect_cfg *btn_det,
- const enum taiko_mbhc_btn_det_mem mem)
-{
- void *ret = &btn_det->_v_btn_low;
-
- switch (mem) {
- case TAIKO_BTN_DET_GAIN:
- ret += sizeof(btn_det->_n_cic);
- case TAIKO_BTN_DET_N_CIC:
- ret += sizeof(btn_det->_n_ready);
- case TAIKO_BTN_DET_N_READY:
- ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
- case TAIKO_BTN_DET_V_BTN_HIGH:
- ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
- case TAIKO_BTN_DET_V_BTN_LOW:
- /* do nothing */
- break;
- default:
- ret = NULL;
- }
-
- return ret;
-}
-
-static s16 taiko_scale_v_micb_vddio(struct taiko_priv *taiko, int v,
- bool tovddio)
-{
- int r;
- int vddio_k, mb_k;
- vddio_k = taiko_find_k_value(taiko->pdata->micbias.ldoh_v,
- VDDIO_MICBIAS_MV);
- mb_k = taiko_find_k_value(taiko->pdata->micbias.ldoh_v,
- taiko->mbhc_data.micb_mv);
- if (tovddio)
- r = v * vddio_k / mb_k;
- else
- r = v * mb_k / vddio_k;
- return r;
-}
-
-static void taiko_mbhc_calc_thres(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko;
- s16 btn_mv = 0, btn_delta_mv;
- struct taiko_mbhc_btn_detect_cfg *btn_det;
- struct taiko_mbhc_plug_type_cfg *plug_type;
- u16 *btn_high;
- u8 *n_ready;
- int i;
-
- taiko = snd_soc_codec_get_drvdata(codec);
- btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(taiko->mbhc_cfg.calibration);
- plug_type = TAIKO_MBHC_CAL_PLUG_TYPE_PTR(taiko->mbhc_cfg.calibration);
-
- n_ready = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_READY);
- if (taiko->mbhc_cfg.mclk_rate == TAIKO_MCLK_RATE_12288KHZ) {
- taiko->mbhc_data.npoll = 4;
- taiko->mbhc_data.nbounce_wait = 30;
- } else if (taiko->mbhc_cfg.mclk_rate == TAIKO_MCLK_RATE_9600KHZ) {
- taiko->mbhc_data.npoll = 7;
- taiko->mbhc_data.nbounce_wait = 23;
- }
-
- taiko->mbhc_data.t_sta_dce = ((1000 * 256) /
- (taiko->mbhc_cfg.mclk_rate / 1000) *
- n_ready[taiko_codec_mclk_index(taiko)]) +
- 10;
- taiko->mbhc_data.v_ins_hu =
- taiko_codec_v_sta_dce(codec, STA, plug_type->v_hs_max);
- taiko->mbhc_data.v_ins_h =
- taiko_codec_v_sta_dce(codec, DCE, plug_type->v_hs_max);
-
- taiko->mbhc_data.v_inval_ins_low = TAIKO_MBHC_FAKE_INSERT_LOW;
- if (taiko->mbhc_cfg.gpio)
- taiko->mbhc_data.v_inval_ins_high =
- TAIKO_MBHC_FAKE_INSERT_HIGH;
- else
- taiko->mbhc_data.v_inval_ins_high =
- TAIKO_MBHC_FAKE_INS_HIGH_NO_GPIO;
-
- if (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
- taiko->mbhc_data.adj_v_hs_max =
- taiko_scale_v_micb_vddio(taiko, plug_type->v_hs_max, true);
- taiko->mbhc_data.adj_v_ins_hu =
- taiko_codec_v_sta_dce(codec, STA,
- taiko->mbhc_data.adj_v_hs_max);
- taiko->mbhc_data.adj_v_ins_h =
- taiko_codec_v_sta_dce(codec, DCE,
- taiko->mbhc_data.adj_v_hs_max);
- taiko->mbhc_data.v_inval_ins_low =
- taiko_scale_v_micb_vddio(taiko,
- taiko->mbhc_data.v_inval_ins_low,
- false);
- taiko->mbhc_data.v_inval_ins_high =
- taiko_scale_v_micb_vddio(taiko,
- taiko->mbhc_data.v_inval_ins_high,
- false);
- }
-
- btn_high = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_V_BTN_HIGH);
- for (i = 0; i < btn_det->num_btn; i++)
- btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
-
- taiko->mbhc_data.v_b1_h = taiko_codec_v_sta_dce(codec, DCE, btn_mv);
- btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_sta;
- taiko->mbhc_data.v_b1_hu =
- taiko_codec_v_sta_dce(codec, STA, btn_delta_mv);
-
- btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_cic;
-
- taiko->mbhc_data.v_b1_huc =
- taiko_codec_v_sta_dce(codec, DCE, btn_delta_mv);
-
- taiko->mbhc_data.v_brh = taiko->mbhc_data.v_b1_h;
- taiko->mbhc_data.v_brl = TAIKO_MBHC_BUTTON_MIN;
-
- taiko->mbhc_data.v_no_mic =
- taiko_codec_v_sta_dce(codec, STA, plug_type->v_no_mic);
-}
-
-void taiko_mbhc_init(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko;
- struct taiko_mbhc_general_cfg *generic;
- struct taiko_mbhc_btn_detect_cfg *btn_det;
- int n;
- u8 *n_cic, *gain;
-
- taiko = snd_soc_codec_get_drvdata(codec);
- generic = TAIKO_MBHC_CAL_GENERAL_PTR(taiko->mbhc_cfg.calibration);
- btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(taiko->mbhc_cfg.calibration);
-
- for (n = 0; n < 8; n++) {
- snd_soc_update_bits(codec,
- TAIKO_A_CDC_MBHC_FIR_B1_CFG,
- 0x07, n);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_FIR_B2_CFG,
- btn_det->c[n]);
- }
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B2_CTL, 0x07,
- btn_det->nc);
-
- n_cic = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_CIC);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_TIMER_B6_CTL, 0xFF,
- n_cic[taiko_codec_mclk_index(taiko)]);
-
- gain = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_GAIN);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B2_CTL, 0x78,
- gain[taiko_codec_mclk_index(taiko)] << 3);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
- generic->mbhc_nsa << 4);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
- btn_det->n_meas);
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B5_CTL, generic->mbhc_navg);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x78,
- btn_det->mbhc_nsc << 3);
-
- snd_soc_update_bits(codec, taiko->reg_addr.micb_4_mbhc, 0x03,
- TAIKO_MICBIAS2);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
-
- snd_soc_update_bits(codec, TAIKO_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
-}
-
-static bool taiko_mbhc_fw_validate(const struct firmware *fw)
-{
- u32 cfg_offset;
- struct taiko_mbhc_imped_detect_cfg *imped_cfg;
- struct taiko_mbhc_btn_detect_cfg *btn_cfg;
-
- if (fw->size < TAIKO_MBHC_CAL_MIN_SIZE)
- return false;
-
- /* previous check guarantees that there is enough fw data up
- * to num_btn
- */
- btn_cfg = TAIKO_MBHC_CAL_BTN_DET_PTR(fw->data);
- cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
- if (fw->size < (cfg_offset + TAIKO_MBHC_CAL_BTN_SZ(btn_cfg)))
- return false;
-
- /* previous check guarantees that there is enough fw data up
- * to start of impedance detection configuration
- */
- imped_cfg = TAIKO_MBHC_CAL_IMPED_DET_PTR(fw->data);
- cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
-
- if (fw->size < (cfg_offset + TAIKO_MBHC_CAL_IMPED_MIN_SZ))
- return false;
-
- if (fw->size < (cfg_offset + TAIKO_MBHC_CAL_IMPED_SZ(imped_cfg)))
- return false;
-
- return true;
-}
-
-/* called under codec_resource_lock acquisition */
-static int taiko_determine_button(const struct taiko_priv *priv,
- const s32 micmv)
-{
- s16 *v_btn_low, *v_btn_high;
- struct taiko_mbhc_btn_detect_cfg *btn_det;
- int i, btn = -1;
-
- btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(priv->mbhc_cfg.calibration);
- v_btn_low = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_V_BTN_LOW);
- v_btn_high = taiko_mbhc_cal_btn_det_mp(btn_det,
- TAIKO_BTN_DET_V_BTN_HIGH);
-
- for (i = 0; i < btn_det->num_btn; i++) {
- if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
- btn = i;
- break;
- }
- }
-
- if (btn == -1)
- pr_debug("%s: couldn't find button number for mic mv %d\n",
- __func__, micmv);
-
- return btn;
-}
-
-static int taiko_get_button_mask(const int btn)
-{
- int mask = 0;
- switch (btn) {
- case 0:
- mask = SND_JACK_BTN_0;
- break;
- case 1:
- mask = SND_JACK_BTN_1;
- break;
- case 2:
- mask = SND_JACK_BTN_2;
- break;
- case 3:
- mask = SND_JACK_BTN_3;
- break;
- case 4:
- mask = SND_JACK_BTN_4;
- break;
- case 5:
- mask = SND_JACK_BTN_5;
- break;
- case 6:
- mask = SND_JACK_BTN_6;
- break;
- case 7:
- mask = SND_JACK_BTN_7;
- break;
- }
- return mask;
-}
-
-static irqreturn_t taiko_dce_handler(int irq, void *data)
-{
- int i, mask;
- short dce, sta;
- s32 mv, mv_s, stamv_s;
- bool vddio;
- int btn = -1, meas = 0;
- struct taiko_priv *priv = data;
- const struct taiko_mbhc_btn_detect_cfg *d =
- TAIKO_MBHC_CAL_BTN_DET_PTR(priv->mbhc_cfg.calibration);
- short btnmeas[d->n_btn_meas + 1];
- struct snd_soc_codec *codec = priv->codec;
- struct wcd9xxx *core = dev_get_drvdata(priv->codec->dev->parent);
- int n_btn_meas = d->n_btn_meas;
- u8 mbhc_status = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B1_STATUS) & 0x3E;
-
- pr_debug("%s: enter\n", __func__);
-
- TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
- if (priv->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
- pr_debug("%s: mbhc is being recovered, skip button press\n",
- __func__);
- goto done;
- }
-
- priv->mbhc_state = MBHC_STATE_POTENTIAL;
-
- if (!priv->mbhc_polling_active) {
- pr_warn("%s: mbhc polling is not active, skip button press\n",
- __func__);
- goto done;
- }
-
- dce = taiko_codec_read_dce_result(codec);
- mv = taiko_codec_sta_dce_v(codec, 1, dce);
-
- /* If GPIO interrupt already kicked in, ignore button press */
- if (priv->in_gpio_handler) {
- pr_debug("%s: GPIO State Changed, ignore button press\n",
- __func__);
- btn = -1;
- goto done;
- }
-
- vddio = (priv->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
- priv->mbhc_micbias_switched);
- mv_s = vddio ? taiko_scale_v_micb_vddio(priv, mv, false) : mv;
-
- if (mbhc_status != TAIKO_MBHC_STATUS_REL_DETECTION) {
- if (priv->mbhc_last_resume &&
- !time_after(jiffies, priv->mbhc_last_resume + HZ)) {
- pr_debug("%s: Button is already released shortly after resume\n",
- __func__);
- n_btn_meas = 0;
- } else {
- pr_debug("%s: Button is already released without resume",
- __func__);
- sta = taiko_codec_read_sta_result(codec);
- stamv_s = taiko_codec_sta_dce_v(codec, 0, sta);
- if (vddio)
- stamv_s = taiko_scale_v_micb_vddio(priv,
- stamv_s,
- false);
- btn = taiko_determine_button(priv, mv_s);
- if (btn != taiko_determine_button(priv, stamv_s))
- btn = -1;
- goto done;
- }
- }
-
- /* determine pressed button */
- btnmeas[meas++] = taiko_determine_button(priv, mv_s);
- pr_debug("%s: meas %d - DCE %d,%d,%d button %d\n", __func__,
- meas - 1, dce, mv, mv_s, btnmeas[meas - 1]);
- if (n_btn_meas == 0)
- btn = btnmeas[0];
- for (; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1))); meas++) {
- dce = taiko_codec_sta_dce(codec, 1, false);
- mv = taiko_codec_sta_dce_v(codec, 1, dce);
- mv_s = vddio ? taiko_scale_v_micb_vddio(priv, mv, false) : mv;
-
- btnmeas[meas] = taiko_determine_button(priv, mv_s);
- pr_debug("%s: meas %d - DCE %d,%d,%d button %d\n",
- __func__, meas, dce, mv, mv_s, btnmeas[meas]);
- /* if large enough measurements are collected,
- * start to check if last all n_btn_con measurements were
- * in same button low/high range */
- if (meas + 1 >= d->n_btn_con) {
- for (i = 0; i < d->n_btn_con; i++)
- if ((btnmeas[meas] < 0) ||
- (btnmeas[meas] != btnmeas[meas - i]))
- break;
- if (i == d->n_btn_con) {
- /* button pressed */
- btn = btnmeas[meas];
- break;
- } else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
- /* if left measurements are less than n_btn_con,
- * it's impossible to find button number */
- break;
- }
- }
- }
-
- if (btn >= 0) {
- if (priv->in_gpio_handler) {
- pr_debug(
- "%s: GPIO already triggered, ignore button press\n",
- __func__);
- goto done;
- }
- mask = taiko_get_button_mask(btn);
- priv->buttons_pressed |= mask;
- wcd9xxx_lock_sleep(core);
- if (schedule_delayed_work(&priv->mbhc_btn_dwork,
- msecs_to_jiffies(400)) == 0) {
- WARN(1, "Button pressed twice without release event\n");
- wcd9xxx_unlock_sleep(core);
- }
- } else {
- pr_debug("%s: bogus button press, too short press?\n",
- __func__);
- }
-
- done:
- pr_debug("%s: leave\n", __func__);
- TAIKO_RELEASE_LOCK(priv->codec_resource_lock);
- return IRQ_HANDLED;
-}
-
-static int taiko_is_fake_press(struct taiko_priv *priv)
-{
- int i;
- int r = 0;
- struct snd_soc_codec *codec = priv->codec;
- const int dces = MBHC_NUM_DCE_PLUG_DETECT;
- s16 mb_v, v_ins_hu, v_ins_h;
-
- v_ins_hu = taiko_get_current_v_ins(priv, true);
- v_ins_h = taiko_get_current_v_ins(priv, false);
-
- for (i = 0; i < dces; i++) {
- usleep_range(10000, 10000);
- if (i == 0) {
- mb_v = taiko_codec_sta_dce(codec, 0, true);
- pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
- taiko_codec_sta_dce_v(codec, 0, mb_v));
- if (mb_v < (s16)priv->mbhc_data.v_b1_hu ||
- mb_v > v_ins_hu) {
- r = 1;
- break;
- }
- } else {
- mb_v = taiko_codec_sta_dce(codec, 1, true);
- pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
- taiko_codec_sta_dce_v(codec, 1, mb_v));
- if (mb_v < (s16)priv->mbhc_data.v_b1_h ||
- mb_v > v_ins_h) {
- r = 1;
- break;
- }
- }
- }
-
- return r;
-}
-
-static irqreturn_t taiko_release_handler(int irq, void *data)
-{
- int ret;
- struct taiko_priv *priv = data;
- struct snd_soc_codec *codec = priv->codec;
-
- pr_debug("%s: enter\n", __func__);
-
- TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
- priv->mbhc_state = MBHC_STATE_RELEASE;
-
- taiko_codec_drive_v_to_micbias(codec, 10000);
-
- if (priv->buttons_pressed & TAIKO_JACK_BUTTON_MASK) {
- ret = taiko_cancel_btn_work(priv);
- if (ret == 0) {
- pr_debug("%s: Reporting long button release event\n",
- __func__);
- if (priv->mbhc_cfg.button_jack)
- taiko_snd_soc_jack_report(priv,
- priv->mbhc_cfg.button_jack, 0,
- priv->buttons_pressed);
- } else {
- if (taiko_is_fake_press(priv)) {
- pr_debug("%s: Fake button press interrupt\n",
- __func__);
- } else if (priv->mbhc_cfg.button_jack) {
- if (priv->in_gpio_handler) {
- pr_debug("%s: GPIO kicked in, ignore\n",
- __func__);
- } else {
- pr_debug(
- "%s: Reporting short button press and release\n",
- __func__);
- taiko_snd_soc_jack_report(priv,
- priv->mbhc_cfg.button_jack,
- priv->buttons_pressed,
- priv->buttons_pressed);
- taiko_snd_soc_jack_report(priv,
- priv->mbhc_cfg.button_jack, 0,
- priv->buttons_pressed);
- }
- }
- }
-
- priv->buttons_pressed &= ~TAIKO_JACK_BUTTON_MASK;
- }
-
- taiko_codec_calibrate_hs_polling(codec);
-
- if (priv->mbhc_cfg.gpio)
- msleep(TAIKO_MBHC_GPIO_REL_DEBOUNCE_TIME_MS);
-
- taiko_codec_start_hs_polling(codec);
-
- pr_debug("%s: leave\n", __func__);
- TAIKO_RELEASE_LOCK(priv->codec_resource_lock);
- return IRQ_HANDLED;
-}
-
-static void taiko_codec_shutdown_hs_removal_detect(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- const struct taiko_mbhc_general_cfg *generic =
- TAIKO_MBHC_CAL_GENERAL_PTR(taiko->mbhc_cfg.calibration);
-
- if (!taiko->mclk_enabled && !taiko->mbhc_polling_active)
- taiko_codec_enable_config_mode(codec, 1);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
-
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x80, 0x00);
-
- usleep_range(generic->t_shutdown_plug_rem,
- generic->t_shutdown_plug_rem);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
- if (!taiko->mclk_enabled && !taiko->mbhc_polling_active)
- taiko_codec_enable_config_mode(codec, 0);
-
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x00);
-}
-
-static void taiko_codec_cleanup_hs_polling(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- taiko_codec_shutdown_hs_removal_detect(codec);
-
- if (!taiko->mclk_enabled) {
- taiko_codec_disable_clock_block(codec);
- taiko_codec_enable_bandgap(codec, TAIKO_BANDGAP_OFF);
- }
-
- taiko->mbhc_polling_active = false;
- taiko->mbhc_state = MBHC_STATE_NONE;
-}
-
-static irqreturn_t taiko_hphl_ocp_irq(int irq, void *data)
-{
- struct taiko_priv *taiko = data;
- struct snd_soc_codec *codec;
-
- pr_info("%s: received HPHL OCP irq\n", __func__);
-
- if (taiko) {
- codec = taiko->codec;
- if (taiko->hphlocp_cnt++ < TAIKO_OCP_ATTEMPT) {
- pr_info("%s: retry\n", __func__);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10,
- 0x00);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10,
- 0x10);
- } else {
- wcd9xxx_disable_irq(codec->control_data,
- TAIKO_IRQ_HPH_PA_OCPL_FAULT);
- taiko->hphlocp_cnt = 0;
- taiko->hph_status |= SND_JACK_OC_HPHL;
- if (taiko->mbhc_cfg.headset_jack)
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.headset_jack,
- taiko->hph_status,
- TAIKO_JACK_MASK);
- }
- } else {
- pr_err("%s: Bad taiko private data\n", __func__);
- }
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t taiko_hphr_ocp_irq(int irq, void *data)
-{
- struct taiko_priv *taiko = data;
- struct snd_soc_codec *codec;
-
- pr_info("%s: received HPHR OCP irq\n", __func__);
-
- if (taiko) {
- codec = taiko->codec;
- if (taiko->hphrocp_cnt++ < TAIKO_OCP_ATTEMPT) {
- pr_info("%s: retry\n", __func__);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10,
- 0x00);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10,
- 0x10);
- } else {
- wcd9xxx_disable_irq(codec->control_data,
- TAIKO_IRQ_HPH_PA_OCPR_FAULT);
- taiko->hphrocp_cnt = 0;
- taiko->hph_status |= SND_JACK_OC_HPHR;
- if (taiko->mbhc_cfg.headset_jack)
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.headset_jack,
- taiko->hph_status,
- TAIKO_JACK_MASK);
- }
- } else {
- pr_err("%s: Bad taiko private data\n", __func__);
- }
-
- return IRQ_HANDLED;
-}
-
-static bool taiko_is_inval_ins_range(struct snd_soc_codec *codec,
- s32 mic_volt, bool highhph, bool *highv)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- bool invalid = false;
- s16 v_hs_max;
-
- /* Perform this check only when the high voltage headphone
- * needs to be considered as invalid
- */
- v_hs_max = taiko_get_current_v_hs_max(taiko);
- *highv = mic_volt > v_hs_max;
- if (!highhph && *highv)
- invalid = true;
- else if (mic_volt < taiko->mbhc_data.v_inval_ins_high &&
- (mic_volt > taiko->mbhc_data.v_inval_ins_low))
- invalid = true;
-
- return invalid;
-}
-
-static bool taiko_is_inval_ins_delta(struct snd_soc_codec *codec,
- int mic_volt, int mic_volt_prev,
- int threshold)
-{
- return abs(mic_volt - mic_volt_prev) > threshold;
-}
-
-/* called under codec_resource_lock acquisition */
-void taiko_find_plug_and_report(struct snd_soc_codec *codec,
- enum taiko_mbhc_plug_type plug_type)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- if (plug_type == PLUG_TYPE_HEADPHONE &&
- taiko->current_plug == PLUG_TYPE_NONE) {
- /* Nothing was reported previously
- * report a headphone or unsupported
- */
- taiko_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
- taiko_codec_cleanup_hs_polling(codec);
- } else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
- if (taiko->current_plug == PLUG_TYPE_HEADSET)
- taiko_codec_report_plug(codec, 0, SND_JACK_HEADSET);
- else if (taiko->current_plug == PLUG_TYPE_HEADPHONE)
- taiko_codec_report_plug(codec, 0, SND_JACK_HEADPHONE);
-
- taiko_codec_report_plug(codec, 1, SND_JACK_UNSUPPORTED);
- taiko_codec_cleanup_hs_polling(codec);
- } else if (plug_type == PLUG_TYPE_HEADSET) {
- /* If Headphone was reported previously, this will
- * only report the mic line
- */
- taiko_codec_report_plug(codec, 1, SND_JACK_HEADSET);
- msleep(100);
- taiko_codec_start_hs_polling(codec);
- } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
- if (taiko->current_plug == PLUG_TYPE_NONE)
- taiko_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
- taiko_codec_cleanup_hs_polling(codec);
- pr_debug("setup mic trigger for further detection\n");
- taiko->lpi_enabled = true;
- taiko_codec_enable_hs_detect(codec, 1,
- MBHC_USE_MB_TRIGGER |
- MBHC_USE_HPHL_TRIGGER,
- false);
- } else {
- WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
- taiko->current_plug, plug_type);
- }
-}
-
-/* should be called under interrupt context that hold suspend */
-static void taiko_schedule_hs_detect_plug(struct taiko_priv *taiko)
-{
- pr_debug("%s: scheduling taiko_hs_correct_gpio_plug\n", __func__);
- taiko->hs_detect_work_stop = false;
- wcd9xxx_lock_sleep(taiko->codec->control_data);
- schedule_work(&taiko->hs_correct_plug_work);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_cancel_hs_detect_plug(struct taiko_priv *taiko)
-{
- pr_debug("%s: canceling hs_correct_plug_work\n", __func__);
- taiko->hs_detect_work_stop = true;
- wmb();
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- if (cancel_work_sync(&taiko->hs_correct_plug_work)) {
- pr_debug("%s: hs_correct_plug_work is canceled\n", __func__);
- wcd9xxx_unlock_sleep(taiko->codec->control_data);
- }
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-}
-
-static bool taiko_hs_gpio_level_remove(struct taiko_priv *taiko)
-{
- return (gpio_get_value_cansleep(taiko->mbhc_cfg.gpio) !=
- taiko->mbhc_cfg.gpio_level_insert);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
-{
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x01, on);
- if (on)
- usleep_range(5000, 5000);
-}
-
-/* called under codec_resource_lock acquisition and mbhc override = 1 */
-static enum taiko_mbhc_plug_type
-taiko_codec_get_plug_type(struct snd_soc_codec *codec, bool highhph)
-{
- int i;
- bool gndswitch, vddioswitch;
- int scaled;
- struct taiko_mbhc_plug_type_cfg *plug_type_ptr;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- const bool vddio = (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV);
- int num_det = (MBHC_NUM_DCE_PLUG_DETECT + vddio);
- enum taiko_mbhc_plug_type plug_type[num_det];
- s16 mb_v[num_det];
- s32 mic_mv[num_det];
- bool inval;
- bool highdelta;
- bool ahighv = false, highv;
-
- /* make sure override is on */
- WARN_ON(!(snd_soc_read(codec, TAIKO_A_CDC_MBHC_B1_CTL) & 0x04));
-
- /* GND and MIC swap detection requires at least 2 rounds of DCE */
- BUG_ON(num_det < 2);
-
- plug_type_ptr =
- TAIKO_MBHC_CAL_PLUG_TYPE_PTR(taiko->mbhc_cfg.calibration);
-
- plug_type[0] = PLUG_TYPE_INVALID;
-
- /* performs DCEs for N times
- * 1st: check if voltage is in invalid range
- * 2nd - N-2nd: check voltage range and delta
- * N-1st: check voltage range, delta with HPHR GND switch
- * Nth: check voltage range with VDDIO switch if micbias V != vddio V*/
- for (i = 0; i < num_det; i++) {
- gndswitch = (i == (num_det - 1 - vddio));
- vddioswitch = (vddio && ((i == num_det - 1) ||
- (i == num_det - 2)));
- if (i == 0) {
- mb_v[i] = taiko_codec_setup_hs_polling(codec);
- mic_mv[i] = taiko_codec_sta_dce_v(codec, 1 , mb_v[i]);
- inval = taiko_is_inval_ins_range(codec, mic_mv[i],
- highhph, &highv);
- ahighv |= highv;
- scaled = mic_mv[i];
- } else {
- if (vddioswitch)
- __taiko_codec_switch_micbias(taiko->codec, 1,
- false, false);
- if (gndswitch)
- taiko_codec_hphr_gnd_switch(codec, true);
- mb_v[i] = __taiko_codec_sta_dce(codec, 1, true, true);
- mic_mv[i] = taiko_codec_sta_dce_v(codec, 1 , mb_v[i]);
- if (vddioswitch)
- scaled = taiko_scale_v_micb_vddio(taiko,
- mic_mv[i],
- false);
- else
- scaled = mic_mv[i];
- /* !gndswitch & vddioswitch means the previous DCE
- * was done with gndswitch, don't compare with DCE
- * with gndswitch */
- highdelta = taiko_is_inval_ins_delta(codec, scaled,
- mic_mv[i - !gndswitch - vddioswitch],
- TAIKO_MBHC_FAKE_INS_DELTA_SCALED_MV);
- inval = (taiko_is_inval_ins_range(codec, mic_mv[i],
- highhph, &highv) ||
- highdelta);
- ahighv |= highv;
- if (gndswitch)
- taiko_codec_hphr_gnd_switch(codec, false);
- if (vddioswitch)
- __taiko_codec_switch_micbias(taiko->codec, 0,
- false, false);
- /* claim UNSUPPORTED plug insertion when
- * good headset is detected but HPHR GND switch makes
- * delta difference */
- if (i == (num_det - 2) && highdelta && !ahighv)
- plug_type[0] = PLUG_TYPE_GND_MIC_SWAP;
- else if (i == (num_det - 1) && inval)
- plug_type[0] = PLUG_TYPE_INVALID;
- }
- pr_debug("%s: DCE #%d, %04x, V %d, scaled V %d, GND %d, VDDIO %d, inval %d\n",
- __func__, i + 1, mb_v[i] & 0xffff, mic_mv[i], scaled,
- gndswitch, vddioswitch, inval);
- /* don't need to run further DCEs */
- if (ahighv && inval)
- break;
- mic_mv[i] = scaled;
- }
-
- for (i = 0; (plug_type[0] != PLUG_TYPE_GND_MIC_SWAP && !inval) &&
- i < num_det; i++) {
- /*
- * If we are here, means none of the all
- * measurements are fake, continue plug type detection.
- * If all three measurements do not produce same
- * plug type, restart insertion detection
- */
- if (mic_mv[i] < plug_type_ptr->v_no_mic) {
- plug_type[i] = PLUG_TYPE_HEADPHONE;
- pr_debug("%s: Detect attempt %d, detected Headphone\n",
- __func__, i);
- } else if (highhph && (mic_mv[i] > plug_type_ptr->v_hs_max)) {
- plug_type[i] = PLUG_TYPE_HIGH_HPH;
- pr_debug(
- "%s: Detect attempt %d, detected High Headphone\n",
- __func__, i);
- } else {
- plug_type[i] = PLUG_TYPE_HEADSET;
- pr_debug("%s: Detect attempt %d, detected Headset\n",
- __func__, i);
- }
-
- if (i > 0 && (plug_type[i - 1] != plug_type[i])) {
- pr_err("%s: Detect attempt %d and %d are not same",
- __func__, i - 1, i);
- plug_type[0] = PLUG_TYPE_INVALID;
- inval = true;
- break;
- }
- }
-
- pr_debug("%s: Detected plug type %d\n", __func__, plug_type[0]);
- return plug_type[0];
-}
-
-static void taiko_hs_correct_gpio_plug(struct work_struct *work)
-{
- struct taiko_priv *taiko;
- struct snd_soc_codec *codec;
- int retry = 0, pt_gnd_mic_swap_cnt = 0;
- bool correction = false;
- enum taiko_mbhc_plug_type plug_type;
- unsigned long timeout;
-
- taiko = container_of(work, struct taiko_priv, hs_correct_plug_work);
- codec = taiko->codec;
-
- pr_debug("%s: enter\n", __func__);
- taiko->mbhc_cfg.mclk_cb_fn(codec, 1, false);
-
- /* Keep override on during entire plug type correction work.
- *
- * This is okay under the assumption that any GPIO irqs which use
- * MBHC block cancel and sync this work so override is off again
- * prior to GPIO interrupt handler's MBHC block usage.
- * Also while this correction work is running, we can guarantee
- * DAPM doesn't use any MBHC block as this work only runs with
- * headphone detection.
- */
- taiko_turn_onoff_override(codec, true);
-
- timeout = jiffies + msecs_to_jiffies(TAIKO_HS_DETECT_PLUG_TIME_MS);
- while (!time_after(jiffies, timeout)) {
- ++retry;
- rmb();
- if (taiko->hs_detect_work_stop) {
- pr_debug("%s: stop requested\n", __func__);
- break;
- }
-
- msleep(TAIKO_HS_DETECT_PLUG_INERVAL_MS);
- if (taiko_hs_gpio_level_remove(taiko)) {
- pr_debug("%s: GPIO value is low\n", __func__);
- break;
- }
-
- /* can race with removal interrupt */
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- plug_type = taiko_codec_get_plug_type(codec, true);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-
- if (plug_type == PLUG_TYPE_INVALID) {
- pr_debug("Invalid plug in attempt # %d\n", retry);
- if (retry == NUM_ATTEMPTS_TO_REPORT &&
- taiko->current_plug == PLUG_TYPE_NONE) {
- taiko_codec_report_plug(codec, 1,
- SND_JACK_HEADPHONE);
- }
- } else if (plug_type == PLUG_TYPE_HEADPHONE) {
- pr_debug("Good headphone detected, continue polling mic\n");
- if (taiko->current_plug == PLUG_TYPE_NONE)
- taiko_codec_report_plug(codec, 1,
- SND_JACK_HEADPHONE);
- } else {
- if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
- pt_gnd_mic_swap_cnt++;
- if (pt_gnd_mic_swap_cnt <
- TAIKO_MBHC_GND_MIC_SWAP_THRESHOLD)
- continue;
- else if (pt_gnd_mic_swap_cnt >
- TAIKO_MBHC_GND_MIC_SWAP_THRESHOLD) {
- /* This is due to GND/MIC switch didn't
- * work, Report unsupported plug */
- } else if (taiko->mbhc_cfg.swap_gnd_mic) {
- /* if switch is toggled, check again,
- * otherwise report unsupported plug */
- if (taiko->mbhc_cfg.swap_gnd_mic(codec))
- continue;
- }
- } else
- pt_gnd_mic_swap_cnt = 0;
-
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- /* Turn off override */
- taiko_turn_onoff_override(codec, false);
- /* The valid plug also includes PLUG_TYPE_GND_MIC_SWAP
- */
- taiko_find_plug_and_report(codec, plug_type);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- pr_debug("Attempt %d found correct plug %d\n", retry,
- plug_type);
- correction = true;
- break;
- }
- }
-
- /* Turn off override */
- if (!correction)
- taiko_turn_onoff_override(codec, false);
-
- taiko->mbhc_cfg.mclk_cb_fn(codec, 0, false);
- pr_debug("%s: leave\n", __func__);
- /* unlock sleep */
- wcd9xxx_unlock_sleep(taiko->codec->control_data);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_decide_gpio_plug(struct snd_soc_codec *codec)
-{
- enum taiko_mbhc_plug_type plug_type;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- pr_debug("%s: enter\n", __func__);
-
- taiko_turn_onoff_override(codec, true);
- plug_type = taiko_codec_get_plug_type(codec, true);
- taiko_turn_onoff_override(codec, false);
-
- if (taiko_hs_gpio_level_remove(taiko)) {
- pr_debug("%s: GPIO value is low when determining plug\n",
- __func__);
- return;
- }
-
- if (plug_type == PLUG_TYPE_INVALID ||
- plug_type == PLUG_TYPE_GND_MIC_SWAP) {
- taiko_schedule_hs_detect_plug(taiko);
- } else if (plug_type == PLUG_TYPE_HEADPHONE) {
- taiko_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
-
- taiko_schedule_hs_detect_plug(taiko);
- } else {
- pr_debug("%s: Valid plug found, determine plug type %d\n",
- __func__, plug_type);
- taiko_find_plug_and_report(codec, plug_type);
- }
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_detect_plug_type(struct snd_soc_codec *codec)
-{
- enum taiko_mbhc_plug_type plug_type;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- const struct taiko_mbhc_plug_detect_cfg *plug_det =
- TAIKO_MBHC_CAL_PLUG_DET_PTR(taiko->mbhc_cfg.calibration);
-
- /* Turn on the override,
- * taiko_codec_setup_hs_polling requires override on */
- taiko_turn_onoff_override(codec, true);
-
- if (plug_det->t_ins_complete > 20)
- msleep(plug_det->t_ins_complete);
- else
- usleep_range(plug_det->t_ins_complete * 1000,
- plug_det->t_ins_complete * 1000);
-
- if (taiko->mbhc_cfg.gpio) {
- /* Turn off the override */
- taiko_turn_onoff_override(codec, false);
- if (taiko_hs_gpio_level_remove(taiko))
- pr_debug(
- "%s: GPIO value is low when determining plug\n",
- __func__);
- else
- taiko_codec_decide_gpio_plug(codec);
- return;
- }
-
- plug_type = taiko_codec_get_plug_type(codec, false);
- taiko_turn_onoff_override(codec, false);
-
- if (plug_type == PLUG_TYPE_INVALID) {
- pr_debug("%s: Invalid plug type detected\n", __func__);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
- taiko_codec_cleanup_hs_polling(codec);
- taiko_codec_enable_hs_detect(codec, 1,
- MBHC_USE_MB_TRIGGER |
- MBHC_USE_HPHL_TRIGGER, false);
- } else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
- pr_debug("%s: GND-MIC swapped plug type detected\n", __func__);
- taiko_codec_report_plug(codec, 1, SND_JACK_UNSUPPORTED);
- taiko_codec_cleanup_hs_polling(codec);
- taiko_codec_enable_hs_detect(codec, 0, 0, false);
- } else if (plug_type == PLUG_TYPE_HEADPHONE) {
- pr_debug("%s: Headphone Detected\n", __func__);
- taiko_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
- taiko_codec_cleanup_hs_polling(codec);
- taiko_codec_enable_hs_detect(codec, 0, 0, false);
- } else if (plug_type == PLUG_TYPE_HEADSET) {
- pr_debug("%s: Headset detected\n", __func__);
- taiko_codec_report_plug(codec, 1, SND_JACK_HEADSET);
-
- /* avoid false button press detect */
- msleep(50);
- taiko_codec_start_hs_polling(codec);
- }
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static void taiko_hs_insert_irq_gpio(struct taiko_priv *priv, bool is_removal)
-{
- struct snd_soc_codec *codec = priv->codec;
-
- if (!is_removal) {
- pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
-
- rmb();
- if (priv->lpi_enabled)
- msleep(100);
-
- rmb();
- if (!priv->lpi_enabled) {
- pr_debug("%s: lpi is disabled\n", __func__);
- } else if (gpio_get_value_cansleep(priv->mbhc_cfg.gpio) ==
- priv->mbhc_cfg.gpio_level_insert) {
- pr_debug(
- "%s: Valid insertion, detect plug type\n", __func__);
- taiko_codec_decide_gpio_plug(codec);
- } else {
- pr_debug(
- "%s: Invalid insertion stop plug detection\n",
- __func__);
- }
- } else {
- pr_err("%s: GPIO used, invalid MBHC Removal\n", __func__);
- }
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static void taiko_hs_insert_irq_nogpio(struct taiko_priv *priv, bool is_removal,
- bool is_mb_trigger)
-{
- int ret;
- struct snd_soc_codec *codec = priv->codec;
- struct wcd9xxx *core = dev_get_drvdata(priv->codec->dev->parent);
-
- if (is_removal) {
- /* cancel possiblely running hs detect work */
- taiko_cancel_hs_detect_plug(priv);
-
- /*
- * If headphone is removed while playback is in progress,
- * it is possible that micbias will be switched to VDDIO.
- */
- taiko_codec_switch_micbias(codec, 0);
- if (priv->current_plug == PLUG_TYPE_HEADPHONE)
- taiko_codec_report_plug(codec, 0, SND_JACK_HEADPHONE);
- else if (priv->current_plug == PLUG_TYPE_GND_MIC_SWAP)
- taiko_codec_report_plug(codec, 0, SND_JACK_UNSUPPORTED);
- else
- WARN(1, "%s: Unexpected current plug type %d\n",
- __func__, priv->current_plug);
- taiko_codec_shutdown_hs_removal_detect(codec);
- taiko_codec_enable_hs_detect(codec, 1,
- MBHC_USE_MB_TRIGGER |
- MBHC_USE_HPHL_TRIGGER,
- true);
- } else if (is_mb_trigger && !is_removal) {
- pr_debug("%s: Waiting for Headphone left trigger\n",
- __func__);
- wcd9xxx_lock_sleep(core);
- if (schedule_delayed_work(&priv->mbhc_insert_dwork,
- usecs_to_jiffies(1000000)) == 0) {
- pr_err("%s: mbhc_insert_dwork is already scheduled\n",
- __func__);
- wcd9xxx_unlock_sleep(core);
- }
- taiko_codec_enable_hs_detect(codec, 1, MBHC_USE_HPHL_TRIGGER,
- false);
- } else {
- ret = cancel_delayed_work(&priv->mbhc_insert_dwork);
- if (ret != 0) {
- pr_debug(
- "%s: Complete plug insertion, Detecting plug type\n",
- __func__);
- taiko_codec_detect_plug_type(codec);
- wcd9xxx_unlock_sleep(core);
- } else {
- wcd9xxx_enable_irq(codec->control_data,
- TAIKO_IRQ_MBHC_INSERTION);
- pr_err("%s: Error detecting plug insertion\n",
- __func__);
- }
- }
-}
-
-static irqreturn_t taiko_hs_insert_irq(int irq, void *data)
-{
- bool is_mb_trigger, is_removal;
- struct taiko_priv *priv = data;
- struct snd_soc_codec *codec = priv->codec;
-
- pr_debug("%s: enter\n", __func__);
- TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
- wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION);
-
- is_mb_trigger = !!(snd_soc_read(codec, priv->mbhc_bias_regs.mbhc_reg) &
- 0x10);
- is_removal = !!(snd_soc_read(codec, TAIKO_A_CDC_MBHC_INT_CTL) & 0x02);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
-
- /* Turn off both HPH and MIC line schmitt triggers */
- snd_soc_update_bits(codec, priv->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x13, 0x00);
- snd_soc_update_bits(codec, priv->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
-
- if (priv->mbhc_cfg.gpio)
- taiko_hs_insert_irq_gpio(priv, is_removal);
- else
- taiko_hs_insert_irq_nogpio(priv, is_removal, is_mb_trigger);
-
- TAIKO_RELEASE_LOCK(priv->codec_resource_lock);
- return IRQ_HANDLED;
-}
-
-static bool is_valid_mic_voltage(struct snd_soc_codec *codec, s32 mic_mv)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- const struct taiko_mbhc_plug_type_cfg *plug_type =
- TAIKO_MBHC_CAL_PLUG_TYPE_PTR(taiko->mbhc_cfg.calibration);
- const s16 v_hs_max = taiko_get_current_v_hs_max(taiko);
-
- return (!(mic_mv > 10 && mic_mv < 80) && (mic_mv > plug_type->v_no_mic)
- && (mic_mv < v_hs_max)) ? true : false;
-}
-
-/* called under codec_resource_lock acquisition
- * returns true if mic voltage range is back to normal insertion
- * returns false either if timedout or removed */
-static bool taiko_hs_remove_settle(struct snd_soc_codec *codec)
-{
- int i;
- bool timedout, settled = false;
- s32 mic_mv[MBHC_NUM_DCE_PLUG_DETECT];
- short mb_v[MBHC_NUM_DCE_PLUG_DETECT];
- unsigned long retry = 0, timeout;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- const s16 v_hs_max = taiko_get_current_v_hs_max(taiko);
-
- timeout = jiffies + msecs_to_jiffies(TAIKO_HS_DETECT_PLUG_TIME_MS);
- while (!(timedout = time_after(jiffies, timeout))) {
- retry++;
- if (taiko->mbhc_cfg.gpio && taiko_hs_gpio_level_remove(taiko)) {
- pr_debug("%s: GPIO indicates removal\n", __func__);
- break;
- }
-
- if (taiko->mbhc_cfg.gpio) {
- if (retry > 1)
- msleep(250);
- else
- msleep(50);
- }
-
- if (taiko->mbhc_cfg.gpio && taiko_hs_gpio_level_remove(taiko)) {
- pr_debug("%s: GPIO indicates removal\n", __func__);
- break;
- }
-
- for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++) {
- mb_v[i] = taiko_codec_sta_dce(codec, 1, true);
- mic_mv[i] = taiko_codec_sta_dce_v(codec, 1 , mb_v[i]);
- pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
- __func__, retry, mic_mv[i], mb_v[i]);
- }
-
- if (taiko->mbhc_cfg.gpio && taiko_hs_gpio_level_remove(taiko)) {
- pr_debug("%s: GPIO indicates removal\n", __func__);
- break;
- }
-
- if (taiko->current_plug == PLUG_TYPE_NONE) {
- pr_debug("%s : headset/headphone is removed\n",
- __func__);
- break;
- }
-
- for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++)
- if (!is_valid_mic_voltage(codec, mic_mv[i]))
- break;
-
- if (i == MBHC_NUM_DCE_PLUG_DETECT) {
- pr_debug("%s: MIC voltage settled\n", __func__);
- settled = true;
- msleep(200);
- break;
- }
-
- /* only for non-GPIO remove irq */
- if (!taiko->mbhc_cfg.gpio) {
- for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++)
- if (mic_mv[i] < v_hs_max)
- break;
- if (i == MBHC_NUM_DCE_PLUG_DETECT) {
- pr_debug("%s: Headset is removed\n", __func__);
- break;
- }
- }
- }
-
- if (timedout)
- pr_debug("%s: Microphone did not settle in %d seconds\n",
- __func__, TAIKO_HS_DETECT_PLUG_TIME_MS);
- return settled;
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static void taiko_hs_remove_irq_gpio(struct taiko_priv *priv)
-{
- struct snd_soc_codec *codec = priv->codec;
-
- if (taiko_hs_remove_settle(codec))
- taiko_codec_start_hs_polling(codec);
- pr_debug("%s: remove settle done\n", __func__);
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static void taiko_hs_remove_irq_nogpio(struct taiko_priv *priv)
-{
- short bias_value;
- bool removed = true;
- struct snd_soc_codec *codec = priv->codec;
- const struct taiko_mbhc_general_cfg *generic =
- TAIKO_MBHC_CAL_GENERAL_PTR(priv->mbhc_cfg.calibration);
- int min_us = TAIKO_FAKE_REMOVAL_MIN_PERIOD_MS * 1000;
-
- if (priv->current_plug != PLUG_TYPE_HEADSET) {
- pr_debug("%s(): Headset is not inserted, ignore removal\n",
- __func__);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL,
- 0x08, 0x08);
- return;
- }
-
- usleep_range(generic->t_shutdown_plug_rem,
- generic->t_shutdown_plug_rem);
-
- do {
- bias_value = taiko_codec_sta_dce(codec, 1, true);
- pr_debug("%s: DCE %d,%d, %d us left\n", __func__, bias_value,
- taiko_codec_sta_dce_v(codec, 1, bias_value), min_us);
- if (bias_value < taiko_get_current_v_ins(priv, false)) {
- pr_debug("%s: checking false removal\n", __func__);
- msleep(500);
- removed = !taiko_hs_remove_settle(codec);
- pr_debug("%s: headset %sactually removed\n", __func__,
- removed ? "" : "not ");
- break;
- }
- min_us -= priv->mbhc_data.t_dce;
- } while (min_us > 0);
-
- if (removed) {
- /* cancel possiblely running hs detect work */
- taiko_cancel_hs_detect_plug(priv);
- /*
- * If this removal is not false, first check the micbias
- * switch status and switch it to LDOH if it is already
- * switched to VDDIO.
- */
- taiko_codec_switch_micbias(codec, 0);
-
- taiko_codec_report_plug(codec, 0, SND_JACK_HEADSET);
- taiko_codec_cleanup_hs_polling(codec);
- taiko_codec_enable_hs_detect(codec, 1,
- MBHC_USE_MB_TRIGGER |
- MBHC_USE_HPHL_TRIGGER,
- true);
- } else {
- taiko_codec_start_hs_polling(codec);
- }
-}
-
-static irqreturn_t taiko_hs_remove_irq(int irq, void *data)
-{
- struct taiko_priv *priv = data;
- bool vddio;
- pr_debug("%s: enter, removal interrupt\n", __func__);
-
- TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
- vddio = (priv->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
- priv->mbhc_micbias_switched);
- if (vddio)
- __taiko_codec_switch_micbias(priv->codec, 0, false, true);
-
- if (priv->mbhc_cfg.gpio)
- taiko_hs_remove_irq_gpio(priv);
- else
- taiko_hs_remove_irq_nogpio(priv);
-
- /* if driver turned off vddio switch and headset is not removed,
- * turn on the vddio switch back, if headset is removed then vddio
- * switch is off by time now and shouldn't be turn on again from here */
- if (vddio && priv->current_plug == PLUG_TYPE_HEADSET)
- __taiko_codec_switch_micbias(priv->codec, 1, true, true);
- TAIKO_RELEASE_LOCK(priv->codec_resource_lock);
-
- return IRQ_HANDLED;
-}
-
-static void taiko_mbhc_insert_work(struct work_struct *work)
-{
- struct delayed_work *dwork;
- struct taiko_priv *taiko;
- struct snd_soc_codec *codec;
- struct wcd9xxx *taiko_core;
-
- dwork = to_delayed_work(work);
- taiko = container_of(dwork, struct taiko_priv, mbhc_insert_dwork);
- codec = taiko->codec;
- taiko_core = dev_get_drvdata(codec->dev->parent);
-
- pr_debug("%s:\n", __func__);
-
- /* Turn off both HPH and MIC line schmitt triggers */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x13, 0x00);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
- wcd9xxx_disable_irq_sync(codec->control_data, TAIKO_IRQ_MBHC_INSERTION);
- taiko_codec_detect_plug_type(codec);
- wcd9xxx_unlock_sleep(taiko_core);
-}
-
-static void taiko_hs_gpio_handler(struct snd_soc_codec *codec)
-{
- bool insert;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- bool is_removed = false;
-
- pr_debug("%s: enter\n", __func__);
-
- taiko->in_gpio_handler = true;
- /* Wait here for debounce time */
- usleep_range(TAIKO_GPIO_IRQ_DEBOUNCE_TIME_US,
- TAIKO_GPIO_IRQ_DEBOUNCE_TIME_US);
-
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-
- /* cancel pending button press */
- if (taiko_cancel_btn_work(taiko))
- pr_debug("%s: button press is canceled\n", __func__);
-
- insert = (gpio_get_value_cansleep(taiko->mbhc_cfg.gpio) ==
- taiko->mbhc_cfg.gpio_level_insert);
- if ((taiko->current_plug == PLUG_TYPE_NONE) && insert) {
- taiko->lpi_enabled = false;
- wmb();
-
- /* cancel detect plug */
- taiko_cancel_hs_detect_plug(taiko);
-
- /* Disable Mic Bias pull down and HPH Switch to GND */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01,
- 0x00);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x01, 0x00);
- taiko_codec_detect_plug_type(codec);
- } else if ((taiko->current_plug != PLUG_TYPE_NONE) && !insert) {
- taiko->lpi_enabled = false;
- wmb();
-
- /* cancel detect plug */
- taiko_cancel_hs_detect_plug(taiko);
-
- if (taiko->current_plug == PLUG_TYPE_HEADPHONE) {
- taiko_codec_report_plug(codec, 0, SND_JACK_HEADPHONE);
- is_removed = true;
- } else if (taiko->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
- taiko_codec_report_plug(codec, 0, SND_JACK_UNSUPPORTED);
- is_removed = true;
- } else if (taiko->current_plug == PLUG_TYPE_HEADSET) {
- taiko_codec_pause_hs_polling(codec);
- taiko_codec_cleanup_hs_polling(codec);
- taiko_codec_report_plug(codec, 0, SND_JACK_HEADSET);
- is_removed = true;
- }
-
- if (is_removed) {
- /* Enable Mic Bias pull down and HPH Switch to GND */
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.ctl_reg, 0x01,
- 0x01);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x01,
- 0x01);
- /* Make sure mic trigger is turned off */
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.ctl_reg,
- 0x01, 0x01);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.mbhc_reg,
- 0x90, 0x00);
- /* Reset MBHC State Machine */
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL,
- 0x08, 0x08);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL,
- 0x08, 0x00);
- /* Turn off override */
- taiko_turn_onoff_override(codec, false);
- }
- }
-
- taiko->in_gpio_handler = false;
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- pr_debug("%s: leave\n", __func__);
-}
-
-static irqreturn_t taiko_mechanical_plug_detect_irq(int irq, void *data)
-{
- int r = IRQ_HANDLED;
- struct snd_soc_codec *codec = data;
-
- if (unlikely(wcd9xxx_lock_sleep(codec->control_data) == false)) {
- pr_warn("%s: failed to hold suspend\n", __func__);
- r = IRQ_NONE;
- } else {
- taiko_hs_gpio_handler(codec);
- wcd9xxx_unlock_sleep(codec->control_data);
- }
-
- return r;
-}
-
-static int taiko_mbhc_init_and_calibrate(struct taiko_priv *taiko)
-{
- int ret = 0;
- struct snd_soc_codec *codec = taiko->codec;
-
- taiko->mbhc_cfg.mclk_cb_fn(codec, 1, false);
- taiko_mbhc_init(codec);
- taiko_mbhc_cal(codec);
- taiko_mbhc_calc_thres(codec);
- taiko->mbhc_cfg.mclk_cb_fn(codec, 0, false);
- taiko_codec_calibrate_hs_polling(codec);
- if (!taiko->mbhc_cfg.gpio) {
- ret = taiko_codec_enable_hs_detect(codec, 1,
- MBHC_USE_MB_TRIGGER |
- MBHC_USE_HPHL_TRIGGER,
- false);
-
- if (IS_ERR_VALUE(ret))
- pr_err("%s: Failed to setup MBHC detection\n",
- __func__);
- } else {
- /* Enable Mic Bias pull down and HPH Switch to GND */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg,
- 0x01, 0x01);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x01, 0x01);
- INIT_WORK(&taiko->hs_correct_plug_work,
- taiko_hs_correct_gpio_plug);
- }
-
- if (!IS_ERR_VALUE(ret)) {
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10, 0x10);
- wcd9xxx_enable_irq(codec->control_data,
- TAIKO_IRQ_HPH_PA_OCPL_FAULT);
- wcd9xxx_enable_irq(codec->control_data,
- TAIKO_IRQ_HPH_PA_OCPR_FAULT);
-
- if (taiko->mbhc_cfg.gpio) {
- ret = request_threaded_irq(taiko->mbhc_cfg.gpio_irq,
- NULL,
- taiko_mechanical_plug_detect_irq,
- (IRQF_TRIGGER_RISING |
- IRQF_TRIGGER_FALLING),
- "taiko-gpio", codec);
- if (!IS_ERR_VALUE(ret)) {
- ret = enable_irq_wake(taiko->mbhc_cfg.gpio_irq);
- /* Bootup time detection */
- taiko_hs_gpio_handler(codec);
- }
- }
- }
-
- return ret;
-}
-
-static void mbhc_fw_read(struct work_struct *work)
-{
- struct delayed_work *dwork;
- struct taiko_priv *taiko;
- struct snd_soc_codec *codec;
- const struct firmware *fw;
- int ret = -1, retry = 0;
-
- dwork = to_delayed_work(work);
- taiko = container_of(dwork, struct taiko_priv, mbhc_firmware_dwork);
- codec = taiko->codec;
-
- while (retry < MBHC_FW_READ_ATTEMPTS) {
- retry++;
- pr_info("%s:Attempt %d to request MBHC firmware\n",
- __func__, retry);
- ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
- codec->dev);
-
- if (ret != 0) {
- usleep_range(MBHC_FW_READ_TIMEOUT,
- MBHC_FW_READ_TIMEOUT);
- } else {
- pr_info("%s: MBHC Firmware read succesful\n", __func__);
- break;
- }
- }
-
- if (ret != 0) {
- pr_err("%s: Cannot load MBHC firmware use default cal\n",
- __func__);
- } else if (taiko_mbhc_fw_validate(fw) == false) {
- pr_err("%s: Invalid MBHC cal data size use default cal\n",
- __func__);
- release_firmware(fw);
- } else {
- taiko->mbhc_cfg.calibration = (void *)fw->data;
- taiko->mbhc_fw = fw;
- }
-
- (void) taiko_mbhc_init_and_calibrate(taiko);
-}
-
-int taiko_hs_detect(struct snd_soc_codec *codec,
- const struct taiko_mbhc_config *cfg)
-{
- struct taiko_priv *taiko;
- int rc = 0;
-
- if (!codec) {
- pr_err("%s: no codec\n", __func__);
- return -EINVAL;
- }
-
- if (!cfg->calibration) {
- pr_warn("%s: mbhc is not configured\n", __func__);
- return 0;
- }
-
- if (cfg->mclk_rate != TAIKO_MCLK_RATE_12288KHZ) {
- if (cfg->mclk_rate == TAIKO_MCLK_RATE_9600KHZ)
- pr_err("Error: clock rate %dHz is not yet supported\n",
- cfg->mclk_rate);
- else
- pr_err("Error: unsupported clock rate %d\n",
- cfg->mclk_rate);
- return -EINVAL;
- }
-
- taiko = snd_soc_codec_get_drvdata(codec);
- taiko->mbhc_cfg = *cfg;
- taiko->in_gpio_handler = false;
- taiko->current_plug = PLUG_TYPE_NONE;
- taiko->lpi_enabled = false;
- taiko_get_mbhc_micbias_regs(codec, &taiko->mbhc_bias_regs);
-
- /* Put CFILT in fast mode by default */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl,
- 0x40, TAIKO_CFILT_FAST_MODE);
- INIT_DELAYED_WORK(&taiko->mbhc_firmware_dwork, mbhc_fw_read);
- INIT_DELAYED_WORK(&taiko->mbhc_btn_dwork, btn_lpress_fn);
- INIT_WORK(&taiko->hphlocp_work, hphlocp_off_report);
- INIT_WORK(&taiko->hphrocp_work, hphrocp_off_report);
- INIT_DELAYED_WORK(&taiko->mbhc_insert_dwork, taiko_mbhc_insert_work);
-
- if (!taiko->mbhc_cfg.read_fw_bin)
- rc = taiko_mbhc_init_and_calibrate(taiko);
- else
- schedule_delayed_work(&taiko->mbhc_firmware_dwork,
- usecs_to_jiffies(MBHC_FW_READ_TIMEOUT));
-
- return rc;
-}
-EXPORT_SYMBOL_GPL(taiko_hs_detect);
-
static unsigned long slimbus_value;
static irqreturn_t taiko_slimbus_irq(int irq, void *data)
@@ -6871,8 +4180,8 @@
}
wcd9xxx_interface_reg_write(codec->control_data,
TAIKO_SLIM_PGD_PORT_INT_CLR0 + i, 0xFF);
- }
+ }
return IRQ_HANDLED;
}
@@ -6903,7 +4212,6 @@
/** end of Ear PA load 32 */
};
-
static const struct taiko_reg_mask_val taiko_1_0_class_h_hph[] = {
/* CLASS-H HPH IDLE_THRESHOLD Table */
@@ -6958,7 +4266,7 @@
static int taiko_handle_pdata(struct taiko_priv *taiko)
{
struct snd_soc_codec *codec = taiko->codec;
- struct wcd9xxx_pdata *pdata = taiko->pdata;
+ struct wcd9xxx_pdata *pdata = taiko->resmgr.pdata;
int k1, k2, k3, rc = 0;
u8 leg_mode, txfe_bypass, txfe_buff, flag;
u8 i = 0, j = 0;
@@ -6976,46 +4284,38 @@
flag = pdata->amic_settings.use_pdata;
/* Make sure settings are correct */
- if ((pdata->micbias.ldoh_v > TAIKO_LDOH_2P85_V) ||
- (pdata->micbias.bias1_cfilt_sel > TAIKO_CFILT3_SEL) ||
- (pdata->micbias.bias2_cfilt_sel > TAIKO_CFILT3_SEL) ||
- (pdata->micbias.bias3_cfilt_sel > TAIKO_CFILT3_SEL) ||
- (pdata->micbias.bias4_cfilt_sel > TAIKO_CFILT3_SEL)) {
+ if ((pdata->micbias.ldoh_v > WCD9XXX_LDOH_3P0_V) ||
+ (pdata->micbias.bias1_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+ (pdata->micbias.bias2_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+ (pdata->micbias.bias3_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+ (pdata->micbias.bias4_cfilt_sel > WCD9XXX_CFILT3_SEL)) {
rc = -EINVAL;
goto done;
}
-
/* figure out k value */
- k1 = taiko_find_k_value(pdata->micbias.ldoh_v,
- pdata->micbias.cfilt1_mv);
- k2 = taiko_find_k_value(pdata->micbias.ldoh_v,
- pdata->micbias.cfilt2_mv);
- k3 = taiko_find_k_value(pdata->micbias.ldoh_v,
- pdata->micbias.cfilt3_mv);
+ k1 = wcd9xxx_resmgr_get_k_val(&taiko->resmgr, pdata->micbias.cfilt1_mv);
+ k2 = wcd9xxx_resmgr_get_k_val(&taiko->resmgr, pdata->micbias.cfilt2_mv);
+ k3 = wcd9xxx_resmgr_get_k_val(&taiko->resmgr, pdata->micbias.cfilt3_mv);
if (IS_ERR_VALUE(k1) || IS_ERR_VALUE(k2) || IS_ERR_VALUE(k3)) {
rc = -EINVAL;
goto done;
}
-
/* Set voltage level and always use LDO */
snd_soc_update_bits(codec, TAIKO_A_LDO_H_MODE_1, 0x0C,
- (pdata->micbias.ldoh_v << 2));
+ (pdata->micbias.ldoh_v << 2));
- snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_1_VAL, 0xFC,
- (k1 << 2));
- snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_2_VAL, 0xFC,
- (k2 << 2));
- snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_3_VAL, 0xFC,
- (k3 << 2));
+ snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_1_VAL, 0xFC, (k1 << 2));
+ snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_2_VAL, 0xFC, (k2 << 2));
+ snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_3_VAL, 0xFC, (k3 << 2));
snd_soc_update_bits(codec, TAIKO_A_MICB_1_CTL, 0x60,
- (pdata->micbias.bias1_cfilt_sel << 5));
+ (pdata->micbias.bias1_cfilt_sel << 5));
snd_soc_update_bits(codec, TAIKO_A_MICB_2_CTL, 0x60,
- (pdata->micbias.bias2_cfilt_sel << 5));
+ (pdata->micbias.bias2_cfilt_sel << 5));
snd_soc_update_bits(codec, TAIKO_A_MICB_3_CTL, 0x60,
- (pdata->micbias.bias3_cfilt_sel << 5));
- snd_soc_update_bits(codec, taiko->reg_addr.micb_4_ctl, 0x60,
+ (pdata->micbias.bias3_cfilt_sel << 5));
+ snd_soc_update_bits(codec, taiko->resmgr.reg_addr->micb_4_ctl, 0x60,
(pdata->micbias.bias4_cfilt_sel << 5));
for (i = 0; i < 6; j++, i += 2) {
@@ -7258,227 +4558,52 @@
taiko_codec_reg_init_val[i].val);
}
-static void taiko_update_reg_address(struct taiko_priv *priv)
-{
- struct taiko_reg_address *reg_addr = &priv->reg_addr;
- reg_addr->micb_4_mbhc = TAIKO_A_MICB_4_MBHC;
- reg_addr->micb_4_int_rbias = TAIKO_A_MICB_4_INT_RBIAS;
- reg_addr->micb_4_ctl = TAIKO_A_MICB_4_CTL;
-
-}
-
-#ifdef CONFIG_DEBUG_FS
-static int codec_debug_open(struct inode *inode, struct file *file)
-{
- file->private_data = inode->i_private;
- return 0;
-}
-
-static ssize_t codec_debug_write(struct file *filp,
- const char __user *ubuf, size_t cnt, loff_t *ppos)
-{
- char lbuf[32];
- char *buf;
- int rc;
- struct taiko_priv *taiko = filp->private_data;
-
- if (cnt > sizeof(lbuf) - 1)
- return -EINVAL;
-
- rc = copy_from_user(lbuf, ubuf, cnt);
- if (rc)
- return -EFAULT;
-
- lbuf[cnt] = '\0';
- buf = (char *)lbuf;
- taiko->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
- false : true;
- return rc;
-}
-
-static ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
- size_t count, loff_t *pos)
-{
- const int size = 768;
- char buffer[size];
- int n = 0;
- struct taiko_priv *taiko = file->private_data;
- struct snd_soc_codec *codec = taiko->codec;
- const struct mbhc_internal_cal_data *p = &taiko->mbhc_data;
- const s16 v_ins_hu_cur = taiko_get_current_v_ins(taiko, true);
- const s16 v_ins_h_cur = taiko_get_current_v_ins(taiko, false);
-
- n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n", p->dce_z,
- taiko_codec_sta_dce_v(codec, 1, p->dce_z));
- n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
- p->dce_mb, taiko_codec_sta_dce_v(codec, 1, p->dce_mb));
- n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
- p->sta_z, taiko_codec_sta_dce_v(codec, 0, p->sta_z));
- n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
- p->sta_mb, taiko_codec_sta_dce_v(codec, 0, p->sta_mb));
- n += scnprintf(buffer + n, size - n, "t_dce = %x\n", p->t_dce);
- n += scnprintf(buffer + n, size - n, "t_sta = %x\n", p->t_sta);
- n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n",
- p->micb_mv);
- n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)%s\n",
- p->v_ins_hu,
- taiko_codec_sta_dce_v(codec, 0, p->v_ins_hu),
- p->v_ins_hu == v_ins_hu_cur ? "*" : "");
- n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)%s\n",
- p->v_ins_h, taiko_codec_sta_dce_v(codec, 1, p->v_ins_h),
- p->v_ins_h == v_ins_h_cur ? "*" : "");
- n += scnprintf(buffer + n, size - n, "adj_v_ins_hu = %x(%dmv)%s\n",
- p->adj_v_ins_hu,
- taiko_codec_sta_dce_v(codec, 0, p->adj_v_ins_hu),
- p->adj_v_ins_hu == v_ins_hu_cur ? "*" : "");
- n += scnprintf(buffer + n, size - n, "adj_v_ins_h = %x(%dmv)%s\n",
- p->adj_v_ins_h,
- taiko_codec_sta_dce_v(codec, 1, p->adj_v_ins_h),
- p->adj_v_ins_h == v_ins_h_cur ? "*" : "");
- n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
- p->v_b1_hu, taiko_codec_sta_dce_v(codec, 0, p->v_b1_hu));
- n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
- p->v_b1_h, taiko_codec_sta_dce_v(codec, 1, p->v_b1_h));
- n += scnprintf(buffer + n, size - n, "v_b1_huc = %x(%dmv)\n",
- p->v_b1_huc,
- taiko_codec_sta_dce_v(codec, 1, p->v_b1_huc));
- n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
- p->v_brh, taiko_codec_sta_dce_v(codec, 1, p->v_brh));
- n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n", p->v_brl,
- taiko_codec_sta_dce_v(codec, 0, p->v_brl));
- n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
- p->v_no_mic,
- taiko_codec_sta_dce_v(codec, 0, p->v_no_mic));
- n += scnprintf(buffer + n, size - n, "npoll = %d\n", p->npoll);
- n += scnprintf(buffer + n, size - n, "nbounce_wait = %d\n",
- p->nbounce_wait);
- n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
- p->v_inval_ins_low);
- n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
- p->v_inval_ins_high);
- if (taiko->mbhc_cfg.gpio)
- n += scnprintf(buffer + n, size - n, "GPIO insert = %d\n",
- taiko_hs_gpio_level_remove(taiko));
- buffer[n] = 0;
-
- return simple_read_from_buffer(buf, count, pos, buffer, n);
-}
-
-static const struct file_operations codec_debug_ops = {
- .open = codec_debug_open,
- .write = codec_debug_write,
-};
-
-static const struct file_operations codec_mbhc_debug_ops = {
- .open = codec_debug_open,
- .read = codec_mbhc_debug_read,
-};
-#endif
-
static int taiko_setup_irqs(struct taiko_priv *taiko)
{
- int ret;
int i;
+ int ret = 0;
struct snd_soc_codec *codec = taiko->codec;
- ret = wcd9xxx_request_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION,
- taiko_hs_insert_irq, "Headset insert detect",
- taiko);
- if (ret) {
- pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_MBHC_INSERTION);
- goto err_insert_irq;
- }
- wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION);
-
- ret = wcd9xxx_request_irq(codec->control_data, TAIKO_IRQ_MBHC_REMOVAL,
- taiko_hs_remove_irq, "Headset remove detect",
- taiko);
- if (ret) {
- pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_MBHC_REMOVAL);
- goto err_remove_irq;
- }
-
- ret = wcd9xxx_request_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL,
- taiko_dce_handler, "DC Estimation detect",
- taiko);
- if (ret) {
- pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_MBHC_POTENTIAL);
- goto err_potential_irq;
- }
-
- ret = wcd9xxx_request_irq(codec->control_data, TAIKO_IRQ_MBHC_RELEASE,
- taiko_release_handler, "Button Release detect",
- taiko);
- if (ret) {
- pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_MBHC_RELEASE);
- goto err_release_irq;
- }
-
- ret = wcd9xxx_request_irq(codec->control_data, TAIKO_IRQ_SLIMBUS,
+ ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
taiko_slimbus_irq, "SLIMBUS Slave", taiko);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_SLIMBUS);
- goto err_slimbus_irq;
+ WCD9XXX_IRQ_SLIMBUS);
+ goto exit;
}
for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++)
wcd9xxx_interface_reg_write(codec->control_data,
- TAIKO_SLIM_PGD_PORT_INT_EN0 + i,
- 0xFF);
-
- ret = wcd9xxx_request_irq(codec->control_data,
- TAIKO_IRQ_HPH_PA_OCPL_FAULT,
- taiko_hphl_ocp_irq,
- "HPH_L OCP detect", taiko);
- if (ret) {
- pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_HPH_PA_OCPL_FAULT);
- goto err_hphl_ocp_irq;
- }
- wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_HPH_PA_OCPL_FAULT);
-
- ret = wcd9xxx_request_irq(codec->control_data,
- TAIKO_IRQ_HPH_PA_OCPR_FAULT,
- taiko_hphr_ocp_irq,
- "HPH_R OCP detect", taiko);
- if (ret) {
- pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_HPH_PA_OCPR_FAULT);
- goto err_hphr_ocp_irq;
- }
- wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_HPH_PA_OCPR_FAULT);
-
-err_hphr_ocp_irq:
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_HPH_PA_OCPL_FAULT,
- taiko);
-err_hphl_ocp_irq:
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_SLIMBUS, taiko);
-err_slimbus_irq:
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_RELEASE, taiko);
-err_release_irq:
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL, taiko);
-err_potential_irq:
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_REMOVAL, taiko);
-err_remove_irq:
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION, taiko);
-err_insert_irq:
-
+ TAIKO_SLIM_PGD_PORT_INT_EN0 + i,
+ 0xFF);
+exit:
return ret;
}
+int taiko_hs_detect(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc_config *mbhc_cfg)
+{
+ struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
+ return wcd9xxx_mbhc_start(&taiko->mbhc, mbhc_cfg);
+}
+EXPORT_SYMBOL_GPL(taiko_hs_detect);
+
+static struct wcd9xxx_reg_address taiko_reg_address = {
+ .micb_4_mbhc = TAIKO_A_MICB_4_MBHC,
+ .micb_4_int_rbias = TAIKO_A_MICB_4_INT_RBIAS,
+ .micb_4_ctl = TAIKO_A_MICB_4_CTL,
+};
+
static int taiko_codec_probe(struct snd_soc_codec *codec)
{
struct wcd9xxx *control;
struct taiko_priv *taiko;
+ struct wcd9xxx_pdata *pdata;
+ struct wcd9xxx *wcd9xxx;
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret = 0;
int i;
- int ch_cnt;
+ void *ptr = NULL;
codec->control_data = dev_get_drvdata(codec->dev->parent);
control = codec->control_data;
@@ -7497,41 +4622,34 @@
tx_hpf_corner_freq_callback);
}
- /* Make sure mbhc micbias register addresses are zeroed out */
- memset(&taiko->mbhc_bias_regs, 0,
- sizeof(struct mbhc_micbias_regs));
- taiko->mbhc_micbias_switched = false;
-
- /* Make sure mbhc intenal calibration data is zeroed out */
- memset(&taiko->mbhc_data, 0,
- sizeof(struct mbhc_internal_cal_data));
- taiko->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
- taiko->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
- taiko->mbhc_data.t_sta = DEFAULT_STA_WAIT;
snd_soc_codec_set_drvdata(codec, taiko);
- taiko->mclk_enabled = false;
- taiko->bandgap_type = TAIKO_BANDGAP_OFF;
- taiko->clock_active = false;
- taiko->config_mode_active = false;
- taiko->mbhc_polling_active = false;
- taiko->mbhc_fake_ins_start = 0;
- taiko->no_mic_headset_override = false;
- taiko->hs_polling_irq_prepared = false;
- mutex_init(&taiko->codec_resource_lock);
+ /* codec resmgr module init */
+ wcd9xxx = codec->control_data;
+ pdata = dev_get_platdata(codec->dev->parent);
+ ret = wcd9xxx_resmgr_init(&taiko->resmgr, codec, wcd9xxx, pdata,
+ &taiko_reg_address);
+ if (ret) {
+ pr_err("%s: wcd9xxx init failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ /* init and start mbhc */
+ ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec);
+ if (ret) {
+ pr_err("%s: mbhc init failed %d\n", __func__, ret);
+ return ret;
+ }
+
taiko->codec = codec;
- taiko->mbhc_state = MBHC_STATE_NONE;
- taiko->mbhc_last_resume = 0;
for (i = 0; i < COMPANDER_MAX; i++) {
taiko->comp_enabled[i] = 0;
taiko->comp_fs[i] = COMPANDER_FS_48KHZ;
}
- taiko->pdata = dev_get_platdata(codec->dev->parent);
taiko->intf_type = wcd9xxx_get_intf_type();
taiko->aux_pga_cnt = 0;
taiko->aux_l_gain = 0x1F;
taiko->aux_r_gain = 0x1F;
- taiko_update_reg_address(taiko);
taiko_update_reg_defaults(codec);
taiko_codec_init_reg(codec);
ret = taiko_handle_pdata(taiko);
@@ -7540,84 +4658,57 @@
goto err_pdata;
}
+ ptr = kmalloc((sizeof(taiko_rx_chs) +
+ sizeof(taiko_tx_chs)), GFP_KERNEL);
+ if (!ptr) {
+ pr_err("%s: no mem for slim chan ctl data\n", __func__);
+ ret = -ENOMEM;
+ goto err_nomem_slimch;
+ }
+
if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
snd_soc_dapm_new_controls(dapm, taiko_dapm_i2s_widgets,
ARRAY_SIZE(taiko_dapm_i2s_widgets));
snd_soc_dapm_add_routes(dapm, audio_i2s_map,
ARRAY_SIZE(audio_i2s_map));
+ for (i = 0; i < ARRAY_SIZE(taiko_i2s_dai); i++)
+ INIT_LIST_HEAD(&taiko->dai[i].wcd9xxx_ch_list);
+ } else if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ for (i = 0; i < NUM_CODEC_DAIS; i++) {
+ INIT_LIST_HEAD(&taiko->dai[i].wcd9xxx_ch_list);
+ init_waitqueue_head(&taiko->dai[i].dai_wait);
+ }
}
+ control->num_rx_port = TAIKO_RX_MAX;
+ control->rx_chs = ptr;
+ memcpy(control->rx_chs, taiko_rx_chs, sizeof(taiko_rx_chs));
+ control->num_tx_port = TAIKO_TX_MAX;
+ control->tx_chs = ptr + sizeof(taiko_rx_chs);
+ memcpy(control->tx_chs, taiko_tx_chs, sizeof(taiko_tx_chs));
+
snd_soc_dapm_sync(dapm);
(void) taiko_setup_irqs(taiko);
- for (i = 0; i < ARRAY_SIZE(taiko_dai); i++) {
- switch (taiko_dai[i].id) {
- case AIF1_PB:
- ch_cnt = taiko_dai[i].playback.channels_max;
- break;
- case AIF1_CAP:
- ch_cnt = taiko_dai[i].capture.channels_max;
- break;
- case AIF2_PB:
- ch_cnt = taiko_dai[i].playback.channels_max;
- break;
- case AIF2_CAP:
- ch_cnt = taiko_dai[i].capture.channels_max;
- break;
- case AIF3_PB:
- ch_cnt = taiko_dai[i].playback.channels_max;
- break;
- case AIF3_CAP:
- ch_cnt = taiko_dai[i].capture.channels_max;
- break;
- default:
- continue;
- }
- taiko->dai[i].ch_num = kzalloc((sizeof(unsigned int)*
- ch_cnt), GFP_KERNEL);
- }
-
-#ifdef CONFIG_DEBUG_FS
- if (ret == 0) {
- taiko->debugfs_poke =
- debugfs_create_file("TRRS", S_IFREG | S_IRUGO, NULL, taiko,
- &codec_debug_ops);
- taiko->debugfs_mbhc =
- debugfs_create_file("taiko_mbhc", S_IFREG | S_IRUGO,
- NULL, taiko, &codec_mbhc_debug_ops);
- }
-#endif
codec->ignore_pmdown_time = 1;
return ret;
err_pdata:
- mutex_destroy(&taiko->codec_resource_lock);
+ kfree(ptr);
+err_nomem_slimch:
kfree(taiko);
return ret;
}
static int taiko_codec_remove(struct snd_soc_codec *codec)
{
- int i;
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_SLIMBUS, taiko);
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_RELEASE, taiko);
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL, taiko);
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_REMOVAL, taiko);
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION, taiko);
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- taiko_codec_disable_clock_block(codec);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- taiko_codec_enable_bandgap(codec, TAIKO_BANDGAP_OFF);
- if (taiko->mbhc_fw)
- release_firmware(taiko->mbhc_fw);
- for (i = 0; i < ARRAY_SIZE(taiko_dai); i++)
- kfree(taiko->dai[i].ch_num);
- mutex_destroy(&taiko->codec_resource_lock);
-#ifdef CONFIG_DEBUG_FS
- debugfs_remove(taiko->debugfs_poke);
- debugfs_remove(taiko->debugfs_mbhc);
-#endif
+
+ /* cleanup MBHC */
+ wcd9xxx_mbhc_deinit(&taiko->mbhc);
+ /* cleanup resmgr */
+ wcd9xxx_resmgr_deinit(&taiko->resmgr);
+
kfree(taiko);
return 0;
}
@@ -7655,7 +4746,8 @@
struct platform_device *pdev = to_platform_device(dev);
struct taiko_priv *taiko = platform_get_drvdata(pdev);
dev_dbg(dev, "%s: system resume\n", __func__);
- taiko->mbhc_last_resume = jiffies;
+ /* Notify */
+ wcd9xxx_resmgr_notifier_call(&taiko->resmgr, WCD9XXX_EVENT_POST_RESUME);
return 0;
}
diff --git a/sound/soc/codecs/wcd9320.h b/sound/soc/codecs/wcd9320.h
index 7ca8ff0..7bc5a57 100644
--- a/sound/soc/codecs/wcd9320.h
+++ b/sound/soc/codecs/wcd9320.h
@@ -15,6 +15,8 @@
#include <sound/soc.h>
#include <sound/jack.h>
#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+#include "wcd9xxx-mbhc.h"
+#include "wcd9xxx-resmgr.h"
#define TAIKO_NUM_REGISTERS 0x400
#define TAIKO_MAX_REGISTER (TAIKO_NUM_REGISTERS-1)
@@ -22,27 +24,13 @@
#define TAIKO_REG_VAL(reg, val) {reg, 0, val}
-#define DEFAULT_DCE_STA_WAIT 55
-#define DEFAULT_DCE_WAIT 60000
-#define DEFAULT_STA_WAIT 5000
-#define VDDIO_MICBIAS_MV 1800
-
-#define STA 0
-#define DCE 1
-
-#define TAIKO_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
- SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
- SND_JACK_BTN_4 | SND_JACK_BTN_5 | \
- SND_JACK_BTN_6 | SND_JACK_BTN_7)
-
extern const u8 taiko_reg_readable[TAIKO_CACHE_SIZE];
extern const u8 taiko_reset_reg_defaults[TAIKO_CACHE_SIZE];
-
-enum taiko_micbias_num {
- TAIKO_MICBIAS1 = 0,
- TAIKO_MICBIAS2,
- TAIKO_MICBIAS3,
- TAIKO_MICBIAS4,
+struct taiko_codec_dai_data {
+ u32 rate;
+ u32 *ch_num;
+ u32 ch_act;
+ u32 ch_tot;
};
enum taiko_pid_current {
@@ -58,130 +46,50 @@
u8 val;
};
-enum taiko_mbhc_clk_freq {
- TAIKO_MCLK_12P2MHZ = 0,
- TAIKO_MCLK_9P6MHZ,
- TAIKO_NUM_CLK_FREQS,
-};
-
enum taiko_mbhc_analog_pwr_cfg {
TAIKO_ANALOG_PWR_COLLAPSED = 0,
TAIKO_ANALOG_PWR_ON,
TAIKO_NUM_ANALOG_PWR_CONFIGS,
};
-enum taiko_mbhc_btn_det_mem {
- TAIKO_BTN_DET_V_BTN_LOW,
- TAIKO_BTN_DET_V_BTN_HIGH,
- TAIKO_BTN_DET_N_READY,
- TAIKO_BTN_DET_N_CIC,
- TAIKO_BTN_DET_GAIN
+/* Number of input and output Slimbus port */
+enum {
+ TAIKO_RX1 = 0,
+ TAIKO_RX2,
+ TAIKO_RX3,
+ TAIKO_RX4,
+ TAIKO_RX5,
+ TAIKO_RX6,
+ TAIKO_RX7,
+ TAIKO_RX8,
+ TAIKO_RX9,
+ TAIKO_RX10,
+ TAIKO_RX11,
+ TAIKO_RX12,
+ TAIKO_RX13,
+ TAIKO_RX_MAX,
};
-struct taiko_mbhc_general_cfg {
- u8 t_ldoh;
- u8 t_bg_fast_settle;
- u8 t_shutdown_plug_rem;
- u8 mbhc_nsa;
- u8 mbhc_navg;
- u8 v_micbias_l;
- u8 v_micbias;
- u8 mbhc_reserved;
- u16 settle_wait;
- u16 t_micbias_rampup;
- u16 t_micbias_rampdown;
- u16 t_supply_bringup;
-} __packed;
-
-struct taiko_mbhc_plug_detect_cfg {
- u32 mic_current;
- u32 hph_current;
- u16 t_mic_pid;
- u16 t_ins_complete;
- u16 t_ins_retry;
- u16 v_removal_delta;
- u8 micbias_slow_ramp;
- u8 reserved0;
- u8 reserved1;
- u8 reserved2;
-} __packed;
-
-struct taiko_mbhc_plug_type_cfg {
- u8 av_detect;
- u8 mono_detect;
- u8 num_ins_tries;
- u8 reserved0;
- s16 v_no_mic;
- s16 v_av_min;
- s16 v_av_max;
- s16 v_hs_min;
- s16 v_hs_max;
- u16 reserved1;
-} __packed;
-
-
-struct taiko_mbhc_btn_detect_cfg {
- s8 c[8];
- u8 nc;
- u8 n_meas;
- u8 mbhc_nsc;
- u8 n_btn_meas;
- u8 n_btn_con;
- u8 num_btn;
- u8 reserved0;
- u8 reserved1;
- u16 t_poll;
- u16 t_bounce_wait;
- u16 t_rel_timeout;
- s16 v_btn_press_delta_sta;
- s16 v_btn_press_delta_cic;
- u16 t_btn0_timeout;
- s16 _v_btn_low[0]; /* v_btn_low[num_btn] */
- s16 _v_btn_high[0]; /* v_btn_high[num_btn] */
- u8 _n_ready[TAIKO_NUM_CLK_FREQS];
- u8 _n_cic[TAIKO_NUM_CLK_FREQS];
- u8 _gain[TAIKO_NUM_CLK_FREQS];
-} __packed;
-
-struct taiko_mbhc_imped_detect_cfg {
- u8 _hs_imped_detect;
- u8 _n_rload;
- u8 _hph_keep_on;
- u8 _repeat_rload_calc;
- u16 _t_dac_ramp_time;
- u16 _rhph_high;
- u16 _rhph_low;
- u16 _rload[0]; /* rload[n_rload] */
- u16 _alpha[0]; /* alpha[n_rload] */
- u16 _beta[3];
-} __packed;
-
-struct taiko_mbhc_config {
- struct snd_soc_jack *headset_jack;
- struct snd_soc_jack *button_jack;
- bool read_fw_bin;
- /* void* calibration contains:
- * struct taiko_mbhc_general_cfg generic;
- * struct taiko_mbhc_plug_detect_cfg plug_det;
- * struct taiko_mbhc_plug_type_cfg plug_type;
- * struct taiko_mbhc_btn_detect_cfg btn_det;
- * struct taiko_mbhc_imped_detect_cfg imped_det;
- * Note: various size depends on btn_det->num_btn
- */
- void *calibration;
- enum taiko_micbias_num micbias;
- int (*mclk_cb_fn) (struct snd_soc_codec*, int, bool);
- unsigned int mclk_rate;
- unsigned int gpio;
- unsigned int gpio_irq;
- int gpio_level_insert;
- /* swap_gnd_mic returns true if extern GND/MIC swap switch toggled */
- bool (*swap_gnd_mic) (struct snd_soc_codec *);
+enum {
+ TAIKO_TX1 = 0,
+ TAIKO_TX2,
+ TAIKO_TX3,
+ TAIKO_TX4,
+ TAIKO_TX5,
+ TAIKO_TX6,
+ TAIKO_TX7,
+ TAIKO_TX8,
+ TAIKO_TX9,
+ TAIKO_TX10,
+ TAIKO_TX11,
+ TAIKO_TX12,
+ TAIKO_TX13,
+ TAIKO_TX14,
+ TAIKO_TX15,
+ TAIKO_TX16,
+ TAIKO_TX_MAX,
};
-extern int taiko_hs_detect(struct snd_soc_codec *codec,
- const struct taiko_mbhc_config *cfg);
-
struct anc_header {
u32 reserved[3];
u32 num_anc_slots;
@@ -189,64 +97,7 @@
extern int taiko_mclk_enable(struct snd_soc_codec *codec, int mclk_enable,
bool dapm);
-
-extern void *taiko_mbhc_cal_btn_det_mp(const struct taiko_mbhc_btn_detect_cfg
- *btn_det,
- const enum taiko_mbhc_btn_det_mem mem);
-
-#define TAIKO_MBHC_CAL_SIZE(buttons, rload) ( \
- sizeof(enum taiko_micbias_num) + \
- sizeof(struct taiko_mbhc_general_cfg) + \
- sizeof(struct taiko_mbhc_plug_detect_cfg) + \
- ((sizeof(s16) + sizeof(s16)) * buttons) + \
- sizeof(struct taiko_mbhc_plug_type_cfg) + \
- sizeof(struct taiko_mbhc_btn_detect_cfg) + \
- sizeof(struct taiko_mbhc_imped_detect_cfg) + \
- ((sizeof(u16) + sizeof(u16)) * rload) \
- )
-
-#define TAIKO_MBHC_CAL_GENERAL_PTR(cali) ( \
- (struct taiko_mbhc_general_cfg *) cali)
-#define TAIKO_MBHC_CAL_PLUG_DET_PTR(cali) ( \
- (struct taiko_mbhc_plug_detect_cfg *) \
- &(TAIKO_MBHC_CAL_GENERAL_PTR(cali)[1]))
-#define TAIKO_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \
- (struct taiko_mbhc_plug_type_cfg *) \
- &(TAIKO_MBHC_CAL_PLUG_DET_PTR(cali)[1]))
-#define TAIKO_MBHC_CAL_BTN_DET_PTR(cali) ( \
- (struct taiko_mbhc_btn_detect_cfg *) \
- &(TAIKO_MBHC_CAL_PLUG_TYPE_PTR(cali)[1]))
-#define TAIKO_MBHC_CAL_IMPED_DET_PTR(cali) ( \
- (struct taiko_mbhc_imped_detect_cfg *) \
- (((void *)&TAIKO_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \
- (TAIKO_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \
- (sizeof(TAIKO_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \
- sizeof(TAIKO_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \
- )
-
-/* minimum size of calibration data assuming there is only one button and
- * one rload.
- */
-#define TAIKO_MBHC_CAL_MIN_SIZE ( \
- sizeof(struct taiko_mbhc_general_cfg) + \
- sizeof(struct taiko_mbhc_plug_detect_cfg) + \
- sizeof(struct taiko_mbhc_plug_type_cfg) + \
- sizeof(struct taiko_mbhc_btn_detect_cfg) + \
- sizeof(struct taiko_mbhc_imped_detect_cfg) + \
- (sizeof(u16) * 2))
-
-#define TAIKO_MBHC_CAL_BTN_SZ(cfg_ptr) ( \
- sizeof(struct taiko_mbhc_btn_detect_cfg) + \
- (cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \
- sizeof(cfg_ptr->_v_btn_high[0]))))
-
-#define TAIKO_MBHC_CAL_IMPED_MIN_SZ ( \
- sizeof(struct taiko_mbhc_imped_detect_cfg) + \
- sizeof(u16) * 2)
-
-#define TAIKO_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \
- sizeof(struct taiko_mbhc_imped_detect_cfg) + \
- (cfg_ptr->_n_rload * (sizeof(cfg_ptr->_rload[0]) + \
- sizeof(cfg_ptr->_alpha[0]))))
+extern int taiko_hs_detect(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc_config *mbhc_cfg);
#endif
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
new file mode 100644
index 0000000..16d415b
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -0,0 +1,3113 @@
+/* Copyright (c) 2012, 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/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include "wcd9320.h"
+#include "wcd9xxx-mbhc.h"
+#include "wcd9xxx-resmgr.h"
+
+#define WCD9XXX_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
+ SND_JACK_OC_HPHR | SND_JACK_UNSUPPORTED)
+#define WCD9XXX_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
+ SND_JACK_BTN_4 | SND_JACK_BTN_5 | \
+ SND_JACK_BTN_6 | SND_JACK_BTN_7)
+
+#define NUM_DCE_PLUG_DETECT 3
+#define NUM_ATTEMPTS_INSERT_DETECT 25
+#define NUM_ATTEMPTS_TO_REPORT 5
+
+#define FAKE_INS_LOW 10
+#define FAKE_INS_HIGH 80
+#define FAKE_INS_HIGH_NO_SWCH 150
+#define FAKE_REMOVAL_MIN_PERIOD_MS 50
+#define FAKE_INS_DELTA_SCALED_MV 300
+
+#define BUTTON_MIN 0x8000
+#define STATUS_REL_DETECTION 0x0C
+
+#define HS_DETECT_PLUG_TIME_MS (5 * 1000)
+#define HS_DETECT_PLUG_INERVAL_MS 100
+#define SWCH_REL_DEBOUNCE_TIME_MS 50
+#define SWCH_IRQ_DEBOUNCE_TIME_US 5000
+
+#define GND_MIC_SWAP_THRESHOLD 2
+#define OCP_ATTEMPT 1
+
+#define FW_READ_ATTEMPTS 15
+#define FW_READ_TIMEOUT 2000000
+
+#define BUTTON_POLLING_SUPPORTED false
+
+#define MCLK_RATE_12288KHZ 12288000
+#define MCLK_RATE_9600KHZ 9600000
+#define WCD9XXX_RCO_CLK_RATE MCLK_RATE_12288KHZ
+
+#define DEFAULT_DCE_STA_WAIT 55
+#define DEFAULT_DCE_WAIT 60000
+#define DEFAULT_STA_WAIT 5000
+
+#define VDDIO_MICBIAS_MV 1800
+
+enum meas_type {
+ STA = 0,
+ DCE,
+};
+
+enum {
+ MBHC_USE_HPHL_TRIGGER = 1,
+ MBHC_USE_MB_TRIGGER = 2
+};
+
+/*
+ * Flags to track of PA and DAC state.
+ * PA and DAC should be tracked separately as AUXPGA loopback requires
+ * only PA to be turned on without DAC being on.
+ */
+enum pa_dac_ack_flags {
+ WCD9XXX_HPHL_PA_OFF_ACK = 0,
+ WCD9XXX_HPHR_PA_OFF_ACK,
+ WCD9XXX_HPHL_DAC_OFF_ACK,
+ WCD9XXX_HPHR_DAC_OFF_ACK
+};
+
+static bool wcd9xxx_mbhc_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ return mbhc->polling_active;
+}
+
+static void wcd9xxx_turn_onoff_override(struct snd_soc_codec *codec, bool on)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, on << 2);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_pause_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+ if (!mbhc->polling_active) {
+ pr_debug("polling not active, nothing to pause\n");
+ return;
+ }
+
+ /* Soft reset MBHC block */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_start_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ int mbhc_state = mbhc->mbhc_state;
+
+ pr_debug("%s: enter\n", __func__);
+ if (!mbhc->polling_active) {
+ pr_debug("Polling is not active, do not start polling\n");
+ return;
+ }
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x84);
+
+ if (!mbhc->no_mic_headset_override &&
+ mbhc_state == MBHC_STATE_POTENTIAL) {
+ pr_debug("%s recovering MBHC state macine\n", __func__);
+ mbhc->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
+ /* set to max button press threshold */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL, 0x7F);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL, 0x7F);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, 0xFF);
+ /* set to max */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL, 0x7F);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, 0xFF);
+ }
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void __wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc,
+ int vddio_switch, bool restartpolling,
+ bool checkpolling)
+{
+ int cfilt_k_val;
+ bool override;
+ struct snd_soc_codec *codec;
+
+ codec = mbhc->codec;
+
+ if (vddio_switch && !mbhc->mbhc_micbias_switched &&
+ (!checkpolling || mbhc->polling_active)) {
+ if (restartpolling)
+ wcd9xxx_pause_hs_polling(mbhc);
+ override = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) &
+ 0x04;
+ if (!override)
+ wcd9xxx_turn_onoff_override(codec, true);
+ /* Adjust threshold if Mic Bias voltage changes */
+ if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+ cfilt_k_val = wcd9xxx_resmgr_get_k_val(mbhc->resmgr,
+ VDDIO_MICBIAS_MV);
+ usleep_range(10000, 10000);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.cfilt_val,
+ 0xFC, (cfilt_k_val << 2));
+ usleep_range(10000, 10000);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
+ mbhc->mbhc_data.adj_v_ins_hu & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+ (mbhc->mbhc_data.adj_v_ins_hu >> 8) &
+ 0xFF);
+ pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
+ __func__);
+ }
+
+ /* Enable MIC BIAS Switch to VDDIO */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x80, 0x80);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x10, 0x00);
+ if (!override)
+ wcd9xxx_turn_onoff_override(codec, false);
+ if (restartpolling)
+ wcd9xxx_start_hs_polling(mbhc);
+
+ mbhc->mbhc_micbias_switched = true;
+ pr_debug("%s: VDDIO switch enabled\n", __func__);
+ } else if (!vddio_switch && mbhc->mbhc_micbias_switched) {
+ if ((!checkpolling || mbhc->polling_active) &&
+ restartpolling)
+ wcd9xxx_pause_hs_polling(mbhc);
+ /* Reprogram thresholds */
+ if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+ cfilt_k_val =
+ wcd9xxx_resmgr_get_k_val(mbhc->resmgr,
+ mbhc->mbhc_data.micb_mv);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.cfilt_val,
+ 0xFC, (cfilt_k_val << 2));
+ usleep_range(10000, 10000);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
+ mbhc->mbhc_data.v_ins_hu & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+ (mbhc->mbhc_data.v_ins_hu >> 8) & 0xFF);
+ pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
+ __func__);
+ }
+
+ /* Disable MIC BIAS Switch to VDDIO */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80,
+ 0x00);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x10,
+ 0x00);
+
+ if ((!checkpolling || mbhc->polling_active) && restartpolling)
+ wcd9xxx_start_hs_polling(mbhc);
+
+ mbhc->mbhc_micbias_switched = false;
+ pr_debug("%s: VDDIO switch disabled\n", __func__);
+ }
+}
+
+static void wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc, int vddio_switch)
+{
+ return __wcd9xxx_switch_micbias(mbhc, vddio_switch, true, true);
+}
+
+static s16 wcd9xxx_get_current_v_ins(struct wcd9xxx_mbhc *mbhc, bool hu)
+{
+ s16 v_ins;
+ if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
+ mbhc->mbhc_micbias_switched)
+ v_ins = hu ? (s16)mbhc->mbhc_data.adj_v_ins_hu :
+ (s16)mbhc->mbhc_data.adj_v_ins_h;
+ else
+ v_ins = hu ? (s16)mbhc->mbhc_data.v_ins_hu :
+ (s16)mbhc->mbhc_data.v_ins_h;
+ return v_ins;
+}
+
+void *wcd9xxx_mbhc_cal_btn_det_mp(
+ const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
+ const enum wcd9xxx_mbhc_btn_det_mem mem)
+{
+ void *ret = &btn_det->_v_btn_low;
+
+ switch (mem) {
+ case MBHC_BTN_DET_GAIN:
+ ret += sizeof(btn_det->_n_cic);
+ case MBHC_BTN_DET_N_CIC:
+ ret += sizeof(btn_det->_n_ready);
+ case MBHC_BTN_DET_N_READY:
+ ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
+ case MBHC_BTN_DET_V_BTN_HIGH:
+ ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
+ case MBHC_BTN_DET_V_BTN_LOW:
+ /* do nothing */
+ break;
+ default:
+ ret = NULL;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_cal_btn_det_mp);
+
+static void wcd9xxx_calibrate_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ const s16 v_ins_hu = wcd9xxx_get_current_v_ins(mbhc, true);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, v_ins_hu & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+ (v_ins_hu >> 8) & 0xFF);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
+ mbhc->mbhc_data.v_b1_hu & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
+ (mbhc->mbhc_data.v_b1_hu >> 8) & 0xFF);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
+ mbhc->mbhc_data.v_b1_h & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
+ (mbhc->mbhc_data.v_b1_h >> 8) & 0xFF);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
+ mbhc->mbhc_data.v_brh & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
+ (mbhc->mbhc_data.v_brh >> 8) & 0xFF);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL,
+ mbhc->mbhc_data.v_brl & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL,
+ (mbhc->mbhc_data.v_brl >> 8) & 0xFF);
+}
+
+static void wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
+ bool fast)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ u8 reg_mode_val, cur_mode_val;
+
+ if (fast)
+ reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
+ else
+ reg_mode_val = WCD9XXX_CFILT_SLOW_MODE;
+
+ cur_mode_val =
+ snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40;
+
+ if (cur_mode_val != reg_mode_val) {
+ if (mbhc->polling_active)
+ wcd9xxx_pause_hs_polling(mbhc);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40,
+ reg_mode_val);
+ if (mbhc->polling_active)
+ wcd9xxx_start_hs_polling(mbhc);
+ pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
+ cur_mode_val, reg_mode_val);
+ } else {
+ pr_debug("%s: CFILT Value is already %x\n",
+ __func__, cur_mode_val);
+ }
+}
+
+static void wcd9xxx_jack_report(struct snd_soc_jack *jack, int status, int mask)
+{
+ snd_soc_jack_report_no_dapm(jack, status, mask);
+}
+
+static void __hphocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status,
+ int irq)
+{
+ struct snd_soc_codec *codec;
+
+ pr_debug("%s: clear ocp status %x\n", __func__, jack_status);
+ codec = mbhc->codec;
+ if (mbhc->hph_status & jack_status) {
+ mbhc->hph_status &= ~jack_status;
+ wcd9xxx_jack_report(&mbhc->headset_jack,
+ mbhc->hph_status, WCD9XXX_JACK_MASK);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+ 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+ 0x10);
+ /*
+ * reset retry counter as PA is turned off signifying
+ * start of new OCP detection session
+ */
+ if (WCD9XXX_IRQ_HPH_PA_OCPL_FAULT)
+ mbhc->hphlocp_cnt = 0;
+ else
+ mbhc->hphrocp_cnt = 0;
+ wcd9xxx_enable_irq(codec->control_data, irq);
+ }
+}
+
+static void hphrocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
+{
+ __hphocp_off_report(mbhc, SND_JACK_OC_HPHR,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+}
+
+static void hphlocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
+{
+ __hphocp_off_report(mbhc, SND_JACK_OC_HPHL,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+}
+
+static void wcd9xxx_get_mbhc_micbias_regs(struct wcd9xxx_mbhc *mbhc,
+ struct mbhc_micbias_regs *micbias_regs)
+{
+ unsigned int cfilt;
+ struct wcd9xxx_pdata *pdata = mbhc->resmgr->pdata;
+
+ switch (mbhc->mbhc_cfg->micbias) {
+ case MBHC_MICBIAS1:
+ cfilt = pdata->micbias.bias1_cfilt_sel;
+ micbias_regs->mbhc_reg = WCD9XXX_A_MICB_1_MBHC;
+ micbias_regs->int_rbias = WCD9XXX_A_MICB_1_INT_RBIAS;
+ micbias_regs->ctl_reg = WCD9XXX_A_MICB_1_CTL;
+ break;
+ case MBHC_MICBIAS2:
+ cfilt = pdata->micbias.bias2_cfilt_sel;
+ micbias_regs->mbhc_reg = WCD9XXX_A_MICB_2_MBHC;
+ micbias_regs->int_rbias = WCD9XXX_A_MICB_2_INT_RBIAS;
+ micbias_regs->ctl_reg = WCD9XXX_A_MICB_2_CTL;
+ break;
+ case MBHC_MICBIAS3:
+ cfilt = pdata->micbias.bias3_cfilt_sel;
+ micbias_regs->mbhc_reg = WCD9XXX_A_MICB_3_MBHC;
+ micbias_regs->int_rbias = WCD9XXX_A_MICB_3_INT_RBIAS;
+ micbias_regs->ctl_reg = WCD9XXX_A_MICB_3_CTL;
+ break;
+ case MBHC_MICBIAS4:
+ cfilt = pdata->micbias.bias4_cfilt_sel;
+ micbias_regs->mbhc_reg = mbhc->resmgr->reg_addr->micb_4_mbhc;
+ micbias_regs->int_rbias =
+ mbhc->resmgr->reg_addr->micb_4_int_rbias;
+ micbias_regs->ctl_reg = mbhc->resmgr->reg_addr->micb_4_ctl;
+ break;
+ default:
+ /* Should never reach here */
+ pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__);
+ return;
+ }
+
+ micbias_regs->cfilt_sel = cfilt;
+
+ switch (cfilt) {
+ case WCD9XXX_CFILT1_SEL:
+ micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_1_VAL;
+ micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_1_CTL;
+ mbhc->mbhc_data.micb_mv =
+ mbhc->resmgr->pdata->micbias.cfilt1_mv;
+ break;
+ case WCD9XXX_CFILT2_SEL:
+ micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_2_VAL;
+ micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_2_CTL;
+ mbhc->mbhc_data.micb_mv =
+ mbhc->resmgr->pdata->micbias.cfilt2_mv;
+ break;
+ case WCD9XXX_CFILT3_SEL:
+ micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_3_VAL;
+ micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_3_CTL;
+ mbhc->mbhc_data.micb_mv =
+ mbhc->resmgr->pdata->micbias.cfilt3_mv;
+ break;
+ }
+}
+
+static void wcd9xxx_clr_and_turnon_hph_padac(struct wcd9xxx_mbhc *mbhc)
+{
+ bool pa_turned_on = false;
+ struct snd_soc_codec *codec = mbhc->codec;
+ u8 wg_time;
+
+ wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) ;
+ wg_time += 1;
+
+ if (test_and_clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK,
+ &mbhc->hph_pa_dac_state)) {
+ pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL,
+ 0xC0, 0xC0);
+ }
+ if (test_and_clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK,
+ &mbhc->hph_pa_dac_state)) {
+ pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL,
+ 0xC0, 0xC0);
+ }
+
+ if (test_and_clear_bit(WCD9XXX_HPHR_PA_OFF_ACK,
+ &mbhc->hph_pa_dac_state)) {
+ pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x10,
+ 1 << 4);
+ pa_turned_on = true;
+ }
+ if (test_and_clear_bit(WCD9XXX_HPHL_PA_OFF_ACK,
+ &mbhc->hph_pa_dac_state)) {
+ pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x20, 1
+ << 5);
+ pa_turned_on = true;
+ }
+
+ if (pa_turned_on) {
+ pr_debug("%s: PA was turned off by MBHC and not by DAPM\n",
+ __func__);
+ usleep_range(wg_time * 1000, wg_time * 1000);
+ }
+}
+
+static int wcd9xxx_cancel_btn_work(struct wcd9xxx_mbhc *mbhc)
+{
+ int r;
+ r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
+ if (r)
+ /* if scheduled mbhc.mbhc_btn_dwork is canceled from here,
+ * we have to unlock from here instead btn_work */
+ wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+ return r;
+}
+
+static bool wcd9xxx_is_hph_dac_on(struct snd_soc_codec *codec, int left)
+{
+ u8 hph_reg_val = 0;
+ if (left)
+ hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL);
+ else
+ hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL);
+
+ return (hph_reg_val & 0xC0) ? true : false;
+}
+
+static bool wcd9xxx_is_hph_pa_on(struct snd_soc_codec *codec)
+{
+ u8 hph_reg_val = 0;
+ hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_EN);
+
+ return (hph_reg_val & 0x30) ? true : false;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_set_and_turnoff_hph_padac(struct wcd9xxx_mbhc *mbhc)
+{
+ u8 wg_time;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME);
+ wg_time += 1;
+
+ /* If headphone PA is on, check if userspace receives
+ * removal event to sync-up PA's state */
+ if (wcd9xxx_is_hph_pa_on(codec)) {
+ pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
+ set_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ set_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ } else {
+ pr_debug("%s PA is off\n", __func__);
+ }
+
+ if (wcd9xxx_is_hph_dac_on(codec, 1))
+ set_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+ if (wcd9xxx_is_hph_dac_on(codec, 0))
+ set_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL, 0xC0, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xC0, 0x00);
+ usleep_range(wg_time * 1000, wg_time * 1000);
+}
+
+static void wcd9xxx_insert_detect_setup(struct wcd9xxx_mbhc *mbhc, bool ins)
+{
+ if (!mbhc->mbhc_cfg->insert_detect)
+ return;
+ pr_debug("%s: Setting up %s detection\n", __func__,
+ ins ? "insert" : "removal");
+ /* Enable interrupt and insertion detection */
+ snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
+ (0x69 | (ins ? (1 << 1) : 0)));
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_report_plug(struct wcd9xxx_mbhc *mbhc, int insertion,
+ enum snd_jack_types jack_type)
+{
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ if (!insertion) {
+ /* Report removal */
+ mbhc->hph_status &= ~jack_type;
+ /*
+ * cancel possibly scheduled btn work and
+ * report release if we reported button press
+ */
+ if (wcd9xxx_cancel_btn_work(mbhc))
+ pr_debug("%s: button press is canceled\n", __func__);
+ else if (mbhc->buttons_pressed) {
+ pr_debug("%s: release of button press%d\n",
+ __func__, jack_type);
+ wcd9xxx_jack_report(&mbhc->button_jack, 0,
+ mbhc->buttons_pressed);
+ mbhc->buttons_pressed &=
+ ~WCD9XXX_JACK_BUTTON_MASK;
+ }
+ pr_debug("%s: Reporting removal %d(%x)\n", __func__,
+ jack_type, mbhc->hph_status);
+ wcd9xxx_jack_report(&mbhc->headset_jack, mbhc->hph_status,
+ WCD9XXX_JACK_MASK);
+ wcd9xxx_set_and_turnoff_hph_padac(mbhc);
+ hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
+ hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
+ mbhc->current_plug = PLUG_TYPE_NONE;
+ mbhc->polling_active = false;
+ } else {
+ /* Report insertion */
+ mbhc->hph_status |= jack_type;
+
+ if (jack_type == SND_JACK_HEADPHONE) {
+ mbhc->current_plug = PLUG_TYPE_HEADPHONE;
+ } else if (jack_type == SND_JACK_UNSUPPORTED) {
+ mbhc->current_plug = PLUG_TYPE_GND_MIC_SWAP;
+ } else if (jack_type == SND_JACK_HEADSET) {
+ mbhc->polling_active = BUTTON_POLLING_SUPPORTED;
+ mbhc->current_plug = PLUG_TYPE_HEADSET;
+ }
+ pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
+ jack_type, mbhc->hph_status);
+ wcd9xxx_jack_report(&mbhc->headset_jack,
+ mbhc->hph_status, WCD9XXX_JACK_MASK);
+ wcd9xxx_clr_and_turnon_hph_padac(mbhc);
+ }
+ /* Setup insert detect */
+ wcd9xxx_insert_detect_setup(mbhc, !insertion);
+}
+
+/* should be called under interrupt context that hold suspend */
+static void wcd9xxx_schedule_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
+ struct work_struct *work)
+{
+ pr_debug("%s: scheduling wcd9xxx_correct_swch_plug\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+ mbhc->hs_detect_work_stop = false;
+ wcd9xxx_lock_sleep(mbhc->resmgr->core);
+ schedule_work(work);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_cancel_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
+ struct work_struct *work)
+{
+ pr_debug("%s: Canceling correct_plug_swch\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+ mbhc->hs_detect_work_stop = true;
+ wmb();
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ if (cancel_work_sync(work)) {
+ pr_debug("%s: correct_plug_swch is canceled\n",
+ __func__);
+ wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+ }
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+}
+
+static s16 wcd9xxx_get_current_v_hs_max(struct wcd9xxx_mbhc *mbhc)
+{
+ s16 v_hs_max;
+ struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
+
+ plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+ if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
+ mbhc->mbhc_micbias_switched)
+ v_hs_max = mbhc->mbhc_data.adj_v_hs_max;
+ else
+ v_hs_max = plug_type->v_hs_max;
+ return v_hs_max;
+}
+
+static bool wcd9xxx_is_inval_ins_range(struct wcd9xxx_mbhc *mbhc,
+ s32 mic_volt, bool highhph, bool *highv)
+{
+ s16 v_hs_max;
+ bool invalid = false;
+
+ /* Perform this check only when the high voltage headphone
+ * needs to be considered as invalid
+ */
+ v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
+ *highv = mic_volt > v_hs_max;
+ if (!highhph && *highv)
+ invalid = true;
+ else if (mic_volt < mbhc->mbhc_data.v_inval_ins_high &&
+ (mic_volt > mbhc->mbhc_data.v_inval_ins_low))
+ invalid = true;
+
+ return invalid;
+}
+
+static short wcd9xxx_read_sta_result(struct snd_soc_codec *codec)
+{
+ u8 bias_msb, bias_lsb;
+ short bias_value;
+
+ bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B3_STATUS);
+ bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B2_STATUS);
+ bias_value = (bias_msb << 8) | bias_lsb;
+ return bias_value;
+}
+
+static short wcd9xxx_read_dce_result(struct snd_soc_codec *codec)
+{
+ u8 bias_msb, bias_lsb;
+ short bias_value;
+
+ bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B5_STATUS);
+ bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B4_STATUS);
+ bias_value = (bias_msb << 8) | bias_lsb;
+ return bias_value;
+}
+
+static void wcd9xxx_turn_onoff_rel_detection(struct snd_soc_codec *codec,
+ bool on)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
+}
+
+static short __wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
+ bool override_bypass, bool noreldetection)
+{
+ short bias_value;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ wcd9xxx_disable_irq(mbhc->resmgr->core, WCD9XXX_IRQ_MBHC_POTENTIAL);
+ if (noreldetection)
+ wcd9xxx_turn_onoff_rel_detection(codec, false);
+
+ /* Turn on the override */
+ if (!override_bypass)
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
+ if (dce) {
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+ 0x8);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+ 0x0);
+ usleep_range(mbhc->mbhc_data.t_sta_dce,
+ mbhc->mbhc_data.t_sta_dce);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
+ usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
+ bias_value = wcd9xxx_read_dce_result(codec);
+ } else {
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+ 0x8);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+ 0x0);
+ usleep_range(mbhc->mbhc_data.t_sta_dce,
+ mbhc->mbhc_data.t_sta_dce);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
+ usleep_range(mbhc->mbhc_data.t_sta,
+ mbhc->mbhc_data.t_sta);
+ bias_value = wcd9xxx_read_sta_result(codec);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+ 0x8);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x0);
+ }
+ /* Turn off the override after measuring mic voltage */
+ if (!override_bypass)
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04,
+ 0x00);
+
+ if (noreldetection)
+ wcd9xxx_turn_onoff_rel_detection(codec, true);
+ wcd9xxx_enable_irq(mbhc->resmgr->core, WCD9XXX_IRQ_MBHC_POTENTIAL);
+
+ return bias_value;
+}
+
+static short wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
+ bool norel)
+{
+ return __wcd9xxx_codec_sta_dce(mbhc, dce, false, norel);
+}
+
+static s32 wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
+ u16 bias_value)
+{
+ s16 value, z, mb;
+ s32 mv;
+
+ value = bias_value;
+ if (dce) {
+ z = (mbhc->mbhc_data.dce_z);
+ mb = (mbhc->mbhc_data.dce_mb);
+ mv = (value - z) * (s32)mbhc->mbhc_data.micb_mv / (mb - z);
+ } else {
+ z = (mbhc->mbhc_data.sta_z);
+ mb = (mbhc->mbhc_data.sta_mb);
+ mv = (value - z) * (s32)mbhc->mbhc_data.micb_mv / (mb - z);
+ }
+
+ return mv;
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static short wcd9xxx_mbhc_setup_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ short bias_value;
+ u8 cfilt_mode;
+
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ pr_debug("%s: enter\n", __func__);
+ if (!mbhc->mbhc_cfg->calibration) {
+ pr_err("%s: Error, no calibration exists\n", __func__);
+ return -ENODEV;
+ }
+
+ /*
+ * Request BG and clock.
+ * These will be released by wcd9xxx_cleanup_hs_polling
+ */
+ wcd9xxx_resmgr_get_bandgap(mbhc->resmgr, WCD9XXX_BANDGAP_MBHC_MODE);
+ wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x01);
+
+ /* Make sure CFILT is in fast mode, save current mode */
+ cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x70, 0x00);
+
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x1F, 0x16);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x84);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x80);
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x1F, 0x1C);
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+
+ wcd9xxx_calibrate_hs_polling(mbhc);
+
+ /* don't flip override */
+ bias_value = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40,
+ cfilt_mode);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+
+ return bias_value;
+}
+
+static void wcd9xxx_shutdown_hs_removal_detect(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ const struct wcd9xxx_mbhc_general_cfg *generic =
+ WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+
+ /* Need MBHC clock */
+ wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80, 0x00);
+
+ usleep_range(generic->t_shutdown_plug_rem,
+ generic->t_shutdown_plug_rem);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
+
+ /* Put requested CLK back */
+ wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x00);
+}
+
+static void wcd9xxx_cleanup_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ wcd9xxx_shutdown_hs_removal_detect(mbhc);
+
+ /* Release clock and BG requested by wcd9xxx_mbhc_setup_hs_polling */
+ wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+ wcd9xxx_resmgr_put_bandgap(mbhc->resmgr, WCD9XXX_BANDGAP_MBHC_MODE);
+
+ mbhc->polling_active = false;
+ mbhc->mbhc_state = MBHC_STATE_NONE;
+}
+
+static s16 scale_v_micb_vddio(struct wcd9xxx_mbhc *mbhc, int v, bool tovddio)
+{
+ int r;
+ int vddio_k, mb_k;
+ vddio_k = wcd9xxx_resmgr_get_k_val(mbhc->resmgr, VDDIO_MICBIAS_MV);
+ mb_k = wcd9xxx_resmgr_get_k_val(mbhc->resmgr, mbhc->mbhc_data.micb_mv);
+ if (tovddio)
+ r = v * vddio_k / mb_k;
+ else
+ r = v * mb_k / vddio_k;
+ return r;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, on);
+ if (on)
+ usleep_range(5000, 5000);
+}
+
+static bool wcd9xxx_is_inval_ins_delta(struct snd_soc_codec *codec,
+ int mic_volt, int mic_volt_prev,
+ int threshold)
+{
+ return abs(mic_volt - mic_volt_prev) > threshold;
+}
+
+/* called under codec_resource_lock acquisition and mbhc override = 1 */
+static enum wcd9xxx_mbhc_plug_type
+wcd9xxx_codec_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
+{
+ int i;
+ bool gndswitch, vddioswitch;
+ int scaled;
+ struct wcd9xxx_mbhc_plug_type_cfg *plug_type_ptr;
+ struct snd_soc_codec *codec = mbhc->codec;
+ const bool vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV);
+ int num_det = (NUM_DCE_PLUG_DETECT + vddio);
+ enum wcd9xxx_mbhc_plug_type plug_type[num_det];
+ s16 mb_v[num_det];
+ s32 mic_mv[num_det];
+ bool inval;
+ bool highdelta;
+ bool ahighv = false, highv;
+
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ /* make sure override is on */
+ WARN_ON(!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04));
+
+ /* GND and MIC swap detection requires at least 2 rounds of DCE */
+ BUG_ON(num_det < 2);
+
+ plug_type_ptr =
+ WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+
+ plug_type[0] = PLUG_TYPE_INVALID;
+
+ /* performs DCEs for N times
+ * 1st: check if voltage is in invalid range
+ * 2nd - N-2nd: check voltage range and delta
+ * N-1st: check voltage range, delta with HPHR GND switch
+ * Nth: check voltage range with VDDIO switch if micbias V != vddio V*/
+ for (i = 0; i < num_det; i++) {
+ gndswitch = (i == (num_det - 1 - vddio));
+ vddioswitch = (vddio && ((i == num_det - 1) ||
+ (i == num_det - 2)));
+ if (i == 0) {
+ mb_v[i] = wcd9xxx_mbhc_setup_hs_polling(mbhc);
+ mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1 , mb_v[i]);
+ inval = wcd9xxx_is_inval_ins_range(mbhc, mic_mv[i],
+ highhph, &highv);
+ ahighv |= highv;
+ scaled = mic_mv[i];
+ } else {
+ if (vddioswitch)
+ __wcd9xxx_switch_micbias(mbhc, 1,
+ false, false);
+ if (gndswitch)
+ wcd9xxx_codec_hphr_gnd_switch(codec, true);
+ mb_v[i] = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
+ mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1 , mb_v[i]);
+ if (vddioswitch)
+ scaled = scale_v_micb_vddio(mbhc, mic_mv[i],
+ false);
+ else
+ scaled = mic_mv[i];
+ /* !gndswitch & vddioswitch means the previous DCE
+ * was done with gndswitch, don't compare with DCE
+ * with gndswitch */
+ highdelta = wcd9xxx_is_inval_ins_delta(codec, scaled,
+ mic_mv[i - !gndswitch - vddioswitch],
+ FAKE_INS_DELTA_SCALED_MV);
+ inval = (wcd9xxx_is_inval_ins_range(mbhc, mic_mv[i],
+ highhph, &highv) ||
+ highdelta);
+ ahighv |= highv;
+ if (gndswitch)
+ wcd9xxx_codec_hphr_gnd_switch(codec, false);
+ if (vddioswitch)
+ __wcd9xxx_switch_micbias(mbhc, 0,
+ false, false);
+ /* claim UNSUPPORTED plug insertion when
+ * good headset is detected but HPHR GND switch makes
+ * delta difference */
+ if (i == (num_det - 2) && highdelta && !ahighv)
+ plug_type[0] = PLUG_TYPE_GND_MIC_SWAP;
+ else if (i == (num_det - 1) && inval)
+ plug_type[0] = PLUG_TYPE_INVALID;
+ }
+ pr_debug("%s: DCE #%d, %04x, V %d, scaled V %d, GND %d, VDDIO %d, inval %d\n",
+ __func__, i + 1, mb_v[i] & 0xffff, mic_mv[i], scaled,
+ gndswitch, vddioswitch, inval);
+ /* don't need to run further DCEs */
+ if (ahighv && inval)
+ break;
+ mic_mv[i] = scaled;
+ }
+
+ for (i = 0; (plug_type[0] != PLUG_TYPE_GND_MIC_SWAP && !inval) &&
+ (i < num_det); i++) {
+ /*
+ * If we are here, means none of the all
+ * measurements are fake, continue plug type detection.
+ * If all three measurements do not produce same
+ * plug type, restart insertion detection
+ */
+ if (mic_mv[i] < plug_type_ptr->v_no_mic) {
+ plug_type[i] = PLUG_TYPE_HEADPHONE;
+ pr_debug("%s: Detect attempt %d, detected Headphone\n",
+ __func__, i);
+ } else if (highhph && (mic_mv[i] > plug_type_ptr->v_hs_max)) {
+ plug_type[i] = PLUG_TYPE_HIGH_HPH;
+ pr_debug("%s: Detect attempt %d, detected High Headphone\n",
+ __func__, i);
+ } else {
+ plug_type[i] = PLUG_TYPE_HEADSET;
+ pr_debug("%s: Detect attempt %d, detected Headset\n",
+ __func__, i);
+ }
+
+ if (i > 0 && (plug_type[i - 1] != plug_type[i])) {
+ pr_err("%s: Detect attempt %d and %d are not same",
+ __func__, i - 1, i);
+ plug_type[0] = PLUG_TYPE_INVALID;
+ inval = true;
+ break;
+ }
+ }
+
+ pr_debug("%s: Detected plug type %d\n", __func__, plug_type[0]);
+ return plug_type[0];
+}
+
+static bool wcd9xxx_swch_level_remove(struct wcd9xxx_mbhc *mbhc)
+{
+ if (mbhc->mbhc_cfg->gpio)
+ return (gpio_get_value_cansleep(mbhc->mbhc_cfg->gpio) !=
+ mbhc->mbhc_cfg->gpio_level_insert);
+ else if (mbhc->mbhc_cfg->insert_detect)
+ return snd_soc_read(mbhc->codec,
+ WCD9XXX_A_MBHC_INSERT_DET_STATUS) &
+ (1 << 2);
+ else
+ WARN(1, "Invalid jack detection configuration\n");
+
+ return true;
+}
+
+static bool is_clk_active(struct snd_soc_codec *codec)
+{
+ return !!(snd_soc_read(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL) & 0x05);
+}
+
+static int wcd9xxx_enable_hs_detect(struct wcd9xxx_mbhc *mbhc,
+ int insertion, int trigger, bool padac_off)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ int central_bias_enabled = 0;
+ const struct wcd9xxx_mbhc_general_cfg *generic =
+ WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+ const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
+ WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+ if (!mbhc->mbhc_cfg->calibration) {
+ pr_err("Error, no wcd9xxx calibration\n");
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0);
+
+ /*
+ * Make sure mic bias and Mic line schmitt trigger
+ * are turned OFF
+ */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+
+ if (insertion) {
+ wcd9xxx_switch_micbias(mbhc, 0);
+
+ /* DAPM can manipulate PA/DAC bits concurrently */
+ if (padac_off == true)
+ wcd9xxx_set_and_turnoff_hph_padac(mbhc);
+
+ if (trigger & MBHC_USE_HPHL_TRIGGER) {
+ /* Enable HPH Schmitt Trigger */
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x11,
+ 0x11);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x0C,
+ plug_det->hph_current << 2);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x02,
+ 0x02);
+ }
+ if (trigger & MBHC_USE_MB_TRIGGER) {
+ /* enable the mic line schmitt trigger */
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x60, plug_det->mic_current << 5);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x80, 0x80);
+ usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+ 0x00);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x10, 0x10);
+ }
+
+ /* setup for insetion detection */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2, 0);
+ } else {
+ pr_debug("setup for removal detection\n");
+ /* Make sure the HPH schmitt trigger is OFF */
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x00);
+
+ /* enable the mic line schmitt trigger */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x60,
+ plug_det->mic_current << 5);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x80, 0x80);
+ usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x10, 0x10);
+
+ /* Setup for low power removal detection */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2,
+ 0x2);
+ }
+
+ if (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x4) {
+ /* called by interrupt */
+ if (!is_clk_active(codec)) {
+ wcd9xxx_resmgr_enable_config_mode(codec, 1);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x06, 0);
+ usleep_range(generic->t_shutdown_plug_rem,
+ generic->t_shutdown_plug_rem);
+ wcd9xxx_resmgr_enable_config_mode(codec, 0);
+ } else
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x06, 0);
+ }
+
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.int_rbias, 0x80, 0);
+
+ /* If central bandgap disabled */
+ if (!(snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE1) & 1)) {
+ snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x3, 0x3);
+ usleep_range(generic->t_bg_fast_settle,
+ generic->t_bg_fast_settle);
+ central_bias_enabled = 1;
+ }
+
+ /* If LDO_H disabled */
+ if (snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE0) & 0x80) {
+ snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x10, 0);
+ snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0x80);
+ usleep_range(generic->t_ldoh, generic->t_ldoh);
+ snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0);
+
+ if (central_bias_enabled)
+ snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x1,
+ 0);
+ }
+
+ snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc, 0x3,
+ mbhc->mbhc_cfg->micbias);
+
+ wcd9xxx_enable_irq(mbhc->resmgr->core, WCD9XXX_IRQ_MBHC_INSERTION);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
+
+ return 0;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_find_plug_and_report(struct wcd9xxx_mbhc *mbhc,
+ enum wcd9xxx_mbhc_plug_type plug_type)
+{
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ if (plug_type == PLUG_TYPE_HEADPHONE &&
+ mbhc->current_plug == PLUG_TYPE_NONE) {
+ /*
+ * Nothing was reported previously
+ * report a headphone or unsupported
+ */
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ } else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+ if (mbhc->current_plug == PLUG_TYPE_HEADSET)
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
+ else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE)
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
+
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ } else if (plug_type == PLUG_TYPE_HEADSET) {
+ /*
+ * If Headphone was reported previously, this will
+ * only report the mic line
+ */
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADSET);
+ msleep(100);
+ wcd9xxx_start_hs_polling(mbhc);
+ } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
+ if (mbhc->current_plug == PLUG_TYPE_NONE)
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ pr_debug("setup mic trigger for further detection\n");
+ mbhc->lpi_enabled = true;
+ wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
+ MBHC_USE_HPHL_TRIGGER, false);
+ } else {
+ WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
+ mbhc->current_plug, plug_type);
+ }
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_mbhc_decide_swch_plug(struct wcd9xxx_mbhc *mbhc)
+{
+ enum wcd9xxx_mbhc_plug_type plug_type;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ wcd9xxx_turn_onoff_override(codec, true);
+ plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
+ wcd9xxx_turn_onoff_override(codec, false);
+
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch level is low when determining plug\n",
+ __func__);
+ return;
+ }
+
+ if (plug_type == PLUG_TYPE_INVALID ||
+ plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+ wcd9xxx_schedule_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
+ } else if (plug_type == PLUG_TYPE_HEADPHONE) {
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+ wcd9xxx_schedule_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
+ } else {
+ pr_debug("%s: Valid plug found, determine plug type %d\n",
+ __func__, plug_type);
+ wcd9xxx_find_plug_and_report(mbhc, plug_type);
+ }
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_mbhc_detect_plug_type(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
+ WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ /*
+ * Turn on the override,
+ * wcd9xxx_mbhc_setup_hs_polling requires override on
+ */
+ wcd9xxx_turn_onoff_override(codec, true);
+ if (plug_det->t_ins_complete > 20)
+ msleep(plug_det->t_ins_complete);
+ else
+ usleep_range(plug_det->t_ins_complete * 1000,
+ plug_det->t_ins_complete * 1000);
+ /* Turn off the override */
+ wcd9xxx_turn_onoff_override(codec, false);
+
+ if (wcd9xxx_swch_level_remove(mbhc))
+ pr_debug("%s: Switch level low when determining plug\n",
+ __func__);
+ else
+ wcd9xxx_mbhc_decide_swch_plug(mbhc);
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_insert_irq_swch(struct wcd9xxx_mbhc *mbhc,
+ bool is_removal)
+{
+ if (!is_removal) {
+ pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
+
+ rmb();
+ if (mbhc->lpi_enabled)
+ msleep(100);
+
+ rmb();
+ if (!mbhc->lpi_enabled) {
+ pr_debug("%s: lpi is disabled\n", __func__);
+ } else if (!wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Valid insertion, detect plug type\n",
+ __func__);
+ wcd9xxx_mbhc_decide_swch_plug(mbhc);
+ } else {
+ pr_debug("%s: Invalid insertion stop plug detection\n",
+ __func__);
+ }
+ } else {
+ pr_err("%s: Switch IRQ used, invalid MBHC Removal\n", __func__);
+ }
+}
+
+static bool is_valid_mic_voltage(struct wcd9xxx_mbhc *mbhc, s32 mic_mv)
+{
+ const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
+ WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+ const s16 v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
+
+ return (!(mic_mv > 10 && mic_mv < 80) && (mic_mv > plug_type->v_no_mic)
+ && (mic_mv < v_hs_max)) ? true : false;
+}
+
+/*
+ * called under codec_resource_lock acquisition
+ * returns true if mic voltage range is back to normal insertion
+ * returns false either if timedout or removed
+ */
+static bool wcd9xxx_hs_remove_settle(struct wcd9xxx_mbhc *mbhc)
+{
+ int i;
+ bool timedout, settled = false;
+ s32 mic_mv[NUM_DCE_PLUG_DETECT];
+ short mb_v[NUM_DCE_PLUG_DETECT];
+ unsigned long retry = 0, timeout;
+
+ timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
+ while (!(timedout = time_after(jiffies, timeout))) {
+ retry++;
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch indicates removal\n", __func__);
+ break;
+ }
+
+ if (retry > 1)
+ msleep(250);
+ else
+ msleep(50);
+
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch indicates removal\n", __func__);
+ break;
+ }
+
+ for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
+ mb_v[i] = wcd9xxx_codec_sta_dce(mbhc, 1, true);
+ mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1 , mb_v[i]);
+ pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
+ __func__, retry, mic_mv[i], mb_v[i]);
+ }
+
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switcn indicates removal\n", __func__);
+ break;
+ }
+
+ if (mbhc->current_plug == PLUG_TYPE_NONE) {
+ pr_debug("%s : headset/headphone is removed\n",
+ __func__);
+ break;
+ }
+
+ for (i = 0; i < NUM_DCE_PLUG_DETECT; i++)
+ if (!is_valid_mic_voltage(mbhc, mic_mv[i]))
+ break;
+
+ if (i == NUM_DCE_PLUG_DETECT) {
+ pr_debug("%s: MIC voltage settled\n", __func__);
+ settled = true;
+ msleep(200);
+ break;
+ }
+ }
+
+ if (timedout)
+ pr_debug("%s: Microphone did not settle in %d seconds\n",
+ __func__, HS_DETECT_PLUG_TIME_MS);
+ return settled;
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_remove_irq_swch(struct wcd9xxx_mbhc *mbhc)
+{
+ if (wcd9xxx_hs_remove_settle(mbhc))
+ wcd9xxx_start_hs_polling(mbhc);
+ pr_debug("%s: remove settle done\n", __func__);
+}
+
+static irqreturn_t wcd9xxx_hs_remove_irq(int irq, void *data)
+{
+ bool vddio;
+ struct wcd9xxx_mbhc *mbhc = data;
+
+ pr_debug("%s: enter, removal interrupt\n", __func__);
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
+ mbhc->mbhc_micbias_switched);
+ if (vddio)
+ __wcd9xxx_switch_micbias(mbhc, 0, false, true);
+
+ wcd9xxx_hs_remove_irq_swch(mbhc);
+
+ /*
+ * if driver turned off vddio switch and headset is not removed,
+ * turn on the vddio switch back, if headset is removed then vddio
+ * switch is off by time now and shouldn't be turn on again from here
+ */
+ if (vddio && mbhc->current_plug == PLUG_TYPE_HEADSET)
+ __wcd9xxx_switch_micbias(mbhc, 1, true, true);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hs_insert_irq(int irq, void *data)
+{
+ bool is_mb_trigger, is_removal;
+ struct wcd9xxx_mbhc *mbhc = data;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
+
+ is_mb_trigger = !!(snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg) &
+ 0x10);
+ is_removal = !!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_INT_CTL) & 0x02);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
+
+ /* Turn off both HPH and MIC line schmitt triggers */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+
+ wcd9xxx_hs_insert_irq_swch(mbhc, is_removal);
+
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ return IRQ_HANDLED;
+}
+
+static void wcd9xxx_btn_lpress_fn(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ short bias_value;
+ int dce_mv, sta_mv;
+ struct wcd9xxx_mbhc *mbhc;
+
+ pr_debug("%s:\n", __func__);
+
+ dwork = to_delayed_work(work);
+ mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_btn_dwork);
+
+ bias_value = wcd9xxx_read_sta_result(mbhc->codec);
+ sta_mv = wcd9xxx_codec_sta_dce_v(mbhc, 0, bias_value);
+
+ bias_value = wcd9xxx_read_dce_result(mbhc->codec);
+ dce_mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value);
+ pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv, dce_mv);
+
+ pr_debug("%s: Reporting long button press event\n", __func__);
+ wcd9xxx_jack_report(&mbhc->button_jack, mbhc->buttons_pressed,
+ mbhc->buttons_pressed);
+
+ pr_debug("%s: leave\n", __func__);
+ wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+}
+
+static void wcd9xxx_mbhc_insert_work(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ struct wcd9xxx_mbhc *mbhc;
+ struct snd_soc_codec *codec;
+ struct wcd9xxx *core;
+
+ dwork = to_delayed_work(work);
+ mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_insert_dwork);
+ codec = mbhc->codec;
+ core = mbhc->resmgr->core;
+
+ pr_debug("%s:\n", __func__);
+
+ /* Turn off both HPH and MIC line schmitt triggers */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+ wcd9xxx_disable_irq_sync(core, WCD9XXX_IRQ_MBHC_INSERTION);
+ wcd9xxx_mbhc_detect_plug_type(mbhc);
+ wcd9xxx_unlock_sleep(core);
+}
+
+static bool wcd9xxx_mbhc_fw_validate(const struct firmware *fw)
+{
+ u32 cfg_offset;
+ struct wcd9xxx_mbhc_imped_detect_cfg *imped_cfg;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
+
+ if (fw->size < WCD9XXX_MBHC_CAL_MIN_SIZE)
+ return false;
+
+ /*
+ * Previous check guarantees that there is enough fw data up
+ * to num_btn
+ */
+ btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw->data);
+ cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
+ if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg)))
+ return false;
+
+ /*
+ * Previous check guarantees that there is enough fw data up
+ * to start of impedance detection configuration
+ */
+ imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw->data);
+ cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
+
+ if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ))
+ return false;
+
+ if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg)))
+ return false;
+
+ return true;
+}
+
+static u16 wcd9xxx_codec_v_sta_dce(struct wcd9xxx_mbhc *mbhc,
+ enum meas_type dce, s16 vin_mv)
+{
+ s16 diff, zero;
+ u32 mb_mv, in;
+ u16 value;
+
+ mb_mv = mbhc->mbhc_data.micb_mv;
+
+ if (mb_mv == 0) {
+ pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
+ return -EINVAL;
+ }
+
+ if (dce) {
+ diff = (mbhc->mbhc_data.dce_mb) - (mbhc->mbhc_data.dce_z);
+ zero = (mbhc->mbhc_data.dce_z);
+ } else {
+ diff = (mbhc->mbhc_data.sta_mb) - (mbhc->mbhc_data.sta_z);
+ zero = (mbhc->mbhc_data.sta_z);
+ }
+ in = (u32) diff * vin_mv;
+
+ value = (u16) (in / mb_mv) + zero;
+ return value;
+}
+
+static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec;
+ s16 btn_mv = 0, btn_delta_mv;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
+ u16 *btn_high;
+ int i;
+
+ pr_debug("%s: enter\n", __func__);
+ codec = mbhc->codec;
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+ plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+
+ mbhc->mbhc_data.v_ins_hu =
+ wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_hs_max);
+ mbhc->mbhc_data.v_ins_h =
+ wcd9xxx_codec_v_sta_dce(mbhc, DCE, plug_type->v_hs_max);
+
+ mbhc->mbhc_data.v_inval_ins_low = FAKE_INS_LOW;
+ mbhc->mbhc_data.v_inval_ins_high = FAKE_INS_HIGH;
+
+ if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+ mbhc->mbhc_data.adj_v_hs_max =
+ scale_v_micb_vddio(mbhc, plug_type->v_hs_max, true);
+ mbhc->mbhc_data.adj_v_ins_hu =
+ wcd9xxx_codec_v_sta_dce(mbhc, STA,
+ mbhc->mbhc_data.adj_v_hs_max);
+ mbhc->mbhc_data.adj_v_ins_h =
+ wcd9xxx_codec_v_sta_dce(mbhc, DCE,
+ mbhc->mbhc_data.adj_v_hs_max);
+ mbhc->mbhc_data.v_inval_ins_low =
+ scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_low,
+ false);
+ mbhc->mbhc_data.v_inval_ins_high =
+ scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_high,
+ false);
+ }
+
+ btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+ MBHC_BTN_DET_V_BTN_HIGH);
+ for (i = 0; i < btn_det->num_btn; i++)
+ btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
+
+ mbhc->mbhc_data.v_b1_h = wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv);
+ btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_sta;
+ mbhc->mbhc_data.v_b1_hu =
+ wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_delta_mv);
+
+ btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_cic;
+
+ mbhc->mbhc_data.v_b1_huc =
+ wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_delta_mv);
+
+ mbhc->mbhc_data.v_brh = mbhc->mbhc_data.v_b1_h;
+ mbhc->mbhc_data.v_brl = BUTTON_MIN;
+
+ mbhc->mbhc_data.v_no_mic =
+ wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_no_mic);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_onoff_ext_mclk(struct wcd9xxx_mbhc *mbhc, bool on)
+{
+ /*
+ * XXX: {codec}_mclk_enable holds WCD9XXX_BCL_LOCK,
+ * therefore wcd9xxx_onoff_ext_mclk caller SHOULDN'T hold
+ * WCD9XXX_BCL_LOCK when it calls wcd9xxx_onoff_ext_mclk()
+ */
+ mbhc->mbhc_cfg->mclk_cb_fn(mbhc->codec, on, false);
+}
+
+static void wcd9xxx_correct_swch_plug(struct work_struct *work)
+{
+ struct wcd9xxx_mbhc *mbhc;
+ struct snd_soc_codec *codec;
+ enum wcd9xxx_mbhc_plug_type plug_type;
+ unsigned long timeout;
+ int retry = 0, pt_gnd_mic_swap_cnt = 0;
+ bool correction = false;
+
+ pr_debug("%s: enter\n", __func__);
+
+ mbhc = container_of(work, struct wcd9xxx_mbhc, correct_plug_swch);
+ codec = mbhc->codec;
+
+ wcd9xxx_onoff_ext_mclk(mbhc, true);
+
+ /*
+ * Keep override on during entire plug type correction work.
+ *
+ * This is okay under the assumption that any switch irqs which use
+ * MBHC block cancel and sync this work so override is off again
+ * prior to switch interrupt handler's MBHC block usage.
+ * Also while this correction work is running, we can guarantee
+ * DAPM doesn't use any MBHC block as this work only runs with
+ * headphone detection.
+ */
+ wcd9xxx_turn_onoff_override(codec, true);
+
+ timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
+ while (!time_after(jiffies, timeout)) {
+ ++retry;
+ rmb();
+ if (mbhc->hs_detect_work_stop) {
+ pr_debug("%s: stop requested\n", __func__);
+ break;
+ }
+
+ msleep(HS_DETECT_PLUG_INERVAL_MS);
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch level is low\n", __func__);
+ break;
+ }
+
+ /* can race with removal interrupt */
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+
+ if (plug_type == PLUG_TYPE_INVALID) {
+ pr_debug("Invalid plug in attempt # %d\n", retry);
+ if (retry == NUM_ATTEMPTS_TO_REPORT &&
+ mbhc->current_plug == PLUG_TYPE_NONE) {
+ wcd9xxx_report_plug(mbhc, 1,
+ SND_JACK_HEADPHONE);
+ }
+ } else if (plug_type == PLUG_TYPE_HEADPHONE) {
+ pr_debug("Good headphone detected, continue polling\n");
+ if (mbhc->current_plug == PLUG_TYPE_NONE)
+ wcd9xxx_report_plug(mbhc, 1,
+ SND_JACK_HEADPHONE);
+ } else {
+ if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+ pt_gnd_mic_swap_cnt++;
+ if (pt_gnd_mic_swap_cnt <
+ GND_MIC_SWAP_THRESHOLD)
+ continue;
+ else if (pt_gnd_mic_swap_cnt >
+ GND_MIC_SWAP_THRESHOLD) {
+ /*
+ * This is due to GND/MIC switch didn't
+ * work, Report unsupported plug
+ */
+ } else if (mbhc->mbhc_cfg->swap_gnd_mic) {
+ /*
+ * if switch is toggled, check again,
+ * otherwise report unsupported plug
+ */
+ if (mbhc->mbhc_cfg->swap_gnd_mic(codec))
+ continue;
+ }
+ } else
+ pt_gnd_mic_swap_cnt = 0;
+
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ /* Turn off override */
+ wcd9xxx_turn_onoff_override(codec, false);
+ /*
+ * The valid plug also includes PLUG_TYPE_GND_MIC_SWAP
+ */
+ wcd9xxx_find_plug_and_report(mbhc, plug_type);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ pr_debug("Attempt %d found correct plug %d\n", retry,
+ plug_type);
+ correction = true;
+ break;
+ }
+ }
+
+ /* Turn off override */
+ if (!correction)
+ wcd9xxx_turn_onoff_override(codec, false);
+
+ wcd9xxx_onoff_ext_mclk(mbhc, false);
+ pr_debug("%s: leave\n", __func__);
+ /* unlock sleep */
+ wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+}
+
+static void wcd9xxx_swch_irq_handler(struct wcd9xxx_mbhc *mbhc)
+{
+ bool insert;
+ bool is_removed = false;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+
+ mbhc->in_swch_irq_handler = true;
+ /* Wait here for debounce time */
+ usleep_range(SWCH_IRQ_DEBOUNCE_TIME_US, SWCH_IRQ_DEBOUNCE_TIME_US);
+
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+
+ /* cancel pending button press */
+ if (wcd9xxx_cancel_btn_work(mbhc))
+ pr_debug("%s: button press is canceled\n", __func__);
+
+ insert = !wcd9xxx_swch_level_remove(mbhc);
+ pr_debug("%s: Current plug type %d, insert %d\n", __func__,
+ mbhc->current_plug, insert);
+ if ((mbhc->current_plug == PLUG_TYPE_NONE) && insert) {
+ mbhc->lpi_enabled = false;
+ wmb();
+
+ /* cancel detect plug */
+ wcd9xxx_cancel_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
+
+ /* Disable Mic Bias pull down and HPH Switch to GND */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+ 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x00);
+ wcd9xxx_mbhc_detect_plug_type(mbhc);
+ } else if ((mbhc->current_plug != PLUG_TYPE_NONE) && !insert) {
+ mbhc->lpi_enabled = false;
+ wmb();
+
+ /* cancel detect plug */
+ wcd9xxx_cancel_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
+
+ if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
+ is_removed = true;
+ } else if (mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
+ is_removed = true;
+ } else if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
+ wcd9xxx_pause_hs_polling(mbhc);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
+ is_removed = true;
+ }
+
+ if (is_removed) {
+ /* Enable Mic Bias pull down and HPH Switch to GND */
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+ 0x01);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01,
+ 0x01);
+ /* Make sure mic trigger is turned off */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x90, 0x00);
+ /* Reset MBHC State Machine */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+ 0x08, 0x08);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+ 0x08, 0x00);
+ /* Turn off override */
+ wcd9xxx_turn_onoff_override(codec, false);
+ }
+ }
+
+ mbhc->in_swch_irq_handler = false;
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static irqreturn_t wcd9xxx_mech_plug_detect_irq(int irq, void *data)
+{
+ int r = IRQ_HANDLED;
+ struct wcd9xxx_mbhc *mbhc = data;
+
+ pr_debug("%s: enter\n", __func__);
+ if (unlikely(wcd9xxx_lock_sleep(mbhc->resmgr->core) == false)) {
+ pr_warn("%s: failed to hold suspend\n", __func__);
+ r = IRQ_NONE;
+ } else {
+ /* Call handler */
+ wcd9xxx_swch_irq_handler(mbhc);
+ wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+ }
+
+ pr_debug("%s: leave %d\n", __func__, r);
+ return r;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_codec_drive_v_to_micbias(struct wcd9xxx_mbhc *mbhc,
+ int usec)
+{
+ int cfilt_k_val;
+ bool set = true;
+
+ if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
+ mbhc->mbhc_micbias_switched) {
+ pr_debug("%s: set mic V to micbias V\n", __func__);
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+ 0x2, 0x2);
+ wcd9xxx_turn_onoff_override(mbhc->codec, true);
+ while (1) {
+ cfilt_k_val =
+ wcd9xxx_resmgr_get_k_val(mbhc->resmgr,
+ set ? mbhc->mbhc_data.micb_mv :
+ VDDIO_MICBIAS_MV);
+ snd_soc_update_bits(mbhc->codec,
+ mbhc->mbhc_bias_regs.cfilt_val,
+ 0xFC, (cfilt_k_val << 2));
+ if (!set)
+ break;
+ usleep_range(usec, usec);
+ set = false;
+ }
+ wcd9xxx_turn_onoff_override(mbhc->codec, false);
+ }
+}
+
+static int wcd9xxx_is_fake_press(struct wcd9xxx_mbhc *mbhc)
+{
+ int i;
+ int r = 0;
+ const int dces = NUM_DCE_PLUG_DETECT;
+ s16 mb_v, v_ins_hu, v_ins_h;
+
+ v_ins_hu = wcd9xxx_get_current_v_ins(mbhc, true);
+ v_ins_h = wcd9xxx_get_current_v_ins(mbhc, false);
+
+ for (i = 0; i < dces; i++) {
+ usleep_range(10000, 10000);
+ if (i == 0) {
+ mb_v = wcd9xxx_codec_sta_dce(mbhc, 0, true);
+ pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, mb_v));
+ if (mb_v < (s16)mbhc->mbhc_data.v_b1_hu ||
+ mb_v > v_ins_hu) {
+ r = 1;
+ break;
+ }
+ } else {
+ mb_v = wcd9xxx_codec_sta_dce(mbhc, 1, true);
+ pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
+ wcd9xxx_codec_sta_dce_v(mbhc, 1, mb_v));
+ if (mb_v < (s16)mbhc->mbhc_data.v_b1_h ||
+ mb_v > v_ins_h) {
+ r = 1;
+ break;
+ }
+ }
+ }
+
+ return r;
+}
+
+/* called under codec_resource_lock acquisition */
+static int wcd9xxx_determine_button(const struct wcd9xxx_mbhc *mbhc,
+ const s32 micmv)
+{
+ s16 *v_btn_low, *v_btn_high;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ int i, btn = -1;
+
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+ v_btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+ MBHC_BTN_DET_V_BTN_LOW);
+ v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+ MBHC_BTN_DET_V_BTN_HIGH);
+
+ for (i = 0; i < btn_det->num_btn; i++) {
+ if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
+ btn = i;
+ break;
+ }
+ }
+
+ if (btn == -1)
+ pr_debug("%s: couldn't find button number for mic mv %d\n",
+ __func__, micmv);
+
+ return btn;
+}
+
+static int wcd9xxx_get_button_mask(const int btn)
+{
+ int mask = 0;
+ switch (btn) {
+ case 0:
+ mask = SND_JACK_BTN_0;
+ break;
+ case 1:
+ mask = SND_JACK_BTN_1;
+ break;
+ case 2:
+ mask = SND_JACK_BTN_2;
+ break;
+ case 3:
+ mask = SND_JACK_BTN_3;
+ break;
+ case 4:
+ mask = SND_JACK_BTN_4;
+ break;
+ case 5:
+ mask = SND_JACK_BTN_5;
+ break;
+ case 6:
+ mask = SND_JACK_BTN_6;
+ break;
+ case 7:
+ mask = SND_JACK_BTN_7;
+ break;
+ }
+ return mask;
+}
+
+irqreturn_t wcd9xxx_dce_handler(int irq, void *data)
+{
+ int i, mask;
+ short dce, sta;
+ s32 mv, mv_s, stamv_s;
+ bool vddio;
+ u8 mbhc_status;
+ int btn = -1, meas = 0;
+ struct wcd9xxx_mbhc *mbhc = data;
+ const struct wcd9xxx_mbhc_btn_detect_cfg *d =
+ WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+ short btnmeas[d->n_btn_meas + 1];
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct wcd9xxx *core = mbhc->resmgr->core;
+ int n_btn_meas = d->n_btn_meas;
+
+ pr_debug("%s: enter\n", __func__);
+
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ mbhc_status = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_STATUS) & 0x3E;
+
+ if (mbhc->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
+ pr_debug("%s: mbhc is being recovered, skip button press\n",
+ __func__);
+ goto done;
+ }
+
+ mbhc->mbhc_state = MBHC_STATE_POTENTIAL;
+
+ if (!mbhc->polling_active) {
+ pr_warn("%s: mbhc polling is not active, skip button press\n",
+ __func__);
+ goto done;
+ }
+
+ dce = wcd9xxx_read_dce_result(codec);
+ mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, dce);
+
+ /* If switch nterrupt already kicked in, ignore button press */
+ if (mbhc->in_swch_irq_handler) {
+ pr_debug("%s: Swtich level changed, ignore button press\n",
+ __func__);
+ btn = -1;
+ goto done;
+ }
+
+ /* Measure scaled HW DCE */
+ vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
+ mbhc->mbhc_micbias_switched);
+ mv_s = vddio ? scale_v_micb_vddio(mbhc, mv, false) : mv;
+
+ /* Measure scaled HW STA */
+ sta = wcd9xxx_read_sta_result(codec);
+ stamv_s = wcd9xxx_codec_sta_dce_v(mbhc, 0, sta);
+ if (vddio)
+ stamv_s = scale_v_micb_vddio(mbhc, stamv_s, false);
+ if (mbhc_status != STATUS_REL_DETECTION) {
+ if (mbhc->mbhc_last_resume &&
+ !time_after(jiffies, mbhc->mbhc_last_resume + HZ)) {
+ pr_debug("%s: Button is released after resume\n",
+ __func__);
+ n_btn_meas = 0;
+ } else {
+ pr_debug("%s: Button is released without resume",
+ __func__);
+ btn = wcd9xxx_determine_button(mbhc, mv_s);
+ if (btn != wcd9xxx_determine_button(mbhc, stamv_s))
+ btn = -1;
+ goto done;
+ }
+ }
+
+ pr_debug("%s: Meas HW - STA 0x%x,%d,%d\n", __func__,
+ sta & 0xFFFF, wcd9xxx_codec_sta_dce_v(mbhc, 0, sta), stamv_s);
+
+ /* determine pressed button */
+ btnmeas[meas++] = wcd9xxx_determine_button(mbhc, mv_s);
+ pr_debug("%s: Meas HW - DCE 0x%x,%d,%d button %d\n", __func__,
+ dce & 0xFFFF, mv, mv_s, btnmeas[meas - 1]);
+ if (n_btn_meas == 0)
+ btn = btnmeas[0];
+ for (; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1))); meas++) {
+ dce = wcd9xxx_codec_sta_dce(mbhc, 1, false);
+ mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, dce);
+ mv_s = vddio ? scale_v_micb_vddio(mbhc, mv, false) : mv;
+
+ btnmeas[meas] = wcd9xxx_determine_button(mbhc, mv_s);
+ pr_debug("%s: Meas %d - DCE 0x%x,%d,%d button %d\n",
+ __func__, meas, dce & 0xFFFF, mv, mv_s, btnmeas[meas]);
+ /*
+ * if large enough measurements are collected,
+ * start to check if last all n_btn_con measurements were
+ * in same button low/high range
+ */
+ if (meas + 1 >= d->n_btn_con) {
+ for (i = 0; i < d->n_btn_con; i++)
+ if ((btnmeas[meas] < 0) ||
+ (btnmeas[meas] != btnmeas[meas - i]))
+ break;
+ if (i == d->n_btn_con) {
+ /* button pressed */
+ btn = btnmeas[meas];
+ break;
+ } else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
+ /*
+ * if left measurements are less than n_btn_con,
+ * it's impossible to find button number
+ */
+ break;
+ }
+ }
+ }
+
+ if (btn >= 0) {
+ if (mbhc->in_swch_irq_handler) {
+ pr_debug(
+ "%s: Switch irq triggered, ignore button press\n",
+ __func__);
+ goto done;
+ }
+ mask = wcd9xxx_get_button_mask(btn);
+ mbhc->buttons_pressed |= mask;
+ wcd9xxx_lock_sleep(core);
+ if (schedule_delayed_work(&mbhc->mbhc_btn_dwork,
+ msecs_to_jiffies(400)) == 0) {
+ WARN(1, "Button pressed twice without release event\n");
+ wcd9xxx_unlock_sleep(core);
+ }
+ } else {
+ pr_debug("%s: bogus button press, too short press?\n",
+ __func__);
+ }
+
+ done:
+ pr_debug("%s: leave\n", __func__);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_release_handler(int irq, void *data)
+{
+ int ret;
+ struct wcd9xxx_mbhc *mbhc = data;
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ mbhc->mbhc_state = MBHC_STATE_RELEASE;
+
+ wcd9xxx_codec_drive_v_to_micbias(mbhc, 10000);
+
+ if (mbhc->buttons_pressed & WCD9XXX_JACK_BUTTON_MASK) {
+ ret = wcd9xxx_cancel_btn_work(mbhc);
+ if (ret == 0) {
+ pr_debug("%s: Reporting long button release event\n",
+ __func__);
+ wcd9xxx_jack_report(&mbhc->button_jack, 0,
+ mbhc->buttons_pressed);
+ } else {
+ if (wcd9xxx_is_fake_press(mbhc)) {
+ pr_debug("%s: Fake button press interrupt\n",
+ __func__);
+ } else {
+ if (mbhc->in_swch_irq_handler) {
+ pr_debug("%s: Switch irq kicked in, ignore\n",
+ __func__);
+ } else {
+ pr_debug("%s: Reporting btn press\n",
+ __func__);
+ wcd9xxx_jack_report(&mbhc->button_jack,
+ mbhc->buttons_pressed,
+ mbhc->buttons_pressed);
+ pr_debug("%s: Reporting btn release\n",
+ __func__);
+ wcd9xxx_jack_report(&mbhc->button_jack,
+ 0, mbhc->buttons_pressed);
+ }
+ }
+ }
+
+ mbhc->buttons_pressed &= ~WCD9XXX_JACK_BUTTON_MASK;
+ }
+
+ wcd9xxx_calibrate_hs_polling(mbhc);
+
+ msleep(SWCH_REL_DEBOUNCE_TIME_MS);
+ wcd9xxx_start_hs_polling(mbhc);
+
+ pr_debug("%s: leave\n", __func__);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hphl_ocp_irq(int irq, void *data)
+{
+ struct wcd9xxx_mbhc *mbhc = data;
+ struct snd_soc_codec *codec;
+
+ pr_info("%s: received HPHL OCP irq\n", __func__);
+
+ if (mbhc) {
+ codec = mbhc->codec;
+ if (mbhc->hphlocp_cnt++ < OCP_ATTEMPT) {
+ pr_info("%s: retry\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+ 0x10, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+ 0x10, 0x10);
+ } else {
+ wcd9xxx_disable_irq(codec->control_data,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+ mbhc->hphlocp_cnt = 0;
+ mbhc->hph_status |= SND_JACK_OC_HPHL;
+ wcd9xxx_jack_report(&mbhc->headset_jack,
+ mbhc->hph_status,
+ WCD9XXX_JACK_MASK);
+ }
+ } else {
+ pr_err("%s: Bad wcd9xxx private data\n", __func__);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hphr_ocp_irq(int irq, void *data)
+{
+ struct wcd9xxx_mbhc *mbhc = data;
+ struct snd_soc_codec *codec;
+
+ pr_info("%s: received HPHR OCP irq\n", __func__);
+ codec = mbhc->codec;
+ if (mbhc->hphrocp_cnt++ < OCP_ATTEMPT) {
+ pr_info("%s: retry\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+ 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+ 0x10);
+ } else {
+ wcd9xxx_disable_irq(mbhc->resmgr->core,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+ mbhc->hphrocp_cnt = 0;
+ mbhc->hph_status |= SND_JACK_OC_HPHR;
+ wcd9xxx_jack_report(&mbhc->headset_jack,
+ mbhc->hph_status, WCD9XXX_JACK_MASK);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int wcd9xxx_acdb_mclk_index(const int rate)
+{
+ if (rate == MCLK_RATE_12288KHZ)
+ return 0;
+ else if (rate == MCLK_RATE_9600KHZ)
+ return 1;
+ else {
+ BUG_ON(1);
+ return -EINVAL;
+ }
+}
+
+static void wcd9xxx_update_mbhc_clk_rate(struct wcd9xxx_mbhc *mbhc, u32 rate)
+{
+ u32 dce_wait, sta_wait;
+ u8 ncic, nmeas, navg;
+ void *calibration;
+ u8 *n_cic, *n_ready;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ u8 npoll = 4, nbounce_wait = 30;
+ struct snd_soc_codec *codec = mbhc->codec;
+ int idx = wcd9xxx_acdb_mclk_index(rate);
+ int idxmclk = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
+
+ pr_debug("%s: Updating clock rate dependents, rate = %u\n", __func__,
+ rate);
+ calibration = mbhc->mbhc_cfg->calibration;
+
+ /*
+ * First compute the DCE / STA wait times depending on tunable
+ * parameters. The value is computed in microseconds
+ */
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
+ n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_READY);
+ n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_CIC);
+ nmeas = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
+ navg = WCD9XXX_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
+
+ /* ncic stays with the same what we had during calibration */
+ ncic = n_cic[idxmclk];
+ dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (rate / 1000);
+ sta_wait = (1000 * 128 * (navg + 1)) / (rate / 1000);
+ mbhc->mbhc_data.t_dce = dce_wait;
+ mbhc->mbhc_data.t_sta = sta_wait;
+ mbhc->mbhc_data.t_sta_dce = ((1000 * 256) / (rate / 1000) *
+ n_ready[idx]) + 10;
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL, n_ready[idx]);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL, ncic);
+
+ if (rate == MCLK_RATE_12288KHZ) {
+ npoll = 4;
+ nbounce_wait = 30;
+ } else if (rate == MCLK_RATE_9600KHZ) {
+ npoll = 3;
+ nbounce_wait = 23;
+ }
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL, npoll);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL, nbounce_wait);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_mbhc_cal(struct wcd9xxx_mbhc *mbhc)
+{
+ u8 cfilt_mode, bg_mode;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_turn_onoff_rel_detection(codec, false);
+
+ /* t_dce and t_sta are updated by wcd9xxx_update_mbhc_clk_rate() */
+ WARN_ON(!mbhc->mbhc_data.t_dce);
+ WARN_ON(!mbhc->mbhc_data.t_sta);
+
+ /*
+ * LDOH and CFILT are already configured during pdata handling.
+ * Only need to make sure CFILT and bandgap are in Fast mode.
+ * Need to restore defaults once calculation is done.
+ */
+ cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40, 0x00);
+ bg_mode = snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL,
+ 0x02, 0x02);
+
+ /*
+ * Micbias, CFILT, LDOH, MBHC MUX mode settings
+ * to perform ADC calibration
+ */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60,
+ mbhc->mbhc_cfg->micbias << 5);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60);
+ snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x04);
+
+ /* DCE measurement for 0 volts */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x81);
+ usleep_range(100, 100);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
+ usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
+ mbhc->mbhc_data.dce_z = wcd9xxx_read_dce_result(codec);
+
+ /* DCE measurment for MB voltage */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x82);
+ usleep_range(100, 100);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
+ usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
+ mbhc->mbhc_data.dce_mb = wcd9xxx_read_dce_result(codec);
+
+ /* STA measuremnt for 0 volts */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x81);
+ usleep_range(100, 100);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
+ usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
+ mbhc->mbhc_data.sta_z = wcd9xxx_read_sta_result(codec);
+
+ /* STA Measurement for MB Voltage */
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x82);
+ usleep_range(100, 100);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
+ usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
+ mbhc->mbhc_data.sta_mb = wcd9xxx_read_sta_result(codec);
+
+ /* Restore default settings. */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40,
+ cfilt_mode);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x02,
+ bg_mode);
+
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x84);
+ usleep_range(100, 100);
+
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_turn_onoff_rel_detection(codec, true);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc)
+{
+ int n;
+ u8 *gain;
+ struct wcd9xxx_mbhc_general_cfg *generic;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ struct snd_soc_codec *codec = mbhc->codec;
+ const int idx = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
+
+ pr_debug("%s: enter\n", __func__);
+ generic = WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+ for (n = 0; n < 8; n++) {
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_FIR_B1_CFG,
+ 0x07, n);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_FIR_B2_CFG,
+ btn_det->c[n]);
+ }
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x07,
+ btn_det->nc);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
+ generic->mbhc_nsa << 4);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
+ btn_det->n_meas);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL,
+ generic->mbhc_navg);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
+ btn_det->mbhc_nsc << 3);
+
+ snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc, 0x03,
+ MBHC_MICBIAS2);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
+
+ gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_GAIN);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x78,
+ gain[idx] << 3);
+
+ pr_debug("%s: leave\n", __func__);
+}
+
+static int wcd9xxx_setup_jack_detect_irq(struct wcd9xxx_mbhc *mbhc)
+{
+ int ret = 0;
+ void *core = mbhc->resmgr->core;
+
+ if (mbhc->mbhc_cfg->gpio) {
+ ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL,
+ wcd9xxx_mech_plug_detect_irq,
+ (IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_DISABLED),
+ "headset detect", mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request gpio irq %d\n", __func__,
+ mbhc->mbhc_cfg->gpio_irq);
+ } else {
+ ret = enable_irq_wake(mbhc->mbhc_cfg->gpio_irq);
+ if (ret)
+ pr_err("%s: Failed to enable wake up irq %d\n",
+ __func__, mbhc->mbhc_cfg->gpio_irq);
+ }
+ } else if (mbhc->mbhc_cfg->insert_detect) {
+ /* Enable HPHL_10K_SW */
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+ 1 << 1, 1 << 1);
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_JACK_SWITCH,
+ wcd9xxx_mech_plug_detect_irq,
+ "Jack Detect",
+ mbhc);
+ if (ret)
+ pr_err("%s: Failed to request insert detect irq %d\n",
+ __func__, WCD9XXX_IRQ_MBHC_JACK_SWITCH);
+ }
+
+ return ret;
+}
+
+static int wcd9xxx_init_and_calibrate(struct wcd9xxx_mbhc *mbhc)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+
+ /* Enable MCLK during calibration */
+ wcd9xxx_onoff_ext_mclk(mbhc, true);
+ wcd9xxx_mbhc_setup(mbhc);
+ wcd9xxx_mbhc_cal(mbhc);
+ wcd9xxx_mbhc_calc_thres(mbhc);
+ wcd9xxx_onoff_ext_mclk(mbhc, false);
+ wcd9xxx_calibrate_hs_polling(mbhc);
+
+ /* Enable Mic Bias pull down and HPH Switch to GND */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x01);
+ INIT_WORK(&mbhc->correct_plug_swch, wcd9xxx_correct_swch_plug);
+
+ if (!IS_ERR_VALUE(ret)) {
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+ 0x10);
+ wcd9xxx_enable_irq(codec->control_data,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+ wcd9xxx_enable_irq(codec->control_data,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+
+ /* Initialize mechanical mbhc */
+ ret = wcd9xxx_setup_jack_detect_irq(mbhc);
+
+ if (!ret && mbhc->mbhc_cfg->gpio) {
+ /* Requested with IRQF_DISABLED */
+ enable_irq(mbhc->mbhc_cfg->gpio_irq);
+
+ /* Bootup time detection */
+ wcd9xxx_swch_irq_handler(mbhc);
+ } else if (!ret && mbhc->mbhc_cfg->insert_detect) {
+ pr_debug("%s: Setting up codec own insert detection\n",
+ __func__);
+ /* Setup for insertion detection */
+ wcd9xxx_insert_detect_setup(mbhc, true);
+ }
+ }
+
+ pr_debug("%s: leave\n", __func__);
+
+ return ret;
+}
+
+static void wcd9xxx_mbhc_fw_read(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ struct wcd9xxx_mbhc *mbhc;
+ struct snd_soc_codec *codec;
+ const struct firmware *fw;
+ int ret = -1, retry = 0;
+
+ dwork = to_delayed_work(work);
+ mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_firmware_dwork);
+ codec = mbhc->codec;
+
+ while (retry < FW_READ_ATTEMPTS) {
+ retry++;
+ pr_info("%s:Attempt %d to request MBHC firmware\n",
+ __func__, retry);
+ ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
+ codec->dev);
+
+ if (ret != 0) {
+ usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT);
+ } else {
+ pr_info("%s: MBHC Firmware read succesful\n", __func__);
+ break;
+ }
+ }
+
+ if (ret != 0) {
+ pr_err("%s: Cannot load MBHC firmware use default cal\n",
+ __func__);
+ } else if (wcd9xxx_mbhc_fw_validate(fw) == false) {
+ pr_err("%s: Invalid MBHC cal data size use default cal\n",
+ __func__);
+ release_firmware(fw);
+ } else {
+ mbhc->mbhc_cfg->calibration = (void *)fw->data;
+ mbhc->mbhc_fw = fw;
+ }
+
+ (void) wcd9xxx_init_and_calibrate(mbhc);
+}
+
+#ifdef CONFIG_DEBUG_FS
+ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ const int size = 768;
+ char buffer[size];
+ int n = 0;
+ struct wcd9xxx_mbhc *mbhc = file->private_data;
+ const struct mbhc_internal_cal_data *p = &mbhc->mbhc_data;
+ const s16 v_ins_hu_cur = wcd9xxx_get_current_v_ins(mbhc, true);
+ const s16 v_ins_h_cur = wcd9xxx_get_current_v_ins(mbhc, false);
+
+ n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n", p->dce_z,
+ wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_z));
+ n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
+ p->dce_mb, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_mb));
+ n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
+ p->sta_z, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_z));
+ n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
+ p->sta_mb, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_mb));
+ n += scnprintf(buffer + n, size - n, "t_dce = %x\n", p->t_dce);
+ n += scnprintf(buffer + n, size - n, "t_sta = %x\n", p->t_sta);
+ n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n",
+ p->micb_mv);
+ n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)%s\n",
+ p->v_ins_hu,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_ins_hu),
+ p->v_ins_hu == v_ins_hu_cur ? "*" : "");
+ n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)%s\n",
+ p->v_ins_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_ins_h),
+ p->v_ins_h == v_ins_h_cur ? "*" : "");
+ n += scnprintf(buffer + n, size - n, "adj_v_ins_hu = %x(%dmv)%s\n",
+ p->adj_v_ins_hu,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, p->adj_v_ins_hu),
+ p->adj_v_ins_hu == v_ins_hu_cur ? "*" : "");
+ n += scnprintf(buffer + n, size - n, "adj_v_ins_h = %x(%dmv)%s\n",
+ p->adj_v_ins_h,
+ wcd9xxx_codec_sta_dce_v(mbhc, 1, p->adj_v_ins_h),
+ p->adj_v_ins_h == v_ins_h_cur ? "*" : "");
+ n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
+ p->v_b1_hu,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_b1_hu));
+ n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
+ p->v_b1_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_b1_h));
+ n += scnprintf(buffer + n, size - n, "v_b1_huc = %x(%dmv)\n",
+ p->v_b1_huc,
+ wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_b1_huc));
+ n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
+ p->v_brh, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_brh));
+ n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n", p->v_brl,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_brl));
+ n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
+ p->v_no_mic,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_no_mic));
+ n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
+ p->v_inval_ins_low);
+ n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
+ p->v_inval_ins_high);
+ n += scnprintf(buffer + n, size - n, "Insert detect insert = %d\n",
+ !wcd9xxx_swch_level_remove(mbhc));
+ buffer[n] = 0;
+
+ return simple_read_from_buffer(buf, count, pos, buffer, n);
+}
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t codec_debug_write(struct file *filp,
+ const char __user *ubuf, size_t cnt,
+ loff_t *ppos)
+{
+ char lbuf[32];
+ char *buf;
+ int rc;
+ struct wcd9xxx_mbhc *mbhc = filp->private_data;
+
+ if (cnt > sizeof(lbuf) - 1)
+ return -EINVAL;
+
+ rc = copy_from_user(lbuf, ubuf, cnt);
+ if (rc)
+ return -EFAULT;
+
+ lbuf[cnt] = '\0';
+ buf = (char *)lbuf;
+ mbhc->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
+ false : true;
+ return rc;
+}
+
+static const struct file_operations mbhc_trrs_debug_ops = {
+ .open = codec_debug_open,
+ .write = codec_debug_write,
+};
+
+static const struct file_operations mbhc_debug_ops = {
+ .open = codec_debug_open,
+ .read = codec_mbhc_debug_read,
+};
+
+static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+ mbhc->debugfs_poke =
+ debugfs_create_file("TRRS", S_IFREG | S_IRUGO, NULL, mbhc,
+ &mbhc_trrs_debug_ops);
+ mbhc->debugfs_mbhc =
+ debugfs_create_file("wcd9xxx_mbhc", S_IFREG | S_IRUGO,
+ NULL, mbhc, &mbhc_debug_ops);
+}
+
+static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+ debugfs_remove(mbhc->debugfs_poke);
+ debugfs_remove(mbhc->debugfs_mbhc);
+}
+#else
+static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+}
+
+static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+}
+#endif
+
+int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
+ struct wcd9xxx_mbhc_config *mbhc_cfg)
+{
+ int rc = 0;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+
+ if (!codec) {
+ pr_err("%s: no codec\n", __func__);
+ return -EINVAL;
+ }
+
+ if (mbhc_cfg->mclk_rate != MCLK_RATE_12288KHZ &&
+ mbhc_cfg->mclk_rate != MCLK_RATE_9600KHZ) {
+ pr_err("Error: unsupported clock rate %d\n",
+ mbhc_cfg->mclk_rate);
+ return -EINVAL;
+ }
+
+ /* Save mbhc config */
+ mbhc->mbhc_cfg = mbhc_cfg;
+
+ /* Get HW specific mbhc registers' address */
+ wcd9xxx_get_mbhc_micbias_regs(mbhc, &mbhc->mbhc_bias_regs);
+
+ /* Put CFILT in fast mode by default */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x40, WCD9XXX_CFILT_FAST_MODE);
+
+ if (!mbhc->mbhc_cfg->read_fw_bin)
+ rc = wcd9xxx_init_and_calibrate(mbhc);
+ else
+ schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
+ usecs_to_jiffies(FW_READ_TIMEOUT));
+
+ pr_debug("%s: leave %d\n", __func__, rc);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_start);
+
+static enum wcd9xxx_micbias_num
+wcd9xxx_event_to_micbias(const enum wcd9xxx_notify_event event)
+{
+ enum wcd9xxx_micbias_num ret;
+ switch (event) {
+ case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
+ ret = MBHC_MICBIAS1;
+ case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
+ ret = MBHC_MICBIAS2;
+ case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
+ ret = MBHC_MICBIAS3;
+ case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
+ ret = MBHC_MICBIAS4;
+ default:
+ ret = MBHC_MICBIAS_INVALID;
+ }
+ return ret;
+}
+
+static int wcd9xxx_event_to_cfilt(const enum wcd9xxx_notify_event event)
+{
+ int ret;
+ switch (event) {
+ case WCD9XXX_EVENT_PRE_CFILT_1_OFF:
+ case WCD9XXX_EVENT_POST_CFILT_1_OFF:
+ case WCD9XXX_EVENT_PRE_CFILT_1_ON:
+ case WCD9XXX_EVENT_POST_CFILT_1_ON:
+ ret = WCD9XXX_CFILT1_SEL;
+ break;
+ case WCD9XXX_EVENT_PRE_CFILT_2_OFF:
+ case WCD9XXX_EVENT_POST_CFILT_2_OFF:
+ case WCD9XXX_EVENT_PRE_CFILT_2_ON:
+ case WCD9XXX_EVENT_POST_CFILT_2_ON:
+ ret = WCD9XXX_CFILT2_SEL;
+ break;
+ case WCD9XXX_EVENT_PRE_CFILT_3_OFF:
+ case WCD9XXX_EVENT_POST_CFILT_3_OFF:
+ case WCD9XXX_EVENT_PRE_CFILT_3_ON:
+ case WCD9XXX_EVENT_POST_CFILT_3_ON:
+ ret = WCD9XXX_CFILT3_SEL;
+ break;
+ default:
+ ret = -1;
+ }
+ return ret;
+}
+
+static int wcd9xxx_get_mbhc_cfilt_sel(struct wcd9xxx_mbhc *mbhc)
+{
+ int cfilt;
+ const struct wcd9xxx_pdata *pdata = mbhc->resmgr->pdata;
+
+ switch (mbhc->mbhc_cfg->micbias) {
+ case MBHC_MICBIAS1:
+ cfilt = pdata->micbias.bias1_cfilt_sel;
+ break;
+ case MBHC_MICBIAS2:
+ cfilt = pdata->micbias.bias2_cfilt_sel;
+ break;
+ case MBHC_MICBIAS3:
+ cfilt = pdata->micbias.bias3_cfilt_sel;
+ break;
+ case MBHC_MICBIAS4:
+ cfilt = pdata->micbias.bias4_cfilt_sel;
+ break;
+ default:
+ cfilt = MBHC_MICBIAS_INVALID;
+ break;
+ }
+ return cfilt;
+}
+
+static int wcd9xxx_event_notify(struct notifier_block *self, unsigned long val,
+ void *data)
+{
+ int ret = 0;
+ struct wcd9xxx_mbhc *mbhc = ((struct wcd9xxx_resmgr *)data)->mbhc;
+ struct snd_soc_codec *codec = mbhc->codec;
+ enum wcd9xxx_notify_event event = (enum wcd9xxx_notify_event)val;
+
+ pr_debug("%s: enter event %s(%d)\n", __func__,
+ wcd9xxx_get_event_string(event), event);
+
+ switch (event) {
+ /* MICBIAS usage change */
+ case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
+ case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
+ case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
+ case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
+ if (mbhc->mbhc_cfg->micbias == wcd9xxx_event_to_micbias(event))
+ wcd9xxx_switch_micbias(mbhc, 0);
+ break;
+ case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
+ case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
+ case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
+ case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
+ if (mbhc->mbhc_cfg->micbias ==
+ wcd9xxx_event_to_micbias(event) &&
+ wcd9xxx_mbhc_polling(mbhc)) {
+ /* if polling is on, restart it */
+ wcd9xxx_pause_hs_polling(mbhc);
+ wcd9xxx_start_hs_polling(mbhc);
+ }
+ break;
+ case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
+ case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
+ case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
+ case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
+ if (mbhc->mbhc_cfg->micbias ==
+ wcd9xxx_event_to_micbias(event) &&
+ wcd9xxx_is_hph_pa_on(codec))
+ wcd9xxx_switch_micbias(mbhc, 1);
+ break;
+ /* PA usage change */
+ case WCD9XXX_EVENT_PRE_HPHL_PA_ON:
+ if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg & 0x80)))
+ /* if micbias is enabled, switch to vddio */
+ wcd9xxx_switch_micbias(mbhc, 1);
+ break;
+ case WCD9XXX_EVENT_PRE_HPHR_PA_ON:
+ /* Not used now */
+ break;
+ case WCD9XXX_EVENT_POST_HPHL_PA_OFF:
+ /* if HPH PAs are off, report OCP and switch back to CFILT */
+ clear_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+ if (mbhc->hph_status & SND_JACK_OC_HPHL)
+ hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
+ wcd9xxx_switch_micbias(mbhc, 0);
+ break;
+ case WCD9XXX_EVENT_POST_HPHR_PA_OFF:
+ /* if HPH PAs are off, report OCP and switch back to CFILT */
+ clear_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+ if (mbhc->hph_status & SND_JACK_OC_HPHR)
+ hphrocp_off_report(mbhc, SND_JACK_OC_HPHL);
+ wcd9xxx_switch_micbias(mbhc, 0);
+ break;
+ /* Clock usage change */
+ case WCD9XXX_EVENT_PRE_MCLK_ON:
+ break;
+ case WCD9XXX_EVENT_POST_MCLK_ON:
+ /* Change to lower TxAAF frequency */
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
+ 1 << 4);
+ /* Re-calibrate clock rate dependent values */
+ wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->mbhc_cfg->mclk_rate);
+ /* If clock source changes, stop and restart polling */
+ if (wcd9xxx_mbhc_polling(mbhc)) {
+ wcd9xxx_calibrate_hs_polling(mbhc);
+ wcd9xxx_start_hs_polling(mbhc);
+ }
+ break;
+ case WCD9XXX_EVENT_PRE_MCLK_OFF:
+ /* If clock source changes, stop and restart polling */
+ if (wcd9xxx_mbhc_polling(mbhc))
+ wcd9xxx_pause_hs_polling(mbhc);
+ break;
+ case WCD9XXX_EVENT_POST_MCLK_OFF:
+ break;
+ case WCD9XXX_EVENT_PRE_RCO_ON:
+ break;
+ case WCD9XXX_EVENT_POST_RCO_ON:
+ /* Change to higher TxAAF frequency */
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
+ 0 << 4);
+ /* Re-calibrate clock rate dependent values */
+ wcd9xxx_update_mbhc_clk_rate(mbhc, WCD9XXX_RCO_CLK_RATE);
+ /* If clock source changes, stop and restart polling */
+ if (wcd9xxx_mbhc_polling(mbhc)) {
+ wcd9xxx_calibrate_hs_polling(mbhc);
+ wcd9xxx_start_hs_polling(mbhc);
+ }
+ break;
+ case WCD9XXX_EVENT_PRE_RCO_OFF:
+ /* If clock source changes, stop and restart polling */
+ if (wcd9xxx_mbhc_polling(mbhc))
+ wcd9xxx_pause_hs_polling(mbhc);
+ break;
+ case WCD9XXX_EVENT_POST_RCO_OFF:
+ break;
+ /* CFILT usage change */
+ case WCD9XXX_EVENT_PRE_CFILT_1_ON:
+ case WCD9XXX_EVENT_PRE_CFILT_2_ON:
+ case WCD9XXX_EVENT_PRE_CFILT_3_ON:
+ if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
+ wcd9xxx_event_to_cfilt(event))
+ /*
+ * Switch CFILT to slow mode if MBHC CFILT is being
+ * used.
+ */
+ wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
+ break;
+ case WCD9XXX_EVENT_POST_CFILT_1_OFF:
+ case WCD9XXX_EVENT_POST_CFILT_2_OFF:
+ case WCD9XXX_EVENT_POST_CFILT_3_OFF:
+ if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
+ wcd9xxx_event_to_cfilt(event))
+ /*
+ * Switch CFILT to fast mode if MBHC CFILT is not
+ * used anymore.
+ */
+ wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
+ break;
+ /* System resume */
+ case WCD9XXX_EVENT_POST_RESUME:
+ mbhc->mbhc_last_resume = jiffies;
+ break;
+ /* BG mode chage */
+ case WCD9XXX_EVENT_PRE_BG_OFF:
+ case WCD9XXX_EVENT_POST_BG_OFF:
+ case WCD9XXX_EVENT_PRE_BG_AUDIO_ON:
+ case WCD9XXX_EVENT_POST_BG_AUDIO_ON:
+ case WCD9XXX_EVENT_PRE_BG_MBHC_ON:
+ case WCD9XXX_EVENT_POST_BG_MBHC_ON:
+ /* Not used for now */
+ break;
+ default:
+ WARN(1, "Unknown event %d\n", event);
+ ret = -EINVAL;
+ }
+
+ pr_debug("%s: leave\n", __func__);
+
+ return 0;
+}
+
+/*
+ * wcd9xxx_mbhc_init : initialize MBHC internal structures.
+ *
+ * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
+ */
+int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
+ struct snd_soc_codec *codec)
+{
+ int ret;
+ void *core;
+
+ pr_debug("%s: enter\n", __func__);
+ memset(&mbhc->mbhc_bias_regs, 0, sizeof(struct mbhc_micbias_regs));
+ memset(&mbhc->mbhc_data, 0, sizeof(struct mbhc_internal_cal_data));
+
+ mbhc->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
+ mbhc->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
+ mbhc->mbhc_data.t_sta = DEFAULT_STA_WAIT;
+ mbhc->mbhc_micbias_switched = false;
+ mbhc->polling_active = false;
+ mbhc->mbhc_state = MBHC_STATE_NONE;
+ mbhc->in_swch_irq_handler = false;
+ mbhc->current_plug = PLUG_TYPE_NONE;
+ mbhc->lpi_enabled = false;
+ mbhc->no_mic_headset_override = false;
+ mbhc->mbhc_last_resume = 0;
+ mbhc->codec = codec;
+ mbhc->resmgr = resmgr;
+ mbhc->resmgr->mbhc = mbhc;
+
+ ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
+ &mbhc->headset_jack);
+ if (ret) {
+ pr_err("%s: Failed to create new jack\n", __func__);
+ return ret;
+ }
+
+ ret = snd_soc_jack_new(codec, "Button Jack", WCD9XXX_JACK_BUTTON_MASK,
+ &mbhc->button_jack);
+ if (ret) {
+ pr_err("Failed to create new jack\n");
+ return ret;
+ }
+
+ mbhc->mbhc_cfg = kzalloc(sizeof(*mbhc->mbhc_cfg), GFP_KERNEL);
+ if (!mbhc->mbhc_cfg)
+ return -ENOMEM;
+
+ INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork, wcd9xxx_mbhc_fw_read);
+ INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd9xxx_btn_lpress_fn);
+ INIT_DELAYED_WORK(&mbhc->mbhc_insert_dwork, wcd9xxx_mbhc_insert_work);
+
+ /* Register event notifier */
+ mbhc->nblock.notifier_call = wcd9xxx_event_notify;
+ ret = wcd9xxx_resmgr_register_notifier(mbhc->resmgr, &mbhc->nblock);
+ if (ret) {
+ pr_err("%s: Failed to register notifier %d\n", __func__, ret);
+ return ret;
+ }
+
+ wcd9xxx_init_debugfs(mbhc);
+
+ core = mbhc->resmgr->core;
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_INSERTION,
+ wcd9xxx_hs_insert_irq,
+ "Headset insert detect", mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_MBHC_INSERTION);
+ goto err_insert_irq;
+ }
+ wcd9xxx_disable_irq(core, WCD9XXX_IRQ_MBHC_INSERTION);
+
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_REMOVAL,
+ wcd9xxx_hs_remove_irq,
+ "Headset remove detect", mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_MBHC_REMOVAL);
+ goto err_remove_irq;
+ }
+
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_POTENTIAL,
+ wcd9xxx_dce_handler, "DC Estimation detect",
+ mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_MBHC_POTENTIAL);
+ goto err_potential_irq;
+ }
+
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_RELEASE,
+ wcd9xxx_release_handler,
+ "Button Release detect", mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_MBHC_RELEASE);
+ goto err_release_irq;
+ }
+
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+ wcd9xxx_hphl_ocp_irq, "HPH_L OCP detect",
+ mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+ goto err_hphl_ocp_irq;
+ }
+ wcd9xxx_disable_irq(core, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+ wcd9xxx_hphr_ocp_irq, "HPH_R OCP detect",
+ mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+ goto err_hphr_ocp_irq;
+ }
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+
+ pr_debug("%s: leave ret %d\n", __func__, ret);
+ return ret;
+
+err_hphr_ocp_irq:
+ wcd9xxx_free_irq(core, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, mbhc);
+err_hphl_ocp_irq:
+ wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_RELEASE, mbhc);
+err_release_irq:
+ wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_POTENTIAL, mbhc);
+err_potential_irq:
+ wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_REMOVAL, mbhc);
+err_remove_irq:
+ wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_INSERTION, mbhc);
+err_insert_irq:
+ wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
+
+ pr_debug("%s: leave ret %d\n", __func__, ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_init);
+
+void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc)
+{
+ void *cdata = mbhc->codec->control_data;
+
+ wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_SLIMBUS, mbhc);
+ wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_RELEASE, mbhc);
+ wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_POTENTIAL, mbhc);
+ wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_REMOVAL, mbhc);
+ wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_INSERTION, mbhc);
+
+ if (mbhc->mbhc_fw)
+ release_firmware(mbhc->mbhc_fw);
+
+ wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
+
+ wcd9xxx_cleanup_debugfs(mbhc);
+
+ kfree(mbhc->mbhc_cfg);
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_deinit);
+
+MODULE_DESCRIPTION("wcd9xxx MBHC module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.h b/sound/soc/codecs/wcd9xxx-mbhc.h
new file mode 100644
index 0000000..0934b5e
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-mbhc.h
@@ -0,0 +1,314 @@
+/* Copyright (c) 2012, 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 __WCD9XXX_MBHC_H__
+#define __WCD9XXX_MBHC_H__
+
+#include "wcd9xxx-resmgr.h"
+
+#define WCD9XXX_CFILT_FAST_MODE 0x00
+#define WCD9XXX_CFILT_SLOW_MODE 0x40
+
+struct mbhc_micbias_regs {
+ u16 cfilt_val;
+ u16 cfilt_ctl;
+ u16 mbhc_reg;
+ u16 int_rbias;
+ u16 ctl_reg;
+ u8 cfilt_sel;
+};
+
+/* Data used by MBHC */
+struct mbhc_internal_cal_data {
+ u16 dce_z;
+ u16 dce_mb;
+ u16 sta_z;
+ u16 sta_mb;
+ u32 t_sta_dce;
+ u32 t_dce;
+ u32 t_sta;
+ u32 micb_mv;
+ u16 v_ins_hu;
+ u16 v_ins_h;
+ u16 v_b1_hu;
+ u16 v_b1_h;
+ u16 v_b1_huc;
+ u16 v_brh;
+ u16 v_brl;
+ u16 v_no_mic;
+ s16 adj_v_hs_max;
+ u16 adj_v_ins_hu;
+ u16 adj_v_ins_h;
+ s16 v_inval_ins_low;
+ s16 v_inval_ins_high;
+};
+
+enum wcd9xxx_mbhc_plug_type {
+ PLUG_TYPE_INVALID = -1,
+ PLUG_TYPE_NONE,
+ PLUG_TYPE_HEADSET,
+ PLUG_TYPE_HEADPHONE,
+ PLUG_TYPE_HIGH_HPH,
+ PLUG_TYPE_GND_MIC_SWAP,
+};
+
+enum wcd9xxx_micbias_num {
+ MBHC_MICBIAS_INVALID = -1,
+ MBHC_MICBIAS1,
+ MBHC_MICBIAS2,
+ MBHC_MICBIAS3,
+ MBHC_MICBIAS4,
+};
+
+enum wcd9xxx_mbhc_state {
+ MBHC_STATE_NONE = -1,
+ MBHC_STATE_POTENTIAL,
+ MBHC_STATE_POTENTIAL_RECOVERY,
+ MBHC_STATE_RELEASE,
+};
+
+enum wcd9xxx_mbhc_btn_det_mem {
+ MBHC_BTN_DET_V_BTN_LOW,
+ MBHC_BTN_DET_V_BTN_HIGH,
+ MBHC_BTN_DET_N_READY,
+ MBHC_BTN_DET_N_CIC,
+ MBHC_BTN_DET_GAIN
+};
+
+enum wcd9xxx_mbhc_clk_freq {
+ TAIKO_MCLK_12P2MHZ = 0,
+ TAIKO_MCLK_9P6MHZ,
+ TAIKO_NUM_CLK_FREQS,
+};
+
+struct wcd9xxx_mbhc_general_cfg {
+ u8 t_ldoh;
+ u8 t_bg_fast_settle;
+ u8 t_shutdown_plug_rem;
+ u8 mbhc_nsa;
+ u8 mbhc_navg;
+ u8 v_micbias_l;
+ u8 v_micbias;
+ u8 mbhc_reserved;
+ u16 settle_wait;
+ u16 t_micbias_rampup;
+ u16 t_micbias_rampdown;
+ u16 t_supply_bringup;
+} __packed;
+
+struct wcd9xxx_mbhc_plug_detect_cfg {
+ u32 mic_current;
+ u32 hph_current;
+ u16 t_mic_pid;
+ u16 t_ins_complete;
+ u16 t_ins_retry;
+ u16 v_removal_delta;
+ u8 micbias_slow_ramp;
+ u8 reserved0;
+ u8 reserved1;
+ u8 reserved2;
+} __packed;
+
+struct wcd9xxx_mbhc_plug_type_cfg {
+ u8 av_detect;
+ u8 mono_detect;
+ u8 num_ins_tries;
+ u8 reserved0;
+ s16 v_no_mic;
+ s16 v_av_min;
+ s16 v_av_max;
+ s16 v_hs_min;
+ s16 v_hs_max;
+ u16 reserved1;
+} __packed;
+
+struct wcd9xxx_mbhc_btn_detect_cfg {
+ s8 c[8];
+ u8 nc;
+ u8 n_meas;
+ u8 mbhc_nsc;
+ u8 n_btn_meas;
+ u8 n_btn_con;
+ u8 num_btn;
+ u8 reserved0;
+ u8 reserved1;
+ u16 t_poll;
+ u16 t_bounce_wait;
+ u16 t_rel_timeout;
+ s16 v_btn_press_delta_sta;
+ s16 v_btn_press_delta_cic;
+ u16 t_btn0_timeout;
+ s16 _v_btn_low[0]; /* v_btn_low[num_btn] */
+ s16 _v_btn_high[0]; /* v_btn_high[num_btn] */
+ u8 _n_ready[TAIKO_NUM_CLK_FREQS];
+ u8 _n_cic[TAIKO_NUM_CLK_FREQS];
+ u8 _gain[TAIKO_NUM_CLK_FREQS];
+} __packed;
+
+struct wcd9xxx_mbhc_imped_detect_cfg {
+ u8 _hs_imped_detect;
+ u8 _n_rload;
+ u8 _hph_keep_on;
+ u8 _repeat_rload_calc;
+ u16 _t_dac_ramp_time;
+ u16 _rhph_high;
+ u16 _rhph_low;
+ u16 _rload[0]; /* rload[n_rload] */
+ u16 _alpha[0]; /* alpha[n_rload] */
+ u16 _beta[3];
+} __packed;
+
+struct wcd9xxx_mbhc_config {
+ bool read_fw_bin;
+ /*
+ * void* calibration contains:
+ * struct wcd9xxx_mbhc_general_cfg generic;
+ * struct wcd9xxx_mbhc_plug_detect_cfg plug_det;
+ * struct wcd9xxx_mbhc_plug_type_cfg plug_type;
+ * struct wcd9xxx_mbhc_btn_detect_cfg btn_det;
+ * struct wcd9xxx_mbhc_imped_detect_cfg imped_det;
+ * Note: various size depends on btn_det->num_btn
+ */
+ void *calibration;
+ enum wcd9xxx_micbias_num micbias;
+ int (*mclk_cb_fn) (struct snd_soc_codec*, int, bool);
+ unsigned int mclk_rate;
+ unsigned int gpio;
+ unsigned int gpio_irq;
+ int gpio_level_insert;
+ bool insert_detect; /* codec has own MBHC_INSERT_DETECT */
+ /* swap_gnd_mic returns true if extern GND/MIC swap switch toggled */
+ bool (*swap_gnd_mic) (struct snd_soc_codec *);
+};
+
+struct wcd9xxx_mbhc {
+ bool polling_active;
+ /* Delayed work to report long button press */
+ struct delayed_work mbhc_btn_dwork;
+ int buttons_pressed;
+ enum wcd9xxx_mbhc_state mbhc_state;
+ struct wcd9xxx_mbhc_config *mbhc_cfg;
+
+ struct mbhc_internal_cal_data mbhc_data;
+
+ struct mbhc_micbias_regs mbhc_bias_regs;
+ bool mbhc_micbias_switched;
+
+ u32 hph_status; /* track headhpone status */
+ u8 hphlocp_cnt; /* headphone left ocp retry */
+ u8 hphrocp_cnt; /* headphone right ocp retry */
+
+ /* Work to perform MBHC Firmware Read */
+ struct delayed_work mbhc_firmware_dwork;
+ const struct firmware *mbhc_fw;
+
+ struct delayed_work mbhc_insert_dwork;
+
+ u8 current_plug;
+ struct work_struct correct_plug_swch;
+ /*
+ * Work to perform polling on microphone voltage
+ * in order to correct plug type once plug type
+ * is detected as headphone
+ */
+ struct work_struct correct_plug_noswch;
+ bool hs_detect_work_stop;
+
+ bool lpi_enabled; /* low power insertion detection */
+ bool in_swch_irq_handler;
+
+ struct wcd9xxx_resmgr *resmgr;
+ struct snd_soc_codec *codec;
+
+ bool no_mic_headset_override;
+
+ /* track PA/DAC state */
+ unsigned long hph_pa_dac_state;
+
+ unsigned long mbhc_last_resume; /* in jiffies */
+
+ bool insert_detect_level_insert;
+
+ struct snd_soc_jack headset_jack;
+ struct snd_soc_jack button_jack;
+
+ struct notifier_block nblock;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_poke;
+ struct dentry *debugfs_mbhc;
+#endif
+};
+
+#define WCD9XXX_MBHC_CAL_SIZE(buttons, rload) ( \
+ sizeof(enum wcd9xxx_micbias_num) + \
+ sizeof(struct wcd9xxx_mbhc_general_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_plug_detect_cfg) + \
+ ((sizeof(s16) + sizeof(s16)) * buttons) + \
+ sizeof(struct wcd9xxx_mbhc_plug_type_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+ ((sizeof(u16) + sizeof(u16)) * rload) \
+ )
+
+#define WCD9XXX_MBHC_CAL_GENERAL_PTR(cali) ( \
+ (struct wcd9xxx_mbhc_general_cfg *) cali)
+#define WCD9XXX_MBHC_CAL_PLUG_DET_PTR(cali) ( \
+ (struct wcd9xxx_mbhc_plug_detect_cfg *) \
+ &(WCD9XXX_MBHC_CAL_GENERAL_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \
+ (struct wcd9xxx_mbhc_plug_type_cfg *) \
+ &(WCD9XXX_MBHC_CAL_PLUG_DET_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali) ( \
+ (struct wcd9xxx_mbhc_btn_detect_cfg *) \
+ &(WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_IMPED_DET_PTR(cali) ( \
+ (struct wcd9xxx_mbhc_imped_detect_cfg *) \
+ (((void *)&WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \
+ (WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \
+ (sizeof(WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \
+ sizeof(WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \
+ )
+
+/* minimum size of calibration data assuming there is only one button and
+ * one rload.
+ */
+#define WCD9XXX_MBHC_CAL_MIN_SIZE ( \
+ sizeof(struct wcd9xxx_mbhc_general_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_plug_detect_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_plug_type_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+ (sizeof(u16) * 2) \
+ )
+
+#define WCD9XXX_MBHC_CAL_BTN_SZ(cfg_ptr) ( \
+ sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+ (cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \
+ sizeof(cfg_ptr->_v_btn_high[0]))))
+
+#define WCD9XXX_MBHC_CAL_IMPED_MIN_SZ ( \
+ sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + sizeof(u16) * 2)
+
+#define WCD9XXX_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \
+ sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+ (cfg_ptr->_n_rload * \
+ (sizeof(cfg_ptr->_rload[0]) + sizeof(cfg_ptr->_alpha[0]))))
+
+int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
+ struct wcd9xxx_mbhc_config *mbhc_cfg);
+int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
+ struct snd_soc_codec *codec);
+void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc);
+void *wcd9xxx_mbhc_cal_btn_det_mp(
+ const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
+ const enum wcd9xxx_mbhc_btn_det_mem mem);
+#endif /* __WCD9XXX_MBHC_H__ */
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.c b/sound/soc/codecs/wcd9xxx-resmgr.c
new file mode 100644
index 0000000..5dfa41c
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-resmgr.c
@@ -0,0 +1,654 @@
+/* Copyright (c) 2012, 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/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include "wcd9xxx-resmgr.h"
+
+static char wcd9xxx_event_string[][64] = {
+ "WCD9XXX_EVENT_INVALID",
+
+ "WCD9XXX_EVENT_PRE_RCO_ON",
+ "WCD9XXX_EVENT_POST_RCO_ON",
+ "WCD9XXX_EVENT_PRE_RCO_OFF",
+ "WCD9XXX_EVENT_POST_RCO_OFF",
+
+ "WCD9XXX_EVENT_PRE_MCLK_ON",
+ "WCD9XXX_EVENT_POST_MCLK_ON",
+ "WCD9XXX_EVENT_PRE_MCLK_OFF",
+ "WCD9XXX_EVENT_POST_MCLK_OFF",
+
+ "WCD9XXX_EVENT_PRE_BG_OFF",
+ "WCD9XXX_EVENT_POST_BG_OFF",
+ "WCD9XXX_EVENT_PRE_BG_AUDIO_ON",
+ "WCD9XXX_EVENT_POST_BG_AUDIO_ON",
+ "WCD9XXX_EVENT_PRE_BG_MBHC_ON",
+ "WCD9XXX_EVENT_POST_BG_MBHC_ON",
+
+ "WCD9XXX_EVENT_PRE_MICBIAS_1_OFF",
+ "WCD9XXX_EVENT_POST_MICBIAS_1_OFF",
+ "WCD9XXX_EVENT_PRE_MICBIAS_2_OFF",
+ "WCD9XXX_EVENT_POST_MICBIAS_2_OFF",
+ "WCD9XXX_EVENT_PRE_MICBIAS_3_OFF",
+ "WCD9XXX_EVENT_POST_MICBIAS_3_OFF",
+ "WCD9XXX_EVENT_PRE_MICBIAS_4_OFF",
+ "WCD9XXX_EVENT_POST_MICBIAS_4_OFF",
+ "WCD9XXX_EVENT_PRE_MICBIAS_1_ON",
+ "WCD9XXX_EVENT_POST_MICBIAS_1_ON",
+ "WCD9XXX_EVENT_PRE_MICBIAS_2_ON",
+ "WCD9XXX_EVENT_POST_MICBIAS_2_ON",
+ "WCD9XXX_EVENT_PRE_MICBIAS_3_ON",
+ "WCD9XXX_EVENT_POST_MICBIAS_3_ON",
+ "WCD9XXX_EVENT_PRE_MICBIAS_4_ON",
+ "WCD9XXX_EVENT_POST_MICBIAS_4_ON",
+
+ "WCD9XXX_EVENT_PRE_CFILT_1_OFF",
+ "WCD9XXX_EVENT_POST_CFILT_1_OFF",
+ "WCD9XXX_EVENT_PRE_CFILT_2_OFF",
+ "WCD9XXX_EVENT_POST_CFILT_2_OFF",
+ "WCD9XXX_EVENT_PRE_CFILT_3_OFF",
+ "WCD9XXX_EVENT_POST_CFILT_3_OFF",
+ "WCD9XXX_EVENT_PRE_CFILT_1_ON",
+ "WCD9XXX_EVENT_POST_CFILT_1_ON",
+ "WCD9XXX_EVENT_PRE_CFILT_2_ON",
+ "WCD9XXX_EVENT_POST_CFILT_2_ON",
+ "WCD9XXX_EVENT_PRE_CFILT_3_ON",
+ "WCD9XXX_EVENT_POST_CFILT_3_ON",
+
+ "WCD9XXX_EVENT_PRE_HPHL_PA_ON",
+ "WCD9XXX_EVENT_POST_HPHL_PA_OFF",
+ "WCD9XXX_EVENT_PRE_HPHR_PA_ON",
+ "WCD9XXX_EVENT_POST_HPHR_PA_OFF",
+
+ "WCD9XXX_EVENT_POST_RESUME",
+
+ "WCD9XXX_EVENT_LAST",
+};
+
+static enum wcd9xxx_clock_type wcd9xxx_save_clock(struct wcd9xxx_resmgr
+ *resmgr);
+static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type);
+
+const char *wcd9xxx_get_event_string(enum wcd9xxx_notify_event type)
+{
+ return wcd9xxx_event_string[type];
+}
+
+void wcd9xxx_resmgr_notifier_call(struct wcd9xxx_resmgr *resmgr,
+ const enum wcd9xxx_notify_event e)
+{
+ pr_debug("%s: notifier call event %d\n", __func__, e);
+ blocking_notifier_call_chain(&resmgr->notifier, e, resmgr);
+}
+
+static void wcd9xxx_codec_disable_bg(struct wcd9xxx_resmgr *resmgr)
+{
+ /* Notify bg mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_OFF);
+ /* Disable bg */
+ snd_soc_write(resmgr->codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x00);
+ usleep_range(100, 100);
+ /* Notify bg mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_OFF);
+}
+
+static void wcd9xxx_codec_enable_bg_audio(struct wcd9xxx_resmgr *resmgr)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ /* Notify bandgap mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_AUDIO_ON);
+ /* Enable bg */
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x80);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x01, 0x01);
+ usleep_range(1000, 1000);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x00);
+ /* Notify bandgap mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_AUDIO_ON);
+}
+
+static void wcd9xxx_enable_bg_mbhc(struct wcd9xxx_resmgr *resmgr)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ /* Notify bandgap mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_MBHC_ON);
+
+ /*
+ * bandgap mode becomes fast,
+ * mclk should be off or clk buff source souldn't be VBG
+ * Let's turn off mclk always
+ */
+ WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x2, 0x2);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x80);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x4, 0x4);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x01, 0x01);
+ usleep_range(1000, 1000);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x00);
+
+ /* Notify bandgap mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_MBHC_ON);
+}
+
+static void wcd9xxx_disable_bg(struct wcd9xxx_resmgr *resmgr)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+ snd_soc_write(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x00);
+}
+
+static void wcd9xxx_disable_clock_block(struct wcd9xxx_resmgr *resmgr)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+
+ /* Notify */
+ if (resmgr->clk_type == WCD9XXX_CLK_RCO)
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_OFF);
+ else
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_PRE_MCLK_OFF);
+ /* Disable clock */
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x00);
+ usleep_range(50, 50);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x02);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x00);
+ usleep_range(50, 50);
+ /* Notify */
+ if (resmgr->clk_type == WCD9XXX_CLK_RCO)
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_POST_RCO_OFF);
+ else
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_POST_MCLK_OFF);
+ pr_debug("%s: leave\n", __func__);
+}
+
+/*
+ * wcd9xxx_resmgr_get_bandgap : Vote for bandgap ref
+ * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
+ */
+void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr,
+ const enum wcd9xxx_bandgap_type choice)
+{
+ enum wcd9xxx_clock_type clock_save;
+
+ pr_debug("%s: enter, wants %d\n", __func__, choice);
+
+ WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+ switch (choice) {
+ case WCD9XXX_BANDGAP_AUDIO_MODE:
+ resmgr->bg_audio_users++;
+ if (resmgr->bg_audio_users == 1 && resmgr->bg_mbhc_users) {
+ /*
+ * Current bg is MBHC mode, about to switch to
+ * audio mode.
+ */
+ WARN_ON(resmgr->bandgap_type !=
+ WCD9XXX_BANDGAP_MBHC_MODE);
+
+ /* BG mode can be changed only with clock off */
+ clock_save = wcd9xxx_save_clock(resmgr);
+ /* Swtich BG mode */
+ wcd9xxx_codec_disable_bg(resmgr);
+ wcd9xxx_codec_enable_bg_audio(resmgr);
+ /* restore clock */
+ wcd9xxx_restore_clock(resmgr, clock_save);
+ } else if (resmgr->bg_audio_users == 1) {
+ /* currently off, just enable it */
+ WARN_ON(resmgr->bandgap_type != WCD9XXX_BANDGAP_OFF);
+ wcd9xxx_codec_enable_bg_audio(resmgr);
+ }
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_AUDIO_MODE;
+ break;
+ case WCD9XXX_BANDGAP_MBHC_MODE:
+ resmgr->bg_mbhc_users++;
+ if (resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE ||
+ resmgr->bandgap_type == WCD9XXX_BANDGAP_AUDIO_MODE)
+ /* do nothing */
+ break;
+
+ /* bg mode can be changed only with clock off */
+ clock_save = wcd9xxx_save_clock(resmgr);
+ /* enable bg with MBHC mode */
+ wcd9xxx_enable_bg_mbhc(resmgr);
+ /* restore clock */
+ wcd9xxx_restore_clock(resmgr, clock_save);
+ /* save current mode */
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_MBHC_MODE;
+ break;
+ default:
+ pr_err("%s: Error, Invalid bandgap settings\n", __func__);
+ break;
+ }
+
+ pr_debug("%s: bg users audio %d, mbhc %d\n", __func__,
+ resmgr->bg_audio_users, resmgr->bg_mbhc_users);
+}
+
+/*
+ * wcd9xxx_resmgr_put_bandgap : Unvote bandgap ref that has been voted
+ * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
+ */
+void wcd9xxx_resmgr_put_bandgap(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_bandgap_type choice)
+{
+ enum wcd9xxx_clock_type clock_save;
+
+ pr_debug("%s: enter choice %d\n", __func__, choice);
+
+ WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+ switch (choice) {
+ case WCD9XXX_BANDGAP_AUDIO_MODE:
+ if (--resmgr->bg_audio_users == 0) {
+ if (resmgr->bg_mbhc_users) {
+ /* bg mode can be changed only with clock off */
+ clock_save = wcd9xxx_save_clock(resmgr);
+ /* switch to MBHC mode */
+ wcd9xxx_enable_bg_mbhc(resmgr);
+ /* restore clock */
+ wcd9xxx_restore_clock(resmgr, clock_save);
+ resmgr->bandgap_type =
+ WCD9XXX_BANDGAP_MBHC_MODE;
+ } else {
+ /* turn off */
+ wcd9xxx_disable_bg(resmgr);
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+ }
+ }
+ break;
+ case WCD9XXX_BANDGAP_MBHC_MODE:
+ WARN(resmgr->bandgap_type == WCD9XXX_BANDGAP_OFF,
+ "Unexpected bandgap type %d\n", resmgr->bandgap_type);
+ if (--resmgr->bg_mbhc_users == 0 &&
+ resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE) {
+ wcd9xxx_disable_bg(resmgr);
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+ }
+ break;
+ default:
+ pr_err("%s: Error, Invalid bandgap settings\n", __func__);
+ break;
+ }
+
+ pr_debug("%s: bg users audio %d, mbhc %d\n", __func__,
+ resmgr->bg_audio_users, resmgr->bg_mbhc_users);
+}
+
+void wcd9xxx_resmgr_enable_rx_bias(struct wcd9xxx_resmgr *resmgr, u32 enable)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ if (enable) {
+ resmgr->rx_bias_count++;
+ if (resmgr->rx_bias_count == 1)
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS,
+ 0x80, 0x80);
+ } else {
+ resmgr->rx_bias_count--;
+ if (!resmgr->rx_bias_count)
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS,
+ 0x80, 0x00);
+ }
+}
+
+int wcd9xxx_resmgr_enable_config_mode(struct snd_soc_codec *codec, int enable)
+{
+ pr_debug("%s: enable = %d\n", __func__, enable);
+ if (enable) {
+ snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x10, 0);
+ /* bandgap mode to fast */
+ snd_soc_write(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x17);
+ usleep_range(5, 5);
+ snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0x80);
+ snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0x80);
+ usleep_range(10, 10);
+ snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0);
+ usleep_range(10000, 10000);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x08, 0x08);
+ } else {
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x1, 0);
+ snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0);
+ /* clk source to ext clk and clk buff ref to VBG */
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x0C, 0x04);
+ }
+
+ return 0;
+}
+
+static void wcd9xxx_enable_clock_block(struct wcd9xxx_resmgr *resmgr,
+ int config_mode)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ pr_debug("%s: config_mode = %d\n", __func__, config_mode);
+ /* transit to RCO requires mclk off */
+ WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));
+ if (config_mode) {
+ /* Notify */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_ON);
+ /* enable RCO and switch to it */
+ wcd9xxx_resmgr_enable_config_mode(codec, 1);
+ snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02);
+ usleep_range(1000, 1000);
+ } else {
+ /* Notify */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_MCLK_ON);
+ /* switch to MCLK */
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x08, 0x00);
+ /* if RCO is enabled, switch from it */
+ if (snd_soc_read(codec, WCD9XXX_A_RC_OSC_FREQ) & 0x80) {
+ snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02);
+ wcd9xxx_resmgr_enable_config_mode(codec, 0);
+ }
+ }
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x01, 0x01);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x00);
+
+ /* on MCLK */
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL, 0x01, 0x01);
+ usleep_range(50, 50);
+
+ /* Notify */
+ if (config_mode)
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_POST_RCO_ON);
+ else
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_POST_MCLK_ON);
+}
+
+/*
+ * disable clock and return previous clock state
+ */
+static enum wcd9xxx_clock_type wcd9xxx_save_clock(struct wcd9xxx_resmgr *resmgr)
+{
+ WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+ if (resmgr->clk_type != WCD9XXX_CLK_OFF)
+ wcd9xxx_disable_clock_block(resmgr);
+ return resmgr->clk_type != WCD9XXX_CLK_OFF;
+}
+
+static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type)
+{
+ if (type != WCD9XXX_CLK_OFF)
+ wcd9xxx_enable_clock_block(resmgr, type == WCD9XXX_CLK_RCO);
+}
+
+void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type)
+{
+ pr_debug("%s: current %d, requested %d, rco_users %d, mclk_users %d\n",
+ __func__, resmgr->clk_type, type,
+ resmgr->clk_rco_users, resmgr->clk_mclk_users);
+ WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+ switch (type) {
+ case WCD9XXX_CLK_RCO:
+ if (++resmgr->clk_rco_users == 1 &&
+ resmgr->clk_type == WCD9XXX_CLK_OFF) {
+ /* enable RCO and switch to it */
+ wcd9xxx_enable_clock_block(resmgr, 1);
+ resmgr->clk_type = WCD9XXX_CLK_RCO;
+ }
+ break;
+ case WCD9XXX_CLK_MCLK:
+ if (++resmgr->clk_mclk_users == 1 &&
+ resmgr->clk_type == WCD9XXX_CLK_OFF) {
+ /* switch to MCLK */
+ wcd9xxx_enable_clock_block(resmgr, 0);
+ resmgr->clk_type = WCD9XXX_CLK_MCLK;
+ } else if (resmgr->clk_mclk_users == 1 &&
+ resmgr->clk_type == WCD9XXX_CLK_RCO) {
+ /* if RCO is enabled, switch from it */
+ WARN_ON(!(snd_soc_read(resmgr->codec,
+ WCD9XXX_A_RC_OSC_FREQ) & 0x80));
+ /* disable clock block */
+ wcd9xxx_disable_clock_block(resmgr);
+ /* switch to RCO */
+ wcd9xxx_enable_clock_block(resmgr, 0);
+ resmgr->clk_type = WCD9XXX_CLK_MCLK;
+ }
+ break;
+ default:
+ pr_err("%s: Error, Invalid clock get request %d\n", __func__,
+ type);
+ break;
+ }
+ pr_debug("%s: leave\n", __func__);
+}
+
+void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type)
+{
+ pr_debug("%s: current %d, put %d\n", __func__, resmgr->clk_type, type);
+
+ WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+ switch (type) {
+ case WCD9XXX_CLK_RCO:
+ if (--resmgr->clk_rco_users == 0 &&
+ resmgr->clk_type == WCD9XXX_CLK_RCO) {
+ wcd9xxx_disable_clock_block(resmgr);
+ resmgr->clk_type = WCD9XXX_CLK_OFF;
+ }
+ break;
+ case WCD9XXX_CLK_MCLK:
+ if (--resmgr->clk_mclk_users == 0 &&
+ resmgr->clk_rco_users == 0) {
+ wcd9xxx_disable_clock_block(resmgr);
+ resmgr->clk_type = WCD9XXX_CLK_OFF;
+ } else if (resmgr->clk_mclk_users == 0 &&
+ resmgr->clk_rco_users) {
+ /* disable clock */
+ wcd9xxx_disable_clock_block(resmgr);
+ /* switch to RCO */
+ wcd9xxx_enable_clock_block(resmgr, 1);
+ resmgr->clk_type = WCD9XXX_CLK_RCO;
+ }
+ break;
+ default:
+ pr_err("%s: Error, Invalid clock get request %d\n", __func__,
+ type);
+ break;
+ }
+ WARN_ON(resmgr->clk_rco_users < 0);
+ WARN_ON(resmgr->clk_mclk_users < 0);
+
+ pr_debug("%s: new rco_users %d, mclk_users %d\n", __func__,
+ resmgr->clk_rco_users, resmgr->clk_mclk_users);
+}
+
+static void wcd9xxx_resmgr_update_cfilt_usage(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_cfilt_sel cfilt_sel,
+ bool inc)
+{
+ u16 micb_cfilt_reg;
+ enum wcd9xxx_notify_event e_pre_on, e_post_off;
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ switch (cfilt_sel) {
+ case WCD9XXX_CFILT1_SEL:
+ micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_1_CTL;
+ e_pre_on = WCD9XXX_EVENT_PRE_CFILT_1_ON;
+ e_post_off = WCD9XXX_EVENT_POST_CFILT_1_OFF;
+ break;
+ case WCD9XXX_CFILT2_SEL:
+ micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_2_CTL;
+ e_pre_on = WCD9XXX_EVENT_PRE_CFILT_2_ON;
+ e_post_off = WCD9XXX_EVENT_POST_CFILT_2_OFF;
+ break;
+ case WCD9XXX_CFILT3_SEL:
+ micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_3_CTL;
+ e_pre_on = WCD9XXX_EVENT_PRE_CFILT_3_ON;
+ e_post_off = WCD9XXX_EVENT_POST_CFILT_3_OFF;
+ break;
+ default:
+ WARN(1, "Invalid CFILT selection %d\n", cfilt_sel);
+ return; /* should not happen */
+ }
+
+ if (inc) {
+ if ((resmgr->cfilt_users[cfilt_sel]++) == 0) {
+ /* Notify */
+ wcd9xxx_resmgr_notifier_call(resmgr, e_pre_on);
+ /* Enable CFILT */
+ snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0x80);
+ }
+ } else {
+ /*
+ * Check if count not zero, decrease
+ * then check if zero, go ahead disable cfilter
+ */
+ WARN(resmgr->cfilt_users[cfilt_sel] == 0,
+ "Invalid CFILT use count 0\n");
+ if ((--resmgr->cfilt_users[cfilt_sel]) == 0) {
+ /* Disable CFILT */
+ snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0);
+ /* Notify MBHC so MBHC can switch CFILT to fast mode */
+ wcd9xxx_resmgr_notifier_call(resmgr, e_post_off);
+ }
+ }
+}
+
+void wcd9xxx_resmgr_cfilt_get(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_cfilt_sel cfilt_sel)
+{
+ return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, true);
+}
+
+void wcd9xxx_resmgr_cfilt_put(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_cfilt_sel cfilt_sel)
+{
+ return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, false);
+}
+
+int wcd9xxx_resmgr_get_k_val(struct wcd9xxx_resmgr *resmgr,
+ unsigned int cfilt_mv)
+{
+ int rc = -EINVAL;
+ unsigned int ldoh_v = resmgr->pdata->micbias.ldoh_v;
+ unsigned min_mv, max_mv;
+
+ switch (ldoh_v) {
+ case WCD9XXX_LDOH_1P95_V:
+ min_mv = 160;
+ max_mv = 1800;
+ break;
+ case WCD9XXX_LDOH_2P35_V:
+ min_mv = 200;
+ max_mv = 2200;
+ break;
+ case WCD9XXX_LDOH_2P75_V:
+ min_mv = 240;
+ max_mv = 2600;
+ break;
+ case WCD9XXX_LDOH_3P0_V:
+ min_mv = 260;
+ max_mv = 2875;
+ break;
+ default:
+ goto done;
+ }
+
+ if (cfilt_mv < min_mv || cfilt_mv > max_mv)
+ goto done;
+
+ for (rc = 4; rc <= 44; rc++) {
+ min_mv = max_mv * (rc) / 44;
+ if (min_mv >= cfilt_mv) {
+ rc -= 4;
+ break;
+ }
+ }
+done:
+ return rc;
+}
+
+int wcd9xxx_resmgr_register_notifier(struct wcd9xxx_resmgr *resmgr,
+ struct notifier_block *nblock)
+{
+ return blocking_notifier_chain_register(&resmgr->notifier, nblock);
+}
+
+int wcd9xxx_resmgr_unregister_notifier(struct wcd9xxx_resmgr *resmgr,
+ struct notifier_block *nblock)
+{
+ return blocking_notifier_chain_unregister(&resmgr->notifier, nblock);
+}
+
+int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr,
+ struct snd_soc_codec *codec,
+ struct wcd9xxx *wcd9xxx,
+ struct wcd9xxx_pdata *pdata,
+ struct wcd9xxx_reg_address *reg_addr)
+{
+ WARN(ARRAY_SIZE(wcd9xxx_event_string) != WCD9XXX_EVENT_LAST + 1,
+ "Event string table isn't up to date!, %d != %d\n",
+ ARRAY_SIZE(wcd9xxx_event_string), WCD9XXX_EVENT_LAST + 1);
+
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+ resmgr->codec = codec;
+ /* This gives access of core handle to lock/unlock suspend */
+ resmgr->core = wcd9xxx;
+ resmgr->pdata = pdata;
+ resmgr->reg_addr = reg_addr;
+
+ BLOCKING_INIT_NOTIFIER_HEAD(&resmgr->notifier);
+
+ mutex_init(&resmgr->codec_resource_lock);
+
+ return 0;
+}
+
+void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr)
+{
+ mutex_destroy(&resmgr->codec_resource_lock);
+}
+
+void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr)
+{
+ mutex_lock(&resmgr->codec_resource_lock);
+}
+
+void wcd9xxx_resmgr_bcl_unlock(struct wcd9xxx_resmgr *resmgr)
+{
+ mutex_unlock(&resmgr->codec_resource_lock);
+}
+
+MODULE_DESCRIPTION("wcd9xxx resmgr module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.h b/sound/soc/codecs/wcd9xxx-resmgr.h
new file mode 100644
index 0000000..2d04102
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-resmgr.h
@@ -0,0 +1,191 @@
+/* Copyright (c) 2012, 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 __WCD9XXX_COMMON_H__
+#define __WCD9XXX_COMMON_H__
+
+#include <linux/notifier.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+
+enum wcd9xxx_bandgap_type {
+ WCD9XXX_BANDGAP_OFF,
+ WCD9XXX_BANDGAP_AUDIO_MODE,
+ WCD9XXX_BANDGAP_MBHC_MODE,
+};
+
+enum wcd9xxx_clock_type {
+ WCD9XXX_CLK_OFF,
+ WCD9XXX_CLK_RCO,
+ WCD9XXX_CLK_MCLK,
+};
+
+enum wcd9xxx_cfilt_sel {
+ WCD9XXX_CFILT1_SEL,
+ WCD9XXX_CFILT2_SEL,
+ WCD9XXX_CFILT3_SEL,
+ WCD9XXX_NUM_OF_CFILT,
+};
+
+struct wcd9xxx_reg_address {
+ u16 micb_4_ctl;
+ u16 micb_4_int_rbias;
+ u16 micb_4_mbhc;
+};
+
+enum wcd9xxx_notify_event {
+ WCD9XXX_EVENT_INVALID,
+
+ WCD9XXX_EVENT_PRE_RCO_ON,
+ WCD9XXX_EVENT_POST_RCO_ON,
+ WCD9XXX_EVENT_PRE_RCO_OFF,
+ WCD9XXX_EVENT_POST_RCO_OFF,
+
+ WCD9XXX_EVENT_PRE_MCLK_ON,
+ WCD9XXX_EVENT_POST_MCLK_ON,
+ WCD9XXX_EVENT_PRE_MCLK_OFF,
+ WCD9XXX_EVENT_POST_MCLK_OFF,
+
+ WCD9XXX_EVENT_PRE_BG_OFF,
+ WCD9XXX_EVENT_POST_BG_OFF,
+ WCD9XXX_EVENT_PRE_BG_AUDIO_ON,
+ WCD9XXX_EVENT_POST_BG_AUDIO_ON,
+ WCD9XXX_EVENT_PRE_BG_MBHC_ON,
+ WCD9XXX_EVENT_POST_BG_MBHC_ON,
+
+ WCD9XXX_EVENT_PRE_MICBIAS_1_OFF,
+ WCD9XXX_EVENT_POST_MICBIAS_1_OFF,
+ WCD9XXX_EVENT_PRE_MICBIAS_2_OFF,
+ WCD9XXX_EVENT_POST_MICBIAS_2_OFF,
+ WCD9XXX_EVENT_PRE_MICBIAS_3_OFF,
+ WCD9XXX_EVENT_POST_MICBIAS_3_OFF,
+ WCD9XXX_EVENT_PRE_MICBIAS_4_OFF,
+ WCD9XXX_EVENT_POST_MICBIAS_4_OFF,
+ WCD9XXX_EVENT_PRE_MICBIAS_1_ON,
+ WCD9XXX_EVENT_POST_MICBIAS_1_ON,
+ WCD9XXX_EVENT_PRE_MICBIAS_2_ON,
+ WCD9XXX_EVENT_POST_MICBIAS_2_ON,
+ WCD9XXX_EVENT_PRE_MICBIAS_3_ON,
+ WCD9XXX_EVENT_POST_MICBIAS_3_ON,
+ WCD9XXX_EVENT_PRE_MICBIAS_4_ON,
+ WCD9XXX_EVENT_POST_MICBIAS_4_ON,
+
+ WCD9XXX_EVENT_PRE_CFILT_1_OFF,
+ WCD9XXX_EVENT_POST_CFILT_1_OFF,
+ WCD9XXX_EVENT_PRE_CFILT_2_OFF,
+ WCD9XXX_EVENT_POST_CFILT_2_OFF,
+ WCD9XXX_EVENT_PRE_CFILT_3_OFF,
+ WCD9XXX_EVENT_POST_CFILT_3_OFF,
+ WCD9XXX_EVENT_PRE_CFILT_1_ON,
+ WCD9XXX_EVENT_POST_CFILT_1_ON,
+ WCD9XXX_EVENT_PRE_CFILT_2_ON,
+ WCD9XXX_EVENT_POST_CFILT_2_ON,
+ WCD9XXX_EVENT_PRE_CFILT_3_ON,
+ WCD9XXX_EVENT_POST_CFILT_3_ON,
+
+ WCD9XXX_EVENT_PRE_HPHL_PA_ON,
+ WCD9XXX_EVENT_POST_HPHL_PA_OFF,
+ WCD9XXX_EVENT_PRE_HPHR_PA_ON,
+ WCD9XXX_EVENT_POST_HPHR_PA_OFF,
+
+ WCD9XXX_EVENT_POST_RESUME,
+
+ WCD9XXX_EVENT_LAST,
+};
+
+struct wcd9xxx_resmgr {
+ struct snd_soc_codec *codec;
+ struct wcd9xxx *core;
+
+ u32 rx_bias_count;
+
+ enum wcd9xxx_bandgap_type bandgap_type;
+ u16 bg_audio_users;
+ u16 bg_mbhc_users;
+
+ enum wcd9xxx_clock_type clk_type;
+ u16 clk_rco_users;
+ u16 clk_mclk_users;
+
+ /* cfilt users per cfilts */
+ u16 cfilt_users[WCD9XXX_NUM_OF_CFILT];
+
+ struct wcd9xxx_reg_address *reg_addr;
+
+ struct wcd9xxx_pdata *pdata;
+
+ struct blocking_notifier_head notifier;
+ /* Notifier needs mbhc pointer with resmgr */
+ struct wcd9xxx_mbhc *mbhc;
+
+ /*
+ * Currently, only used for mbhc purpose, to protect
+ * concurrent execution of mbhc threaded irq handlers and
+ * kill race between DAPM and MBHC. But can serve as a
+ * general lock to protect codec resource
+ */
+ struct mutex codec_resource_lock;
+};
+
+int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr,
+ struct snd_soc_codec *codec,
+ struct wcd9xxx *wcd9xxx,
+ struct wcd9xxx_pdata *pdata,
+ struct wcd9xxx_reg_address *reg_addr);
+void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr);
+
+int wcd9xxx_resmgr_enable_config_mode(struct snd_soc_codec *codec, int enable);
+
+void wcd9xxx_resmgr_enable_rx_bias(struct wcd9xxx_resmgr *resmgr, u32 enable);
+void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type);
+void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type);
+void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr,
+ const enum wcd9xxx_bandgap_type choice);
+void wcd9xxx_resmgr_put_bandgap(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_bandgap_type choice);
+void wcd9xxx_resmgr_cfilt_get(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_cfilt_sel cfilt_sel);
+void wcd9xxx_resmgr_cfilt_put(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_cfilt_sel cfilt_sel);
+
+void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr);
+#define WCD9XXX_BCL_LOCK(resmgr) \
+{ \
+ pr_debug("%s: Acquiring BCL\n", __func__); \
+ wcd9xxx_resmgr_bcl_lock(resmgr); \
+ pr_debug("%s: Acquiring BCL done\n", __func__); \
+}
+
+void wcd9xxx_resmgr_bcl_unlock(struct wcd9xxx_resmgr *resmgr);
+#define WCD9XXX_BCL_UNLOCK(resmgr) \
+{ \
+ pr_debug("%s: Release BCL\n", __func__); \
+ wcd9xxx_resmgr_bcl_unlock(resmgr); \
+}
+
+#define WCD9XXX_BCL_ASSERT_LOCKED(resmgr) \
+{ \
+ WARN_ONCE(!mutex_is_locked(&resmgr->codec_resource_lock), \
+ "%s: BCL should have acquired\n", __func__); \
+}
+
+const char *wcd9xxx_get_event_string(enum wcd9xxx_notify_event type);
+int wcd9xxx_resmgr_get_k_val(struct wcd9xxx_resmgr *resmgr,
+ unsigned int cfilt_mv);
+int wcd9xxx_resmgr_register_notifier(struct wcd9xxx_resmgr *resmgr,
+ struct notifier_block *nblock);
+int wcd9xxx_resmgr_unregister_notifier(struct wcd9xxx_resmgr *resmgr,
+ struct notifier_block *nblock);
+void wcd9xxx_resmgr_notifier_call(struct wcd9xxx_resmgr *resmgr,
+ const enum wcd9xxx_notify_event e);
+
+#endif /* __WCD9XXX_COMMON_H__ */
diff --git a/sound/soc/msm/apq8064.c b/sound/soc/msm/apq8064.c
index 7894368..1316a0e 100644
--- a/sound/soc/msm/apq8064.c
+++ b/sound/soc/msm/apq8064.c
@@ -859,7 +859,6 @@
unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0;
unsigned int num_tx_ch = 0;
-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
pr_debug("%s: rx_0_ch=%d\n", __func__, msm_slim_0_rx_ch);
@@ -877,25 +876,7 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
- msm_slim_0_rx_ch, rx_ch);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
} else {
-
- if (codec_dai->id == 2)
- num_tx_ch = msm_slim_0_tx_ch;
- else if (codec_dai->id == 5) {
- /* DAI 5 is used for external EC reference from codec.
- * Since Rx is fed as reference for EC, the config of
- * this DAI is based on that of the Rx path.
- */
- num_tx_ch = msm_slim_0_rx_ch;
- }
-
pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__,
codec_dai->name, codec_dai->id, num_tx_ch);
@@ -905,6 +886,19 @@
pr_err("%s: failed to get codec chan map\n", __func__);
goto end;
}
+ /* For tabla_tx1 case */
+ if (codec_dai->id == 1)
+ num_tx_ch = msm_slim_0_tx_ch;
+ /* For tabla_tx3 case */
+ else if (codec_dai->id == 4) {
+ /* DAI 5 is used for external EC reference from codec.
+ * Since Rx is fed as reference for EC, the config of
+ * this DAI is based on that of the Rx path.
+ */
+ num_tx_ch = msm_slim_0_rx_ch;
+ } else {
+ num_tx_ch = tx_ch_cnt;
+ }
ret = snd_soc_dai_set_channel_map(cpu_dai,
num_tx_ch, tx_ch, 0 , 0);
@@ -912,15 +906,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai,
- num_tx_ch, tx_ch, 0, 0);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
-
-
}
end:
return ret;
@@ -965,13 +950,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
- num_rx_ch, rx_ch);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
} else {
num_tx_ch = params_channels(params);
@@ -992,13 +970,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai,
- num_tx_ch, tx_ch, 0, 0);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
}
end:
return ret;
@@ -1128,14 +1099,14 @@
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ unsigned int rx_ch[TABLA_RX_MAX] = {138, 139, 140, 141, 142, 143, 144};
+ unsigned int tx_ch[TABLA_TX_MAX] = {128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137};
+
pr_debug("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
- /*if (machine_is_msm_liquid()) {
- top_spk_pamp_gpio = (PM8921_GPIO_PM_TO_SYS(19));
- bottom_spk_pamp_gpio = (PM8921_GPIO_PM_TO_SYS(18));
- }*/
-
snd_soc_dapm_new_controls(dapm, apq8064_dapm_widgets,
ARRAY_SIZE(apq8064_dapm_widgets));
@@ -1221,6 +1192,8 @@
mbhc_cfg.read_fw_bin = apq8064_hs_detect_use_firmware;
err = tabla_hs_detect(codec, &mbhc_cfg);
+ snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
return err;
}
diff --git a/sound/soc/msm/mdm9615.c b/sound/soc/msm/mdm9615.c
index 1000a8b..5a47efe 100644
--- a/sound/soc/msm/mdm9615.c
+++ b/sound/soc/msm/mdm9615.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, 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
@@ -89,6 +89,20 @@
.drv = GPIOMUX_DRV_8MA,
.pull = GPIOMUX_PULL_NONE,
};
+static struct gpiomux_setting audio_sec_i2s[] = {
+ /* Suspend state */
+ {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ },
+ /* Active state */
+ {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+ }
+};
static struct gpiomux_setting cdc_i2s_dout = {
.func = GPIOMUX_FUNC_1,
@@ -142,6 +156,42 @@
},
};
+static struct msm_gpiomux_config msm9615_audio_sec_i2s_codec_configs[] = {
+ {
+ .gpio = GPIO_SPKR_I2S_MCLK,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &cdc_i2s_mclk,
+ },
+ },
+ {
+ .gpio = GPIO_SEC_I2S_SCK,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &audio_sec_i2s[0],
+ [GPIOMUX_ACTIVE] = &audio_sec_i2s[1],
+ },
+ },
+ {
+ .gpio = GPIO_SEC_I2S_DOUT,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &audio_sec_i2s[0],
+ [GPIOMUX_ACTIVE] = &audio_sec_i2s[1],
+ },
+ },
+ {
+ .gpio = GPIO_SEC_I2S_WS,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &audio_sec_i2s[0],
+ [GPIOMUX_ACTIVE] = &audio_sec_i2s[1],
+ },
+ },
+ {
+ .gpio = GPIO_SEC_I2S_DIN,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &audio_sec_i2s[0],
+ [GPIOMUX_ACTIVE] = &audio_sec_i2s[1],
+ },
+ },
+};
/* Physical address for LPA CSR
* LPA SIF mux registers. These are
* ioremap( ) for Virtual address.
@@ -274,6 +324,9 @@
static struct platform_device *mdm9615_snd_device_slim;
static struct platform_device *mdm9615_snd_device_i2s;
+static u32 sif_reg_value = 0x0000;
+static u32 spare_reg_value = 0x0000;
+
static bool hs_detect_use_gpio;
module_param(hs_detect_use_gpio, bool, 0444);
MODULE_PARM_DESC(hs_detect_use_gpio, "Use GPIO for headset detection");
@@ -862,13 +915,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
- mdm9615_slim_0_rx_ch, rx_ch);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
} else {
ret = snd_soc_dai_get_channel_map(codec_dai,
&tx_ch_cnt, tx_ch, &rx_ch_cnt , rx_ch);
@@ -882,13 +928,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai,
- mdm9615_slim_0_tx_ch, tx_ch, 0, 0);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
}
end:
return ret;
@@ -947,6 +986,10 @@
msm9615_i2s_rx_ch_get, msm9615_i2s_rx_ch_put),
SOC_ENUM_EXT("PRI_TX Channels", mdm9615_enum[2],
msm9615_i2s_tx_ch_get, msm9615_i2s_tx_ch_put),
+ SOC_ENUM_EXT("SEC_RX Channels", mdm9615_enum[3],
+ msm9615_i2s_rx_ch_get, msm9615_i2s_rx_ch_put),
+ SOC_ENUM_EXT("SEC_TX Channels", mdm9615_enum[4],
+ msm9615_i2s_tx_ch_get, msm9615_i2s_tx_ch_put),
};
static int msm9615_i2s_audrx_init(struct snd_soc_pcm_runtime *rtd)
@@ -1234,47 +1277,64 @@
return ret;
}
-static void msm9615_config_i2s_sif_mux(u8 value)
+static void msm9615_config_i2s_sif_mux(u8 value, u8 i2s_intf)
{
struct msm_i2s_ctl *pintf = &msm9x15_i2s_ctl;
- u32 sif_shadow = 0x0000;
- /* Make this variable global if both secondary and
- * primary needs to be supported. This is required
- * to retain bits in interace and set only specific
- * bits in the register. Also set Sec Intf bits.
- * Secondary interface bits are 0,1.
- **/
- sif_shadow = (sif_shadow & LPASS_SIF_MUX_CTL_PRI_MUX_SEL_BMSK) |
- (value << LPASS_SIF_MUX_CTL_PRI_MUX_SEL_SHFT);
+ u32 sif_shadow = 0x0000;
+
+ pr_debug("%s() Value = 0x%x intf = 0x%x\n", __func__, value, i2s_intf);
+ if (i2s_intf == MSM_INTF_PRIM) {
+ sif_shadow = (sif_shadow & LPASS_SIF_MUX_CTL_PRI_MUX_SEL_BMSK) |
+ (value << LPASS_SIF_MUX_CTL_PRI_MUX_SEL_SHFT);
+ pr_debug("%s() Sif shadow = 0x%x\n", __func__, sif_shadow);
+ sif_reg_value =
+ ((sif_reg_value & LPASS_SIF_MUX_CTL_SEC_MUX_SEL_BMSK) |
+ sif_shadow);
+ }
+ if (i2s_intf == MSM_INTF_SECN) {
+ sif_shadow = (sif_shadow & LPASS_SIF_MUX_CTL_SEC_MUX_SEL_BMSK) |
+ (value << LPASS_SIF_MUX_CTL_SEC_MUX_SEL_SHFT);
+ pr_debug("%s() Sif shadow = 0x%x\n", __func__, sif_shadow);
+ sif_reg_value =
+ ((sif_reg_value & LPASS_SIF_MUX_CTL_PRI_MUX_SEL_BMSK) |
+ sif_shadow);
+ }
if (pintf->sif_virt_addr != NULL)
- iowrite32(sif_shadow, pintf->sif_virt_addr);
+ iowrite32(sif_reg_value, pintf->sif_virt_addr);
/* Dont read SIF register. Device crashes. */
- pr_debug("%s() SIF Reg = 0x%x\n", __func__, sif_shadow);
+ pr_debug("%s() SIF Reg = 0x%x\n", __func__, sif_reg_value);
}
static void msm9615_config_i2s_spare_mux(u8 value, u8 i2s_intf)
{
struct msm_i2s_ctl *pintf = &msm9x15_i2s_ctl;
u32 spare_shadow = 0x0000;
- /* Make this variable global if both secondary and
- * primary needs to be supported. This is required
- * to retain bits in interace and set only specific
- * bits in the register. Also set Sec Intf bits.
- **/
+
+ pr_debug("%s() Value = 0x%x intf = 0x%x\n", __func__, value, i2s_intf);
if (i2s_intf == MSM_INTF_PRIM) {
/* Configure Primary SIF */
- spare_shadow = (spare_shadow & LPAIF_SPARE_MUX_CTL_PRI_MUX_SEL_BMSK
- ) | (value << LPAIF_SPARE_MUX_CTL_PRI_MUX_SEL_SHFT);
+ spare_shadow =
+ (spare_shadow & LPAIF_SPARE_MUX_CTL_PRI_MUX_SEL_BMSK) |
+ (value << LPAIF_SPARE_MUX_CTL_PRI_MUX_SEL_SHFT);
+ pr_debug("%s() Spare shadow = 0x%x\n", __func__, spare_shadow);
+ spare_reg_value =
+ ((spare_shadow & LPAIF_SPARE_MUX_CTL_SEC_MUX_SEL_BMSK) |
+ spare_shadow);
}
if (i2s_intf == MSM_INTF_SECN) {
/*Secondary interface configuration*/
- spare_shadow = (spare_shadow & LPAIF_SPARE_MUX_CTL_SEC_MUX_SEL_BMSK
- ) | (value << LPAIF_SPARE_MUX_CTL_SEC_MUX_SEL_SHFT);
+ spare_shadow =
+ (spare_shadow & LPAIF_SPARE_MUX_CTL_SEC_MUX_SEL_BMSK) |
+ (value << LPAIF_SPARE_MUX_CTL_SEC_MUX_SEL_SHFT);
+ pr_debug("%s() Spare shadow = 0x%x\n", __func__, spare_shadow);
+ spare_reg_value =
+ ((spare_shadow & LPAIF_SPARE_MUX_CTL_PRI_MUX_SEL_BMSK) |
+ spare_shadow);
}
if (pintf->spare_virt_addr != NULL)
- iowrite32(spare_shadow, pintf->spare_virt_addr);
+ iowrite32(spare_reg_value, pintf->spare_virt_addr);
/* Dont read SPARE register. Device crashes. */
- pr_debug("%s( ): SPARE Reg =0x%x\n", __func__, spare_shadow);
+ pr_debug("%s( ): SPARE Reg =0x%x\n", __func__, spare_reg_value);
}
static int msm9615_i2s_hw_params(struct snd_pcm_substream *substream,
@@ -1342,7 +1402,8 @@
return -EINVAL;
}
msm9615_config_i2s_sif_mux(
- pintf->mux_ctl[MSM_DIR_BOTH].sifconfig);
+ pintf->mux_ctl[MSM_DIR_BOTH].sifconfig,
+ i2s_intf);
msm9615_config_i2s_spare_mux(
pintf->mux_ctl[MSM_DIR_BOTH].spareconfig,
i2s_intf);
@@ -1368,7 +1429,8 @@
return -EINVAL;
}
msm9615_config_i2s_sif_mux(
- pintf->mux_ctl[MSM_DIR_TX].sifconfig);
+ pintf->mux_ctl[MSM_DIR_TX].sifconfig,
+ i2s_intf);
msm9615_config_i2s_spare_mux(
pintf->mux_ctl[MSM_DIR_TX].spareconfig,
i2s_intf);
@@ -1397,7 +1459,8 @@
return -EINVAL;
}
msm9615_config_i2s_sif_mux(
- pintf->mux_ctl[MSM_DIR_RX].sifconfig);
+ pintf->mux_ctl[MSM_DIR_RX].sifconfig,
+ i2s_intf);
msm9615_config_i2s_spare_mux(
pintf->mux_ctl[MSM_DIR_RX].spareconfig,
i2s_intf);
@@ -1451,11 +1514,34 @@
pintf->intf_status[i2s_intf][MSM_DIR_TX]);
}
-static void mdm9615_install_codec_i2s_gpio(void)
+void msm9615_config_port_select(void)
{
- msm_gpiomux_install(msm9615_audio_prim_i2s_codec_configs,
- ARRAY_SIZE(msm9615_audio_prim_i2s_codec_configs));
+ iowrite32(SEC_PCM_PORT_SLC_VALUE, secpcm_portslc_virt_addr);
+ pr_debug("%s() port select after updating = 0x%x\n",
+ __func__, ioread32(secpcm_portslc_virt_addr));
}
+static void mdm9615_install_codec_i2s_gpio(struct snd_pcm_substream *substream)
+{
+ u8 i2s_intf, i2s_dir;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+
+ if (!msm9615_i2s_intf_dir_sel(cpu_dai->name, &i2s_intf, &i2s_dir)) {
+ pr_debug("%s( ): cpu name = %s intf =%d dir = %d\n",
+ __func__, cpu_dai->name, i2s_intf, i2s_dir);
+ if (i2s_intf == MSM_INTF_PRIM) {
+ msm_gpiomux_install(
+ msm9615_audio_prim_i2s_codec_configs,
+ ARRAY_SIZE(msm9615_audio_prim_i2s_codec_configs));
+ } else if (i2s_intf == MSM_INTF_SECN) {
+ msm_gpiomux_install(msm9615_audio_sec_i2s_codec_configs,
+ ARRAY_SIZE(msm9615_audio_sec_i2s_codec_configs));
+ msm9615_config_port_select();
+
+ }
+ }
+}
+
static int msm9615_i2s_prepare(struct snd_pcm_substream *substream)
{
u8 ret = 0;
@@ -1463,7 +1549,7 @@
if (wcd9xxx_get_intf_type() < 0)
ret = -ENODEV;
else if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
- mdm9615_install_codec_i2s_gpio();
+ mdm9615_install_codec_i2s_gpio(substream);
return ret;
}
@@ -1488,6 +1574,15 @@
.vin_sel = 2,
.inv_int_pol = 0,
};
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ /* Tabla SLIMBUS configuration
+ * RX1, RX2, RX3, RX4, RX5, RX6, RX7
+ * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10
+ */
+ unsigned int rx_ch[TABLA_RX_MAX] = {138, 139, 140, 141, 142, 143, 144};
+ unsigned int tx_ch[TABLA_TX_MAX] = {128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137};
pr_debug("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
@@ -1539,6 +1634,10 @@
err = tabla_hs_detect(codec, &mbhc_cfg);
+ snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+
+
return err;
}
@@ -1734,15 +1833,6 @@
pr_debug("%s() SIF Reg = 0x%x\n", __func__, sif_shadow);
}
-void msm9615_config_port_select(void)
-{
- pr_debug("%s() port select defualt = 0x%x\n",
- __func__, ioread32(secpcm_portslc_virt_addr));
- iowrite32(SEC_PCM_PORT_SLC_VALUE, secpcm_portslc_virt_addr);
- pr_debug("%s() port select after updating = 0x%x\n",
- __func__, ioread32(secpcm_portslc_virt_addr));
-}
-
static int mdm9615_auxpcm_startup(struct snd_pcm_substream *substream)
{
int ret = 0;
@@ -2049,6 +2139,30 @@
.be_hw_params_fixup = msm9615_i2s_tx_be_hw_params_fixup,
.ops = &msm9615_i2s_be_ops,
},
+ {
+ .name = LPASS_BE_SEC_I2S_RX,
+ .stream_name = "Secondary I2S Playback",
+ .cpu_dai_name = "msm-dai-q6.4",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_I2S_RX,
+ .be_hw_params_fixup = msm9615_i2s_rx_be_hw_params_fixup,
+ .ops = &msm9615_i2s_be_ops,
+ },
+ {
+ .name = LPASS_BE_SEC_I2S_TX,
+ .stream_name = "Secondary I2S Capture",
+ .cpu_dai_name = "msm-dai-q6.5",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_I2S_TX,
+ .be_hw_params_fixup = msm9615_i2s_tx_be_hw_params_fixup,
+ .ops = &msm9615_i2s_be_ops,
+ },
};
static struct snd_soc_dai_link mdm9615_dai_slimbus_tabla[] = {
@@ -2097,8 +2211,8 @@
},
[1] = {
.name = "mdm9615-tabla-snd-card-i2s",
- .controls = tabla_mdm9615_controls,
- .num_controls = ARRAY_SIZE(tabla_mdm9615_controls),
+ .controls = tabla_msm9615_i2s_controls,
+ .num_controls = ARRAY_SIZE(tabla_msm9615_i2s_controls),
},
};
diff --git a/sound/soc/msm/mpq8064.c b/sound/soc/msm/mpq8064.c
index 011b798..23703f2 100644
--- a/sound/soc/msm/mpq8064.c
+++ b/sound/soc/msm/mpq8064.c
@@ -724,13 +724,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
- msm_slim_0_rx_ch, rx_ch);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
} else {
ret = snd_soc_dai_get_channel_map(codec_dai,
&tx_ch_cnt, tx_ch, &rx_ch_cnt , rx_ch);
@@ -744,14 +737,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai,
- msm_slim_0_tx_ch, tx_ch, 0, 0);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
-
}
end:
@@ -764,6 +749,10 @@
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ unsigned int rx_ch[TABLA_RX_MAX] = {138, 139, 140, 141, 142, 143, 144};
+ unsigned int tx_ch[TABLA_TX_MAX] = {128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137};
pr_debug("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
@@ -798,6 +787,8 @@
codec_clk = clk_get(cpu_dai->dev, "osr_clk");
err = tabla_hs_detect(codec, &mbhc_cfg);
+ snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
return err;
}
diff --git a/sound/soc/msm/msm-compr-q6.c b/sound/soc/msm/msm-compr-q6.c
index 0b9d54f..a3c59b9 100644
--- a/sound/soc/msm/msm-compr-q6.c
+++ b/sound/soc/msm/msm-compr-q6.c
@@ -32,6 +32,7 @@
#include <linux/android_pmem.h>
#include <sound/timer.h>
#include <mach/qdsp6v2/q6core.h>
+#include <sound/pcm.h>
#include "msm-compr-q6.h"
#include "msm-pcm-routing.h"
@@ -140,6 +141,10 @@
break;
} else
atomic_set(&prtd->pending_buffer, 0);
+ if (runtime->status->hw_ptr >= runtime->control->appl_ptr) {
+ runtime->render_flag |= SNDRV_RENDER_STOPPED;
+ break;
+ }
buf = prtd->audio_client->port[IN].buf;
pr_debug("%s:writing %d bytes of buffer[%d] to dsp 2\n",
__func__, prtd->pcm_count, prtd->out_head);
@@ -461,6 +466,9 @@
return ret;
}
break;
+ case SND_AUDIOCODEC_MP2:
+ pr_debug("%s: SND_AUDIOCODEC_MP2\n", __func__);
+ break;
default:
return -EINVAL;
}
@@ -560,6 +568,70 @@
return ret;
}
+static int msm_compr_restart(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct compr_audio *compr = runtime->private_data;
+ struct msm_audio *prtd = &compr->prtd;
+ struct audio_aio_write_param param;
+ struct audio_buffer *buf = NULL;
+ struct output_meta_data_st output_meta_data;
+ int time_stamp_flag = 0;
+ int buffer_length = 0;
+
+ pr_err("msm_compr_restart\n");
+ if (runtime->render_flag & SNDRV_RENDER_STOPPED) {
+ buf = prtd->audio_client->port[IN].buf;
+ pr_debug("%s:writing %d bytes of buffer[%d] to dsp 2\n",
+ __func__, prtd->pcm_count, prtd->out_head);
+ pr_debug("%s:writing buffer[%d] from 0x%08x\n",
+ __func__, prtd->out_head,
+ ((unsigned int)buf[0].phys
+ + (prtd->out_head * prtd->pcm_count)));
+
+ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+ time_stamp_flag = SET_TIMESTAMP;
+ else
+ time_stamp_flag = NO_TIMESTAMP;
+ memcpy(&output_meta_data, (char *)(buf->data +
+ prtd->out_head * prtd->pcm_count),
+ COMPRE_OUTPUT_METADATA_SIZE);
+
+ buffer_length = output_meta_data.frame_size;
+ pr_debug("meta_data_length: %d, frame_length: %d\n",
+ output_meta_data.meta_data_length,
+ output_meta_data.frame_size);
+ pr_debug("timestamp_msw: %d, timestamp_lsw: %d\n",
+ output_meta_data.timestamp_msw,
+ output_meta_data.timestamp_lsw);
+ if (buffer_length == 0) {
+ pr_debug("Recieved a zero length buffer-break out");
+ return -EINVAL;
+ }
+ param.paddr = (unsigned long)buf[0].phys
+ + (prtd->out_head * prtd->pcm_count)
+ + output_meta_data.meta_data_length;
+ param.len = buffer_length;
+ param.msw_ts = output_meta_data.timestamp_msw;
+ param.lsw_ts = output_meta_data.timestamp_lsw;
+ param.flags = time_stamp_flag;
+ param.uid = (unsigned long)buf[0].phys
+ + (prtd->out_head * prtd->pcm_count
+ + output_meta_data.meta_data_length);
+ if (q6asm_async_write(prtd->audio_client,
+ ¶m) < 0)
+ pr_err("%s:q6asm_async_write failed\n",
+ __func__);
+ else
+ prtd->out_head =
+ (prtd->out_head + 1) & (runtime->periods - 1);
+
+ runtime->render_flag &= ~SNDRV_RENDER_STOPPED;
+ return 0;
+ }
+ return 0;
+}
+
static int msm_compr_trigger(struct snd_pcm_substream *substream, int cmd)
{
int ret = 0;
@@ -580,12 +652,14 @@
break;
case SNDRV_PCM_TRIGGER_STOP:
atomic_set(&prtd->start, 0);
+ runtime->render_flag &= ~SNDRV_RENDER_STOPPED;
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
atomic_set(&prtd->start, 0);
+ runtime->render_flag &= ~SNDRV_RENDER_STOPPED;
break;
default:
ret = -EINVAL;
@@ -600,7 +674,7 @@
{
pr_debug("%s\n", __func__);
/* MP3 Block */
- compr->info.compr_cap.num_codecs = 12;
+ compr->info.compr_cap.num_codecs = 13;
compr->info.compr_cap.min_fragment_size = runtime->hw.period_bytes_min;
compr->info.compr_cap.max_fragment_size = runtime->hw.period_bytes_max;
compr->info.compr_cap.min_fragments = runtime->hw.periods_min;
@@ -617,6 +691,7 @@
compr->info.compr_cap.codecs[9] = SND_AUDIOCODEC_AMRWBPLUS;
compr->info.compr_cap.codecs[10] = SND_AUDIOCODEC_PASS_THROUGH;
compr->info.compr_cap.codecs[11] = SND_AUDIOCODEC_PCM;
+ compr->info.compr_cap.codecs[12] = SND_AUDIOCODEC_MP2;
/* Add new codecs here and update num_codecs*/
}
@@ -646,6 +721,7 @@
}
prtd = &compr->prtd;
prtd->substream = substream;
+ runtime->render_flag = SNDRV_DMA_MODE;
prtd->audio_client = q6asm_audio_client_alloc(
(app_cb)compr_event_handler, compr);
if (!prtd->audio_client) {
@@ -828,6 +904,7 @@
pr_debug("%s\n", __func__);
prtd->mmap_flag = 1;
+ runtime->render_flag = SNDRV_NON_DMA_MODE;
if (runtime->dma_addr && runtime->dma_bytes) {
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
result = remap_pfn_range(vma, vma->vm_start,
@@ -1111,6 +1188,10 @@
pr_debug("msm_compr_ioctl SND_AUDIOCODEC_PCM\n");
compr->codec = FORMAT_MULTI_CHANNEL_LINEAR_PCM;
break;
+ case SND_AUDIOCODEC_MP2:
+ pr_debug("SND_AUDIOCODEC_MP2\n");
+ compr->codec = FORMAT_MP2;
+ break;
default:
pr_err("msm_compr_ioctl failed..unknown codec\n");
return -EFAULT;
@@ -1173,6 +1254,7 @@
.trigger = msm_compr_trigger,
.pointer = msm_compr_pointer,
.mmap = msm_compr_mmap,
+ .restart = msm_compr_restart,
};
static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
diff --git a/sound/soc/msm/msm-dai-q6.c b/sound/soc/msm/msm-dai-q6.c
index ee1ab79..18c2329 100644
--- a/sound/soc/msm/msm-dai-q6.c
+++ b/sound/soc/msm/msm-dai-q6.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, 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
@@ -674,6 +674,7 @@
case PRIMARY_I2S_TX:
case PRIMARY_I2S_RX:
case SECONDARY_I2S_RX:
+ case SECONDARY_I2S_TX:
rc = msm_dai_q6_cdc_hw_params(params, dai, substream->stream);
break;
@@ -1379,6 +1380,7 @@
case PRIMARY_I2S_TX:
case PRIMARY_I2S_RX:
case SECONDARY_I2S_RX:
+ case SECONDARY_I2S_TX:
rc = msm_dai_q6_cdc_set_fmt(dai, fmt);
break;
default:
@@ -1831,6 +1833,7 @@
rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_i2s_rx_dai);
break;
case PRIMARY_I2S_TX:
+ case SECONDARY_I2S_TX:
rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_i2s_tx_dai);
break;
case PCM_RX:
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index f28d01a..5967bb2 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, 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
@@ -183,6 +183,7 @@
{ MI2S_RX, 0, 0, 0, 0, 0},
{ MI2S_TX, 0, 0, 0, 0},
{ SECONDARY_I2S_RX, 0, 0, 0, 0, 0},
+ { SECONDARY_I2S_TX, 0, 0, 0, 0, 0},
{ SLIMBUS_1_RX, 0, 0, 0, 0, 0},
{ SLIMBUS_1_TX, 0, 0, 0, 0, 0},
{ SLIMBUS_4_RX, 0, 0, 0, 0, 0},
@@ -1417,6 +1418,9 @@
SOC_SINGLE_EXT("PRI_TX", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("SEC_TX", MSM_BACKEND_DAI_SEC_I2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
@@ -1661,6 +1665,9 @@
SOC_SINGLE_EXT("PRI_TX_Voice", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SEC_TX_Voice", MSM_BACKEND_DAI_SEC_I2S_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("MI2S_TX_Voice", MSM_BACKEND_DAI_MI2S_TX,
MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -1685,6 +1692,9 @@
SOC_SINGLE_EXT("PRI_TX_VoLTE", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SEC_TX_VoLTE", MSM_BACKEND_DAI_SEC_I2S_TX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("SLIM_0_TX_VoLTE", MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -1706,6 +1716,9 @@
SOC_SINGLE_EXT("PRI_TX_SGLTE", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SEC_TX_SGLTE", MSM_BACKEND_DAI_SEC_I2S_TX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("MI2S_TX_SGLTE", MSM_BACKEND_DAI_MI2S_TX,
MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -1729,6 +1742,9 @@
SOC_SINGLE_EXT("PRI_TX_Voip", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SEC_TX_Voip", MSM_BACKEND_DAI_SEC_I2S_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("MI2S_TX_Voip", MSM_BACKEND_DAI_MI2S_TX,
MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -1771,6 +1787,9 @@
SOC_SINGLE_EXT("PRIMARY_I2S_TX", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("SECONDARY_I2S_TX", MSM_BACKEND_DAI_SEC_I2S_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
@@ -2206,6 +2225,7 @@
SND_SOC_DAPM_AIF_OUT("HDMI", "HDMI Playback", 0, 0, 0 , 0),
SND_SOC_DAPM_AIF_OUT("MI2S_RX", "MI2S Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("PRI_I2S_TX", "Primary I2S Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_I2S_TX", "Secondary I2S Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("MI2S_TX", "MI2S Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("INT_BT_SCO_RX", "Internal BT-SCO Playback",
@@ -2442,6 +2462,7 @@
{"MI2S_RX", NULL, "MI2S_RX Audio Mixer"},
{"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
+ {"MultiMedia1 Mixer", "SEC_TX", "SEC_I2S_TX"},
{"MultiMedia1 Mixer", "MI2S_TX", "MI2S_TX"},
{"MultiMedia2 Mixer", "MI2S_TX", "MI2S_TX"},
{"MultiMedia4 Mixer", "MI2S_TX", "MI2S_TX"},
@@ -2547,6 +2568,7 @@
{"HDMI", NULL, "HDMI_DL_HL"},
{"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"},
+ {"Voice_Tx Mixer", "SEC_TX_Voice", "SEC_I2S_TX"},
{"Voice_Tx Mixer", "MI2S_TX_Voice", "MI2S_TX"},
{"Voice_Tx Mixer", "SLIM_0_TX_Voice", "SLIMBUS_0_TX"},
{"Voice_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice", "INT_BT_SCO_TX"},
@@ -2555,6 +2577,7 @@
{"Voice_Tx Mixer", "SEC_AUX_PCM_TX_Voice", "SEC_AUX_PCM_TX"},
{"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"},
{"VoLTE_Tx Mixer", "PRI_TX_VoLTE", "PRI_I2S_TX"},
+ {"VoLTE_Tx Mixer", "SEC_TX_VoLTE", "SEC_I2S_TX"},
{"VoLTE_Tx Mixer", "SLIM_0_TX_VoLTE", "SLIMBUS_0_TX"},
{"VoLTE_Tx Mixer", "INTERNAL_BT_SCO_TX_VoLTE", "INT_BT_SCO_TX"},
{"VoLTE_Tx Mixer", "AFE_PCM_TX_VoLTE", "PCM_TX"},
@@ -2562,6 +2585,7 @@
{"VoLTE_Tx Mixer", "SEC_AUX_PCM_TX_VoLTE", "SEC_AUX_PCM_TX"},
{"VoLTE_UL", NULL, "VoLTE_Tx Mixer"},
{"SGLTE_Tx Mixer", "PRI_TX_SGLTE", "PRI_I2S_TX"},
+ {"SGLTE_Tx Mixer", "SEC_TX_SGLTE", "SEC_I2S_TX"},
{"SGLTE_Tx Mixer", "MI2S_TX_SGLTE", "MI2S_TX"},
{"SGLTE_Tx Mixer", "SLIM_0_TX_SGLTE", "SLIMBUS_0_TX"},
{"SGLTE_Tx Mixer", "INTERNAL_BT_SCO_TX_SGLTE", "INT_BT_SCO_TX"},
@@ -2570,6 +2594,7 @@
{"SGLTE_Tx Mixer", "SEC_AUX_PCM_TX_SGLTE", "SEC_AUX_PCM_TX"},
{"SGLTE_UL", NULL, "SGLTE_Tx Mixer"},
{"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"},
+ {"Voip_Tx Mixer", "SEC_TX_Voip", "SEC_I2S_TX"},
{"Voip_Tx Mixer", "MI2S_TX_Voip", "MI2S_TX"},
{"Voip_Tx Mixer", "SLIM_0_TX_Voip", "SLIMBUS_0_TX"},
{"Voip_Tx Mixer", "INTERNAL_BT_SCO_TX_Voip", "INT_BT_SCO_TX"},
@@ -2609,6 +2634,7 @@
{"Voice Stub Tx Mixer", "MI2S_TX", "MI2S_TX"},
{"Voice Stub Tx Mixer", "SLIM_3_TX", "SLIMBUS_3_TX"},
{"Voice Stub Tx Mixer", "PRIMARY_I2S_TX", "PRI_I2S_TX"},
+ {"Voice Stub Tx Mixer", "SECONDARY_I2S_TX", "SEC_I2S_TX"},
{"Voice Stub Tx Mixer", "AFE_PCM_TX", "PCM_TX"},
{"VOICE_STUB_UL", NULL, "Voice Stub Tx Mixer"},
@@ -2661,6 +2687,7 @@
{"BE_OUT", NULL, "HDMI"},
{"BE_OUT", NULL, "MI2S_RX"},
{"PRI_I2S_TX", NULL, "BE_IN"},
+ {"SEC_I2S_TX", NULL, "BE_IN"},
{"MI2S_TX", NULL, "BE_IN"},
{"SLIMBUS_0_TX", NULL, "BE_IN" },
{"SLIMBUS_1_TX", NULL, "BE_IN" },
diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h
index 14f330b..e11133e 100644
--- a/sound/soc/msm/msm-pcm-routing.h
+++ b/sound/soc/msm/msm-pcm-routing.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, 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
@@ -32,6 +32,7 @@
#define LPASS_BE_INCALL_RECORD_RX "INCALL_RECORD_TX"
#define LPASS_BE_INCALL_RECORD_TX "INCALL_RECORD_RX"
#define LPASS_BE_SEC_I2S_RX "SECONDARY_I2S_RX"
+#define LPASS_BE_SEC_I2S_TX "SECONDARY_I2S_TX"
#define LPASS_BE_MI2S_RX "MI2S_RX"
#define LPASS_BE_MI2S_TX "MI2S_TX"
@@ -93,6 +94,7 @@
MSM_BACKEND_DAI_MI2S_RX,
MSM_BACKEND_DAI_MI2S_TX,
MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_BACKEND_DAI_SEC_I2S_TX,
MSM_BACKEND_DAI_SLIMBUS_1_RX,
MSM_BACKEND_DAI_SLIMBUS_1_TX,
MSM_BACKEND_DAI_SLIMBUS_4_RX,
diff --git a/sound/soc/msm/msm8930.c b/sound/soc/msm/msm8930.c
index 4725e8e..76b7597 100644
--- a/sound/soc/msm/msm8930.c
+++ b/sound/soc/msm/msm8930.c
@@ -621,13 +621,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
- msm8930_slim_0_rx_ch, rx_ch);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
} else {
ret = snd_soc_dai_get_channel_map(codec_dai,
&tx_ch_cnt, tx_ch, &rx_ch_cnt , rx_ch);
@@ -641,14 +634,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai,
- msm8930_slim_0_tx_ch, tx_ch, 0, 0);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
-
}
end:
return ret;
@@ -660,6 +645,14 @@
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ /* Tabla SLIMBUS configuration
+ * RX1, RX2, RX3, RX4, RX5
+ * TX1, TX2, TX3, TX4, TX5
+ */
+ unsigned int rx_ch[SITAR_RX_MAX] = {138, 139, 140, 141, 142};
+ unsigned int tx_ch[SITAR_TX_MAX] = {128, 129, 130, 131, 132};
pr_debug("%s()\n", __func__);
@@ -690,6 +683,9 @@
}
codec_clk = clk_get(cpu_dai->dev, "osr_clk");
+ snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+
mbhc_cfg.gpio = 37;
mbhc_cfg.gpio_irq = gpio_to_irq(mbhc_cfg.gpio);
sitar_hs_detect(codec, &mbhc_cfg);
diff --git a/sound/soc/msm/msm8960.c b/sound/soc/msm/msm8960.c
index 6ec44bf..da7a129 100644
--- a/sound/soc/msm/msm8960.c
+++ b/sound/soc/msm/msm8960.c
@@ -777,13 +777,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
- msm8960_slim_0_rx_ch, rx_ch);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
} else {
pr_debug("%s: %s tx_dai_id = %d num_ch = %d\n", __func__,
@@ -801,13 +794,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai,
- msm8960_slim_0_tx_ch, tx_ch, 0, 0);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
}
end:
return ret;
@@ -845,13 +831,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
- num_rx_ch, rx_ch);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
} else {
num_tx_ch = params_channels(params);
@@ -871,13 +850,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai,
- num_tx_ch, tx_ch, 0, 0);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
}
end:
return ret;
@@ -896,6 +868,15 @@
.vin_sel = 2,
.inv_int_pol = 0,
};
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ /* Tabla SLIMBUS configuration
+ * RX1, RX2, RX3, RX4, RX5, RX6, RX7
+ * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8
+ */
+ unsigned int rx_ch[TABLA_RX_MAX] = {138, 139, 140, 141, 142, 143, 144};
+ unsigned int tx_ch[TABLA_TX_MAX] = {128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137};
pr_debug("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
@@ -958,6 +939,8 @@
mbhc_cfg.read_fw_bin = hs_detect_use_firmware;
err = tabla_hs_detect(codec, &mbhc_cfg);
+ snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
return err;
}
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index f462299..e65d83f 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -52,10 +52,26 @@
#define GPIO_AUX_PCM_SYNC 45
#define GPIO_AUX_PCM_CLK 46
-#define TABLA_EXT_CLK_RATE 12288000
+#define WCD9XXX_MBHC_DEF_BUTTONS 8
+#define WCD9XXX_MBHC_DEF_RLOADS 5
+#define TAIKO_EXT_CLK_RATE 9600000
-#define TABLA_MBHC_DEF_BUTTONS 8
-#define TABLA_MBHC_DEF_RLOADS 5
+void *def_taiko_mbhc_cal(void);
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+ bool dapm);
+
+static struct wcd9xxx_mbhc_config mbhc_cfg = {
+ .read_fw_bin = false,
+ .calibration = NULL,
+ .micbias = MBHC_MICBIAS2,
+ .mclk_cb_fn = msm_snd_enable_codec_ext_clk,
+ .mclk_rate = TAIKO_EXT_CLK_RATE,
+ .gpio = 0,
+ .gpio_irq = 0,
+ .gpio_level_insert = 1,
+ .insert_detect = true,
+ .swap_gnd_mic = NULL,
+};
struct msm8974_asoc_mach_data {
int mclk_gpio;
@@ -83,9 +99,6 @@
static int msm_btsco_rate = BTSCO_RATE_8KHZ;
static int msm_btsco_ch = 1;
-static struct snd_soc_jack hs_jack;
-static struct snd_soc_jack button_jack;
-
static struct mutex cdc_mclk_mutex;
static struct q_clkdiv *codec_clk;
static int clk_users;
@@ -317,7 +330,7 @@
return 0;
}
-static int msm8974_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
bool dapm)
{
int ret = 0;
@@ -366,9 +379,9 @@
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- return msm8974_enable_codec_ext_clk(w->codec, 1, true);
+ return msm_snd_enable_codec_ext_clk(w->codec, 1, true);
case SND_SOC_DAPM_POST_PMD:
- return msm8974_enable_codec_ext_clk(w->codec, 0, true);
+ return msm_snd_enable_codec_ext_clk(w->codec, 0, true);
}
return 0;
@@ -524,6 +537,23 @@
return 0;
}
+static int msm8974_hdmi_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s channels->min %u channels->max %u ()\n", __func__,
+ channels->min, channels->max);
+
+ rate->min = rate->max = 48000;
+
+ return 0;
+}
+
static int msm_aux_pcm_get_gpios(void)
{
int ret = 0;
@@ -639,6 +669,18 @@
return 0;
}
+static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ pr_debug("%s()\n", __func__);
+ rate->min = rate->max = 48000;
+
+ return 0;
+}
+
static const struct soc_enum msm_snd_enum[] = {
SOC_ENUM_SINGLE_EXT(2, spk_function),
SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
@@ -660,6 +702,19 @@
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ /* Taiko SLIMBUS configuration
+ * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13
+ * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13
+ * TX14, TX15, TX16
+ */
+ unsigned int rx_ch[TAIKO_RX_MAX] = {144, 145, 146, 147, 148, 149, 150,
+ 151, 152, 153, 154, 155, 156};
+ unsigned int tx_ch[TAIKO_TX_MAX] = {128, 129, 130, 131, 132, 133,
+ 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143};
+
pr_info("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
@@ -685,21 +740,15 @@
snd_soc_dapm_sync(dapm);
- err = snd_soc_jack_new(codec, "Headset Jack",
- (SND_JACK_HEADSET | SND_JACK_OC_HPHL |
- SND_JACK_OC_HPHR | SND_JACK_UNSUPPORTED),
- &hs_jack);
- if (err) {
- pr_err("failed to create new jack\n");
- return err;
- }
+ snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
- err = snd_soc_jack_new(codec, "Button Jack",
- TAIKO_JACK_BUTTON_MASK, &button_jack);
- if (err) {
- pr_err("failed to create new jack\n");
- return err;
- }
+ /* start mbhc */
+ mbhc_cfg.calibration = def_taiko_mbhc_cal();
+ if (mbhc_cfg.calibration)
+ err = taiko_hs_detect(codec, &mbhc_cfg);
+ else
+ err = -ENOMEM;
return err;
}
@@ -711,6 +760,84 @@
return 0;
}
+void *def_taiko_mbhc_cal(void)
+{
+ void *taiko_cal;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
+ u16 *btn_low, *btn_high;
+ u8 *n_ready, *n_cic, *gain;
+
+ taiko_cal = kzalloc(WCD9XXX_MBHC_CAL_SIZE(WCD9XXX_MBHC_DEF_BUTTONS,
+ WCD9XXX_MBHC_DEF_RLOADS),
+ GFP_KERNEL);
+ if (!taiko_cal) {
+ pr_err("%s: out of memory\n", __func__);
+ return NULL;
+ }
+
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_GENERAL_PTR(taiko_cal)->X) = (Y))
+ S(t_ldoh, 100);
+ S(t_bg_fast_settle, 100);
+ S(t_shutdown_plug_rem, 255);
+ S(mbhc_nsa, 4);
+ S(mbhc_navg, 4);
+#undef S
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_PLUG_DET_PTR(taiko_cal)->X) = (Y))
+ S(mic_current, TAIKO_PID_MIC_5_UA);
+ S(hph_current, TAIKO_PID_MIC_5_UA);
+ S(t_mic_pid, 100);
+ S(t_ins_complete, 250);
+ S(t_ins_retry, 200);
+#undef S
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(taiko_cal)->X) = (Y))
+ S(v_no_mic, 30);
+ S(v_hs_max, 2400);
+#undef S
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_BTN_DET_PTR(taiko_cal)->X) = (Y))
+ S(c[0], 62);
+ S(c[1], 124);
+ S(nc, 1);
+ S(n_meas, 3);
+ S(mbhc_nsc, 11);
+ S(n_btn_meas, 1);
+ S(n_btn_con, 2);
+ S(num_btn, WCD9XXX_MBHC_DEF_BUTTONS);
+ S(v_btn_press_delta_sta, 100);
+ S(v_btn_press_delta_cic, 50);
+#undef S
+ btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(taiko_cal);
+ btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_V_BTN_LOW);
+ btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg,
+ MBHC_BTN_DET_V_BTN_HIGH);
+ btn_low[0] = -50;
+ btn_high[0] = 34;
+ btn_low[1] = 35;
+ btn_high[1] = 52;
+ btn_low[2] = 53;
+ btn_high[2] = 94;
+ btn_low[3] = 95;
+ btn_high[3] = 133;
+ btn_low[4] = 134;
+ btn_high[4] = 171;
+ btn_low[5] = 172;
+ btn_high[5] = 208;
+ btn_low[6] = 209;
+ btn_high[6] = 244;
+ btn_low[7] = 245;
+ btn_high[7] = 330;
+ n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_N_READY);
+ n_ready[0] = 80;
+ n_ready[1] = 68;
+ n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_N_CIC);
+ n_cic[0] = 60;
+ n_cic[1] = 47;
+ gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_GAIN);
+ gain[0] = 11;
+ gain[1] = 9;
+
+ return taiko_cal;
+}
+
static int msm_snd_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -737,20 +864,8 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
- msm_slim_0_rx_ch, rx_ch);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
} else {
- if (codec_dai->id == 2)
- user_set_tx_ch = msm_slim_0_tx_ch;
- else if (codec_dai->id == 4)
- user_set_tx_ch = params_channels(params);
-
pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__,
codec_dai->name, codec_dai->id, user_set_tx_ch);
@@ -760,19 +875,24 @@
pr_err("%s: failed to get codec chan map\n", __func__);
goto end;
}
+ /* For tabla_tx1 case */
+ if (codec_dai->id == 1)
+ user_set_tx_ch = msm_slim_0_tx_ch;
+ /* For tabla_tx2 case */
+ else if (codec_dai->id == 3)
+ user_set_tx_ch = params_channels(params);
+ else
+ user_set_tx_ch = tx_ch_cnt;
+
+ pr_debug("%s: msm_slim_0_tx_ch(%d)user_set_tx_ch(%d)tx_ch_cnt(%d)\n",
+ __func__, msm_slim_0_tx_ch, user_set_tx_ch, tx_ch_cnt);
+
ret = snd_soc_dai_set_channel_map(cpu_dai,
user_set_tx_ch, tx_ch, 0 , 0);
if (ret < 0) {
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai,
- user_set_tx_ch, tx_ch, 0, 0);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
}
end:
return ret;
@@ -974,6 +1094,30 @@
.be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX,
.be_hw_params_fixup = msm_btsco_be_hw_params_fixup,
},
+ {
+ .name = LPASS_BE_INT_FM_RX,
+ .stream_name = "Internal FM Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.12292",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INT_FM_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = LPASS_BE_INT_FM_TX,
+ .stream_name = "Internal FM Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.12293",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INT_FM_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ },
/* Backend AFE DAI Links */
{
.name = LPASS_BE_AFE_PCM_RX,
@@ -999,6 +1143,34 @@
.be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
.be_hw_params_fixup = msm_proxy_be_hw_params_fixup,
},
+ /* HDMI Hostless */
+ {
+ .name = "HDMI_RX_HOSTLESS",
+ .stream_name = "HDMI_RX_HOSTLESS",
+ .cpu_dai_name = "HDMI_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ /* HDMI BACK END DAI Link */
+ {
+ .name = LPASS_BE_HDMI,
+ .stream_name = "HDMI Playback",
+ .cpu_dai_name = "msm-dai-q6-hdmi.8",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_HDMI_RX,
+ .be_hw_params_fixup = msm8974_hdmi_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ },
/* AUX PCM Backend DAI Links */
{
.name = LPASS_BE_AUXPCM_RX,
diff --git a/sound/soc/msm/qdsp6/q6asm.c b/sound/soc/msm/qdsp6/q6asm.c
index 0aad217..7e70d02 100644
--- a/sound/soc/msm/qdsp6/q6asm.c
+++ b/sound/soc/msm/qdsp6/q6asm.c
@@ -1462,6 +1462,9 @@
case FORMAT_MAT:
open.format = MAT;
break;
+ case FORMAT_MP2:
+ open.format = MP2;
+ break;
default:
pr_err("%s: Invalid format[%d]\n", __func__, format);
goto fail_cmd;
@@ -1555,6 +1558,9 @@
open.format = AMR_WB_PLUS;
pr_debug("q6asm_open_write FORMAT_AMR_WB_PLUS");
break;
+ case FORMAT_MP2:
+ open.format = MP2;
+ break;
default:
pr_err("%s: Invalid format[%d]\n", __func__, format);
goto fail_cmd;
@@ -1642,6 +1648,9 @@
case FORMAT_MP3:
open.write_format = MP3;
break;
+ case FORMAT_MP2:
+ open.write_format = MP2;
+ break;
default:
pr_err("Invalid format[%d]\n", wr_format);
goto fail_cmd;
@@ -2542,6 +2551,9 @@
case FORMAT_DTS_LBR:
fmt.format = DTS_LBR;
break;
+ case FORMAT_MP2:
+ fmt.format = MP2;
+ break;
default:
pr_err("Invalid format[%d]\n", format);
goto fail_cmd;
diff --git a/sound/soc/msm/qdsp6v2/Makefile b/sound/soc/msm/qdsp6v2/Makefile
index acb073d..1d11907 100644
--- a/sound/soc/msm/qdsp6v2/Makefile
+++ b/sound/soc/msm/qdsp6v2/Makefile
@@ -1,5 +1,5 @@
snd-soc-qdsp6v2-objs += msm-dai-q6-v2.o msm-pcm-q6-v2.o msm-pcm-routing-v2.o msm-compr-q6-v2.o msm-multi-ch-pcm-q6-v2.o
-snd-soc-qdsp6v2-objs += msm-pcm-lpa-v2.o msm-pcm-afe-v2.o msm-pcm-voip-v2.o msm-pcm-voice-v2.o
+snd-soc-qdsp6v2-objs += msm-pcm-lpa-v2.o msm-pcm-afe-v2.o msm-pcm-voip-v2.o msm-pcm-voice-v2.o msm-dai-q6-hdmi-v2.o
obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o
obj-y += q6adm.o q6afe.o q6asm.o q6audio-v2.o q6voice.o q6core.o
ocmem-audio-objs += audio_ocmem.o
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
new file mode 100644
index 0000000..f80b5891
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
@@ -0,0 +1,320 @@
+/* Copyright (c) 2012, 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/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/msm-dai-q6-v2.h>
+#include <mach/msm_hdmi_audio.h>
+
+
+enum {
+ STATUS_PORT_STARTED, /* track if AFE port has started */
+ STATUS_MAX
+};
+
+struct msm_dai_q6_hdmi_dai_data {
+ DECLARE_BITMAP(status_mask, STATUS_MAX);
+ u32 rate;
+ u32 channels;
+ union afe_port_config port_config;
+};
+
+static int msm_dai_q6_hdmi_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+ int value = ucontrol->value.integer.value[0];
+ dai_data->port_config.hdmi_multi_ch.datatype = value;
+ pr_debug("%s: value = %d\n", __func__, value);
+ return 0;
+}
+
+static int msm_dai_q6_hdmi_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+ ucontrol->value.integer.value[0] =
+ dai_data->port_config.hdmi_multi_ch.datatype;
+ return 0;
+}
+
+
+/* HDMI format field for AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG command
+ * 0: linear PCM
+ * 1: non-linear PCM
+ */
+static const char * const hdmi_format[] = {
+ "LPCM",
+ "Compr"
+};
+
+static const struct soc_enum hdmi_config_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, hdmi_format),
+};
+
+static const struct snd_kcontrol_new hdmi_config_controls[] = {
+ SOC_ENUM_EXT("HDMI RX Format", hdmi_config_enum[0],
+ msm_dai_q6_hdmi_format_get,
+ msm_dai_q6_hdmi_format_put),
+};
+
+/* Current implementation assumes hw_param is called once
+ * This may not be the case but what to do when ADM and AFE
+ * port are already opened and parameter changes
+ */
+static int msm_dai_q6_hdmi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ u32 channel_allocation = 0;
+ u32 level_shift = 0; /* 0dB */
+ bool down_mix = FALSE;
+
+ dai_data->channels = params_channels(params);
+ dai_data->rate = params_rate(params);
+ dai_data->port_config.hdmi_multi_ch.reserved = 0;
+ dai_data->port_config.hdmi_multi_ch.hdmi_cfg_minor_version = 1;
+ dai_data->port_config.hdmi_multi_ch.sample_rate = dai_data->rate;
+ dai_data->port_config.hdmi_multi_ch.bit_width = 16;
+
+ switch (dai_data->channels) {
+ case 2:
+ channel_allocation = 0;
+ hdmi_msm_audio_info_setup(1, MSM_HDMI_AUDIO_CHANNEL_2,
+ channel_allocation, level_shift, down_mix);
+ dai_data->port_config.hdmi_multi_ch.channel_allocation =
+ channel_allocation;
+ break;
+ case 6:
+ channel_allocation = 0x0B;
+ hdmi_msm_audio_info_setup(1, MSM_HDMI_AUDIO_CHANNEL_6,
+ channel_allocation, level_shift, down_mix);
+ dai_data->port_config.hdmi_multi_ch.channel_allocation =
+ channel_allocation;
+ break;
+ case 8:
+ channel_allocation = 0x1F;
+ hdmi_msm_audio_info_setup(1, MSM_HDMI_AUDIO_CHANNEL_8,
+ channel_allocation, level_shift, down_mix);
+ dai_data->port_config.hdmi_multi_ch.channel_allocation =
+ channel_allocation;
+ break;
+ default:
+ dev_err(dai->dev, "invalid Channels = %u\n",
+ dai_data->channels);
+ return -EINVAL;
+ }
+ dev_dbg(dai->dev, "%s() minor version: %u samplerate: %u bitwidth: %u num_ch = %u channel_allocation = %u datatype = %d\n",
+ __func__,
+ dai_data->port_config.hdmi_multi_ch.hdmi_cfg_minor_version,
+ dai_data->port_config.hdmi_multi_ch.sample_rate,
+ dai_data->port_config.hdmi_multi_ch.bit_width,
+ dai_data->channels,
+ dai_data->port_config.hdmi_multi_ch.channel_allocation,
+ dai_data->port_config.hdmi_multi_ch.datatype);
+
+ return 0;
+}
+
+
+static void msm_dai_q6_hdmi_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ pr_info("%s: afe port not started. dai_data->status_mask = %ld\n",
+ __func__, *dai_data->status_mask);
+ return;
+ }
+
+ rc = afe_close(dai->id); /* can block */
+
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+
+ pr_debug("%s: dai_data->status_mask = %ld\n", __func__,
+ *dai_data->status_mask);
+
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+}
+
+
+static int msm_dai_q6_hdmi_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ rc = afe_port_start(dai->id, &dai_data->port_config,
+ dai_data->rate);
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to open AFE port %x\n",
+ dai->id);
+ else
+ set_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
+ }
+
+ return rc;
+}
+
+static int msm_dai_q6_hdmi_dai_probe(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data;
+ const struct snd_kcontrol_new *kcontrol;
+ int rc = 0;
+
+ dai_data = kzalloc(sizeof(struct msm_dai_q6_hdmi_dai_data),
+ GFP_KERNEL);
+
+ if (!dai_data) {
+ dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
+ dai->id);
+ rc = -ENOMEM;
+ } else
+ dev_set_drvdata(dai->dev, dai_data);
+
+ kcontrol = &hdmi_config_controls[0];
+
+ rc = snd_ctl_add(dai->card->snd_card,
+ snd_ctl_new1(kcontrol, dai_data));
+ return rc;
+}
+
+static int msm_dai_q6_hdmi_dai_remove(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data;
+ int rc;
+
+ dai_data = dev_get_drvdata(dai->dev);
+
+ /* If AFE port is still up, close it */
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ rc = afe_close(dai->id); /* can block */
+
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+ }
+ kfree(dai_data);
+ snd_soc_unregister_dai(dai->dev);
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_hdmi_ops = {
+ .prepare = msm_dai_q6_hdmi_prepare,
+ .hw_params = msm_dai_q6_hdmi_hw_params,
+ .shutdown = msm_dai_q6_hdmi_shutdown,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_hdmi_hdmi_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 6,
+ .rate_max = 48000,
+ .rate_min = 48000,
+ },
+ .ops = &msm_dai_q6_hdmi_ops,
+ .probe = msm_dai_q6_hdmi_dai_probe,
+ .remove = msm_dai_q6_hdmi_dai_remove,
+};
+
+
+/* To do: change to register DAIs as batch */
+static __devinit int msm_dai_q6_hdmi_dev_probe(struct platform_device *pdev)
+{
+ int rc, id;
+ const char *q6_dev_id = "qcom,msm-dai-q6-dev-id";
+
+ rc = of_property_read_u32(pdev->dev.of_node, q6_dev_id, &id);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: missing %s in dt node\n", __func__, q6_dev_id);
+ return rc;
+ }
+
+ pdev->id = id;
+ dev_set_name(&pdev->dev, "%s.%d", "msm-dai-q6-hdmi", id);
+
+ pr_debug("%s: dev name %s, id:%d\n", __func__,
+ dev_name(&pdev->dev), pdev->id);
+
+ switch (pdev->id) {
+ case HDMI_RX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_hdmi_hdmi_rx_dai);
+ break;
+ default:
+ dev_err(&pdev->dev, "invalid device ID %d\n", pdev->id);
+ rc = -ENODEV;
+ break;
+ }
+ return rc;
+}
+
+static __devexit int msm_dai_q6_hdmi_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dai(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_dai_q6_hdmi_dt_match[] = {
+ {.compatible = "qcom,msm-dai-q6-hdmi"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_dai_q6_hdmi_dt_match);
+
+static struct platform_driver msm_dai_q6_hdmi_driver = {
+ .probe = msm_dai_q6_hdmi_dev_probe,
+ .remove = msm_dai_q6_hdmi_dev_remove,
+ .driver = {
+ .name = "msm-dai-q6-hdmi",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dai_q6_hdmi_dt_match,
+ },
+};
+
+static int __init msm_dai_q6_hdmi_init(void)
+{
+ return platform_driver_register(&msm_dai_q6_hdmi_driver);
+}
+module_init(msm_dai_q6_hdmi_init);
+
+static void __exit msm_dai_q6_hdmi_exit(void)
+{
+ platform_driver_unregister(&msm_dai_q6_hdmi_driver);
+}
+module_exit(msm_dai_q6_hdmi_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM DSP HDMI DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index dacd59c..354dece 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -899,6 +899,36 @@
.remove = msm_dai_q6_dai_remove,
};
+static struct snd_soc_dai_driver msm_dai_q6_fm_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_fm_tx_dai = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
static int __devinit msm_auxpcm_dev_probe(struct platform_device *pdev)
{
int id;
@@ -1158,6 +1188,12 @@
rc = snd_soc_register_dai(&pdev->dev,
&msm_dai_q6_bt_sco_tx_dai);
break;
+ case INT_FM_RX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_fm_rx_dai);
+ break;
+ case INT_FM_TX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_fm_tx_dai);
+ break;
case RT_PROXY_DAI_001_RX:
case RT_PROXY_DAI_002_RX:
rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_afe_rx_dai);
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
index f0465a5..4819e0a 100644
--- a/sound/soc/msm/qdsp6v2/q6afe.c
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -344,6 +344,8 @@
break;
case INT_BT_SCO_RX:
case INT_BT_SCO_TX:
+ case INT_FM_RX:
+ case INT_FM_TX:
cfg_type = AFE_PARAM_ID_INTERNAL_BT_FM_CONFIG;
break;
default:
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index f989b17..dc851ce 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -2582,6 +2582,7 @@
rtd->ops.silence = platform->driver->ops->silence;
rtd->ops.page = platform->driver->ops->page;
rtd->ops.mmap = platform->driver->ops->mmap;
+ rtd->ops.restart = platform->driver->ops->restart;
}
if (playback)