Merge "ASoC: msm8x10-wcd: Remove delayed work for mbhc start"
diff --git a/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt b/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt
index 31bf540..d0cad52 100644
--- a/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt
+++ b/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt
@@ -9,7 +9,12 @@
Required properties:
- compatible : should be "qcom,qpnp-iadc" for Current ADC driver.
-- reg : offset and length of the PMIC Aribter register map.
+- reg : offset and length of the PMIC Arbiter register map.
+- reg-names : resource names used for the physical base address of the PMIC IADC
+ peripheral, the SMBB_BAT_IF_TRIM_CNST_RDS register.
+ Should be "iadc-base" for the PMIC IADC peripheral base register.
+ Should be "batt-id-trim-cnst-rds" for reading the
+ SMBB_BAT_IF_TRIM_CNST_RDS register.
- address-cells : Must be one.
- size-cells : Must be zero.
- interrupts : The USR bank peripheral IADC interrupt.
@@ -21,6 +26,13 @@
- qcom,rsense : Use this property when external rsense should be used
for current calculation and specify the units in nano-ohms.
- qcom,iadc-poll-eoc: Use polling instead of interrupts for End of Conversion completion.
+- qcom,use-default-rds-trim : Add this property to check if certain conditions are to be checked
+ reading the SMBB_BAT_IF_CNST_RDS, IADC_RDS trim register and
+ manufacturer type. Check the driver for conditions that each of the type.
+ 0 : Select this type to read the IADC and SMBB trim register and
+ apply the default RSENSE if conditions are met.
+ 1 : Select this type to read the IADC, SMBB trim register and
+ manufacturer type and apply the default RSENSE if conditions are met.
Channel node
NOTE: Atleast one Channel node is required.
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 6277054..e672a14 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -461,7 +461,11 @@
codec.
- qcom,mbhc-bias-internal: Flag to indicate if internal micbias should be used
for headset detection.
-
+- qcom,mbhc-audio-jack-type : String to indicate the jack type on the hardware.
+ Possible Values:
+ 4-pole-jack : Jack on the hardware is 4-pole.
+ 5-pole-jack : Jack on the hardware is 5-pole.
+ 6-pole-jack : Jack on the hardware is 6-pole.
* APQ8074 ASoC Machine driver
Required properties:
@@ -518,6 +522,7 @@
qcom,sec-auxpcm-gpio-sync = <&msmgpio 80 0>;
qcom,sec-auxpcm-gpio-din = <&msmgpio 81 0>;
qcom,sec-auxpcm-gpio-dout = <&msmgpio 82 0>;
+ qcom,mbhc-audio-jack-type = "4-pole-jack";
};
* msm-dai-mi2s
diff --git a/arch/arm/boot/dts/msm-pm8110.dtsi b/arch/arm/boot/dts/msm-pm8110.dtsi
index 20e8a96..9adbf81 100644
--- a/arch/arm/boot/dts/msm-pm8110.dtsi
+++ b/arch/arm/boot/dts/msm-pm8110.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -256,7 +256,9 @@
pm8110_iadc: iadc@3600 {
compatible = "qcom,qpnp-iadc";
- reg = <0x3600 0x100>;
+ reg = <0x3600 0x100>,
+ <0x12f1 0x1>;
+ reg-names = "iadc-base", "batt-id-trim-cnst-rds";
#address-cells = <1>;
#size-cells = <0>;
interrupts = <0x0 0x36 0x0>;
@@ -265,6 +267,7 @@
qcom,adc-vdd-reference = <1800>;
qcom,iadc-vadc = <&pm8110_vadc>;
qcom,iadc-poll-eoc;
+ qcom,use-default-rds-trim = <1>;
chan@0 {
label = "internal_rsense";
diff --git a/arch/arm/boot/dts/msm-pm8226.dtsi b/arch/arm/boot/dts/msm-pm8226.dtsi
index a13e66a..0558ee5 100644
--- a/arch/arm/boot/dts/msm-pm8226.dtsi
+++ b/arch/arm/boot/dts/msm-pm8226.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -414,7 +414,9 @@
pm8226_iadc: iadc@3600 {
compatible = "qcom,qpnp-iadc";
- reg = <0x3600 0x100>;
+ reg = <0x3600 0x100>,
+ <0x12f1 0x1>;
+ reg-names = "iadc-base", "batt-id-trim-cnst-rds";
#address-cells = <1>;
#size-cells = <0>;
interrupts = <0x0 0x36 0x0>;
@@ -422,6 +424,7 @@
qcom,adc-bit-resolution = <16>;
qcom,adc-vdd-reference = <1800>;
qcom,iadc-vadc = <&pm8226_vadc>;
+ qcom,use-default-rds-trim = <0>;
chan@0 {
label = "internal_rsense";
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index 520decd..e678388 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -812,7 +812,9 @@
pm8941_iadc: iadc@3600 {
compatible = "qcom,qpnp-iadc";
- reg = <0x3600 0x100>;
+ reg = <0x3600 0x100>,
+ <0x12f1 0x1>;
+ reg-names = "iadc-base", "batt-id-trim-cnst-rds";
#address-cells = <1>;
#size-cells = <0>;
interrupts = <0x0 0x36 0x0>;
@@ -821,6 +823,7 @@
qcom,adc-vdd-reference = <1800>;
qcom,iadc-vadc = <&pm8941_vadc>;
qcom,iadc-poll-eoc;
+ qcom,use-default-rds-trim = <0>;
chan@0 {
label = "internal_rsense";
diff --git a/arch/arm/boot/dts/msm8226-1080p-cdp.dtsi b/arch/arm/boot/dts/msm8226-1080p-cdp.dtsi
index bb66538..4163b95 100644
--- a/arch/arm/boot/dts/msm8226-1080p-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8226-1080p-cdp.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -31,6 +31,7 @@
synaptics,i2c-pull-up;
synaptics,power-down;
synaptics,disable-gpios;
+ synaptics,fw-image-name = "PR1469074-s3408bt_001F010D.img";
};
};
@@ -129,6 +130,7 @@
qcom,cdc-mclk-gpios = <&pm8226_gpios 1 0>;
qcom,cdc-vdd-spkr-gpios = <&pm8226_gpios 2 0>;
qcom,headset-jack-type-NC;
+ qcom,mbhc-audio-jack-type = "6-pole-jack";
};
sound-9302 {
diff --git a/arch/arm/boot/dts/msm8226-1080p-mtp.dtsi b/arch/arm/boot/dts/msm8226-1080p-mtp.dtsi
index 31624de..a99df65 100644
--- a/arch/arm/boot/dts/msm8226-1080p-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8226-1080p-mtp.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -31,6 +31,7 @@
synaptics,i2c-pull-up;
synaptics,power-down;
synaptics,disable-gpios;
+ synaptics,fw-image-name = "PR1469074-s3408bt_001F010D.img";
};
};
diff --git a/arch/arm/boot/dts/msm8226-720p-cdp.dtsi b/arch/arm/boot/dts/msm8226-720p-cdp.dtsi
index 9a1eb36..00e90f3 100644
--- a/arch/arm/boot/dts/msm8226-720p-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8226-720p-cdp.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -106,6 +106,7 @@
qcom,cdc-mclk-gpios = <&pm8226_gpios 1 0>;
qcom,cdc-vdd-spkr-gpios = <&pm8226_gpios 2 0>;
qcom,headset-jack-type-NC;
+ qcom,mbhc-audio-jack-type = "6-pole-jack";
};
sound-9302 {
diff --git a/arch/arm/boot/dts/msm8226-regulator.dtsi b/arch/arm/boot/dts/msm8226-regulator.dtsi
index 0146367..a76c4df 100644
--- a/arch/arm/boot/dts/msm8226-regulator.dtsi
+++ b/arch/arm/boot/dts/msm8226-regulator.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -36,7 +36,7 @@
interrupts = <0 15 0>;
regulator-name = "apc_corner";
regulator-min-microvolt = <1>;
- regulator-max-microvolt = <12>;
+ regulator-max-microvolt = <14>;
qcom,pvs-fuse-redun-sel = <22 24 3 2 0>;
qcom,pvs-fuse = <22 6 5 0>;
@@ -91,7 +91,7 @@
qcom,cpr-uplift-max-volt = <1350000>;
qcom,cpr-uplift-speed-bin = <1>;
qcom,speed-bin-fuse-sel = <22 0 3 0>;
- qcom,cpr-corner-map = <1 1 2 2 3 3 3 3 3 3 3 3>;
+ qcom,cpr-corner-map = <1 1 2 2 3 3 3 3 3 3 3 3 3 3>;
qcom,cpr-quot-adjust-table =
<1 5 450>,
<1 6 375>,
diff --git a/arch/arm/boot/dts/msm8226-v2.dtsi b/arch/arm/boot/dts/msm8226-v2.dtsi
index a285ec9..5a670c2 100644
--- a/arch/arm/boot/dts/msm8226-v2.dtsi
+++ b/arch/arm/boot/dts/msm8226-v2.dtsi
@@ -100,6 +100,20 @@
<1401600000 10>,
<1497600000 11>,
<1593600000 12>;
+ qcom,speed5-bin-v2 =
+ < 0 0>,
+ < 384000000 2>,
+ < 787200000 4>,
+ < 998400000 5>,
+ <1094400000 6>,
+ <1190400000 7>,
+ <1305600000 8>,
+ <1344000000 9>,
+ <1401600000 10>,
+ <1497600000 11>,
+ <1593600000 12>,
+ <1689600000 13>,
+ <1785600000 14>;
};
};
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index 148d99c..e7d8a4d 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1045,7 +1045,9 @@
< 1344000 4066 >,
< 1401600 4066 >,
< 1497600 4066 >,
- < 1593600 4066 >;
+ < 1593600 4066 >,
+ < 1689600 4066 >,
+ < 1785600 4066 >;
};
qcom,ocmem@fdd00000 {
diff --git a/arch/arm/boot/dts/msm8926.dtsi b/arch/arm/boot/dts/msm8926.dtsi
index 54fcfeb..c274d9f 100644
--- a/arch/arm/boot/dts/msm8926.dtsi
+++ b/arch/arm/boot/dts/msm8926.dtsi
@@ -61,6 +61,20 @@
<1401600000 10>,
<1497600000 11>,
<1593600000 12>;
+ qcom,speed5-bin-v1 =
+ < 0 0>,
+ < 384000000 2>,
+ < 787200000 4>,
+ < 998400000 5>,
+ <1094400000 6>,
+ <1190400000 7>,
+ <1305600000 8>,
+ <1344000000 9>,
+ <1401600000 10>,
+ <1497600000 11>,
+ <1593600000 12>,
+ <1689600000 13>,
+ <1785600000 14>;
};
qcom,msm-thermal {
diff --git a/arch/arm/boot/dts/msm8974-cdp.dtsi b/arch/arm/boot/dts/msm8974-cdp.dtsi
index 27536db..da6d018 100644
--- a/arch/arm/boot/dts/msm8974-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8974-cdp.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -220,6 +220,7 @@
qcom,hdmi-audio-rx;
qcom,us-euro-gpios = <&pm8941_gpios 20 0>;
qcom,cdc-micbias2-headset-only;
+ qcom,mbhc-audio-jack-type = "6-pole-jack";
};
usb2_otg_sw: regulator-tpd4s214 {
diff --git a/arch/arm/boot/dts/msm8974-fluid.dtsi b/arch/arm/boot/dts/msm8974-fluid.dtsi
index f4b4d4a..eb5a5b6 100644
--- a/arch/arm/boot/dts/msm8974-fluid.dtsi
+++ b/arch/arm/boot/dts/msm8974-fluid.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -271,6 +271,7 @@
qcom,hdmi-audio-rx;
qcom,ext-ult-lo-amp-gpio = <&pm8941_gpios 6 0>;
qcom,cdc-micbias2-headset-only;
+ qcom,mbhc-audio-jack-type = "6-pole-jack";
};
};
diff --git a/arch/arm/boot/dts/msm8974-liquid.dtsi b/arch/arm/boot/dts/msm8974-liquid.dtsi
index 397e10e..942aba4 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dtsi
+++ b/arch/arm/boot/dts/msm8974-liquid.dtsi
@@ -318,7 +318,7 @@
compatible = "qca,ar3002";
qca,bt-reset-gpio = <&pm8941_gpios 34 0>;
qca,bt-chip-pwd-supply = <&ath_chip_pwd_l>;
- qca,bt-vdd-io-supply = <&pm8941_s3>;
+ qca,bt-vdd-io-supply = <&pm8941_l10>;
qca,bt-vdd-pa-supply = <&pm8941_l19>;
};
@@ -380,47 +380,34 @@
qcom,prim-auxpcm-gpio-set = "prim-gpio-tert";
};
- hsic_hub {
- compatible = "qcom,hsic-smsc-hub";
- smsc,model-id = <3503>;
- #address-cells = <1>;
- #size-cells = <1>;
- ranges;
- smsc,reset-gpio = <&pm8941_gpios 8 0x00>;
- smsc,refclk-gpio = <&pm8941_gpios 16 0x00>;
- smsc,int-gpio = <&msmgpio 50 0x00>;
- hub_int-supply = <&pm8941_l10>;
- hub_vbus-supply = <&ext_5v>;
+ hsic_host: hsic@f9a00000 {
+ compatible = "qcom,hsic-host";
+ reg = <0xf9a00000 0x400>;
+ #address-cells = <0>;
+ interrupt-parent = <&hsic_host>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 136 0
+ 1 &intc 0 148 0
+ 2 &msmgpio 144 0x8>;
+ interrupt-names = "core_irq", "async_irq", "wakeup";
+ hsic_vdd_dig-supply = <&pm8841_s2_corner>;
+ HSIC_GDSC-supply = <&gdsc_usb_hsic>;
+ hsic,strobe-gpio = <&msmgpio 144 0x00>;
+ hsic,data-gpio = <&msmgpio 145 0x00>;
+ hsic,ignore-cal-pad-config;
+ hsic,strobe-pad-offset = <0x2050>;
+ hsic,data-pad-offset = <0x2054>;
+ qcom,phy-susp-sof-workaround;
+ hsic,vdd-voltage-level = <1 5 7>;
- hsic_host: hsic@f9a00000 {
- compatible = "qcom,hsic-host";
- reg = <0xf9a00000 0x400>;
- #address-cells = <0>;
- interrupt-parent = <&hsic_host>;
- interrupts = <0 1 2>;
- #interrupt-cells = <1>;
- interrupt-map-mask = <0xffffffff>;
- interrupt-map = <0 &intc 0 136 0
- 1 &intc 0 148 0
- 2 &msmgpio 144 0x8>;
- interrupt-names = "core_irq", "async_irq", "wakeup";
- hsic_vdd_dig-supply = <&pm8841_s2_corner>;
- HSIC_GDSC-supply = <&gdsc_usb_hsic>;
- hsic,strobe-gpio = <&msmgpio 144 0x00>;
- hsic,data-gpio = <&msmgpio 145 0x00>;
- hsic,ignore-cal-pad-config;
- hsic,strobe-pad-offset = <0x2050>;
- hsic,data-pad-offset = <0x2054>;
- qcom,phy-susp-sof-workaround;
- hsic,vdd-voltage-level = <1 5 7>;
-
- qcom,msm-bus,name = "hsic";
- qcom,msm-bus,num-cases = <2>;
- qcom,msm-bus,num-paths = <1>;
- qcom,msm-bus,vectors-KBps =
- <85 512 0 0>,
- <85 512 40000 160000>;
- };
+ qcom,msm-bus,name = "hsic";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <85 512 0 0>,
+ <85 512 40000 160000>;
};
wlan0: qca,wlan {
@@ -445,6 +432,38 @@
qcom,msm-bus,name = "wlan_sdio";
qca,wifi-chip-pwd-supply = <&ath_chip_pwd_l>;
};
+
+ qcom,pronto@fb21b000 {
+ status = "disabled";
+ };
+
+ qcom,iris-fm {
+ status = "disabled";
+ };
+
+ qcom,wcnss-wlan@fb000000 {
+ status = "disabled";
+ };
+
+ qcom,smd-wcnss {
+ status = "disabled";
+ };
+
+ qcom,smsm-wcnss {
+ status = "disabled";
+ };
+};
+
+&pm8941_l19 {
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ qcom,init-voltage = <3300000>;
+};
+
+&pm8941_l10 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
};
&mdss_fb0 {
diff --git a/arch/arm/boot/dts/msm8974-mtp.dtsi b/arch/arm/boot/dts/msm8974-mtp.dtsi
index 6cc49c4..d118d51 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8974-mtp.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -214,6 +214,7 @@
sound {
qcom,model = "msm8974-taiko-mtp-snd-card";
qcom,cdc-micbias2-headset-only;
+ qcom,mbhc-audio-jack-type = "6-pole-jack";
};
};
@@ -707,6 +708,7 @@
taiko_codec {
qcom,cdc-micbias1-ext-cap;
qcom,cdc-micbias2-ext-cap;
+ qcom,cdc-micbias3-ext-cap;
qcom,cdc-micbias4-ext-cap;
};
};
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index 5659898..23ddc8c 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -745,7 +745,7 @@
qcom,cdc-micbias-ldoh-v = <0x3>;
qcom,cdc-micbias-cfilt1-mv = <1800>;
qcom,cdc-micbias-cfilt2-mv = <2700>;
- qcom,cdc-micbias-cfilt3-mv = <1800>;
+ qcom,cdc-micbias-cfilt3-mv = <2700>;
qcom,cdc-micbias1-cfilt-sel = <0x0>;
qcom,cdc-micbias2-cfilt-sel = <0x1>;
qcom,cdc-micbias3-cfilt-sel = <0x2>;
diff --git a/arch/arm/mach-msm/clock-8226.c b/arch/arm/mach-msm/clock-8226.c
index 1336ec3..d4b8109 100644
--- a/arch/arm/mach-msm/clock-8226.c
+++ b/arch/arm/mach-msm/clock-8226.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -2846,6 +2846,8 @@
F_APCS_PLL(1401600000, 73, 0x0, 0x1, 0x0, 0x0, 0x0),
F_APCS_PLL(1497600000, 78, 0x0, 0x1, 0x0, 0x0, 0x0),
F_APCS_PLL(1593600000, 83, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1689600000, 88, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1785600000, 93, 0x0, 0x1, 0x0, 0x0, 0x0),
PLL_F_END
};
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 0d88689..7717829 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -1205,9 +1205,11 @@
* after the command has been retired
*/
if (result)
- kgsl_mmu_disable_clk_on_ts(&device->mmu, 0, false);
+ kgsl_mmu_disable_clk(&device->mmu,
+ KGSL_IOMMU_CONTEXT_USER);
else
- kgsl_mmu_disable_clk_on_ts(&device->mmu, rb->global_ts, true);
+ kgsl_mmu_disable_clk_on_ts(&device->mmu, rb->global_ts,
+ KGSL_IOMMU_CONTEXT_USER);
done:
kgsl_context_put(context);
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 800caf1..0f1e01d 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -20,6 +20,8 @@
#include "kgsl_iommu.h"
#include <mach/ocmem.h>
+#include "a3xx_reg.h"
+
#define DEVICE_3D_NAME "kgsl-3d"
#define DEVICE_3D0_NAME "kgsl-3d0"
@@ -895,4 +897,48 @@
return result;
}
+/*
+ * adreno_set_protected_registers() - Protect the specified range of registers
+ * from being accessed by the GPU
+ * @device: pointer to the KGSL device
+ * @index: Pointer to the index of the protect mode register to write to
+ * @reg: Starting dword register to write
+ * @mask_len: Size of the mask to protect (# of registers = 2 ** mask_len)
+ *
+ * Add the range of registers to the list of protected mode registers that will
+ * cause an exception if the GPU accesses them. There are 16 available
+ * protected mode registers. Index is used to specify which register to write
+ * to - the intent is to call this function multiple times with the same index
+ * pointer for each range and the registers will be magically programmed in
+ * incremental fashion
+ */
+static inline void adreno_set_protected_registers(struct kgsl_device *device,
+ unsigned int *index, unsigned int reg, int mask_len)
+{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ unsigned int val;
+
+ /* This function is only for adreno A3XX and beyond */
+ BUG_ON(adreno_is_a2xx(adreno_dev));
+
+ /* There are only 16 registers available */
+ BUG_ON(*index >= 16);
+
+ val = 0x60000000 | ((mask_len & 0x1F) << 24) | ((reg << 2) & 0x1FFFF);
+
+ /*
+ * Write the protection range to the next available protection
+ * register
+ */
+
+ kgsl_regwrite(device, A3XX_CP_PROTECT_REG_0 + *index, val);
+ *index = *index + 1;
+}
+
+#ifdef CONFIG_DEBUG_FS
+void adreno_debugfs_init(struct kgsl_device *device);
+#else
+static inline void adreno_debugfs_init(struct kgsl_device *device) { }
+#endif
+
#endif /*__ADRENO_H */
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index 3d4c66a..eed11c3 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -3029,8 +3029,8 @@
GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000001);
GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
- /* Protected mode control - turned off for A3XX */
- GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
+ /* Enable protected mode */
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x20000000);
GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
@@ -3092,9 +3092,16 @@
case A3XX_INT_CP_HW_FAULT:
err = "ringbuffer hardware fault";
break;
- case A3XX_INT_CP_REG_PROTECT_FAULT:
- err = "ringbuffer protected mode error interrupt";
- break;
+ case A3XX_INT_CP_REG_PROTECT_FAULT: {
+ unsigned int reg;
+ kgsl_regread(device, A3XX_CP_PROTECT_STATUS, ®);
+
+ KGSL_DRV_CRIT(device,
+ "CP | Protected mode error| %s | addr=%x\n",
+ reg & (1 << 24) ? "WRITE" : "READ",
+ (reg & 0x1FFFF) >> 2);
+ goto done;
+ }
case A3XX_INT_CP_AHB_ERROR_HALT:
err = "ringbuffer AHB error interrupt";
break;
@@ -4115,29 +4122,35 @@
*/
static void a3xx_protect_init(struct kgsl_device *device)
{
+ int index = 0;
+
/* enable access protection to privileged registers */
kgsl_regwrite(device, A3XX_CP_PROTECT_CTRL, 0x00000007);
/* RBBM registers */
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_0, 0x63000040);
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_1, 0x62000080);
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_2, 0x600000CC);
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_3, 0x60000108);
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_4, 0x64000140);
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_5, 0x66000400);
+ adreno_set_protected_registers(device, &index, 0x18, 0);
+ adreno_set_protected_registers(device, &index, 0x20, 2);
+ adreno_set_protected_registers(device, &index, 0x33, 0);
+ adreno_set_protected_registers(device, &index, 0x42, 0);
+ adreno_set_protected_registers(device, &index, 0x50, 4);
+ adreno_set_protected_registers(device, &index, 0x63, 0);
+ adreno_set_protected_registers(device, &index, 0x100, 4);
/* CP registers */
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_6, 0x65000700);
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_7, 0x610007D8);
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_8, 0x620007E0);
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_9, 0x61001178);
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_A, 0x64001180);
+ adreno_set_protected_registers(device, &index, 0x1C0, 5);
+ adreno_set_protected_registers(device, &index, 0x1F6, 1);
+ adreno_set_protected_registers(device, &index, 0x1F8, 2);
+ adreno_set_protected_registers(device, &index, 0x45E, 2);
+ adreno_set_protected_registers(device, &index, 0x460, 4);
/* RB registers */
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_B, 0x60003300);
+ adreno_set_protected_registers(device, &index, 0xCC0, 0);
/* VBIF registers */
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_C, 0x6B00C000);
+ adreno_set_protected_registers(device, &index, 0x3000, 6);
+
+ /* SMMU registers */
+ adreno_set_protected_registers(device, &index, 0x4000, 14);
}
static void a3xx_start(struct adreno_device *adreno_dev)
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index 8d7b803..9aae497 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -696,7 +696,7 @@
/* Add space for the power on shader fixup if we need it */
if (flags & KGSL_CMD_FLAGS_PWRON_FIXUP)
- total_sizedwords += 5;
+ total_sizedwords += 9;
ringcmds = adreno_ringbuffer_allocspace(rb, drawctxt, total_sizedwords);
@@ -718,6 +718,11 @@
}
if (flags & KGSL_CMD_FLAGS_PWRON_FIXUP) {
+ /* Disable protected mode for the fixup */
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
+ cp_type3_packet(CP_SET_PROTECTED_MODE, 1));
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, 0);
+
GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, cp_nop_packet(1));
GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
KGSL_PWRON_FIXUP_IDENTIFIER);
@@ -727,6 +732,11 @@
adreno_dev->pwron_fixup.gpuaddr);
GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
adreno_dev->pwron_fixup_dwords);
+
+ /* Re-enable protected mode */
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
+ cp_type3_packet(CP_SET_PROTECTED_MODE, 1));
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, 1);
}
/* Add any IB required for profiling if it is enabled */
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 0a7ba30..2af8d27 100755
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -461,7 +461,7 @@
* Disables iommu clocks
* Return - void
*/
-static void kgsl_iommu_disable_clk(struct kgsl_mmu *mmu)
+static void kgsl_iommu_disable_clk(struct kgsl_mmu *mmu, int ctx_id)
{
struct kgsl_iommu *iommu = mmu->priv;
struct msm_iommu_drvdata *iommu_drvdata;
@@ -470,8 +470,15 @@
for (i = 0; i < iommu->unit_count; i++) {
struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
for (j = 0; j < iommu_unit->dev_count; j++) {
- if (!iommu_unit->dev[j].clk_enabled)
+ if (ctx_id != iommu_unit->dev[j].ctx_id)
continue;
+ atomic_dec(&iommu_unit->dev[j].clk_enable_count);
+ BUG_ON(
+ atomic_read(&iommu_unit->dev[j].clk_enable_count) < 0);
+ /*
+ * the clock calls have a refcount so call them on every
+ * enable/disable call
+ */
iommu_drvdata = dev_get_drvdata(
iommu_unit->dev[j].dev->parent);
if (iommu_drvdata->aclk)
@@ -479,7 +486,6 @@
if (iommu_drvdata->clk)
clk_disable_unprepare(iommu_drvdata->clk);
clk_disable_unprepare(iommu_drvdata->pclk);
- iommu_unit->dev[j].clk_enabled = false;
}
}
}
@@ -500,32 +506,14 @@
unsigned int id, unsigned int ts,
u32 type)
{
- struct kgsl_mmu *mmu = data;
- struct kgsl_iommu *iommu = mmu->priv;
+ struct kgsl_iommu_disable_clk_param *param = data;
- if (!iommu->clk_event_queued) {
- if (0 > timestamp_cmp(ts, iommu->iommu_last_cmd_ts))
- KGSL_DRV_ERR(device,
- "IOMMU disable clock event being cancelled, "
- "iommu_last_cmd_ts: %x, retired ts: %x\n",
- iommu->iommu_last_cmd_ts, ts);
- return;
- }
-
- if (0 <= timestamp_cmp(ts, iommu->iommu_last_cmd_ts)) {
- kgsl_iommu_disable_clk(mmu);
- iommu->clk_event_queued = false;
- } else {
- /* add new event to fire when ts is reached, this can happen
- * if we queued an event and someone requested the clocks to
- * be disbaled on a later timestamp */
- if (kgsl_add_event(device, id, iommu->iommu_last_cmd_ts,
- kgsl_iommu_clk_disable_event, mmu, mmu)) {
- KGSL_DRV_ERR(device,
- "Failed to add IOMMU disable clk event\n");
- iommu->clk_event_queued = false;
- }
- }
+ if ((0 <= timestamp_cmp(ts, param->ts)) ||
+ (KGSL_EVENT_CANCELLED == type))
+ kgsl_iommu_disable_clk(param->mmu, param->ctx_id);
+ else
+ /* something went wrong with the event handling mechanism */
+ BUG_ON(1);
}
/*
@@ -535,6 +523,8 @@
* @ts_valid - Indicates whether ts parameter is valid, if this parameter
* is false then it means that the calling function wants to disable the
* IOMMU clocks immediately without waiting for any timestamp
+ * @ctx_id: Context id of the IOMMU context for which clocks are to be
+ * turned off
*
* Creates an event to disable the IOMMU clocks on timestamp and if event
* already exists then updates the timestamp of disabling the IOMMU clocks
@@ -543,28 +533,25 @@
* Return - void
*/
static void
-kgsl_iommu_disable_clk_on_ts(struct kgsl_mmu *mmu, unsigned int ts,
- bool ts_valid)
+kgsl_iommu_disable_clk_on_ts(struct kgsl_mmu *mmu,
+ unsigned int ts, int ctx_id)
{
- struct kgsl_iommu *iommu = mmu->priv;
+ struct kgsl_iommu_disable_clk_param *param;
- if (iommu->clk_event_queued) {
- if (ts_valid && (0 <
- timestamp_cmp(ts, iommu->iommu_last_cmd_ts)))
- iommu->iommu_last_cmd_ts = ts;
- } else {
- if (ts_valid) {
- iommu->iommu_last_cmd_ts = ts;
- iommu->clk_event_queued = true;
- if (kgsl_add_event(mmu->device, KGSL_MEMSTORE_GLOBAL,
- ts, kgsl_iommu_clk_disable_event, mmu, mmu)) {
- KGSL_DRV_ERR(mmu->device,
- "Failed to add IOMMU disable clk event\n");
- iommu->clk_event_queued = false;
- }
- } else {
- kgsl_iommu_disable_clk(mmu);
- }
+ param = kzalloc(sizeof(*param), GFP_KERNEL);
+ if (!param) {
+ KGSL_CORE_ERR("kzalloc(%d) failed\n", sizeof(*param));
+ return;
+ }
+ param->mmu = mmu;
+ param->ctx_id = ctx_id;
+ param->ts = ts;
+
+ if (kgsl_add_event(mmu->device, KGSL_MEMSTORE_GLOBAL,
+ ts, kgsl_iommu_clk_disable_event, param, mmu)) {
+ KGSL_DRV_ERR(mmu->device,
+ "Failed to add IOMMU disable clk event\n");
+ kfree(param);
}
}
@@ -587,8 +574,7 @@
for (i = 0; i < iommu->unit_count; i++) {
struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
for (j = 0; j < iommu_unit->dev_count; j++) {
- if (iommu_unit->dev[j].clk_enabled ||
- ctx_id != iommu_unit->dev[j].ctx_id)
+ if (ctx_id != iommu_unit->dev[j].ctx_id)
continue;
iommu_drvdata =
dev_get_drvdata(iommu_unit->dev[j].dev->parent);
@@ -614,12 +600,25 @@
goto done;
}
}
- iommu_unit->dev[j].clk_enabled = true;
+ atomic_inc(&iommu_unit->dev[j].clk_enable_count);
}
}
done:
- if (ret)
- kgsl_iommu_disable_clk(mmu);
+ if (ret) {
+ struct kgsl_iommu_unit *iommu_unit;
+ if (iommu->unit_count == i)
+ i--;
+ iommu_unit = &iommu->iommu_units[i];
+ do {
+ for (j--; j >= 0; j--)
+ kgsl_iommu_disable_clk(mmu, ctx_id);
+ i--;
+ if (i >= 0) {
+ iommu_unit = &iommu->iommu_units[i];
+ j = iommu_unit->dev_count;
+ }
+ } while (i >= 0);
+ }
return ret;
}
@@ -848,6 +847,9 @@
ret = -EINVAL;
goto done;
}
+ atomic_set(
+ &(iommu_unit->dev[iommu_unit->dev_count].clk_enable_count),
+ 0);
iommu_unit->dev[iommu_unit->dev_count].dev =
msm_iommu_get_ctx(data->iommu_ctxs[i].iommu_ctx_name);
@@ -1674,6 +1676,7 @@
}
status = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_PRIV);
if (status) {
+ kgsl_iommu_disable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
KGSL_CORE_ERR("clk enable failed\n");
goto done;
}
@@ -1728,14 +1731,11 @@
KGSL_IOMMU_SETSTATE_NOP_OFFSET,
cp_nop_packet(1), sizeof(unsigned int));
- kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
+ kgsl_iommu_disable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
+ kgsl_iommu_disable_clk(mmu, KGSL_IOMMU_CONTEXT_PRIV);
mmu->flags |= KGSL_FLAGS_STARTED;
done:
- if (status) {
- kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
- kgsl_detach_pagetable_iommu_domain(mmu);
- }
return status;
}
@@ -1858,6 +1858,7 @@
iommu_unit,
iommu_unit->dev[j].ctx_id,
FSR, 0);
+ kgsl_iommu_disable_clk(mmu, j);
_iommu_unlock(iommu);
iommu_unit->dev[j].fault = 0;
}
@@ -1870,7 +1871,6 @@
static void kgsl_iommu_stop(struct kgsl_mmu *mmu)
{
- struct kgsl_iommu *iommu = mmu->priv;
/*
* stop device mmu
*
@@ -1886,9 +1886,7 @@
kgsl_iommu_pagefault_resume(mmu);
}
/* switch off MMU clocks and cancel any events it has queued */
- iommu->clk_event_queued = false;
kgsl_cancel_events(mmu->device, mmu);
- kgsl_iommu_disable_clk(mmu);
}
static int kgsl_iommu_close(struct kgsl_mmu *mmu)
@@ -1938,10 +1936,10 @@
return 0;
/* Return the current pt base by reading IOMMU pt_base register */
kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
- pt_base = KGSL_IOMMU_GET_CTX_REG(iommu, (&iommu->iommu_units[0]),
- KGSL_IOMMU_CONTEXT_USER,
- TTBR0);
- kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
+ pt_base = KGSL_IOMMU_GET_CTX_REG(iommu,
+ (&iommu->iommu_units[0]),
+ KGSL_IOMMU_CONTEXT_USER, TTBR0);
+ kgsl_iommu_disable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
return pt_base & KGSL_IOMMU_CTX_TTBR0_ADDR_MASK;
}
@@ -1969,7 +1967,6 @@
phys_addr_t pt_val;
ret = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
-
if (ret) {
KGSL_DRV_ERR(mmu->device, "Failed to enable iommu clocks\n");
return ret;
@@ -2056,7 +2053,7 @@
_iommu_unlock(iommu);
/* Disable smmu clock */
- kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
+ kgsl_iommu_disable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
return ret;
}
diff --git a/drivers/gpu/msm/kgsl_iommu.h b/drivers/gpu/msm/kgsl_iommu.h
index 7dca40e..3878107 100644
--- a/drivers/gpu/msm/kgsl_iommu.h
+++ b/drivers/gpu/msm/kgsl_iommu.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -143,6 +143,7 @@
* are on, else the clocks are off
* fault: Flag when set indicates that this iommu device has caused a page
* fault
+ * @clk_enable_count: The ref count of clock enable calls
*/
struct kgsl_iommu_device {
struct device *dev;
@@ -152,6 +153,7 @@
bool clk_enabled;
struct kgsl_device *kgsldev;
int fault;
+ atomic_t clk_enable_count;
};
/*
@@ -182,10 +184,6 @@
* iommu contexts owned by graphics cores
* @unit_count: Number of IOMMU units that are available for this
* instance of the IOMMU driver
- * @iommu_last_cmd_ts: The timestamp of last command submitted that
- * aceeses iommu registers
- * @clk_event_queued: Indicates whether an event to disable clocks
- * is already queued or not
* @device: Pointer to kgsl device
* @ctx_offset: The context offset to be added to base address when
* accessing IOMMU registers
@@ -201,8 +199,6 @@
struct kgsl_iommu {
struct kgsl_iommu_unit iommu_units[KGSL_IOMMU_MAX_UNITS];
unsigned int unit_count;
- unsigned int iommu_last_cmd_ts;
- bool clk_event_queued;
struct kgsl_device *device;
unsigned int ctx_offset;
struct kgsl_iommu_register_list *iommu_reg_list;
@@ -222,4 +218,18 @@
struct kgsl_iommu *iommu;
};
+/*
+ * struct kgsl_iommu_disable_clk_param - Parameter struct for disble clk event
+ * @mmu: The mmu pointer
+ * @rb_level: the rb level in which the timestamp of the event belongs to
+ * @ctx_id: The IOMMU context whose clock is to be turned off
+ * @ts: Timestamp on which clock is to be disabled
+ */
+struct kgsl_iommu_disable_clk_param {
+ struct kgsl_mmu *mmu;
+ int rb_level;
+ int ctx_id;
+ unsigned int ts;
+};
+
#endif
diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h
index 040a3a7..5e3386a 100644
--- a/drivers/gpu/msm/kgsl_mmu.h
+++ b/drivers/gpu/msm/kgsl_mmu.h
@@ -144,11 +144,12 @@
void (*mmu_pagefault_resume)
(struct kgsl_mmu *mmu);
void (*mmu_disable_clk_on_ts)
- (struct kgsl_mmu *mmu, uint32_t ts, bool ts_valid);
+ (struct kgsl_mmu *mmu,
+ uint32_t ts, int ctx_id);
int (*mmu_enable_clk)
(struct kgsl_mmu *mmu, int ctx_id);
void (*mmu_disable_clk)
- (struct kgsl_mmu *mmu);
+ (struct kgsl_mmu *mmu, int ctx_id);
phys_addr_t (*mmu_get_default_ttbr0)(struct kgsl_mmu *mmu,
unsigned int unit_id,
enum kgsl_iommu_context_id ctx_id);
@@ -327,17 +328,18 @@
return 0;
}
-static inline void kgsl_mmu_disable_clk(struct kgsl_mmu *mmu)
+static inline void kgsl_mmu_disable_clk(struct kgsl_mmu *mmu, int ctx_id)
{
if (mmu->mmu_ops && mmu->mmu_ops->mmu_disable_clk)
- mmu->mmu_ops->mmu_disable_clk(mmu);
+ mmu->mmu_ops->mmu_disable_clk(mmu, ctx_id);
}
static inline void kgsl_mmu_disable_clk_on_ts(struct kgsl_mmu *mmu,
- unsigned int ts, bool ts_valid)
+ unsigned int ts,
+ int ctx_id)
{
if (mmu->mmu_ops && mmu->mmu_ops->mmu_disable_clk_on_ts)
- mmu->mmu_ops->mmu_disable_clk_on_ts(mmu, ts, ts_valid);
+ mmu->mmu_ops->mmu_disable_clk_on_ts(mmu, ts, ctx_id);
}
static inline unsigned int kgsl_mmu_get_int_mask(void)
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 96ff1b8..c00e978 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -1430,8 +1430,6 @@
break;
}
- kgsl_mmu_disable_clk_on_ts(&device->mmu, 0, false);
-
return 0;
}
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index 32c94dc..067a887 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -129,6 +129,12 @@
#define QPNP_BIT_SHIFT_8 8
#define QPNP_RSENSE_MSB_SIGN_CHECK 0x80
#define QPNP_ADC_COMPLETION_TIMEOUT HZ
+#define SMBB_BAT_IF_TRIM_CNST_RDS_MASK 0x7
+#define SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST 2
+#define QPNP_IADC1_USR_TRIM2_ADC_FULLSCALE1_CONST 127
+#define QPNP_IADC_RSENSE_DEFAULT_VALUE 7800000
+#define QPNP_IADC_RSENSE_DEFAULT_TYPEB_GF 9000000
+#define QPNP_IADC_RSENSE_DEFAULT_TYPEB_SMIC 9700000
struct qpnp_iadc_comp {
bool ext_rsense;
@@ -143,6 +149,7 @@
struct qpnp_adc_drv *adc;
int32_t rsense;
bool external_rsense;
+ bool default_internal_rsense;
struct device *iadc_hwmon;
struct list_head list;
int64_t die_temp;
@@ -153,11 +160,20 @@
struct work_struct trigger_completion_work;
bool skip_auto_calibrations;
bool iadc_poll_eoc;
+ u16 batt_id_trim_cnst_rds;
+ int rds_trim_default_type;
+ bool rds_trim_default_check;
+ int32_t rsense_workaround_value;
struct sensor_device_attribute sens_attr[0];
};
LIST_HEAD(qpnp_iadc_device_list);
+enum qpnp_iadc_rsense_rds_workaround {
+ QPNP_IADC_RDS_DEFAULT_TYPEA,
+ QPNP_IADC_RDS_DEFAULT_TYPEB,
+};
+
static int32_t qpnp_iadc_read_reg(struct qpnp_iadc_chip *iadc,
uint32_t reg, u8 *data)
{
@@ -573,6 +589,67 @@
}
EXPORT_SYMBOL(qpnp_iadc_comp_result);
+static int qpnp_iadc_rds_trim_update_check(struct qpnp_iadc_chip *iadc)
+{
+ int rc = 0;
+ u8 trim2_val = 0, smbb_batt_trm_data = 0;
+
+ if (!iadc->rds_trim_default_check) {
+ pr_debug("No internal rds trim check needed\n");
+ return 0;
+ }
+
+ rc = qpnp_iadc_read_reg(iadc, QPNP_IADC_NOMINAL_RSENSE, &trim2_val);
+ if (rc < 0) {
+ pr_err("qpnp adc trim2_fullscale1 reg read failed %d\n", rc);
+ return rc;
+ }
+
+ rc = spmi_ext_register_readl(iadc->adc->spmi->ctrl, iadc->adc->slave,
+ iadc->batt_id_trim_cnst_rds, &smbb_batt_trm_data, 1);
+ if (rc < 0) {
+ pr_err("batt_id trim_cnst rds reg read failed %d\n", rc);
+ return rc;
+ }
+
+ pr_debug("n_trim:0x%x smb_trm:0x%x\n", trim2_val, smbb_batt_trm_data);
+
+ if (iadc->rds_trim_default_type == QPNP_IADC_RDS_DEFAULT_TYPEA) {
+ if (((smbb_batt_trm_data & SMBB_BAT_IF_TRIM_CNST_RDS_MASK) ==
+ SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST) &&
+ (trim2_val == QPNP_IADC1_USR_TRIM2_ADC_FULLSCALE1_CONST)) {
+ iadc->rsense_workaround_value =
+ QPNP_IADC_RSENSE_DEFAULT_VALUE;
+ iadc->default_internal_rsense = true;
+ }
+ } else if (iadc->rds_trim_default_type ==
+ QPNP_IADC_RDS_DEFAULT_TYPEB) {
+ if (((smbb_batt_trm_data & SMBB_BAT_IF_TRIM_CNST_RDS_MASK) >=
+ SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST) &&
+ (trim2_val == QPNP_IADC1_USR_TRIM2_ADC_FULLSCALE1_CONST)) {
+ iadc->rsense_workaround_value =
+ QPNP_IADC_RSENSE_DEFAULT_VALUE;
+ iadc->default_internal_rsense = true;
+ } else if (((smbb_batt_trm_data &
+ SMBB_BAT_IF_TRIM_CNST_RDS_MASK)
+ < SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST) &&
+ (trim2_val ==
+ QPNP_IADC1_USR_TRIM2_ADC_FULLSCALE1_CONST)) {
+ if (iadc->iadc_comp.id == COMP_ID_GF) {
+ iadc->rsense_workaround_value =
+ QPNP_IADC_RSENSE_DEFAULT_TYPEB_GF;
+ iadc->default_internal_rsense = true;
+ } else if (iadc->iadc_comp.id == COMP_ID_SMIC) {
+ iadc->rsense_workaround_value =
+ QPNP_IADC_RSENSE_DEFAULT_TYPEB_SMIC;
+ iadc->default_internal_rsense = true;
+ }
+ }
+ }
+
+ return 0;
+}
+
static int32_t qpnp_iadc_comp_info(struct qpnp_iadc_chip *iadc)
{
int rc = 0;
@@ -948,6 +1025,11 @@
return rc;
}
+ if (iadc->default_internal_rsense) {
+ *rsense = iadc->rsense_workaround_value;
+ return rc;
+ }
+
rc = qpnp_iadc_read_reg(iadc, QPNP_IADC_NOMINAL_RSENSE, &rslt_rsense);
if (rc < 0) {
pr_err("qpnp adc rsense read failed with %d\n", rc);
@@ -968,6 +1050,8 @@
*rsense = QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR +
(rslt_rsense * QPNP_IADC_RSENSE_LSB_N_OHMS_PER_BIT);
+ pr_debug("rsense value is %d\n", *rsense);
+
return rc;
}
EXPORT_SYMBOL(qpnp_iadc_get_rsense);
@@ -1258,6 +1342,7 @@
struct qpnp_adc_drv *adc_qpnp;
struct device_node *node = spmi->dev.of_node;
struct device_node *child;
+ struct resource *res;
int rc, count_adc_channel_list = 0, i = 0;
for_each_child_of_node(node, child)
@@ -1292,6 +1377,22 @@
return rc;
}
+ res = spmi_get_resource_byname(spmi, NULL, IORESOURCE_MEM,
+ "batt-id-trim-cnst-rds");
+ if (!res) {
+ dev_err(&spmi->dev, "failed to read batt_id trim register\n");
+ return -EINVAL;
+ }
+ iadc->batt_id_trim_cnst_rds = res->start;
+ rc = of_property_read_u32(node, "qcom,use-default-rds-trim",
+ &iadc->rds_trim_default_type);
+ if (rc)
+ pr_debug("No trim workaround needed\n");
+ else {
+ pr_debug("Use internal RDS trim workaround\n");
+ iadc->rds_trim_default_check = true;
+ }
+
iadc->vadc_dev = qpnp_get_vadc(&spmi->dev, "iadc");
if (IS_ERR(iadc->vadc_dev)) {
rc = PTR_ERR(iadc->vadc_dev);
@@ -1345,6 +1446,12 @@
goto fail;
}
+ rc = qpnp_iadc_rds_trim_update_check(iadc);
+ if (rc) {
+ dev_err(&spmi->dev, "Rds trim update failed!\n");
+ goto fail;
+ }
+
dev_set_drvdata(&spmi->dev, iadc);
list_add(&iadc->list, &qpnp_iadc_device_list);
rc = qpnp_iadc_calibrate_for_trim(iadc, true);
diff --git a/drivers/iommu/msm_iommu_dev-v1.c b/drivers/iommu/msm_iommu_dev-v1.c
index bbbe77b..a9d164e 100644
--- a/drivers/iommu/msm_iommu_dev-v1.c
+++ b/drivers/iommu/msm_iommu_dev-v1.c
@@ -208,10 +208,14 @@
ret = of_platform_populate(pdev->dev.of_node,
msm_iommu_v1_ctx_match_table,
NULL, &pdev->dev);
- if (ret)
+ if (ret) {
pr_err("Failed to create iommu context device\n");
+ goto fail;
+ }
msm_iommu_add_drv(drvdata);
+ return 0;
+
fail:
__put_bus_vote_client(drvdata);
return ret;
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index 40c5568..3561788 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -1595,27 +1595,30 @@
qpnp_chg_bat_if_batt_temp_irq_handler(int irq, void *_chip)
{
struct qpnp_chg_chip *chip = _chip;
- int batt_temp_good, rc;
+ int batt_temp_good, batt_present, rc;
batt_temp_good = qpnp_chg_is_batt_temp_ok(chip);
pr_debug("batt-temp triggered: %d\n", batt_temp_good);
- rc = qpnp_chg_masked_write(chip,
- chip->buck_base + SEC_ACCESS,
- 0xFF,
- 0xA5, 1);
- if (rc) {
- pr_err("failed to write SEC_ACCESS rc=%d\n", rc);
- return rc;
- }
+ batt_present = qpnp_chg_is_batt_present(chip);
+ if (batt_present) {
+ rc = qpnp_chg_masked_write(chip,
+ chip->buck_base + SEC_ACCESS,
+ 0xFF,
+ 0xA5, 1);
+ if (rc) {
+ pr_err("failed to write SEC_ACCESS rc=%d\n", rc);
+ return rc;
+ }
- rc = qpnp_chg_masked_write(chip,
- chip->buck_base + TEST_EN_SMBC_LOOP,
- IBAT_REGULATION_DISABLE,
- batt_temp_good ? 0 : IBAT_REGULATION_DISABLE, 1);
- if (rc) {
- pr_err("failed to write COMP_OVR1 rc=%d\n", rc);
- return rc;
+ rc = qpnp_chg_masked_write(chip,
+ chip->buck_base + TEST_EN_SMBC_LOOP,
+ IBAT_REGULATION_DISABLE,
+ batt_temp_good ? 0 : IBAT_REGULATION_DISABLE, 1);
+ if (rc) {
+ pr_err("failed to write COMP_OVR1 rc=%d\n", rc);
+ return rc;
+ }
}
pr_debug("psy changed batt_psy\n");
@@ -1627,15 +1630,51 @@
qpnp_chg_bat_if_batt_pres_irq_handler(int irq, void *_chip)
{
struct qpnp_chg_chip *chip = _chip;
- int batt_present;
+ int batt_present, batt_temp_good, rc;
batt_present = qpnp_chg_is_batt_present(chip);
pr_debug("batt-pres triggered: %d\n", batt_present);
if (chip->batt_present ^ batt_present) {
if (batt_present) {
+ batt_temp_good = qpnp_chg_is_batt_temp_ok(chip);
+ rc = qpnp_chg_masked_write(chip,
+ chip->buck_base + SEC_ACCESS,
+ 0xFF,
+ 0xA5, 1);
+ if (rc) {
+ pr_err("failed to write SEC_ACCESS: %d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->buck_base + TEST_EN_SMBC_LOOP,
+ IBAT_REGULATION_DISABLE,
+ batt_temp_good
+ ? 0 : IBAT_REGULATION_DISABLE, 1);
+ if (rc) {
+ pr_err("failed to write COMP_OVR1 rc=%d\n", rc);
+ return rc;
+ }
schedule_work(&chip->insertion_ocv_work);
} else {
+ rc = qpnp_chg_masked_write(chip,
+ chip->buck_base + SEC_ACCESS,
+ 0xFF,
+ 0xA5, 1);
+ if (rc) {
+ pr_err("failed to write SEC_ACCESS: %d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->buck_base + TEST_EN_SMBC_LOOP,
+ IBAT_REGULATION_DISABLE,
+ 0, 1);
+ if (rc) {
+ pr_err("failed to write COMP_OVR1 rc=%d\n", rc);
+ return rc;
+ }
chip->insertion_ocv_uv = 0;
qpnp_chg_charge_en(chip, 0);
}
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
index 45400cb..39e81fa 100644
--- a/drivers/spi/spi_qsd.c
+++ b/drivers/spi/spi_qsd.c
@@ -1167,10 +1167,8 @@
if ((!dd->read_buf || op & SPI_OP_MAX_INPUT_DONE_FLAG) &&
(!dd->write_buf || op & SPI_OP_MAX_OUTPUT_DONE_FLAG)) {
msm_spi_ack_transfer(dd);
- if (dd->rx_unaligned_len == 0) {
if (atomic_inc_return(&dd->rx_irq_called) == 1)
return IRQ_HANDLED;
- }
msm_spi_complete(dd);
return IRQ_HANDLED;
}
diff --git a/drivers/usb/dwc3/dwc3_otg.c b/drivers/usb/dwc3/dwc3_otg.c
index cacd635..8a034d6 100644
--- a/drivers/usb/dwc3/dwc3_otg.c
+++ b/drivers/usb/dwc3/dwc3_otg.c
@@ -197,6 +197,14 @@
if (on) {
dev_dbg(otg->phy->dev, "%s: turn on host\n", __func__);
+ dwc3_otg_notify_host_mode(otg, on);
+ ret = regulator_enable(dotg->vbus_otg);
+ if (ret) {
+ dev_err(otg->phy->dev, "unable to enable vbus_otg\n");
+ dwc3_otg_notify_host_mode(otg, 0);
+ return ret;
+ }
+
/*
* This should be revisited for more testing post-silicon.
* In worst case we may need to disconnect the root hub
@@ -222,14 +230,8 @@
dev_err(otg->phy->dev,
"%s: failed to add XHCI pdev ret=%d\n",
__func__, ret);
- return ret;
- }
-
- dwc3_otg_notify_host_mode(otg, on);
- ret = regulator_enable(dotg->vbus_otg);
- if (ret) {
- dev_err(otg->phy->dev, "unable to enable vbus_otg\n");
- platform_device_del(dwc->xhci);
+ regulator_disable(dotg->vbus_otg);
+ dwc3_otg_notify_host_mode(otg, 0);
return ret;
}
diff --git a/drivers/video/msm/mdss/mdss_hdmi_edid.c b/drivers/video/msm/mdss/mdss_hdmi_edid.c
index a45876b..d0b89a3 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_edid.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_edid.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1120,7 +1120,7 @@
u8 i = 0, offset = 0, std_blk = 0;
u32 video_format = HDMI_VFRMT_640x480p60_4_3;
u32 has480p = false;
- u8 len;
+ u8 len = 0;
int rc;
const u8 *edid_blk0 = NULL;
const u8 *edid_blk1 = NULL;
@@ -1140,7 +1140,7 @@
hdmi_edid_find_block(data_buf+0x80, DBC_START_OFFSET,
VIDEO_DATA_BLOCK, &len) : NULL;
- if (svd == NULL || len == 0 || len > MAX_DATA_BLOCK_SIZE) {
+ if (num_of_cea_blocks && (len == 0 || len > MAX_DATA_BLOCK_SIZE)) {
DEV_DBG("%s: No/Invalid Video Data Block\n",
__func__);
return;
diff --git a/include/linux/input.h b/include/linux/input.h
index 558178b..77fc253 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -850,6 +850,7 @@
#define SW_HPHL_OVERCURRENT 0x0e /* set = over current on left hph */
#define SW_HPHR_OVERCURRENT 0x0f /* set = over current on right hph */
#define SW_UNSUPPORT_INSERT 0x10 /* set = unsupported device inserted */
+#define SW_MICROPHONE2_INSERT 0x11 /* set = inserted */
#define SW_MAX 0x20
#define SW_CNT (SW_MAX+1)
diff --git a/include/sound/jack.h b/include/sound/jack.h
index 8e8c133..4a88c22 100644
--- a/include/sound/jack.h
+++ b/include/sound/jack.h
@@ -47,6 +47,9 @@
SND_JACK_OC_HPHL = 0x0000040,
SND_JACK_OC_HPHR = 0x0000080,
SND_JACK_UNSUPPORTED = 0x0000100,
+ SND_JACK_MICROPHONE2 = 0x0000200,
+ SND_JACK_ANC_HEADPHONE = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
+ SND_JACK_MICROPHONE2,
/* Kept separate from switches to facilitate implementation */
SND_JACK_BTN_0 = 0x4000000,
SND_JACK_BTN_1 = 0x2000000,
diff --git a/sound/core/jack.c b/sound/core/jack.c
index 9e2e085..6e2dbdb 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -35,6 +35,7 @@
SW_HPHL_OVERCURRENT,
SW_HPHR_OVERCURRENT,
SW_UNSUPPORT_INSERT,
+ SW_MICROPHONE2_INSERT,
};
static int snd_jack_dev_free(struct snd_device *device)
diff --git a/sound/soc/codecs/msm8x10-wcd.c b/sound/soc/codecs/msm8x10-wcd.c
index 0c39f7e..fe4eb34 100644
--- a/sound/soc/codecs/msm8x10-wcd.c
+++ b/sound/soc/codecs/msm8x10-wcd.c
@@ -2798,10 +2798,16 @@
}
static int msm8x10_wcd_enable_mbhc_micbias(struct snd_soc_codec *codec,
- bool enable)
+ bool enable,
+ enum wcd9xxx_micbias_num micb_num)
{
int rc;
+ if (micb_num != MBHC_MICBIAS1) {
+ rc = -EINVAL;
+ goto err;
+ }
+
if (enable)
rc = snd_soc_dapm_force_enable_pin(&codec->dapm,
DAPM_MICBIAS_EXTERNAL_STANDALONE);
@@ -2812,6 +2818,7 @@
snd_soc_update_bits(codec, WCD9XXX_A_MICB_1_CTL,
0x80, enable ? 0x80 : 0x00);
+err:
if (rc)
pr_debug("%s: Failed to force %s micbias", __func__,
enable ? "enable" : "disable");
diff --git a/sound/soc/codecs/wcd9306.c b/sound/soc/codecs/wcd9306.c
index 07f4a5b..e3c9ad3 100644
--- a/sound/soc/codecs/wcd9306.c
+++ b/sound/soc/codecs/wcd9306.c
@@ -2284,16 +2284,23 @@
}
/* called under codec_resource_lock acquisition */
-static int tapan_enable_mbhc_micbias(struct snd_soc_codec *codec, bool enable)
+static int tapan_enable_mbhc_micbias(struct snd_soc_codec *codec, bool enable,
+ enum wcd9xxx_micbias_num micb_num)
{
int rc;
+ const char *micbias;
+
+ if (micb_num == MBHC_MICBIAS2)
+ micbias = DAPM_MICBIAS2_EXTERNAL_STANDALONE;
+ else
+ return -EINVAL;
if (enable)
rc = snd_soc_dapm_force_enable_pin(&codec->dapm,
- DAPM_MICBIAS2_EXTERNAL_STANDALONE);
+ micbias);
else
rc = snd_soc_dapm_disable_pin(&codec->dapm,
- DAPM_MICBIAS2_EXTERNAL_STANDALONE);
+ micbias);
if (!rc)
snd_soc_dapm_sync(&codec->dapm);
pr_debug("%s: leave ret %d\n", __func__, rc);
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index f874c43..cf33a9d 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -48,6 +48,7 @@
#define TAIKO_HPH_PA_SETTLE_COMP_OFF 13000
#define DAPM_MICBIAS2_EXTERNAL_STANDALONE "MIC BIAS2 External Standalone"
+#define DAPM_MICBIAS3_EXTERNAL_STANDALONE "MIC BIAS3 External Standalone"
/* RX_HPH_CNP_WG_TIME increases by 0.24ms */
#define TAIKO_WG_TIME_FACTOR_US 240
@@ -2823,16 +2824,26 @@
}
/* called under codec_resource_lock acquisition */
-static int taiko_enable_mbhc_micbias(struct snd_soc_codec *codec, bool enable)
+static int taiko_enable_mbhc_micbias(struct snd_soc_codec *codec, bool enable,
+ enum wcd9xxx_micbias_num micb_num)
{
int rc;
+ const char *micbias;
+
+ if (micb_num != MBHC_MICBIAS3 &&
+ micb_num != MBHC_MICBIAS2)
+ return -EINVAL;
+
+ micbias = (micb_num == MBHC_MICBIAS3) ?
+ DAPM_MICBIAS3_EXTERNAL_STANDALONE :
+ DAPM_MICBIAS2_EXTERNAL_STANDALONE;
if (enable)
rc = snd_soc_dapm_force_enable_pin(&codec->dapm,
- DAPM_MICBIAS2_EXTERNAL_STANDALONE);
+ micbias);
else
rc = snd_soc_dapm_disable_pin(&codec->dapm,
- DAPM_MICBIAS2_EXTERNAL_STANDALONE);
+ micbias);
if (!rc)
snd_soc_dapm_sync(&codec->dapm);
pr_debug("%s: leave ret %d\n", __func__, rc);
@@ -4013,6 +4024,7 @@
{"MIC BIAS3 External", NULL, "LDO_H"},
{"MIC BIAS4 External", NULL, "LDO_H"},
{DAPM_MICBIAS2_EXTERNAL_STANDALONE, NULL, "LDO_H Standalone"},
+ {DAPM_MICBIAS3_EXTERNAL_STANDALONE, NULL, "LDO_H Standalone"},
};
static int taiko_readable(struct snd_soc_codec *ssc, unsigned int reg)
@@ -5535,6 +5547,10 @@
taiko_codec_enable_micbias,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS3_EXTERNAL_STANDALONE, SND_SOC_NOPM,
+ 7, 0, taiko_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MICBIAS_E("MIC BIAS3 External", SND_SOC_NOPM, 7, 0,
taiko_codec_enable_micbias,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
@@ -6466,7 +6482,7 @@
__wr(WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL, 0xFF, 0x10);
/* Reset MBHC and set it up for STA */
__wr(WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xFF, 0x0A);
- __wr(WCD9XXX_A_CDC_MBHC_EN_CTL, 0xFF, 0x02);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
__wr(WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xFF, 0x02);
/* Set HPH_MBHC for zdet */
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
index 0bad20d1..4514378 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.c
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -43,7 +43,7 @@
#define WCD9XXX_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \
- SND_JACK_UNSUPPORTED)
+ SND_JACK_UNSUPPORTED | SND_JACK_MICROPHONE2)
#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 | \
@@ -64,6 +64,7 @@
#define STATUS_REL_DETECTION 0x0C
#define HS_DETECT_PLUG_TIME_MS (5 * 1000)
+#define ANC_HPH_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
@@ -179,7 +180,10 @@
uint32_t *zr);
static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
const enum wcd9xxx_current_v_idx idx);
-static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z);
+static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z,
+ struct mbhc_micbias_regs *micb_regs,
+ bool norel);
+
static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc);
static bool wcd9xxx_mbhc_polling(struct wcd9xxx_mbhc *mbhc)
@@ -610,13 +614,23 @@
}
static void wcd9xxx_get_mbhc_micbias_regs(struct wcd9xxx_mbhc *mbhc,
- struct mbhc_micbias_regs *micbias_regs)
+ enum wcd9xxx_mbhc_micbias_type mb_type)
{
unsigned int cfilt;
struct wcd9xxx_micbias_setting *micbias_pdata =
mbhc->resmgr->micbias_pdata;
+ struct mbhc_micbias_regs *micbias_regs;
+ enum wcd9xxx_micbias_num mb_num;
- switch (mbhc->mbhc_cfg->micbias) {
+ if (mb_type == MBHC_ANC_MIC_MB) {
+ micbias_regs = &mbhc->mbhc_anc_bias_regs;
+ mb_num = mbhc->mbhc_cfg->anc_micbias;
+ } else {
+ micbias_regs = &mbhc->mbhc_bias_regs;
+ mb_num = mbhc->mbhc_cfg->micbias;
+ }
+
+ switch (mb_num) {
case MBHC_MICBIAS1:
cfilt = micbias_pdata->bias1_cfilt_sel;
micbias_regs->mbhc_reg = WCD9XXX_A_MICB_1_MBHC;
@@ -654,19 +668,31 @@
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 = micbias_pdata->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 = micbias_pdata->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 = micbias_pdata->cfilt3_mv;
break;
}
+
+ if (mb_type == MBHC_PRIMARY_MIC_MB) {
+ switch (cfilt) {
+ case WCD9XXX_CFILT1_SEL:
+ mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt1_mv;
+ break;
+ case WCD9XXX_CFILT2_SEL:
+ mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt2_mv;
+ break;
+ case WCD9XXX_CFILT3_SEL:
+ mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt3_mv;
+ break;
+ }
+ }
+
}
static void wcd9xxx_clr_and_turnon_hph_padac(struct wcd9xxx_mbhc *mbhc)
@@ -819,7 +845,8 @@
if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
pr_debug("%s: Disabling micbias\n", __func__);
- mbhc->micbias_enable_cb(mbhc->codec, false);
+ mbhc->micbias_enable_cb(mbhc->codec, false,
+ mbhc->mbhc_cfg->micbias);
mbhc->micbias_enable = false;
}
mbhc->zl = mbhc->zr = 0;
@@ -845,7 +872,8 @@
if (mbhc->micbias_enable && mbhc->micbias_enable_cb &&
mbhc->hph_status == SND_JACK_HEADSET) {
pr_debug("%s: Disabling micbias\n", __func__);
- mbhc->micbias_enable_cb(mbhc->codec, false);
+ mbhc->micbias_enable_cb(mbhc->codec, false,
+ mbhc->mbhc_cfg->micbias);
mbhc->micbias_enable = false;
}
@@ -855,8 +883,10 @@
wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
0, WCD9XXX_JACK_MASK);
mbhc->hph_status &= ~(SND_JACK_HEADSET |
- SND_JACK_LINEOUT);
+ SND_JACK_LINEOUT |
+ SND_JACK_ANC_HEADPHONE);
}
+
/* Report insertion */
mbhc->hph_status |= jack_type;
@@ -870,11 +900,15 @@
mbhc->update_z = true;
} else if (jack_type == SND_JACK_LINEOUT) {
mbhc->current_plug = PLUG_TYPE_HIGH_HPH;
+ } else if (jack_type == SND_JACK_ANC_HEADPHONE) {
+ mbhc->polling_active = BUTTON_POLLING_SUPPORTED;
+ mbhc->current_plug = PLUG_TYPE_ANC_HEADPHONE;
}
if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
pr_debug("%s: Enabling micbias\n", __func__);
- mbhc->micbias_enable_cb(mbhc->codec, true);
+ mbhc->micbias_enable_cb(mbhc->codec, true,
+ mbhc->mbhc_cfg->micbias);
}
if (mbhc->impedance_detect && impedance_detect_en)
@@ -1041,7 +1075,14 @@
static short wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
bool norel)
{
- return __wcd9xxx_codec_sta_dce(mbhc, dce, false, norel);
+ bool override_bypass;
+
+ /* Bypass override if it is already enabled */
+ override_bypass = (snd_soc_read(mbhc->codec,
+ WCD9XXX_A_CDC_MBHC_B1_CTL) &
+ 0x04) ? true : false;
+
+ return __wcd9xxx_codec_sta_dce(mbhc, dce, override_bypass, norel);
}
static s32 __wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
@@ -1094,7 +1135,8 @@
/* called only from interrupt which is under codec_resource_lock acquisition */
static short wcd9xxx_mbhc_setup_hs_polling(struct wcd9xxx_mbhc *mbhc,
- bool is_cs_enable)
+ struct mbhc_micbias_regs *mbhc_micb_regs,
+ bool is_cs_enable)
{
struct snd_soc_codec *codec = mbhc->codec;
short bias_value;
@@ -1132,15 +1174,19 @@
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);
+ cfilt_mode = snd_soc_read(codec, mbhc_micb_regs->cfilt_ctl);
if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
else
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ snd_soc_update_bits(codec, mbhc_micb_regs->cfilt_ctl,
0x70, 0x00);
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
- snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ mbhc->scaling_mux_in);
+ pr_debug("%s: scaling_mux_input: %d\n", __func__,
+ mbhc->scaling_mux_in);
+
if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
mbhc->mbhc_cb->enable_mux_bias_block(codec);
else
@@ -1165,7 +1211,7 @@
/* don't flip override */
bias_value = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
- snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
+ snd_soc_write(codec, mbhc_micb_regs->cfilt_ctl, cfilt_mode);
snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
if (mbhc->mbhc_cfg->do_recalibration) {
@@ -1173,7 +1219,7 @@
reg = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
change = snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
0x78, btn_det->mbhc_nsc << 3);
- wcd9xxx_get_z(mbhc, &dce_z, &sta_z);
+ wcd9xxx_get_z(mbhc, &dce_z, &sta_z, mbhc_micb_regs, true);
if (change)
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg);
if (dce_z && sta_z) {
@@ -1197,7 +1243,8 @@
snd_soc_update_bits(mbhc->codec,
WCD9XXX_A_CDC_MBHC_B1_CTL,
0x78, WCD9XXX_MBHC_NSC_CS << 3);
- wcd9xxx_get_z(mbhc, &dce_z, NULL);
+ wcd9xxx_get_z(mbhc, &dce_z, NULL, mbhc_micb_regs,
+ true);
snd_soc_write(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
reg);
if (dce_z) {
@@ -1664,10 +1711,10 @@
return 0;
}
-void wcd9xxx_turn_onoff_current_source(struct wcd9xxx_mbhc *mbhc, bool on,
- bool highhph)
+void wcd9xxx_turn_onoff_current_source(struct wcd9xxx_mbhc *mbhc,
+ struct mbhc_micbias_regs *mbhc_micb_regs,
+ bool on, bool highhph)
{
-
struct snd_soc_codec *codec;
struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
@@ -1682,7 +1729,7 @@
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
0x78, 0x48);
/* pull down diode bit to 0 */
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ snd_soc_update_bits(codec, mbhc_micb_regs->mbhc_reg,
0x01, 0x00);
/*
* Keep the low power insertion/removal
@@ -1697,7 +1744,7 @@
* (INS_DET_ISRC_EN__ENABLE)
* MICB_2_MBHC__SCHT_TRIG_EN to 1
*/
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ snd_soc_update_bits(codec, mbhc_micb_regs->mbhc_reg,
0xF0, 0xF0);
/* Disconnect MBHC Override from MicBias and LDOH */
snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x00);
@@ -1706,16 +1753,16 @@
/* Connect MBHC Override from MicBias and LDOH */
snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x10);
/* INS_DET_ISRC_CTL to acdb value */
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ snd_soc_update_bits(codec, mbhc_micb_regs->mbhc_reg,
0x60, plug_det->mic_current << 5);
if (!highhph) {
/* INS_DET_ISRC_EN__ENABLE to 0 */
snd_soc_update_bits(codec,
- mbhc->mbhc_bias_regs.mbhc_reg,
+ mbhc_micb_regs->mbhc_reg,
0x80, 0x00);
/* MICB_2_MBHC__SCHT_TRIG_EN to 0 */
snd_soc_update_bits(codec,
- mbhc->mbhc_bias_regs.mbhc_reg,
+ mbhc_micb_regs->mbhc_reg,
0x10, 0x00);
}
/* Nsc to acdb value */
@@ -1742,7 +1789,8 @@
rt[0].vddio = false;
rt[0].hwvalue = true;
rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
- rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, true);
+ rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, &mbhc->mbhc_bias_regs,
+ true);
rt[0].mic_bias = false;
for (i = 1; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) {
@@ -1754,11 +1802,15 @@
wcd9xxx_codec_hphr_gnd_switch(codec, true);
if (rt[i].mic_bias)
- wcd9xxx_turn_onoff_current_source(mbhc, false, false);
+ wcd9xxx_turn_onoff_current_source(mbhc,
+ &mbhc->mbhc_bias_regs,
+ false, false);
rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, !highhph, true);
if (rt[i].mic_bias)
- wcd9xxx_turn_onoff_current_source(mbhc, true, false);
+ wcd9xxx_turn_onoff_current_source(mbhc,
+ &mbhc->mbhc_bias_regs,
+ true, false);
if (rt[i].swap_gnd)
wcd9xxx_codec_hphr_gnd_switch(codec, false);
}
@@ -1811,7 +1863,8 @@
wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
- rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, false);
+ rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, &mbhc->mbhc_bias_regs,
+ false);
rt[0].swap_gnd = false;
rt[0].vddio = false;
rt[0].hwvalue = true;
@@ -2004,10 +2057,178 @@
return 0;
}
+/*
+ * Function to determine whether anc microphone is preset or not.
+ * Return true if anc microphone is detected or false if not detected.
+ */
+static bool wcd9xxx_detect_anc_plug_type(struct wcd9xxx_mbhc *mbhc)
+{
+ struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT - 1];
+ bool anc_mic_found = true;
+ int i, mb_mv;
+ const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
+ WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+ s16 hs_max, dce_z;
+ s16 no_mic;
+ bool override_en;
+ bool timedout;
+ unsigned long timeout, retry = 0;
+ enum wcd9xxx_mbhc_plug_type type;
+ bool cs_enable;
+
+ if (mbhc->mbhc_cfg->anc_micbias != MBHC_MICBIAS3 &&
+ mbhc->mbhc_cfg->anc_micbias != MBHC_MICBIAS2)
+ return false;
+
+ pr_debug("%s: enter\n", __func__);
+
+ override_en = (snd_soc_read(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL) &
+ 0x04) ? true : false;
+ cs_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
+ (1 << MBHC_CS_ENABLE_DET_ANC)) != 0) &&
+ (!(snd_soc_read(mbhc->codec,
+ mbhc->mbhc_anc_bias_regs.ctl_reg) & 0x80)) &&
+ (mbhc->mbhc_cfg->micbias != mbhc->mbhc_cfg->anc_micbias);
+
+ if (cs_enable) {
+ wcd9xxx_turn_onoff_current_source(mbhc,
+ &mbhc->mbhc_anc_bias_regs,
+ true, false);
+ } else {
+ if (mbhc->mbhc_cfg->anc_micbias == MBHC_MICBIAS3) {
+ if (mbhc->micbias_enable_cb)
+ mbhc->micbias_enable_cb(mbhc->codec, true,
+ mbhc->mbhc_cfg->anc_micbias);
+ else
+ return false;
+ } else {
+ /* Enable override */
+ if (!override_en)
+ wcd9xxx_turn_onoff_override(mbhc, true);
+ }
+ }
+
+ if (!cs_enable) {
+ hs_max = plug_type->v_hs_max;
+ no_mic = plug_type->v_no_mic;
+ dce_z = mbhc->mbhc_data.dce_z;
+ mb_mv = mbhc->mbhc_data.micb_mv;
+ } else {
+ hs_max = WCD9XXX_V_CS_HS_MAX;
+ no_mic = WCD9XXX_V_CS_NO_MIC;
+ mb_mv = VDDIO_MICBIAS_MV;
+ dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
+ }
+
+ wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
+
+ timeout = jiffies + msecs_to_jiffies(ANC_HPH_DETECT_PLUG_TIME_MS);
+ anc_mic_found = true;
+
+ while (!(timedout = time_after(jiffies, timeout))) {
+ retry++;
+
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch level is low\n", __func__);
+ anc_mic_found = false;
+ break;
+ }
+
+ pr_debug("%s: Retry attempt %lu", __func__, retry - 1);
+
+ rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
+ rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc,
+ &mbhc->mbhc_anc_bias_regs,
+ cs_enable);
+ rt[0]._vdces = __wcd9xxx_codec_sta_dce_v(mbhc, true, rt[0].dce,
+ dce_z, (u32)mb_mv);
+
+ if (rt[0]._vdces >= no_mic && rt[0]._vdces < hs_max)
+ rt[0]._type = PLUG_TYPE_HEADSET;
+ else if (rt[0]._vdces < no_mic)
+ rt[0]._type = PLUG_TYPE_HEADPHONE;
+ else
+ rt[0]._type = PLUG_TYPE_HIGH_HPH;
+
+ pr_debug("%s: DCE #%d, V %04d, HPHL %d TYPE %d\n",
+ __func__, 0, rt[0]._vdces,
+ rt[0].hphl_status & 0x01,
+ rt[0]._type);
+
+ for (i = 1; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) {
+ rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1,
+ true, true);
+ rt[i]._vdces = __wcd9xxx_codec_sta_dce_v(mbhc, true,
+ rt[i].dce, dce_z,
+ (u32) mb_mv);
+
+ if (rt[i]._vdces >= no_mic && rt[i]._vdces < hs_max)
+ rt[i]._type = PLUG_TYPE_HEADSET;
+ else if (rt[i]._vdces < no_mic)
+ rt[i]._type = PLUG_TYPE_HEADPHONE;
+ else
+ rt[i]._type = PLUG_TYPE_HIGH_HPH;
+
+ rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
+
+ pr_debug("%s: DCE #%d, V %04d, HPHL %d TYPE %d\n",
+ __func__, i, rt[i]._vdces,
+ rt[i].hphl_status & 0x01,
+ rt[i]._type);
+ }
+
+ /*
+ * Check for the "type" of all the 4 measurements
+ * If all 4 measurements have the Type as PLUG_TYPE_HEADSET
+ * then it is proper mic and declare that the plug has two mics
+ */
+ for (i = 0; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) {
+ if (i > 0 && (rt[i - 1]._type != rt[i]._type)) {
+ type = PLUG_TYPE_INVALID;
+ break;
+ } else {
+ type = rt[0]._type;
+ }
+ }
+
+ pr_debug("%s: Plug type found in ANC detection :%d",
+ __func__, type);
+
+ if (type != PLUG_TYPE_HEADSET)
+ anc_mic_found = false;
+ if (anc_mic_found || (type == PLUG_TYPE_HEADPHONE &&
+ mbhc->mbhc_cfg->hw_jack_type == FIVE_POLE_JACK) ||
+ (type == PLUG_TYPE_HIGH_HPH &&
+ mbhc->mbhc_cfg->hw_jack_type == SIX_POLE_JACK))
+ break;
+ }
+
+ wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
+ if (cs_enable) {
+ wcd9xxx_turn_onoff_current_source(mbhc,
+ &mbhc->mbhc_anc_bias_regs,
+ false, false);
+ } else {
+ if (mbhc->mbhc_cfg->anc_micbias == MBHC_MICBIAS3) {
+ if (mbhc->micbias_enable_cb)
+ mbhc->micbias_enable_cb(mbhc->codec, false,
+ mbhc->mbhc_cfg->anc_micbias);
+ } else {
+ /* Disable override */
+ if (!override_en)
+ wcd9xxx_turn_onoff_override(mbhc, false);
+ }
+ }
+ pr_debug("%s: leave\n", __func__);
+ return anc_mic_found;
+}
+
/* called under codec_resource_lock acquisition */
static void wcd9xxx_find_plug_and_report(struct wcd9xxx_mbhc *mbhc,
enum wcd9xxx_mbhc_plug_type plug_type)
{
+ bool anc_mic_found = false;
+
pr_debug("%s: enter current_plug(%d) new_plug(%d)\n",
__func__, mbhc->current_plug, plug_type);
@@ -2033,25 +2254,50 @@
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);
+
+ if (mbhc->mbhc_cfg->enable_anc_mic_detect) {
+ /*
+ * Do not report Headset, because at this point
+ * it could be a ANC headphone having two mics.
+ * So, proceed further to detect if there is a
+ * second mic.
+ */
+ mbhc->scaling_mux_in = 0x08;
+ anc_mic_found = wcd9xxx_detect_anc_plug_type(mbhc);
+ }
+
+ if (anc_mic_found) {
+ /* Report ANC headphone */
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_ANC_HEADPHONE);
+ } else {
+ /*
+ * If Headphone was reported previously, this will
+ * only report the mic line
+ */
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADSET);
+ }
/* Button detection required RC oscillator */
wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
+ /*
+ * sleep so that audio path completely tears down
+ * before report plug insertion to the user space
+ */
msleep(100);
- /* if PA is already on, switch micbias source to VDDIO */
+ /*
+ * if PA is already on, switch micbias
+ * source to VDDIO
+ */
if (mbhc->event_state &
- (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR |
- 1 << MBHC_EVENT_PRE_TX_1_3_ON))
- __wcd9xxx_switch_micbias(mbhc, 1, false, false);
+ (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
+ __wcd9xxx_switch_micbias(mbhc, 1, false,
+ false);
wcd9xxx_start_hs_polling(mbhc);
} else if (plug_type == PLUG_TYPE_HIGH_HPH) {
if (mbhc->mbhc_cfg->detect_extn_cable) {
/* High impedance device found. Report as LINEOUT*/
- wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+ if (mbhc->current_plug == PLUG_TYPE_NONE)
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
wcd9xxx_cleanup_hs_polling(mbhc);
pr_debug("%s: setup mic trigger for further detection\n",
__func__);
@@ -2095,10 +2341,14 @@
(!(snd_soc_read(mbhc->codec,
mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
+ mbhc->scaling_mux_in = 0x04;
+
if (current_source_enable) {
- wcd9xxx_turn_onoff_current_source(mbhc, true, false);
+ wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+ true, false);
plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc, false);
- wcd9xxx_turn_onoff_current_source(mbhc, false, false);
+ wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+ false, false);
} else {
wcd9xxx_turn_onoff_override(mbhc, true);
plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
@@ -2223,7 +2473,8 @@
(!(snd_soc_read(mbhc->codec,
mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
if (cs_enable)
- wcd9xxx_turn_onoff_current_source(mbhc, true, false);
+ wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+ true, false);
timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
while (!(timedout = time_after(jiffies, timeout))) {
@@ -2291,7 +2542,8 @@
}
if (cs_enable)
- wcd9xxx_turn_onoff_current_source(mbhc, false, false);
+ wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+ false, false);
if (timedout)
pr_debug("%s: Microphone did not settle in %d seconds\n",
@@ -2322,7 +2574,8 @@
u32 mb_mv;
pr_debug("%s: enter\n", __func__);
- if (mbhc->current_plug != PLUG_TYPE_HEADSET) {
+ if (mbhc->current_plug != PLUG_TYPE_HEADSET &&
+ mbhc->current_plug != PLUG_TYPE_ANC_HEADPHONE) {
pr_debug("%s(): Headset is not inserted, ignore removal\n",
__func__);
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
@@ -2339,7 +2592,8 @@
(!(snd_soc_read(codec,
mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
if (cs_enable)
- wcd9xxx_turn_onoff_current_source(mbhc, true, false);
+ wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+ true, false);
timeout = jiffies + msecs_to_jiffies(FAKE_REMOVAL_MIN_PERIOD_MS);
do {
@@ -2370,7 +2624,8 @@
removed ? "" : "not ");
if (cs_enable)
- wcd9xxx_turn_onoff_current_source(mbhc, false, false);
+ wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+ false, false);
if (removed) {
if (mbhc->mbhc_cfg->detect_extn_cable) {
@@ -2752,8 +3007,8 @@
* headphone detection.
*/
if (current_source_enable)
- wcd9xxx_turn_onoff_current_source(mbhc, true,
- false);
+ wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+ true, false);
else
wcd9xxx_turn_onoff_override(mbhc, true);
@@ -2811,6 +3066,14 @@
} else if (plug_type == PLUG_TYPE_HIGH_HPH) {
pr_debug("%s: High HPH detected, continue polling\n",
__func__);
+ if (mbhc->mbhc_cfg->detect_extn_cable) {
+ if (mbhc->current_plug != plug_type)
+ wcd9xxx_report_plug(mbhc, 1,
+ SND_JACK_LINEOUT);
+ } else 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++;
@@ -2837,8 +3100,9 @@
WCD9XXX_BCL_LOCK(mbhc->resmgr);
/* Turn off override/current source */
if (current_source_enable)
- wcd9xxx_turn_onoff_current_source(mbhc, false,
- false);
+ wcd9xxx_turn_onoff_current_source(mbhc,
+ &mbhc->mbhc_bias_regs,
+ false, false);
else
wcd9xxx_turn_onoff_override(mbhc, false);
/*
@@ -2864,7 +3128,8 @@
}
if (!correction && current_source_enable)
- wcd9xxx_turn_onoff_current_source(mbhc, false, highhph);
+ wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+ false, highhph);
else if (!correction)
wcd9xxx_turn_onoff_override(mbhc, false);
@@ -2948,6 +3213,12 @@
} else if (mbhc->current_plug == PLUG_TYPE_HIGH_HPH) {
wcd9xxx_report_plug(mbhc, 0, SND_JACK_LINEOUT);
is_removed = true;
+ } else if (mbhc->current_plug == PLUG_TYPE_ANC_HEADPHONE) {
+ wcd9xxx_pause_hs_polling(mbhc);
+ wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_ANC_HEADPHONE);
+ is_removed = true;
}
if (is_removed) {
@@ -3108,7 +3379,9 @@
return mask;
}
-static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z)
+static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z,
+ struct mbhc_micbias_regs *micb_regs,
+ bool norel_detection)
{
s16 reg0, reg1;
int change;
@@ -3116,21 +3389,21 @@
WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
/* Pull down micbias to ground and disconnect vddio switch */
- reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x81, 0x1);
- reg1 = snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg);
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 1 << 7, 0);
+ reg0 = snd_soc_read(codec, micb_regs->ctl_reg);
+ snd_soc_update_bits(codec, micb_regs->ctl_reg, 0x81, 0x1);
+ reg1 = snd_soc_read(codec, micb_regs->mbhc_reg);
+ snd_soc_update_bits(codec, micb_regs->mbhc_reg, 1 << 7, 0);
/* Disconnect override from micbias */
change = snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
1 << 0);
usleep_range(1000, 1000 + 1000);
if (sta_z) {
- *sta_z = wcd9xxx_codec_sta_dce(mbhc, 0, false);
+ *sta_z = wcd9xxx_codec_sta_dce(mbhc, 0, norel_detection);
pr_debug("%s: sta_z 0x%x\n", __func__, *sta_z & 0xFFFF);
}
if (dce_z) {
- *dce_z = wcd9xxx_codec_sta_dce(mbhc, 1, false);
+ *dce_z = wcd9xxx_codec_sta_dce(mbhc, 1, norel_detection);
pr_debug("%s: dce_z 0x%x\n", __func__, *dce_z & 0xFFFF);
}
@@ -3139,16 +3412,22 @@
snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
1 << 4);
/* Disable pull down micbias to ground */
- snd_soc_write(codec, mbhc->mbhc_bias_regs.mbhc_reg, reg1);
- snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
+ snd_soc_write(codec, micb_regs->mbhc_reg, reg1);
+ snd_soc_write(codec, micb_regs->ctl_reg, reg0);
}
+/*
+ * This function recalibrates dce_z and sta_z parameters.
+ * No release detection will be false when this function is
+ * used.
+ */
void wcd9xxx_update_z(struct wcd9xxx_mbhc *mbhc)
{
const u16 sta_z = mbhc->mbhc_data.sta_z;
const u16 dce_z = mbhc->mbhc_data.dce_z;
- wcd9xxx_get_z(mbhc, &mbhc->mbhc_data.dce_z, &mbhc->mbhc_data.sta_z);
+ wcd9xxx_get_z(mbhc, &mbhc->mbhc_data.dce_z, &mbhc->mbhc_data.sta_z,
+ &mbhc->mbhc_bias_regs, false);
pr_debug("%s: sta_z 0x%x,dce_z 0x%x -> sta_z 0x%x,dce_z 0x%x\n",
__func__, sta_z & 0xFFFF, dce_z & 0xFFFF,
mbhc->mbhc_data.sta_z & 0xFFFF,
@@ -4065,7 +4344,10 @@
mbhc->mbhc_cfg = mbhc_cfg;
/* Get HW specific mbhc registers' address */
- wcd9xxx_get_mbhc_micbias_regs(mbhc, &mbhc->mbhc_bias_regs);
+ wcd9xxx_get_mbhc_micbias_regs(mbhc, MBHC_PRIMARY_MIC_MB);
+
+ /* Get HW specific mbhc registers' address for anc */
+ wcd9xxx_get_mbhc_micbias_regs(mbhc, MBHC_ANC_MIC_MB);
/* Put CFILT in fast mode by default */
if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
@@ -4280,7 +4562,7 @@
}
if (mbhc->micbias_enable && mbhc->polling_active &&
!(snd_soc_read(mbhc->codec, mbhc->mbhc_bias_regs.ctl_reg)
- & 0x80)) {
+ & 0x80)) {
pr_debug("%s:Micbias turned off by recording, set up again",
__func__);
snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
@@ -4441,6 +4723,7 @@
s16 *z[] = {
&l[0], &r[0], &r[1], &l[1], &l[2], &r[2],
};
+ bool override_en;
struct snd_soc_codec *codec = mbhc->codec;
const int mux_wait_us = 25;
const struct wcd9xxx_reg_mask_val reg_set_mux[] = {
@@ -4477,7 +4760,10 @@
wcd9xxx_onoff_ext_mclk(mbhc, true);
- wcd9xxx_turn_onoff_override(mbhc, true);
+ override_en = (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04) ?
+ true : false;
+ if (!override_en)
+ wcd9xxx_turn_onoff_override(mbhc, true);
pr_debug("%s: Setting impedance detection\n", __func__);
/* Codec specific setup for L0, R0, L1 and R1 measurements */
@@ -4525,7 +4811,8 @@
wcd9xxx_onoff_ext_mclk(mbhc, false);
- wcd9xxx_turn_onoff_override(mbhc, false);
+ if (!override_en)
+ wcd9xxx_turn_onoff_override(mbhc, false);
mbhc->mbhc_cb->compute_impedance(l, r, zl, zr);
pr_debug("%s: L0: 0x%x(%d), L1: 0x%x(%d), L2: 0x%x(%d)\n",
@@ -4561,7 +4848,8 @@
*/
int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
struct snd_soc_codec *codec,
- int (*micbias_enable_cb) (struct snd_soc_codec*, bool),
+ int (*micbias_enable_cb) (struct snd_soc_codec*, bool,
+ enum wcd9xxx_micbias_num),
const struct wcd9xxx_mbhc_cb *mbhc_cb,
const struct wcd9xxx_mbhc_intr *mbhc_cdc_intr_ids,
int rco_clk_rate,
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.h b/sound/soc/codecs/wcd9xxx-mbhc.h
index 2d37147..cf25798 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.h
+++ b/sound/soc/codecs/wcd9xxx-mbhc.h
@@ -78,6 +78,12 @@
PLUG_TYPE_HEADPHONE,
PLUG_TYPE_HIGH_HPH,
PLUG_TYPE_GND_MIC_SWAP,
+ PLUG_TYPE_ANC_HEADPHONE,
+};
+
+enum wcd9xxx_mbhc_micbias_type {
+ MBHC_PRIMARY_MIC_MB,
+ MBHC_ANC_MIC_MB,
};
enum wcd9xxx_micbias_num {
@@ -88,6 +94,12 @@
MBHC_MICBIAS4,
};
+enum hw_jack_type {
+ FOUR_POLE_JACK = 0,
+ FIVE_POLE_JACK,
+ SIX_POLE_JACK,
+};
+
enum wcd9xx_mbhc_micbias_enable_bits {
MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET,
MBHC_MICBIAS_ENABLE_REGULAR_HEADSET,
@@ -97,6 +109,7 @@
MBHC_CS_ENABLE_POLLING,
MBHC_CS_ENABLE_INSERTION,
MBHC_CS_ENABLE_REMOVAL,
+ MBHC_CS_ENABLE_DET_ANC,
};
enum wcd9xxx_mbhc_state {
@@ -217,6 +230,7 @@
*/
void *calibration;
enum wcd9xxx_micbias_num micbias;
+ enum wcd9xxx_micbias_num anc_micbias;
int (*mclk_cb_fn) (struct snd_soc_codec*, int, bool);
unsigned int mclk_rate;
unsigned int gpio;
@@ -232,6 +246,8 @@
bool use_int_rbias;
bool do_recalibration;
bool use_vddio_meas;
+ bool enable_anc_mic_detect;
+ enum hw_jack_type hw_jack_type;
};
struct wcd9xxx_cfilt_mode {
@@ -283,6 +299,8 @@
struct mbhc_internal_cal_data mbhc_data;
struct mbhc_micbias_regs mbhc_bias_regs;
+ struct mbhc_micbias_regs mbhc_anc_bias_regs;
+
bool mbhc_micbias_switched;
u32 hph_status; /* track headhpone status */
@@ -331,7 +349,8 @@
struct notifier_block nblock;
bool micbias_enable;
- int (*micbias_enable_cb) (struct snd_soc_codec*, bool);
+ int (*micbias_enable_cb) (struct snd_soc_codec*, bool,
+ enum wcd9xxx_micbias_num);
bool impedance_detect;
/* impedance of hphl and hphr */
@@ -340,6 +359,8 @@
u32 rco_clk_rate;
bool update_z;
+
+ u8 scaling_mux_in;
/* Holds codec specific interrupt mapping */
const struct wcd9xxx_mbhc_intr *intr_ids;
@@ -409,7 +430,8 @@
void wcd9xxx_mbhc_stop(struct wcd9xxx_mbhc *mbhc);
int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
struct snd_soc_codec *codec,
- int (*micbias_enable_cb) (struct snd_soc_codec*, bool),
+ int (*micbias_enable_cb) (struct snd_soc_codec*, bool,
+ enum wcd9xxx_micbias_num),
const struct wcd9xxx_mbhc_cb *mbhc_cb,
const struct wcd9xxx_mbhc_intr *mbhc_cdc_intr_ids,
int rco_clk_rate,
diff --git a/sound/soc/msm/msm8226.c b/sound/soc/msm/msm8226.c
index b4ae0a4..be0cb7f 100644
--- a/sound/soc/msm/msm8226.c
+++ b/sound/soc/msm/msm8226.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -82,6 +82,7 @@
.read_fw_bin = false,
.calibration = NULL,
.micbias = MBHC_MICBIAS2,
+ .anc_micbias = MBHC_MICBIAS2,
.mclk_cb_fn = msm_snd_enable_codec_ext_clk,
.mclk_rate = TAPAN_EXT_CLK_RATE,
.gpio = 0,
@@ -93,9 +94,12 @@
.swap_gnd_mic = NULL,
.cs_enable_flags = (1 << MBHC_CS_ENABLE_POLLING |
1 << MBHC_CS_ENABLE_INSERTION |
- 1 << MBHC_CS_ENABLE_REMOVAL),
+ 1 << MBHC_CS_ENABLE_REMOVAL |
+ 1 << MBHC_CS_ENABLE_DET_ANC),
.do_recalibration = true,
.use_vddio_meas = true,
+ .enable_anc_mic_detect = false,
+ .hw_jack_type = FOUR_POLE_JACK,
};
struct msm_auxpcm_gpio {
@@ -2042,6 +2046,8 @@
struct msm8226_asoc_mach_data *pdata;
int ret;
const char *auxpcm_pri_gpio_set = NULL;
+ const char *mbhc_audio_jack_type = NULL;
+ size_t n = strlen("4-pole-jack");
if (!pdev->dev.of_node) {
dev_err(&pdev->dev, "No platform supplied from device tree\n");
@@ -2107,6 +2113,35 @@
mbhc_cfg.gpio_level_insert = of_property_read_bool(pdev->dev.of_node,
"qcom,headset-jack-type-NC");
+ ret = of_property_read_string(pdev->dev.of_node,
+ "qcom,mbhc-audio-jack-type", &mbhc_audio_jack_type);
+ if (ret) {
+ dev_dbg(&pdev->dev, "Looking up %s property in node %s failed",
+ "qcom,mbhc-audio-jack-type",
+ pdev->dev.of_node->full_name);
+ mbhc_cfg.hw_jack_type = FOUR_POLE_JACK;
+ mbhc_cfg.enable_anc_mic_detect = false;
+ dev_dbg(&pdev->dev, "Jack type properties set to default");
+ } else {
+ if (!strncmp(mbhc_audio_jack_type, "4-pole-jack", n)) {
+ mbhc_cfg.hw_jack_type = FOUR_POLE_JACK;
+ mbhc_cfg.enable_anc_mic_detect = false;
+ dev_dbg(&pdev->dev, "This hardware has 4 pole jack");
+ } else if (!strncmp(mbhc_audio_jack_type, "5-pole-jack", n)) {
+ mbhc_cfg.hw_jack_type = FIVE_POLE_JACK;
+ mbhc_cfg.enable_anc_mic_detect = true;
+ dev_dbg(&pdev->dev, "This hardware has 5 pole jack");
+ } else if (!strncmp(mbhc_audio_jack_type, "6-pole-jack", n)) {
+ mbhc_cfg.hw_jack_type = SIX_POLE_JACK;
+ mbhc_cfg.enable_anc_mic_detect = true;
+ dev_dbg(&pdev->dev, "This hardware has 6 pole jack");
+ } else {
+ mbhc_cfg.hw_jack_type = FOUR_POLE_JACK;
+ mbhc_cfg.enable_anc_mic_detect = false;
+ dev_dbg(&pdev->dev, "Unknown value, hence setting to default");
+ }
+ }
+
ret = snd_soc_register_card(card);
if (ret == -EPROBE_DEFER)
goto err;
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index 9c00e95..0eea842 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -117,6 +117,7 @@
.read_fw_bin = false,
.calibration = NULL,
.micbias = MBHC_MICBIAS2,
+ .anc_micbias = MBHC_MICBIAS2,
.mclk_cb_fn = msm_snd_enable_codec_ext_clk,
.mclk_rate = TAIKO_EXT_CLK_RATE,
.gpio = 0,
@@ -128,9 +129,12 @@
.swap_gnd_mic = NULL,
.cs_enable_flags = (1 << MBHC_CS_ENABLE_POLLING |
1 << MBHC_CS_ENABLE_INSERTION |
- 1 << MBHC_CS_ENABLE_REMOVAL),
+ 1 << MBHC_CS_ENABLE_REMOVAL |
+ 1 << MBHC_CS_ENABLE_DET_ANC),
.do_recalibration = true,
.use_vddio_meas = true,
+ .enable_anc_mic_detect = false,
+ .hw_jack_type = SIX_POLE_JACK,
};
struct msm_auxpcm_gpio {
@@ -2713,6 +2717,8 @@
int ret;
const char *auxpcm_pri_gpio_set = NULL;
const char *prop_name_ult_lo_gpio = "qcom,ext-ult-lo-amp-gpio";
+ const char *mbhc_audio_jack_type = NULL;
+ size_t n = strlen("4-pole-jack");
struct resource *pri_muxsel;
struct resource *sec_muxsel;
@@ -2772,6 +2778,34 @@
if (ret)
goto err;
+ ret = of_property_read_string(pdev->dev.of_node,
+ "qcom,mbhc-audio-jack-type", &mbhc_audio_jack_type);
+ if (ret) {
+ dev_dbg(&pdev->dev, "Looking up %s property in node %s failed",
+ "qcom,mbhc-audio-jack-type",
+ pdev->dev.of_node->full_name);
+ mbhc_cfg.hw_jack_type = FOUR_POLE_JACK;
+ mbhc_cfg.enable_anc_mic_detect = false;
+ dev_dbg(&pdev->dev, "Jack type properties set to default");
+ } else {
+ if (!strncmp(mbhc_audio_jack_type, "4-pole-jack", n)) {
+ mbhc_cfg.hw_jack_type = FOUR_POLE_JACK;
+ mbhc_cfg.enable_anc_mic_detect = false;
+ dev_dbg(&pdev->dev, "This hardware has 4 pole jack");
+ } else if (!strncmp(mbhc_audio_jack_type, "5-pole-jack", n)) {
+ mbhc_cfg.hw_jack_type = FIVE_POLE_JACK;
+ mbhc_cfg.enable_anc_mic_detect = true;
+ dev_dbg(&pdev->dev, "This hardware has 5 pole jack");
+ } else if (!strncmp(mbhc_audio_jack_type, "6-pole-jack", n)) {
+ mbhc_cfg.hw_jack_type = SIX_POLE_JACK;
+ mbhc_cfg.enable_anc_mic_detect = true;
+ dev_dbg(&pdev->dev, "This hardware has 6 pole jack");
+ } else {
+ mbhc_cfg.hw_jack_type = FOUR_POLE_JACK;
+ mbhc_cfg.enable_anc_mic_detect = false;
+ dev_dbg(&pdev->dev, "Unknown value, hence setting to default");
+ }
+ }
if (of_property_read_bool(pdev->dev.of_node, "qcom,hdmi-audio-rx")) {
dev_info(&pdev->dev, "%s(): hdmi audio support present\n",
__func__);
diff --git a/sound/soc/msm/msm8x10.c b/sound/soc/msm/msm8x10.c
index 4eab965..7b3a028 100644
--- a/sound/soc/msm/msm8x10.c
+++ b/sound/soc/msm/msm8x10.c
@@ -106,6 +106,7 @@
1 << MBHC_CS_ENABLE_REMOVAL),
.do_recalibration = false,
.use_vddio_meas = false,
+ .hw_jack_type = FOUR_POLE_JACK,
};
/*
@@ -1050,6 +1051,8 @@
static __devinit int msm8x10_asoc_machine_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &snd_soc_card_msm8x10;
+ const char *mbhc_audio_jack_type = NULL;
+ size_t n = strlen("4-pole-jack");
int ret;
dev_dbg(&pdev->dev, "%s\n", __func__);
@@ -1087,6 +1090,35 @@
mbhc_cfg.use_int_rbias = of_property_read_bool(pdev->dev.of_node,
"qcom,mbhc-bias-internal");
+ ret = of_property_read_string(pdev->dev.of_node,
+ "qcom,mbhc-audio-jack-type", &mbhc_audio_jack_type);
+ if (ret) {
+ dev_dbg(&pdev->dev, "Looking up %s property in node %s failed",
+ "qcom,mbhc-audio-jack-type",
+ pdev->dev.of_node->full_name);
+ mbhc_cfg.hw_jack_type = FOUR_POLE_JACK;
+ mbhc_cfg.enable_anc_mic_detect = false;
+ dev_dbg(&pdev->dev, "Jack type properties set to default");
+ } else {
+ if (!strncmp(mbhc_audio_jack_type, "4-pole-jack", n)) {
+ mbhc_cfg.hw_jack_type = FOUR_POLE_JACK;
+ mbhc_cfg.enable_anc_mic_detect = false;
+ dev_dbg(&pdev->dev, "This hardware has 4 pole jack");
+ } else if (!strncmp(mbhc_audio_jack_type, "5-pole-jack", n)) {
+ mbhc_cfg.hw_jack_type = FIVE_POLE_JACK;
+ mbhc_cfg.enable_anc_mic_detect = true;
+ dev_dbg(&pdev->dev, "This hardware has 5 pole jack");
+ } else if (!strncmp(mbhc_audio_jack_type, "6-pole-jack", n)) {
+ mbhc_cfg.hw_jack_type = SIX_POLE_JACK;
+ mbhc_cfg.enable_anc_mic_detect = true;
+ dev_dbg(&pdev->dev, "This hardware has 6 pole jack");
+ } else {
+ mbhc_cfg.hw_jack_type = FOUR_POLE_JACK;
+ mbhc_cfg.enable_anc_mic_detect = false;
+ dev_dbg(&pdev->dev, "Unknown value, hence setting to default");
+ }
+ }
+
spdev = pdev;
ret = snd_soc_register_card(card);
diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
index c78e436..41ef3e3 100644
--- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
@@ -66,9 +66,22 @@
const DECLARE_TLV_DB_LINEAR(msm_compr_vol_gain, 0,
COMPRESSED_LR_VOL_MAX_STEPS);
+/*
+ * LSB 8 bits is used as stream id for some DSP
+ * commands for compressed playback.
+ */
+#define STREAM_ID_FROM_TOKEN(i) (i & 0xFF)
+
+/* Stream id switches between 1 and 2 */
+#define NEXT_STREAM_ID(stream_id) ((stream_id & 1) + 1)
+
+#define STREAM_ARRAY_INDEX(stream_id) (stream_id - 1)
+
+#define MAX_NUMBER_OF_STREAMS 2
+
struct msm_compr_gapless_state {
bool set_next_stream_id;
- int32_t stream_opened[2];
+ int32_t stream_opened[MAX_NUMBER_OF_STREAMS];
uint32_t initial_samples_drop;
uint32_t trailing_samples_drop;
uint32_t gapless_transition;
@@ -210,7 +223,8 @@
pr_debug("%s: bytes_received = %d copied_total = %d\n",
__func__, prtd->bytes_received, prtd->copied_total);
if (prtd->first_buffer && prtd->gapless_state.use_dsp_gapless_mode)
- q6asm_send_meta_data(prtd->audio_client,
+ q6asm_stream_send_meta_data(prtd->audio_client,
+ prtd->audio_client->stream_id,
prtd->gapless_state.initial_samples_drop,
prtd->gapless_state.trailing_samples_drop);
@@ -260,6 +274,7 @@
uint32_t chan_mode = 0;
uint32_t sample_rate = 0;
int bytes_available, stream_id;
+ uint32_t stream_index;
pr_debug("%s opcode =%08x\n", __func__, opcode);
switch (opcode) {
@@ -317,8 +332,10 @@
spin_unlock(&prtd->lock);
break;
case ASM_DATA_EVENT_RENDERED_EOS:
- pr_debug("ASM_DATA_CMDRSP_EOS\n");
spin_lock(&prtd->lock);
+ pr_debug("%s: ASM_DATA_CMDRSP_EOS token 0x%x,stream id %d\n",
+ __func__, token, STREAM_ID_FROM_TOKEN(token));
+ stream_id = STREAM_ID_FROM_TOKEN(token);
if (atomic_read(&prtd->eos) &&
!prtd->gapless_state.set_next_stream_id) {
pr_debug("ASM_DATA_CMDRSP_EOS wake up\n");
@@ -326,13 +343,22 @@
wake_up(&prtd->eos_wait);
}
atomic_set(&prtd->eos, 0);
- stream_id = ac->stream_id^1; /*prev stream */
+ stream_index = STREAM_ARRAY_INDEX(stream_id);
+ if (stream_index >= MAX_NUMBER_OF_STREAMS ||
+ stream_index < 0) {
+ pr_err("%s: Invalid stream index %d", __func__,
+ stream_index);
+ spin_unlock(&prtd->lock);
+ break;
+ }
+
if (prtd->gapless_state.set_next_stream_id &&
- prtd->gapless_state.stream_opened[stream_id]) {
- q6asm_stream_cmd_nowait(prtd->audio_client,
- CMD_CLOSE, stream_id);
+ prtd->gapless_state.stream_opened[stream_index]) {
+ pr_debug("%s: CMD_CLOSE stream_id %d\n",
+ __func__, stream_id);
+ q6asm_stream_cmd_nowait(ac, CMD_CLOSE, stream_id);
atomic_set(&prtd->close, 1);
- prtd->gapless_state.stream_opened[stream_id] = 0;
+ prtd->gapless_state.stream_opened[stream_index] = 0;
prtd->gapless_state.set_next_stream_id = false;
}
if (prtd->gapless_state.gapless_transition)
@@ -369,25 +395,35 @@
spin_unlock(&prtd->lock);
break;
case ASM_STREAM_CMD_FLUSH:
- pr_debug("ASM_STREAM_CMD_FLUSH\n");
+ pr_debug("%s: ASM_STREAM_CMD_FLUSH:", __func__);
+ pr_debug("token 0x%x, stream id %d\n", token,
+ STREAM_ID_FROM_TOKEN(token));
prtd->cmd_ack = 1;
wake_up(&prtd->flush_wait);
break;
case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE:
- pr_debug("ASM_DATA_CMD_REMOVE_INITIAL_SILENCE\n");
+ pr_debug("%s: ASM_DATA_CMD_REMOVE_INITIAL_SILENCE:",
+ __func__);
+ pr_debug("token 0x%x, stream id = %d\n", token,
+ STREAM_ID_FROM_TOKEN(token));
break;
case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE:
- pr_debug("ASM_DATA_CMD_REMOVE_TRAILING_SILENCE\n");
+ pr_debug("%s: ASM_DATA_CMD_REMOVE_TRAILING_SILENCE:",
+ __func__);
+ pr_debug("token = 0x%x, stream id = %d\n", token,
+ STREAM_ID_FROM_TOKEN(token));
break;
case ASM_STREAM_CMD_CLOSE:
- pr_debug("ASM_DATA_CMD_CLOSE\n");
+ pr_debug("%s: ASM_DATA_CMD_CLOSE:", __func__);
+ pr_debug("token 0x%x, stream id %d\n", token,
+ STREAM_ID_FROM_TOKEN(token));
/*
* wakeup wait for stream avail on stream 3
* after stream 1 ends.
*/
if (prtd->next_stream) {
pr_debug("%s:CLOSE:wakeup wait for stream\n",
- __func__);
+ __func__);
prtd->stream_available = 1;
wake_up(&prtd->wait_for_stream_avail);
prtd->next_stream = 0;
@@ -405,10 +441,12 @@
break;
}
case ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3:
- pr_debug("ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3\n");
+ pr_debug("%s: ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3\n",
+ __func__);
break;
case RESET_EVENTS:
- pr_err("Received reset events CB, move to error state");
+ pr_err("%s: Received reset events CB, move to error state",
+ __func__);
spin_lock(&prtd->lock);
snd_compr_fragment_elapsed(cstream);
prtd->copied_total = prtd->bytes_received;
@@ -416,7 +454,8 @@
spin_unlock(&prtd->lock);
break;
default:
- pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
+ pr_debug("%s: Not Supported Event opcode[0x%x]\n",
+ __func__, opcode);
break;
}
}
@@ -486,6 +525,7 @@
uint16_t bits_per_sample = 16;
int dir = IN, ret = 0;
struct audio_client *ac = prtd->audio_client;
+ uint32_t stream_index;
struct asm_softpause_params softpause = {
.enable = SOFT_PAUSE_ENABLE,
.period = SOFT_PAUSE_PERIOD,
@@ -498,7 +538,7 @@
.rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
};
- pr_debug("%s\n", __func__);
+ pr_debug("%s: stream_id %d\n", __func__, ac->stream_id);
ret = q6asm_stream_open_write_v2(ac,
prtd->codec, bits_per_sample,
ac->stream_id,
@@ -508,7 +548,13 @@
return -ENOMEM;
}
- prtd->gapless_state.stream_opened[ac->stream_id] = 1;
+ stream_index = STREAM_ARRAY_INDEX(ac->stream_id);
+ if (stream_index >= MAX_NUMBER_OF_STREAMS || stream_index < 0) {
+ pr_err("%s: Invalid stream index:%d", __func__, stream_index);
+ return -EINVAL;
+ }
+
+ prtd->gapless_state.stream_opened[stream_index] = 1;
pr_debug("%s be_id %d\n", __func__, soc_prtd->dai_link->be_id);
msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
ac->perf_mode,
@@ -529,7 +575,7 @@
pr_err("%s: Send SoftVolume Param failed ret=%d\n",
__func__, ret);
- ret = q6asm_set_io_mode(ac, (COMPRESSED_IO | ASYNC_IO_MODE));
+ ret = q6asm_set_io_mode(ac, (COMPRESSED_STREAM_IO | ASYNC_IO_MODE));
if (ret < 0) {
pr_err("%s: Set IO mode failed\n", __func__);
return -EINVAL;
@@ -673,6 +719,7 @@
struct audio_client *ac = prtd->audio_client;
int dir = IN, ret = 0, stream_id;
unsigned long flags;
+ uint32_t stream_index;
pr_debug("%s\n", __func__);
@@ -693,13 +740,19 @@
spin_lock_irqsave(&prtd->lock, flags);
stream_id = ac->stream_id;
- if (prtd->gapless_state.stream_opened[stream_id^1]) {
+ stream_index = STREAM_ARRAY_INDEX(NEXT_STREAM_ID(stream_id));
+
+ if ((stream_index < MAX_NUMBER_OF_STREAMS && stream_index >= 0) &&
+ (prtd->gapless_state.stream_opened[stream_index])) {
spin_unlock_irqrestore(&prtd->lock, flags);
- pr_debug(" close stream %d", stream_id^1);
- q6asm_stream_cmd(ac, CMD_CLOSE, stream_id^1);
+ pr_debug(" close stream %d", NEXT_STREAM_ID(stream_id));
+ q6asm_stream_cmd(ac, CMD_CLOSE, NEXT_STREAM_ID(stream_id));
spin_lock_irqsave(&prtd->lock, flags);
}
- if (prtd->gapless_state.stream_opened[stream_id]) {
+
+ stream_index = STREAM_ARRAY_INDEX(stream_id);
+ if ((stream_index < MAX_NUMBER_OF_STREAMS && stream_index >= 0) &&
+ (prtd->gapless_state.stream_opened[stream_index])) {
spin_unlock_irqrestore(&prtd->lock, flags);
pr_debug("close stream %d", stream_id);
q6asm_stream_cmd(ac, CMD_CLOSE, stream_id);
@@ -719,8 +772,6 @@
pr_debug("%s: ocmem_req: %d\n", __func__,
atomic_read(&pdata->audio_ocmem_req));
- /* client buf alloc was with stream id 0, so free with the same */
- ac->stream_id = 0;
q6asm_audio_client_buf_free_contiguous(dir, ac);
q6asm_audio_client_free(ac);
@@ -892,6 +943,7 @@
int bytes_to_write;
unsigned long flags;
int stream_id;
+ uint32_t stream_index;
if (cstream->direction != SND_COMPRESS_PLAYBACK) {
pr_err("%s: Unsupported stream type\n", __func__);
@@ -944,9 +996,9 @@
atomic_set(&prtd->drain, 0);
}
prtd->last_buffer = 0;
- pr_debug("issue CMD_FLUSH\n");
prtd->cmd_ack = 0;
if (!prtd->gapless_state.gapless_transition) {
+ pr_debug("issue CMD_FLUSH stream_id %d\n", stream_id);
spin_unlock_irqrestore(&prtd->lock, flags);
rc = q6asm_stream_cmd(
prtd->audio_client, CMD_FLUSH, stream_id);
@@ -981,7 +1033,9 @@
pr_debug("SNDRV_PCM_TRIGGER_PAUSE_PUSH transition %d\n",
prtd->gapless_state.gapless_transition);
if (!prtd->gapless_state.gapless_transition) {
- q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ pr_debug("issue CMD_PAUSE stream_id %d\n",
+ ac->stream_id);
+ q6asm_stream_cmd_nowait(ac, CMD_PAUSE, ac->stream_id);
atomic_set(&prtd->start, 0);
}
break;
@@ -1057,7 +1111,8 @@
/* send EOS */
prtd->cmd_ack = 0;
- q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
+ pr_debug("issue CMD_EOS stream_id %d\n", ac->stream_id);
+ q6asm_stream_cmd_nowait(ac, CMD_EOS, ac->stream_id);
pr_info("PARTIAL DRAIN, do not wait for EOS ack\n");
/* send a zero length buffer */
@@ -1094,8 +1149,9 @@
}
/* move to next stream and reset vars */
- pr_debug("%s: Moving to next stream in gapless\n", __func__);
- ac->stream_id ^= 1;
+ pr_debug("%s: Moving to next stream in gapless\n",
+ __func__);
+ ac->stream_id = NEXT_STREAM_ID(ac->stream_id);
prtd->byte_offset = 0;
prtd->app_pointer = 0;
prtd->first_buffer = 1;
@@ -1122,11 +1178,11 @@
stream can be used for gapless playback
*/
prtd->gapless_state.set_next_stream_id = false;
- pr_debug("%s: CMD_EOS\n", __func__);
+ pr_debug("%s:CMD_EOS stream_id %d\n", __func__, ac->stream_id);
prtd->cmd_ack = 0;
atomic_set(&prtd->eos, 1);
- q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
+ q6asm_stream_cmd_nowait(ac, CMD_EOS, ac->stream_id);
spin_unlock_irqrestore(&prtd->lock, flags);
@@ -1138,7 +1194,8 @@
if (rc < 0)
pr_err("%s: EOS wait failed\n", __func__);
- pr_debug("%s: SNDRV_COMPRESS_DRAIN out of wait for EOS\n", __func__);
+ pr_debug("%s: SNDRV_COMPRESS_DRAIN out of wait for EOS\n",
+ __func__);
if (prtd->cmd_interrupt)
rc = -EINTR;
@@ -1150,12 +1207,14 @@
* so prepare the current stream in session for gapless playback
*/
spin_lock_irqsave(&prtd->lock, flags);
- pr_debug("%s: issue CMD_PAUSE ", __func__);
- q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ pr_debug("%s:issue CMD_PAUSE stream_id %d",
+ __func__, ac->stream_id);
+ q6asm_stream_cmd_nowait(ac, CMD_PAUSE, ac->stream_id);
prtd->cmd_ack = 0;
spin_unlock_irqrestore(&prtd->lock, flags);
- pr_debug("%s: issue CMD_FLUSH", __func__);
- q6asm_cmd(prtd->audio_client, CMD_FLUSH);
+ pr_debug("%s:issue CMD_FLUSH ac->stream_id %d",
+ __func__, ac->stream_id);
+ q6asm_stream_cmd(ac, CMD_FLUSH, ac->stream_id);
wait_event_timeout(prtd->flush_wait,
prtd->cmd_ack, 1 * HZ / 4);
@@ -1191,14 +1250,25 @@
pr_debug("%s: SND_COMPR_TRIGGER_NEXT_TRACK\n", __func__);
spin_lock_irqsave(&prtd->lock, flags);
rc = 0;
- stream_id = ac->stream_id^1; /*next stream in gapless*/
+ /* next stream in gapless */
+ stream_id = NEXT_STREAM_ID(ac->stream_id);
/*
* Wait if stream 1 has not completed before honoring next
* track for stream 3. Scenario happens if second clip is
* small and fills in one buffer so next track will be
* called immediately.
*/
- if (prtd->gapless_state.stream_opened[stream_id]) {
+ stream_index = STREAM_ARRAY_INDEX(stream_id);
+ if (stream_index >= MAX_NUMBER_OF_STREAMS ||
+ stream_index < 0) {
+ pr_err("%s: Invalid stream index: %d", __func__,
+ stream_index);
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ rc = -EINVAL;
+ break;
+ }
+
+ if (prtd->gapless_state.stream_opened[stream_index]) {
if (prtd->gapless_state.gapless_transition) {
rc = msm_compr_wait_for_stream_avail(prtd,
&flags);
@@ -1229,6 +1299,7 @@
}
break;
}
+ pr_debug("%s: open_write stream_id %d", __func__, stream_id);
rc = q6asm_stream_open_write_v2(prtd->audio_client,
prtd->codec, 16,
stream_id,
@@ -1245,7 +1316,7 @@
break;
}
spin_lock_irqsave(&prtd->lock, flags);
- prtd->gapless_state.stream_opened[stream_id] = 1;
+ prtd->gapless_state.stream_opened[stream_index] = 1;
prtd->gapless_state.set_next_stream_id = true;
spin_unlock_irqrestore(&prtd->lock, flags);
break;