Merge "block: urgent request: Update dispatch_urgent in case of requeue/reinsert"
diff --git a/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt b/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
index 3a29004..203730f 100644
--- a/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
+++ b/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
@@ -47,6 +47,22 @@
3 (SUPER_TURBO voltage): 1275000 uV
- vdd-apc-supply: Regulator to supply VDD APC power
+
+Optional properties:
+- vdd-mx-supply: Regulator to supply memory power as dependency
+ of VDD APC.
+- qcom,vdd-mx-vmax: The maximum voltage in uV for vdd-mx-supply. This
+ is required when vdd-mx-supply is present.
+- qcom,vdd-mx-vmin-method: The method to determine the minimum voltage for
+ vdd-mx-supply, which can be one of following
+ choices compared with VDD APC:
+ 0 => equal to the voltage(vmin) of VDD APC
+ 1 => equal to PVS corner ceiling voltage
+ 2 => equal to slow speed corner ceiling
+ 3 => equal to qcom,vdd-mx-vmax
+ This is required when vdd-mx-supply is present.
+
+
Example:
apc_vreg_corner: regulator@f9018000 {
status = "okay";
@@ -65,5 +81,8 @@
qcom,pvs-corner-ceiling-nom = <975000 1075000 1200000 1200000>;
qcom,pvs-corner-ceiling-fast = <900000 1000000 1140000 1140000>;
vdd-apc-supply = <&pm8226_s2>;
+ vdd-mx-supply = <&pm8226_l3_ao>;
+ qcom,vdd-mx-vmax = <1350000>;
+ qcom,vdd-mx-vmin-method = <1>;
};
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_watchdog.txt b/Documentation/devicetree/bindings/arm/msm/msm_watchdog.txt
index a665431..c7a19ef 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm_watchdog.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm_watchdog.txt
@@ -29,7 +29,7 @@
qcom,wdt@f9017000 {
compatible = "qcom,msm-watchdog";
reg = <0xf9017000 0x1000>;
- interrupts = <0 3 0 0 4 0>;
+ interrupts = <0 3 0>, <0 4 0>;
qcom,bark-time = <11000>;
qcom,pet-time = <10000>;
qcom,ipi-ping;
diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
index 383da0c..2d20794 100644
--- a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
+++ b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
@@ -17,6 +17,8 @@
- vdd_mx-supply: Reference to the regulator that supplies the memory rail.
- qcom,firmware-name: Base name of the firmware image. Ex. "mdsp"
- qcom,gpio-err-fatal: GPIO used by the modem to indicate error fatal to the apps.
+- qcom,gpio-proxy-unvote: GPIO used by the modem to trigger proxy unvoting in
+ the apps.
- qcom,gpio-force-stop: GPIO used by the apps to force the modem to shutdown.
Optional properties:
@@ -47,6 +49,7 @@
/* GPIO inputs from mss */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_1_in 0 0>;
+ qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_1_in 2 0>;
/* GPIO output to mss */
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>;
diff --git a/Documentation/devicetree/bindings/power/qpnp-bms.txt b/Documentation/devicetree/bindings/power/qpnp-bms.txt
index 5b22752..6d093f0 100644
--- a/Documentation/devicetree/bindings/power/qpnp-bms.txt
+++ b/Documentation/devicetree/bindings/power/qpnp-bms.txt
@@ -26,12 +26,9 @@
That is to say,
if (abs(shutdown-soc - current-soc) < limit)
then use old SoC.
-- qcom,adjust-soc-low-threshold : The low threshold for the "flat portion"
- of the charging curve. The BMS will not adjust SoC
- based on voltage during this time.
-- qcom,adjust-soc-high-threshold : The high threshold for the "flat
- portion" of the charging curve. The BMS will not
- adjust SoC based on voltage during this time.
+- qcom,adjust-soc-low-threshold : The low threshold for when the BMS algorithm
+ starts adjusting. If the estimated SoC is not below
+ this percentage, do not adjust.
- qcom,ocv-voltage-low-threshold-uv : The low voltage threshold for the
"flat portion" of the discharge curve. The bms will not
accept new ocvs between these thresholds.
@@ -59,6 +56,14 @@
- qcom,batt-type: Type of battery used. This is an integer that corresponds
to the enum defined in
include/linux/mfd/pm8xxx/batterydata-lib.h
+- qcom,high-ocv-correction-limit-uv: how much the bms will correct OCV when
+ voltage is above the flat portion of the discharge
+ curve.
+- qcom,low-ocv-correction-limit-uv: how much the bms will correct OCV when
+ voltage is below the flat portion of the discharge
+ curve.
+- qcom,hold-soc-est: if the voltage-based estimated SoC is above this percent,
+ the BMS will clamp SoC to be at least 1.
Parent node optional properties:
- qcom,ignore-shutdown-soc: A boolean that controls whether BMS will
@@ -107,14 +112,16 @@
qcom,shutdown-soc-valid-limit = <20>;
qcom,ocv-voltage-low-threshold-uv = <3650000>;
qcom,ocv-voltage-high-threshold-uv = <3750000>;
- qcom,adjust-soc-low-threshold = <25>;
- qcom,adjust-soc-high-threshold = <45>;
+ qcom,adjust-soc-low-threshold = <15>;
qcom,low-soc-calculate-soc-threshold = <15>;
qcom,low-voltage-threshold = <3420000>;
qcom,low-soc-calculate-soc-ms = <5000>;
qcom,calculate-soc-ms = <20000>;
qcom,chg-term-ua = <100000>;
qcom,batt-type = <0>;
+ qcom,low-ocv-correction-limit-uv = <100>;
+ qcom,high-ocv-correction-limit-uv = <50>;
+ qcom,hold-soc-est = <3>;
qcom,bms-iadc@3800 {
reg = <0x3800 0x100>;
diff --git a/arch/arm/boot/dts/msm-pm8226.dtsi b/arch/arm/boot/dts/msm-pm8226.dtsi
index 72de900..f702abb 100644
--- a/arch/arm/boot/dts/msm-pm8226.dtsi
+++ b/arch/arm/boot/dts/msm-pm8226.dtsi
@@ -162,10 +162,9 @@
qcom,r-sense-uohm = <10000>;
qcom,v-cutoff-uv = <3400000>;
qcom,max-voltage-uv = <4200000>;
- qcom,r-conn-mohm = <18>;
+ qcom,r-conn-mohm = <0>;
qcom,shutdown-soc-valid-limit = <20>;
- qcom,adjust-soc-low-threshold = <25>;
- qcom,adjust-soc-high-threshold = <45>;
+ qcom,adjust-soc-low-threshold = <15>;
qcom,ocv-voltage-high-threshold-uv = <3750000>;
qcom,ocv-voltage-low-threshold-uv = <3650000>;
qcom,low-soc-calculate-soc-threshold = <15>;
@@ -173,6 +172,10 @@
qcom,calculate-soc-ms = <20000>;
qcom,chg-term-ua = <100000>;
qcom,batt-type = <0>;
+ qcom,low-ocv-correction-limit-uv = <100>;
+ qcom,high-ocv-correction-limit-uv = <50>;
+ qcom,hold-soc-est = <3>;
+ qcom,low-voltage-threshold = <3420000>;
qcom,bms-iadc@3800 {
reg = <0x3800 0x100>;
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index 6042f23..d712e5f 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -107,8 +107,7 @@
qcom,max-voltage-uv = <4200000>;
qcom,r-conn-mohm = <0>;
qcom,shutdown-soc-valid-limit = <20>;
- qcom,adjust-soc-low-threshold = <25>;
- qcom,adjust-soc-high-threshold = <45>;
+ qcom,adjust-soc-low-threshold = <15>;
qcom,ocv-voltage-high-threshold-uv = <3750000>;
qcom,ocv-voltage-low-threshold-uv = <3650000>;
qcom,low-soc-calculate-soc-threshold = <15>;
@@ -117,6 +116,9 @@
qcom,chg-term-ua = <100000>;
qcom,batt-type = <0>;
qcom,low-voltage-threshold = <3420000>;
+ qcom,low-ocv-correction-limit-uv = <100>;
+ qcom,high-ocv-correction-limit-uv = <50>;
+ qcom,hold-soc-est = <3>;
qcom,bms-iadc@3800 {
reg = <0x3800 0x100>;
diff --git a/arch/arm/boot/dts/msm8226-camera-sensor-cdp-qrd.dtsi b/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi
similarity index 97%
copy from arch/arm/boot/dts/msm8226-camera-sensor-cdp-qrd.dtsi
copy to arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi
index b7f837f..c47d48d 100644
--- a/arch/arm/boot/dts/msm8226-camera-sensor-cdp-qrd.dtsi
+++ b/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi
@@ -25,7 +25,7 @@
actuator0: qcom,actuator@6e {
cell-index = <3>;
- reg = <0x6c 0x0>;
+ reg = <0x6c>;
compatible = "qcom,actuator";
qcom,cci-master = <0>;
};
@@ -38,7 +38,7 @@
qcom,csid-sd-index = <0>;
qcom,actuator-src = <&actuator0>;
qcom,led-flash-src = <&led_flash0>;
- qcom,mount-angle = <90>;
+ qcom,mount-angle = <0>;
qcom,sensor-name = "ov8825";
cam_vdig-supply = <&pm8226_l5>;
cam_vana-supply = <&pm8226_l19>;
@@ -74,7 +74,7 @@
qcom,slave-id = <0x20 0x0 0x9724>;
qcom,csiphy-sd-index = <1>;
qcom,csid-sd-index = <0>;
- qcom,mount-angle = <90>;
+ qcom,mount-angle = <0>;
qcom,sensor-name = "ov9724";
cam_vdig-supply = <&pm8226_l5>;
cam_vana-supply = <&pm8226_l19>;
diff --git a/arch/arm/boot/dts/msm8226-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/msm8226-camera-sensor-mtp.dtsi
index 02089be..1f7ba89 100644
--- a/arch/arm/boot/dts/msm8226-camera-sensor-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8226-camera-sensor-mtp.dtsi
@@ -25,7 +25,7 @@
actuator0: qcom,actuator@6e {
cell-index = <3>;
- reg = <0x6c 0x0>;
+ reg = <0x6c>;
compatible = "qcom,actuator";
qcom,cci-master = <0>;
};
@@ -38,7 +38,7 @@
qcom,csid-sd-index = <0>;
qcom,actuator-src = <&actuator0>;
qcom,led-flash-src = <&led_flash0>;
- qcom,mount-angle = <90>;
+ qcom,mount-angle = <0>;
qcom,sensor-name = "ov8825";
cam_vdig-supply = <&pm8226_l5>;
cam_vana-supply = <&pm8226_l19>;
diff --git a/arch/arm/boot/dts/msm8226-camera-sensor-cdp-qrd.dtsi b/arch/arm/boot/dts/msm8226-camera-sensor-qrd.dtsi
similarity index 98%
rename from arch/arm/boot/dts/msm8226-camera-sensor-cdp-qrd.dtsi
rename to arch/arm/boot/dts/msm8226-camera-sensor-qrd.dtsi
index b7f837f..5ea02b4 100644
--- a/arch/arm/boot/dts/msm8226-camera-sensor-cdp-qrd.dtsi
+++ b/arch/arm/boot/dts/msm8226-camera-sensor-qrd.dtsi
@@ -25,7 +25,7 @@
actuator0: qcom,actuator@6e {
cell-index = <3>;
- reg = <0x6c 0x0>;
+ reg = <0x6c>;
compatible = "qcom,actuator";
qcom,cci-master = <0>;
};
@@ -38,7 +38,7 @@
qcom,csid-sd-index = <0>;
qcom,actuator-src = <&actuator0>;
qcom,led-flash-src = <&led_flash0>;
- qcom,mount-angle = <90>;
+ qcom,mount-angle = <270>;
qcom,sensor-name = "ov8825";
cam_vdig-supply = <&pm8226_l5>;
cam_vana-supply = <&pm8226_l19>;
diff --git a/arch/arm/boot/dts/msm8226-cdp.dts b/arch/arm/boot/dts/msm8226-cdp.dts
index 7b8dd59..0186e54 100644
--- a/arch/arm/boot/dts/msm8226-cdp.dts
+++ b/arch/arm/boot/dts/msm8226-cdp.dts
@@ -13,7 +13,7 @@
/dts-v1/;
/include/ "msm8226.dtsi"
/include/ "dsi-panel-nt35590-720p-video.dtsi"
-/include/ "msm8226-camera-sensor-cdp-qrd.dtsi"
+/include/ "msm8226-camera-sensor-cdp.dtsi"
/ {
model = "Qualcomm MSM 8226 CDP";
diff --git a/arch/arm/boot/dts/msm8226-fluid.dts b/arch/arm/boot/dts/msm8226-fluid.dts
index 02a0b0b..d70ef6e 100644
--- a/arch/arm/boot/dts/msm8226-fluid.dts
+++ b/arch/arm/boot/dts/msm8226-fluid.dts
@@ -21,4 +21,8 @@
serial@f991f000 {
status = "disabled";
};
-};
\ No newline at end of file
+};
+
+&pm8226_bms {
+ status = "ok";
+};
diff --git a/arch/arm/boot/dts/msm8226-ion.dtsi b/arch/arm/boot/dts/msm8226-ion.dtsi
index 9a35507..9cef5a9 100644
--- a/arch/arm/boot/dts/msm8226-ion.dtsi
+++ b/arch/arm/boot/dts/msm8226-ion.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -20,6 +20,10 @@
reg = <30>;
};
+ qcom,ion-heap@21 { /* SYSTEM CONTIG HEAP */
+ reg = <21>;
+ };
+
qcom,ion-heap@8 { /* CP_MM HEAP */
compatible = "qcom,msm-ion-reserve";
reg = <8>;
diff --git a/arch/arm/boot/dts/msm8226-mtp.dts b/arch/arm/boot/dts/msm8226-mtp.dts
index dab15ae..b0a4a3d 100644
--- a/arch/arm/boot/dts/msm8226-mtp.dts
+++ b/arch/arm/boot/dts/msm8226-mtp.dts
@@ -299,3 +299,7 @@
qcom,fast-avg-setup = <0>;
};
};
+
+&pm8226_bms {
+ status = "ok";
+};
diff --git a/arch/arm/boot/dts/msm8226-qrd.dts b/arch/arm/boot/dts/msm8226-qrd.dts
index 4879691..7d4f0d5 100644
--- a/arch/arm/boot/dts/msm8226-qrd.dts
+++ b/arch/arm/boot/dts/msm8226-qrd.dts
@@ -13,7 +13,7 @@
/dts-v1/;
/include/ "msm8226.dtsi"
/include/ "dsi-panel-nt35590-720p-video.dtsi"
-/include/ "msm8226-camera-sensor-cdp-qrd.dtsi"
+/include/ "msm8226-camera-sensor-qrd.dtsi"
/ {
model = "Qualcomm MSM 8226 QRD";
diff --git a/arch/arm/boot/dts/msm8226-regulator.dtsi b/arch/arm/boot/dts/msm8226-regulator.dtsi
index f24cea1..70731d2 100644
--- a/arch/arm/boot/dts/msm8226-regulator.dtsi
+++ b/arch/arm/boot/dts/msm8226-regulator.dtsi
@@ -44,6 +44,9 @@
qcom,pvs-corner-ceiling-nom = <975000 1075000 1200000 1200000>;
qcom,pvs-corner-ceiling-fast = <900000 1000000 1140000 1140000>;
vdd-apc-supply = <&pm8226_s2>;
+ vdd-mx-supply = <&pm8226_l3_ao>;
+ qcom,vdd-mx-vmax = <1350000>;
+ qcom,vdd-mx-vmin-method = <1>;
};
};
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index 75cf6e5..a51d4b8 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -686,8 +686,9 @@
qcom,firmware-name = "mba";
qcom,pil-self-auth;
- /* GPIO input from mss */
+ /* GPIO inputs from mss */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_1_in 0 0>;
+ qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_1_in 2 0>;
/* GPIO output to mss */
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>;
diff --git a/arch/arm/boot/dts/msm8610-cdp.dts b/arch/arm/boot/dts/msm8610-cdp.dts
index d523c67..c762405 100644
--- a/arch/arm/boot/dts/msm8610-cdp.dts
+++ b/arch/arm/boot/dts/msm8610-cdp.dts
@@ -17,10 +17,65 @@
/ {
model = "Qualcomm MSM 8610 CDP";
compatible = "qcom,msm8610-cdp", "qcom,msm8610", "qcom,cdp";
- qcom,msm-id = <147 1 0>;
+ qcom,msm-id = <147 1 0>, <165 1 0>;
serial@f991f000 {
status = "ok";
};
};
+&sdhc_1 {
+ vdd-supply = <&pm8110_l17>;
+ qcom,vdd-always-on;
+ qcom,vdd-lpm-sup;
+ qcom,vdd-voltage-level = <2900000 2900000>;
+ qcom,vdd-current-level = <200 400000>;
+
+ vdd-io-supply = <&pm8110_l6>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <200 60000>;
+
+ qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+ qcom,nonremovable;
+
+ status = "ok";
+};
+
+&sdhc_2 {
+ vdd-supply = <&pm8110_l18>;
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <15000 400000>;
+
+ vdd-io-supply = <&pm8110_l21>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-lpm-sup;
+ qcom,vdd-io-voltage-level = <1800000 2950000>;
+ qcom,vdd-io-current-level = <200 50000>;
+
+ qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+
+ #address-cells = <0>;
+ interrupt-parent = <&sdhc_2>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 221 0
+ 2 &msmgpio 42 0x3>;
+ interrupt-names = "hc_irq", "pwr_irq", "status_irq";
+ cd-gpios = <&msmgpio 42 0x1>;
+
+ status = "ok";
+};
diff --git a/arch/arm/boot/dts/msm8610-ion.dtsi b/arch/arm/boot/dts/msm8610-ion.dtsi
index 107961d..848a6f5 100644
--- a/arch/arm/boot/dts/msm8610-ion.dtsi
+++ b/arch/arm/boot/dts/msm8610-ion.dtsi
@@ -20,6 +20,10 @@
reg = <30>;
};
+ qcom,ion-heap@21 { /* SYSTEM CONTIG HEAP */
+ reg = <21>;
+ };
+
qcom,ion-heap@25 { /* IOMMU HEAP */
reg = <25>;
};
diff --git a/arch/arm/boot/dts/msm8610-mtp.dts b/arch/arm/boot/dts/msm8610-mtp.dts
index f718d5e..e3eed72 100644
--- a/arch/arm/boot/dts/msm8610-mtp.dts
+++ b/arch/arm/boot/dts/msm8610-mtp.dts
@@ -17,10 +17,65 @@
/ {
model = "Qualcomm MSM 8610 MTP";
compatible = "qcom,msm8610-mtp", "qcom,msm8610", "qcom,mtp";
- qcom,msm-id = <147 8 0>;
+ qcom,msm-id = <147 8 0>, <165 8 0>;
serial@f991f000 {
status = "ok";
};
};
+&sdhc_1 {
+ vdd-supply = <&pm8110_l17>;
+ qcom,vdd-always-on;
+ qcom,vdd-lpm-sup;
+ qcom,vdd-voltage-level = <2900000 2900000>;
+ qcom,vdd-current-level = <200 400000>;
+
+ vdd-io-supply = <&pm8110_l6>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <200 60000>;
+
+ qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+ qcom,nonremovable;
+
+ status = "ok";
+};
+
+&sdhc_2 {
+ vdd-supply = <&pm8110_l18>;
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <15000 400000>;
+
+ vdd-io-supply = <&pm8110_l21>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-lpm-sup;
+ qcom,vdd-io-voltage-level = <1800000 2950000>;
+ qcom,vdd-io-current-level = <200 50000>;
+
+ qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+
+ #address-cells = <0>;
+ interrupt-parent = <&sdhc_2>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 221 0
+ 2 &msmgpio 42 0x3>;
+ interrupt-names = "hc_irq", "pwr_irq", "status_irq";
+ cd-gpios = <&msmgpio 42 0x1>;
+
+ status = "ok";
+};
diff --git a/arch/arm/boot/dts/msm8610-smp2p.dtsi b/arch/arm/boot/dts/msm8610-smp2p.dtsi
index 4a5273b..9690d12 100644
--- a/arch/arm/boot/dts/msm8610-smp2p.dtsi
+++ b/arch/arm/boot/dts/msm8610-smp2p.dtsi
@@ -177,6 +177,27 @@
#interrupt-cells = <2>;
};
+ smp2pgpio_ssr_smp2p_4_in: qcom,smp2pgpio-ssr-smp2p-4-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "slave-kernel";
+ qcom,remote-pid = <4>;
+ qcom,is-inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ smp2pgpio_ssr_smp2p_4_out: qcom,smp2pgpio-ssr-smp2p-4-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "master-kernel";
+ qcom,remote-pid = <4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
qcom,smp2pgpio_test_smp2p_4_out {
compatible = "qcom,smp2pgpio_test_smp2p_4_out";
gpios = <&smp2pgpio_smp2p_4_out 0 0>;
diff --git a/arch/arm/boot/dts/msm8610.dtsi b/arch/arm/boot/dts/msm8610.dtsi
index 5967dfb..eaf89ee 100644
--- a/arch/arm/boot/dts/msm8610.dtsi
+++ b/arch/arm/boot/dts/msm8610.dtsi
@@ -47,6 +47,8 @@
aliases {
spi0 = &spi_0;
+ sdhc1 = &sdhc_1; /* SDC1 eMMC slot */
+ sdhc2 = &sdhc_2; /* SDC2 SD card slot */
};
timer {
@@ -94,8 +96,17 @@
qcom,hsusb-otg-phy-type = <2>;
qcom,hsusb-otg-mode = <1>;
- qcom,hsusb-otg-otg-control = <1>;
+ qcom,hsusb-otg-otg-control = <2>;
qcom,hsusb-otg-disable-reset;
+ qcom,dp-manual-pullup;
+
+ qcom,msm-bus,name = "usb2";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only = <0>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <87 512 0 0>,
+ <87 512 60000 960000>;
};
android_usb@fe8050c8 {
@@ -135,6 +146,8 @@
qcom,bus-width = <8>;
qcom,nonremovable;
qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+
+ status = "disabled";
};
sdcc2: qcom,sdcc@f98a4000 {
@@ -166,6 +179,32 @@
qcom,xpc;
qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
qcom,current-limit = <800>;
+
+ status = "disabled";
+ };
+
+ sdhc_1: sdhci@f9824900 {
+ compatible = "qcom,sdhci-msm";
+ reg = <0xf9824900 0x11c>, <0xf9824000 0x800>;
+ reg-names = "hc_mem", "core_mem";
+
+ interrupts = <0 123 0>, <0 138 0>;
+ interrupt-names = "hc_irq", "pwr_irq";
+
+ qcom,bus-width = <8>;
+ status = "disabled";
+ };
+
+ sdhc_2: sdhci@f98a4900 {
+ compatible = "qcom,sdhci-msm";
+ reg = <0xf98a4900 0x11c>, <0xf98a4000 0x800>;
+ reg-names = "hc_mem", "core_mem";
+
+ interrupts = <0 125 0>, <0 221 0>;
+ interrupt-names = "hc_irq", "pwr_irq";
+
+ qcom,bus-width = <4>;
+ status = "disabled";
};
qcom,sps {
@@ -173,9 +212,9 @@
qcom,device-type = <3>;
};
- qcom,smem@d600000 {
+ qcom,smem@d900000 {
compatible = "qcom,smem";
- reg = <0xd600000 0x200000>,
+ reg = <0xd900000 0x200000>,
<0xf9011000 0x1000>,
<0xfc428000 0x4000>;
reg-names = "smem", "irq-reg-base", "aux-mem1";
@@ -250,7 +289,7 @@
qcom,msm-mem-hole {
compatible = "qcom,msm-mem-hole";
- qcom,memblock-remove = <0x07C00000 0x6000000>; /* Address and Size of Hole */
+ qcom,memblock-remove = <0x07B00000 0x6400000>; /* Address and Size of Hole */
};
qcom,wdt@f9017000 {
@@ -331,6 +370,12 @@
vdd_pronto_pll-supply = <&pm8110_l10>;
qcom,firmware-name = "wcnss";
+
+ /* GPIO input from wcnss */
+ qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_4_in 0 0>;
+
+ /* GPIO output to wcnss */
+ qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_4_out 0 0>;
};
sound {
@@ -458,8 +503,9 @@
qcom,firmware-name = "mba";
qcom,pil-self-auth;
- /* GPIO input from mss */
+ /* GPIO inputs from mss */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_1_in 0 0>;
+ qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_1_in 2 0>;
/* GPIO output to mss */
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>;
diff --git a/arch/arm/boot/dts/msm8974-camera.dtsi b/arch/arm/boot/dts/msm8974-camera.dtsi
index 0bd303f..3a78a15 100644
--- a/arch/arm/boot/dts/msm8974-camera.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera.dtsi
@@ -104,7 +104,7 @@
cell-index = <0>;
compatible = "qcom,vfe40";
reg = <0xfda10000 0x1000>,
- <0xfda40000 0x200>;
+ <0xfda40000 0x200>;
reg-names = "vfe", "vfe_vbif";
interrupts = <0 57 0>;
interrupt-names = "vfe";
@@ -115,7 +115,7 @@
cell-index = <1>;
compatible = "qcom,vfe40";
reg = <0xfda14000 0x1000>,
- <0xfda40000 0x200>;
+ <0xfda40000 0x200>;
reg-names = "vfe", "vfe_vbif";
interrupts = <0 58 0>;
interrupt-names = "vfe";
@@ -129,7 +129,7 @@
reg-names = "jpeg";
interrupts = <0 59 0>;
interrupt-names = "jpeg";
- vdd-supply = <&gdsc_jpeg>;
+ vdd-supply = <&gdsc_jpeg>;
};
qcom,jpeg@fda20000 {
@@ -163,8 +163,8 @@
cell-index = <0>;
compatible = "qcom,cpp";
reg = <0xfda04000 0x100>,
- <0xfda40000 0x200>,
- <0xfda18000 0x008>;
+ <0xfda40000 0x200>,
+ <0xfda18000 0x008>;
reg-names = "cpp", "cpp_vbif", "cpp_hw";
interrupts = <0 49 0>;
interrupt-names = "cpp";
@@ -182,7 +182,7 @@
cell-index = <0>;
compatible = "qcom,cci";
reg = <0xfda0C000 0x1000>;
- #address-cells = <1>;
+ #address-cells = <1>;
#size-cells = <0>;
reg-names = "cci";
interrupts = <0 50 0>;
@@ -194,9 +194,9 @@
qcom,gpio-tbl-num = <0 1 2 3>;
qcom,gpio-tbl-flags = <1 1 1 1>;
qcom,gpio-tbl-label = "CCI_I2C_DATA0",
- "CCI_I2C_CLK0",
- "CCI_I2C_DATA1",
- "CCI_I2C_CLK1";
+ "CCI_I2C_CLK0",
+ "CCI_I2C_DATA1",
+ "CCI_I2C_CLK1";
qcom,hw-thigh = <78>;
qcom,hw-tlow = <114>;
qcom,hw-tsu-sto = <28>;
diff --git a/arch/arm/boot/dts/msm8974-gpu.dtsi b/arch/arm/boot/dts/msm8974-gpu.dtsi
index a7544ab..28d1d61 100644
--- a/arch/arm/boot/dts/msm8974-gpu.dtsi
+++ b/arch/arm/boot/dts/msm8974-gpu.dtsi
@@ -67,14 +67,14 @@
qcom,gpu-pwrlevel@1 {
reg = <1>;
- qcom,gpu-freq = <300000000>;
+ qcom,gpu-freq = <320000000>;
qcom,bus-freq = <4>;
qcom,io-fraction = <66>;
};
qcom,gpu-pwrlevel@2 {
reg = <2>;
- qcom,gpu-freq = <300000000>;
+ qcom,gpu-freq = <320000000>;
qcom,bus-freq = <3>;
qcom,io-fraction = <66>;
};
diff --git a/arch/arm/boot/dts/msm8974-v2.dtsi b/arch/arm/boot/dts/msm8974-v2.dtsi
index 61f2c4f..50fb380 100644
--- a/arch/arm/boot/dts/msm8974-v2.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2.dtsi
@@ -48,9 +48,9 @@
/* Nominal / SVS */
<26 512 0 4656000>, <89 604 0 3000000>,
/* Nominal */
- <26 512 0 4656000>, <89 604 0 5334880>,
+ <26 512 0 4656000>, <89 604 0 5120000>,
/* Turbo / Nominal */
- <26 512 0 7464000>, <89 604 0 5334880>,
+ <26 512 0 7464000>, <89 604 0 5120000>,
/* Turbo */
<26 512 0 7464000>, <89 604 0 6400000>;
};
@@ -67,6 +67,13 @@
qcom,mdss-pingpong-off = <0x00012D00 0x00012E00 0x00012F00>;
};
+&mdss_hdmi_tx {
+ reg = <0xfd922100 0x370>,
+ <0xfd922500 0x7C>,
+ <0xfc4b8000 0x60F0>;
+ reg-names = "core_physical", "phy_physical", "qfprom_physical";
+};
+
&msm_vidc {
qcom,vidc-ns-map = <0x40000000 0x40000000>;
qcom,load-freq-tbl = <979200 465000000>,
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index 9a5a5d2..56234a1 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -1036,8 +1036,9 @@
qcom,firmware-name = "mba";
qcom,pil-self-auth;
- /* GPIO input from mss */
+ /* GPIO inputs from mss */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_1_in 0 0>;
+ qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_1_in 2 0>;
/* GPIO output to mss */
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>;
@@ -1153,7 +1154,7 @@
qcom,wdt@f9017000 {
compatible = "qcom,msm-watchdog";
reg = <0xf9017000 0x1000>;
- interrupts = <0 3 0 0 4 0>;
+ interrupts = <0 3 0>, <0 4 0>;
qcom,bark-time = <11000>;
qcom,pet-time = <10000>;
qcom,ipi-ping;
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index fe81fa9..ee61dc3 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -689,8 +689,9 @@
compatible = "qcom,pil-q6v5-mss";
interrupts = <0 24 1>;
- /* GPIO input from mss */
+ /* GPIO inputs from mss */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_1_in 0 0>;
+ qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_1_in 2 0>;
/* GPIO output to mss */
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>;
diff --git a/arch/arm/configs/msm8610_defconfig b/arch/arm/configs/msm8610_defconfig
index 5e520f3..45f0868 100644
--- a/arch/arm/configs/msm8610_defconfig
+++ b/arch/arm/configs/msm8610_defconfig
@@ -241,6 +241,7 @@
CONFIG_GPIO_QPNP_PIN=y
CONFIG_POWER_SUPPLY=y
CONFIG_QPNP_CHARGER=y
+CONFIG_QPNP_BMS=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_SENSORS_QPNP_ADC_CURRENT=y
CONFIG_THERMAL=y
@@ -302,7 +303,10 @@
CONFIG_MMC_PARANOID_SD_INIT=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_TEST=m
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_MSM=y
+CONFIG_MMC_SDHCI_MSM=y
CONFIG_MMC_MSM_SPS_SUPPORT=y
CONFIG_LEDS_QPNP=y
CONFIG_LEDS_TRIGGERS=y
diff --git a/arch/arm/mach-msm/acpuclock-8974.c b/arch/arm/mach-msm/acpuclock-8974.c
index b673f93..0533d06 100644
--- a/arch/arm/mach-msm/acpuclock-8974.c
+++ b/arch/arm/mach-msm/acpuclock-8974.c
@@ -39,6 +39,8 @@
.user_val = 0x8,
.user_vco_mask = BIT(20),
.config_val = 0x04D0405D,
+ .has_lock_status = true,
+ .status_offset = 0x1C,
.low_vco_l_max = 65,
.low_vdd_l_max = 52,
.nom_vdd_l_max = 104,
diff --git a/arch/arm/mach-msm/acpuclock-cortex.c b/arch/arm/mach-msm/acpuclock-cortex.c
index ca7fc2b..74ca145 100644
--- a/arch/arm/mach-msm/acpuclock-cortex.c
+++ b/arch/arm/mach-msm/acpuclock-cortex.c
@@ -121,7 +121,7 @@
writel_relaxed(regval, apcs_rcg_cmd);
/* Wait for the update to take effect */
- rc = readl_poll_timeout(apcs_rcg_cmd, regval,
+ rc = readl_poll_timeout_noirq(apcs_rcg_cmd, regval,
!(regval & r->poll_mask),
POLL_INTERVAL_US,
APCS_RCG_UPDATE_TIMEOUT_US);
diff --git a/arch/arm/mach-msm/acpuclock-krait.c b/arch/arm/mach-msm/acpuclock-krait.c
index a6f4423..e3a3f54 100644
--- a/arch/arm/mach-msm/acpuclock-krait.c
+++ b/arch/arm/mach-msm/acpuclock-krait.c
@@ -22,6 +22,7 @@
#include <linux/cpufreq.h>
#include <linux/cpu.h>
#include <linux/regulator/consumer.h>
+#include <linux/iopoll.h>
#include <asm/mach-types.h>
#include <asm/cpu.h>
@@ -131,8 +132,14 @@
writel_relaxed(0x6, sc->hfpll_base + drv.hfpll_data->mode_offset);
/* Wait for PLL to lock. */
- mb();
- udelay(60);
+ if (drv.hfpll_data->has_lock_status) {
+ u32 regval;
+ readl_tight_poll(sc->hfpll_base + drv.hfpll_data->status_offset,
+ regval, regval & BIT(16));
+ } else {
+ mb();
+ udelay(60);
+ }
/* Enable PLL output. */
writel_relaxed(0x7, sc->hfpll_base + drv.hfpll_data->mode_offset);
diff --git a/arch/arm/mach-msm/acpuclock-krait.h b/arch/arm/mach-msm/acpuclock-krait.h
index 11d58dd..f02af98 100644
--- a/arch/arm/mach-msm/acpuclock-krait.h
+++ b/arch/arm/mach-msm/acpuclock-krait.h
@@ -171,8 +171,10 @@
* @user_val: Value to initialize the @user_offset register to.
* @user_vco_mask: Bit in the @user_offset to enable high-frequency VCO mode.
* @has_droop_ctl: Indicates the presence of a voltage droop controller.
+ * @has_lock_status: Indicates the presence of a lock status bit.
* @droop_offset: Droop controller register offset from base address.
* @droop_val: Value to initialize the @config_offset register to.
+ * @status_offset: PLL status register offset.
* @low_vdd_l_max: Maximum "L" value supported at HFPLL_VDD_LOW.
* @nom_vdd_l_max: Maximum "L" value supported at HFPLL_VDD_NOM.
* @low_vco_l_max: Maximum "L" value supported in low-frequency VCO mode.
@@ -190,8 +192,10 @@
const u32 user_val;
const u32 user_vco_mask;
const bool has_droop_ctl;
+ const bool has_lock_status;
const u32 droop_offset;
const u32 droop_val;
+ const u32 status_offset;
u32 low_vdd_l_max;
u32 nom_vdd_l_max;
const u32 low_vco_l_max;
diff --git a/arch/arm/mach-msm/bam_dmux.c b/arch/arm/mach-msm/bam_dmux.c
index 92be927..a0644e6 100644
--- a/arch/arm/mach-msm/bam_dmux.c
+++ b/arch/arm/mach-msm/bam_dmux.c
@@ -1979,6 +1979,8 @@
a2_props.options = SPS_BAM_OPT_IRQ_WAKEUP;
a2_props.num_pipes = A2_NUM_PIPES;
a2_props.summing_threshold = A2_SUMMING_THRESHOLD;
+ a2_props.constrained_logging = true;
+ a2_props.logging_number = 1;
if (cpu_is_msm9615() || satellite_mode)
a2_props.manage = SPS_BAM_MGR_DEVICE_REMOTE;
/* need to free on tear down */
diff --git a/arch/arm/mach-msm/bms-batterydata-desay.c b/arch/arm/mach-msm/bms-batterydata-desay.c
index dd3f346..e2b62be 100644
--- a/arch/arm/mach-msm/bms-batterydata-desay.c
+++ b/arch/arm/mach-msm/bms-batterydata-desay.c
@@ -84,4 +84,5 @@
.pc_sf_lut = &desay_5200_pc_sf,
.default_rbatt_mohm = 156,
.rbatt_capacitive_mohm = 50,
+ .flat_ocv_threshold_uv = 3800000,
};
diff --git a/arch/arm/mach-msm/bms-batterydata-oem.c b/arch/arm/mach-msm/bms-batterydata-oem.c
index 036bf88..e4c42d7 100644
--- a/arch/arm/mach-msm/bms-batterydata-oem.c
+++ b/arch/arm/mach-msm/bms-batterydata-oem.c
@@ -105,4 +105,5 @@
.pc_temp_ocv_lut = &pc_temp_ocv,
.rbatt_sf_lut = &rbatt_sf,
.default_rbatt_mohm = 236,
+ .flat_ocv_threshold_uv = 3800000,
};
diff --git a/arch/arm/mach-msm/bms-batterydata.c b/arch/arm/mach-msm/bms-batterydata.c
index 0c39df6..dc98c57 100644
--- a/arch/arm/mach-msm/bms-batterydata.c
+++ b/arch/arm/mach-msm/bms-batterydata.c
@@ -106,4 +106,5 @@
.rbatt_sf_lut = &rbatt_sf,
.default_rbatt_mohm = 236,
.rbatt_capacitive_mohm = 50,
+ .flat_ocv_threshold_uv = 3800000,
};
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index e36884e..707abef 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -2344,6 +2344,8 @@
static struct msm_pcie_platform msm_pcie_platform_data = {
.axi_addr = PCIE_AXI_BAR_PHYS,
.axi_size = PCIE_AXI_BAR_SIZE,
+ .parf_deemph = 0x282828,
+ .parf_swing = 0x7F7F,
};
/* FSM8064_EP PCIe gpios */
@@ -2357,7 +2359,9 @@
.axi_addr = PCIE_AXI_BAR_PHYS,
.axi_size = PCIE_AXI_BAR_SIZE,
.wake_n = PM8921_GPIO_IRQ(PM8921_IRQ_BASE, PCIE_EP_WAKE_N_PMIC_GPIO),
- .vreg_n = 4
+ .vreg_n = 4,
+ .parf_deemph = 0x101010,
+ .parf_swing = 0x6B6B,
};
static int __init mpq8064_pcie_enabled(void)
diff --git a/arch/arm/mach-msm/board-8610.c b/arch/arm/mach-msm/board-8610.c
index 9495b72..99db345 100644
--- a/arch/arm/mach-msm/board-8610.c
+++ b/arch/arm/mach-msm/board-8610.c
@@ -72,6 +72,10 @@
"msm_sdcc.1", NULL),
OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \
"msm_sdcc.2", NULL),
+ OF_DEV_AUXDATA("qcom,sdhci-msm", 0xF9824900, \
+ "msm_sdcc.1", NULL),
+ OF_DEV_AUXDATA("qcom,sdhci-msm", 0xF98A4900, \
+ "msm_sdcc.2", NULL),
{}
};
diff --git a/arch/arm/mach-msm/board-8974-gpiomux.c b/arch/arm/mach-msm/board-8974-gpiomux.c
index 35f3f99..e30d0ba 100644
--- a/arch/arm/mach-msm/board-8974-gpiomux.c
+++ b/arch/arm/mach-msm/board-8974-gpiomux.c
@@ -100,6 +100,18 @@
.pull = GPIOMUX_PULL_DOWN,
};
+static struct gpiomux_setting ath_gpio_active_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting ath_gpio_suspend_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+
static struct gpiomux_setting gpio_i2c_config = {
.func = GPIOMUX_FUNC_3,
/*
@@ -831,6 +843,24 @@
},
};
+
+static struct msm_gpiomux_config ath_gpio_configs[] = {
+ {
+ .gpio = 51,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &ath_gpio_active_cfg,
+ [GPIOMUX_SUSPENDED] = &ath_gpio_suspend_cfg,
+ },
+ },
+ {
+ .gpio = 79,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &ath_gpio_active_cfg,
+ [GPIOMUX_SUSPENDED] = &ath_gpio_suspend_cfg,
+ },
+ },
+};
+
static struct msm_gpiomux_config msm_taiko_config[] __initdata = {
{
.gpio = 63, /* SYS_RST_N */
@@ -1034,7 +1064,8 @@
ARRAY_SIZE(msm_blsp2_uart7_configs));
msm_gpiomux_install(wcnss_5wire_interface,
ARRAY_SIZE(wcnss_5wire_interface));
-
+ msm_gpiomux_install_nowrite(ath_gpio_configs,
+ ARRAY_SIZE(ath_gpio_configs));
msm_gpiomux_install(msm8974_slimbus_config,
ARRAY_SIZE(msm8974_slimbus_config));
diff --git a/arch/arm/mach-msm/clock-8226.c b/arch/arm/mach-msm/clock-8226.c
index 4079b5a..80907c8 100644
--- a/arch/arm/mach-msm/clock-8226.c
+++ b/arch/arm/mach-msm/clock-8226.c
@@ -2810,6 +2810,9 @@
static DEFINE_CLK_VOTER(pnoc_sps_clk, &pnoc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(qseecom_ce1_clk_src, &ce1_clk_src.c, LONG_MAX);
+static DEFINE_CLK_VOTER(scm_ce1_clk_src, &ce1_clk_src.c, LONG_MAX);
+
static DEFINE_CLK_BRANCH_VOTER(cxo_otg_clk, &xo.c);
static DEFINE_CLK_BRANCH_VOTER(cxo_pil_lpass_clk, &xo.c);
static DEFINE_CLK_BRANCH_VOTER(cxo_pil_mss_clk, &xo.c);
@@ -3171,12 +3174,14 @@
CLK_LOOKUP("core_clk", gcc_ce1_clk.c, "qseecom"),
CLK_LOOKUP("iface_clk", gcc_ce1_ahb_clk.c, "qseecom"),
CLK_LOOKUP("bus_clk", gcc_ce1_axi_clk.c, "qseecom"),
- CLK_LOOKUP("core_clk_src", ce1_clk_src.c, "qseecom"),
+ CLK_LOOKUP("core_clk_src", qseecom_ce1_clk_src.c, "qseecom"),
CLK_LOOKUP("core_clk", gcc_ce1_clk.c, "scm"),
CLK_LOOKUP("iface_clk", gcc_ce1_ahb_clk.c, "scm"),
CLK_LOOKUP("bus_clk", gcc_ce1_axi_clk.c, "scm"),
- CLK_LOOKUP("core_clk_src", ce1_clk_src.c, "scm"),
+ CLK_LOOKUP("core_clk_src", scm_ce1_clk_src.c, "scm"),
+
+ CLK_LOOKUP("core_clk_src", ce1_clk_src.c, ""),
/* SDCC */
CLK_LOOKUP("iface_clk", gcc_sdcc1_ahb_clk.c, "f9824000.qcom,sdcc"),
diff --git a/arch/arm/mach-msm/cpr-regulator.c b/arch/arm/mach-msm/cpr-regulator.c
index 4e95e4e..08923e4 100644
--- a/arch/arm/mach-msm/cpr-regulator.c
+++ b/arch/arm/mach-msm/cpr-regulator.c
@@ -43,10 +43,16 @@
/* Process voltage variables */
u32 pvs_bin;
u32 pvs_process;
- u32 *process_vmax;
+ u32 *corner_ceiling;
/* APC voltage regulator */
struct regulator *vdd_apc;
+
+ /* Dependency parameters */
+ struct regulator *vdd_mx;
+ int vdd_mx_vmax;
+ int vdd_mx_vmin_method;
+ int vdd_mx_vmin;
};
static int cpr_regulator_is_enabled(struct regulator_dev *rdev)
@@ -59,11 +65,23 @@
static int cpr_regulator_enable(struct regulator_dev *rdev)
{
struct cpr_regulator *cpr_vreg = rdev_get_drvdata(rdev);
- int rc;
+ int rc = 0;
+
+ /* Enable dependency power before vdd_apc */
+ if (cpr_vreg->vdd_mx) {
+ rc = regulator_enable(cpr_vreg->vdd_mx);
+ if (rc) {
+ pr_err("regulator_enable: vdd_mx: rc=%d\n", rc);
+ return rc;
+ }
+ }
rc = regulator_enable(cpr_vreg->vdd_apc);
if (!rc)
cpr_vreg->enabled = true;
+ else
+ pr_err("regulator_enable: vdd_apc: rc=%d\n", rc);
+
return rc;
}
@@ -73,8 +91,18 @@
int rc;
rc = regulator_disable(cpr_vreg->vdd_apc);
- if (!rc)
- cpr_vreg->enabled = false;
+ if (!rc) {
+ if (cpr_vreg->vdd_mx)
+ rc = regulator_disable(cpr_vreg->vdd_mx);
+
+ if (rc)
+ pr_err("regulator_disable: vdd_mx: rc=%d\n", rc);
+ else
+ cpr_vreg->enabled = false;
+ } else {
+ pr_err("regulator_disable: vdd_apc: rc=%d\n", rc);
+ }
+
return rc;
}
@@ -83,14 +111,84 @@
{
struct cpr_regulator *cpr_vreg = rdev_get_drvdata(rdev);
int rc;
- int vdd_apc_min, vdd_apc_max;
+ int vdd_apc_min, vdd_apc_max, vdd_mx_vmin = 0;
+ int change_dir = 0;
- vdd_apc_min = cpr_vreg->process_vmax[min_uV];
- vdd_apc_max = cpr_vreg->process_vmax[CPR_CORNER_SUPER_TURBO];
+ if (cpr_vreg->vdd_mx) {
+ if (min_uV > cpr_vreg->corner)
+ change_dir = 1;
+ else if (min_uV < cpr_vreg->corner)
+ change_dir = -1;
+ }
+
+ vdd_apc_min = cpr_vreg->corner_ceiling[min_uV];
+ vdd_apc_max = cpr_vreg->corner_ceiling[CPR_CORNER_SUPER_TURBO];
+
+ if (change_dir) {
+ /* Determine the vdd_mx voltage */
+ switch (cpr_vreg->vdd_mx_vmin_method) {
+ case VDD_MX_VMIN_APC:
+ vdd_mx_vmin = vdd_apc_min;
+ break;
+ case VDD_MX_VMIN_APC_CORNER_CEILING:
+ vdd_mx_vmin = vdd_apc_min;
+ break;
+ case VDD_MX_VMIN_APC_SLOW_CORNER_CEILING:
+ vdd_mx_vmin = cpr_vreg->pvs_corner_ceiling
+ [APC_PVS_SLOW][min_uV];
+ break;
+ case VDD_MX_VMIN_MX_VMAX:
+ default:
+ vdd_mx_vmin = cpr_vreg->vdd_mx_vmax;
+ break;
+ }
+ }
+
+ if (change_dir > 0) {
+ if (vdd_mx_vmin < cpr_vreg->vdd_mx_vmin) {
+ /* Check and report the value in case */
+ pr_err("Up: but new %d < old %d uV\n", vdd_mx_vmin,
+ cpr_vreg->vdd_mx_vmin);
+ }
+
+ rc = regulator_set_voltage(cpr_vreg->vdd_mx, vdd_mx_vmin,
+ cpr_vreg->vdd_mx_vmax);
+ if (!rc) {
+ cpr_vreg->vdd_mx_vmin = vdd_mx_vmin;
+ } else {
+ pr_err("set: vdd_mx [%d] = %d uV: rc=%d\n",
+ min_uV, vdd_mx_vmin, rc);
+ return rc;
+ }
+ }
+
rc = regulator_set_voltage(cpr_vreg->vdd_apc,
vdd_apc_min, vdd_apc_max);
- if (!rc)
+ if (!rc) {
cpr_vreg->corner = min_uV;
+ } else {
+ pr_err("set: vdd_apc [%d] = %d uV: rc=%d\n",
+ min_uV, vdd_apc_min, rc);
+ return rc;
+ }
+
+ if (change_dir < 0) {
+ if (vdd_mx_vmin > cpr_vreg->vdd_mx_vmin) {
+ /* Check and report the value in case */
+ pr_err("Down: but new %d >= old %d uV\n", vdd_mx_vmin,
+ cpr_vreg->vdd_mx_vmin);
+ }
+
+ rc = regulator_set_voltage(cpr_vreg->vdd_mx, vdd_mx_vmin,
+ cpr_vreg->vdd_mx_vmax);
+ if (!rc) {
+ cpr_vreg->vdd_mx_vmin = vdd_mx_vmin;
+ } else {
+ pr_err("set: vdd_mx [%d] = %d uV: rc=%d\n",
+ min_uV, vdd_mx_vmin, rc);
+ return rc;
+ }
+ }
pr_debug("set [corner:%d] = %d uV: rc=%d\n", min_uV, vdd_apc_min, rc);
return rc;
@@ -146,7 +244,7 @@
= cpr_vreg->pvs_corner_ceiling[APC_PVS_SLOW]
[CPR_CORNER_SUPER_TURBO];
- cpr_vreg->process_vmax =
+ cpr_vreg->corner_ceiling =
cpr_vreg->pvs_corner_ceiling[cpr_vreg->pvs_process];
iounmap(efuse_base);
@@ -162,19 +260,62 @@
static int __init cpr_regulator_apc_init(struct platform_device *pdev,
struct cpr_regulator *cpr_vreg)
{
+ struct device_node *of_node = pdev->dev.of_node;
+ int rc;
+
cpr_vreg->vdd_apc = devm_regulator_get(&pdev->dev, "vdd-apc");
if (IS_ERR_OR_NULL(cpr_vreg->vdd_apc)) {
- pr_err("devm_regulator_get: rc=%d\n",
- (int)PTR_ERR(cpr_vreg->vdd_apc));
+ rc = PTR_RET(cpr_vreg->vdd_apc);
+ if (rc != -EPROBE_DEFER)
+ pr_err("devm_regulator_get: rc=%d\n", rc);
+ return rc;
}
- return PTR_RET(cpr_vreg->vdd_apc);
+ /* Check dependencies */
+ if (of_property_read_bool(of_node, "vdd-mx-supply")) {
+ cpr_vreg->vdd_mx = devm_regulator_get(&pdev->dev, "vdd-mx");
+ if (IS_ERR_OR_NULL(cpr_vreg->vdd_mx)) {
+ rc = PTR_RET(cpr_vreg->vdd_mx);
+ if (rc != -EPROBE_DEFER)
+ pr_err("devm_regulator_get: vdd-mx: rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ /* Parse dependency parameters */
+ if (cpr_vreg->vdd_mx) {
+ rc = of_property_read_u32(of_node, "qcom,vdd-mx-vmax",
+ &cpr_vreg->vdd_mx_vmax);
+ if (rc < 0) {
+ pr_err("vdd-mx-vmax missing: rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,vdd-mx-vmin-method",
+ &cpr_vreg->vdd_mx_vmin_method);
+ if (rc < 0) {
+ pr_err("vdd-mx-vmin-method missing: rc=%d\n", rc);
+ return rc;
+ }
+ if (cpr_vreg->vdd_mx_vmin_method > VDD_MX_VMIN_MX_VMAX) {
+ pr_err("Invalid vdd-mx-vmin-method(%d)\n",
+ cpr_vreg->vdd_mx_vmin_method);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
}
static void cpr_regulator_apc_exit(struct cpr_regulator *cpr_vreg)
{
- if (cpr_vreg->enabled)
+ if (cpr_vreg->enabled) {
regulator_disable(cpr_vreg->vdd_apc);
+
+ if (cpr_vreg->vdd_mx)
+ regulator_disable(cpr_vreg->vdd_mx);
+ }
}
static int __init cpr_regulator_parse_dt(struct platform_device *pdev,
@@ -323,10 +464,10 @@
platform_set_drvdata(pdev, cpr_vreg);
pr_info("PVS [%d %d %d %d] uV\n",
- cpr_vreg->process_vmax[CPR_CORNER_SVS],
- cpr_vreg->process_vmax[CPR_CORNER_NORMAL],
- cpr_vreg->process_vmax[CPR_CORNER_TURBO],
- cpr_vreg->process_vmax[CPR_CORNER_SUPER_TURBO]);
+ cpr_vreg->corner_ceiling[CPR_CORNER_SVS],
+ cpr_vreg->corner_ceiling[CPR_CORNER_NORMAL],
+ cpr_vreg->corner_ceiling[CPR_CORNER_TURBO],
+ cpr_vreg->corner_ceiling[CPR_CORNER_SUPER_TURBO]);
return 0;
}
diff --git a/arch/arm/mach-msm/ebi_erp.c b/arch/arm/mach-msm/ebi_erp.c
index eb38101..6b300d8 100644
--- a/arch/arm/mach-msm/ebi_erp.c
+++ b/arch/arm/mach-msm/ebi_erp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -18,6 +18,9 @@
#include <linux/errno.h>
#include <linux/proc_fs.h>
#include <linux/cpu.h>
+#include <mach/usb_trace.h>
+
+DEFINE_TRACE(usb_daytona_invalid_access);
#define MODULE_NAME "msm_ebi_erp"
@@ -113,6 +116,11 @@
err_cntl |= CNTL_CLEAR_ERR;
writel_relaxed(err_cntl, base + SLV_ERR_CNTL);
mb(); /* Ensure interrupt is cleared before returning */
+
+ if ((err_apacket0 & AMID_MASK) == 0x00000102)
+ trace_usb_daytona_invalid_access(err_addr, err_apacket0,
+ err_apacket1);
+
return IRQ_HANDLED;
}
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8610.h b/arch/arm/mach-msm/include/mach/msm_iomap-8610.h
index b07ddba..2a62460 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-8610.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-8610.h
@@ -22,7 +22,7 @@
*
*/
-#define MSM8610_MSM_SHARED_RAM_PHYS 0x0D600000
+#define MSM8610_MSM_SHARED_RAM_PHYS 0x0D900000
#define MSM8610_APCS_GCC_PHYS 0xF9011000
#define MSM8610_APCS_GCC_SIZE SZ_4K
diff --git a/arch/arm/mach-msm/include/mach/msm_pcie.h b/arch/arm/mach-msm/include/mach/msm_pcie.h
index 790a390..99d1a4d 100644
--- a/arch/arm/mach-msm/include/mach/msm_pcie.h
+++ b/arch/arm/mach-msm/include/mach/msm_pcie.h
@@ -37,6 +37,8 @@
uint32_t axi_size;
uint32_t wake_n;
uint32_t vreg_n;
+ uint32_t parf_deemph;
+ uint32_t parf_swing;
};
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_serial_hs.h b/arch/arm/mach-msm/include/mach/msm_serial_hs.h
index dd53911..e6b677e 100644
--- a/arch/arm/mach-msm/include/mach/msm_serial_hs.h
+++ b/arch/arm/mach-msm/include/mach/msm_serial_hs.h
@@ -51,6 +51,7 @@
unsigned int msm_hs_tx_empty(struct uart_port *uport);
void msm_hs_request_clock_off(struct uart_port *uport);
void msm_hs_request_clock_on(struct uart_port *uport);
+struct uart_port *msm_hs_get_uart_port(int port_index);
void msm_hs_set_mctrl(struct uart_port *uport,
unsigned int mctrl);
#endif
diff --git a/arch/arm/mach-msm/include/mach/ocmem_priv.h b/arch/arm/mach-msm/include/mach/ocmem_priv.h
index abb5653..8539dcc 100644
--- a/arch/arm/mach-msm/include/mach/ocmem_priv.h
+++ b/arch/arm/mach-msm/include/mach/ocmem_priv.h
@@ -85,6 +85,12 @@
atomic_long_t z_stat[NR_OCMEM_ZSTAT_ITEMS];
struct gen_pool *z_pool;
struct ocmem_zone_ops *z_ops;
+ unsigned int max_alloc_time;
+ unsigned int min_alloc_time;
+ u64 total_alloc_time;
+ unsigned int max_free_time;
+ unsigned int min_free_time;
+ u64 total_free_time;
};
enum op_code {
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/usf.h b/arch/arm/mach-msm/include/mach/qdsp6v2/usf.h
index ff39929..2e15cae 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/usf.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/usf.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -97,6 +97,10 @@
/* Max size of the client name */
#define USF_MAX_CLIENT_NAME_SIZE 20
+
+/* Max number of the ports (mics/speakers) */
+#define USF_MAX_PORT_NUM 8
+
/* Info structure common for TX and RX */
struct us_xx_info_type {
/* Input: general info */
@@ -115,7 +119,7 @@
/* Number of the microphones (TX) or speakers(RX) */
uint16_t port_cnt;
/* Microphones(TX) or speakers(RX) indexes in their enumeration */
- uint8_t port_id[4];
+ uint8_t port_id[USF_MAX_PORT_NUM];
/* Bits per sample 16 or 32 */
uint16_t bits_per_sample;
/* Input: Transparent info for encoder in the LPASS */
diff --git a/arch/arm/mach-msm/include/mach/sps.h b/arch/arm/mach-msm/include/mach/sps.h
index 662655b..25cbc87 100644
--- a/arch/arm/mach-msm/include/mach/sps.h
+++ b/arch/arm/mach-msm/include/mach/sps.h
@@ -410,6 +410,11 @@
u32 sec_config;
struct sps_bam_sec_config_props *p_sec_config_props;
+
+ /* Logging control */
+
+ bool constrained_logging;
+ u32 logging_number;
};
/**
diff --git a/arch/arm/mach-msm/include/mach/usb_trace.h b/arch/arm/mach-msm/include/mach/usb_trace.h
new file mode 100644
index 0000000..02ca8ca
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/usb_trace.h
@@ -0,0 +1,27 @@
+/* include/asm-arm/arch-msm/usbtrace.h
+ *
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _USB_TRACE_H_
+#define _USB_TRACE_H_
+
+#include <linux/tracepoint.h>
+
+DECLARE_TRACE(usb_daytona_invalid_access,
+ TP_PROTO(unsigned int ebi_addr,
+ unsigned int ebi_apacket0, unsigned int ebi_apacket1),
+ TP_ARGS(ebi_addr, ebi_apacket0, ebi_apacket1));
+
+#endif
+
diff --git a/arch/arm/mach-msm/ocmem.c b/arch/arm/mach-msm/ocmem.c
index 4685f02..d31f3c4 100644
--- a/arch/arm/mach-msm/ocmem.c
+++ b/arch/arm/mach-msm/ocmem.c
@@ -587,6 +587,34 @@
.release = seq_release,
};
+static int ocmem_timing_show(struct seq_file *f, void *dummy)
+{
+ unsigned i = 0;
+ for (i = OCMEM_GRAPHICS; i < OCMEM_CLIENT_MAX; i++) {
+ struct ocmem_zone *z = get_zone(i);
+ if (z && z->active == true)
+ seq_printf(f, "zone %s\t: alloc_delay:[max:%d, min:%d, total:%llu,cnt:%lu] free_delay:[max:%d, min:%d, total:%llu, cnt:%lu]\n",
+ get_name(z->owner), z->max_alloc_time,
+ z->min_alloc_time, z->total_alloc_time,
+ get_ocmem_stat(z, 1), z->max_free_time,
+ z->min_free_time, z->total_free_time,
+ get_ocmem_stat(z, 6));
+ }
+ return 0;
+}
+
+static int ocmem_timing_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ocmem_timing_show, inode->i_private);
+}
+
+static const struct file_operations timing_show_fops = {
+ .open = ocmem_timing_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
static int ocmem_zone_init(struct platform_device *pdev)
{
@@ -656,6 +684,13 @@
zone->max_regions = 0;
INIT_LIST_HEAD(&zone->req_list);
zone->z_ops = z_ops;
+ zone->max_alloc_time = 0;
+ zone->min_alloc_time = 0xFFFFFFFF;
+ zone->total_alloc_time = 0;
+ zone->max_free_time = 0;
+ zone->min_free_time = 0xFFFFFFFF;
+ zone->total_free_time = 0;
+
if (part->p_tail) {
z_ops->allocate = allocate_tail;
z_ops->free = free_tail;
@@ -688,6 +723,12 @@
return -EBUSY;
}
+ if (!debugfs_create_file("timing", S_IRUGO, pdata->debug_node,
+ NULL, &timing_show_fops)) {
+ dev_err(dev, "Unable to create debugfs node for timing\n");
+ return -EBUSY;
+ }
+
dev_dbg(dev, "Total active zones = %d\n", active_zones);
return 0;
}
diff --git a/arch/arm/mach-msm/ocmem_api.c b/arch/arm/mach-msm/ocmem_api.c
index 16dd8b8..13c4c1a 100644
--- a/arch/arm/mach-msm/ocmem_api.c
+++ b/arch/arm/mach-msm/ocmem_api.c
@@ -105,6 +105,11 @@
{
bool can_block = false;
bool can_wait = true;
+ struct ocmem_buf *buffer;
+ struct timeval start_time;
+ struct timeval end_time;
+ unsigned int delay;
+ struct ocmem_zone *zone;
if (!check_id(client_id)) {
pr_err("ocmem: Invalid client id: %d\n", client_id);
@@ -129,8 +134,33 @@
return NULL;
}
- return __ocmem_allocate_range(client_id, size, size,
+ zone = get_zone(client_id);
+ if (!zone) {
+ pr_err("ocmem: Zone not found for client %d\n", client_id);
+ return NULL;
+ }
+
+ do_gettimeofday(&start_time);
+
+ buffer = __ocmem_allocate_range(client_id, size, size,
size, can_block, can_wait);
+
+ do_gettimeofday(&end_time);
+
+ if (!buffer)
+ return NULL;
+
+ delay = (end_time.tv_sec * USEC_PER_SEC + end_time.tv_usec)
+ - (start_time.tv_sec * USEC_PER_SEC + start_time.tv_usec);
+
+ if (delay > zone->max_alloc_time)
+ zone->max_alloc_time = delay;
+ if (delay < zone->min_alloc_time)
+ zone->min_alloc_time = delay;
+ zone->total_alloc_time += delay;
+ inc_ocmem_stat(zone, NR_SYNC_ALLOCATIONS);
+
+ return buffer;
}
EXPORT_SYMBOL(ocmem_allocate);
@@ -250,6 +280,12 @@
int ocmem_free(int client_id, struct ocmem_buf *buffer)
{
+ int rc;
+ struct timeval start_time;
+ struct timeval end_time;
+ unsigned int delay;
+ struct ocmem_zone *zone;
+
if (!check_id(client_id)) {
pr_err("ocmem: Invalid client id: %d\n", client_id);
return -EINVAL;
@@ -261,12 +297,38 @@
return -EINVAL;
}
+ zone = get_zone(client_id);
+ if (!zone) {
+ pr_err("ocmem: Zone not found for client %d\n", client_id);
+ return -EINVAL;
+ }
+
if (!buffer) {
pr_err("ocmem: Invalid buffer\n");
return -EINVAL;
}
- return __ocmem_free(client_id, buffer);
+ do_gettimeofday(&start_time);
+
+ rc = __ocmem_free(client_id, buffer);
+
+ do_gettimeofday(&end_time);
+
+ if (rc < 0)
+ return rc;
+
+ delay = (end_time.tv_sec * USEC_PER_SEC + end_time.tv_usec)
+ - (start_time.tv_sec * USEC_PER_SEC + start_time.tv_usec);
+
+ if (delay > zone->max_free_time)
+ zone->max_free_time = delay;
+ if (delay < zone->min_free_time)
+ zone->min_free_time = delay;
+ zone->total_free_time += delay;
+ inc_ocmem_stat(zone, NR_FREES);
+
+ return rc;
+
}
EXPORT_SYMBOL(ocmem_free);
@@ -473,6 +535,7 @@
}
return process_quota(client_id);
}
+EXPORT_SYMBOL(get_max_quota);
/* Synchronous eviction/restore calls */
/* Only a single eviction or restoration is allowed */
diff --git a/arch/arm/mach-msm/pcie.c b/arch/arm/mach-msm/pcie.c
index 6305abc..c2ba6c1 100644
--- a/arch/arm/mach-msm/pcie.c
+++ b/arch/arm/mach-msm/pcie.c
@@ -528,8 +528,8 @@
msm_pcie_write_mask(dev->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0);
/* PARF programming */
- writel_relaxed(0x282828, dev->parf + PCIE20_PARF_PCS_DEEMPH);
- writel_relaxed(0x7F7F, dev->parf + PCIE20_PARF_PCS_SWING);
+ writel_relaxed(dev->parf_deemph, dev->parf + PCIE20_PARF_PCS_DEEMPH);
+ writel_relaxed(dev->parf_swing, dev->parf + PCIE20_PARF_PCS_SWING);
writel_relaxed((4<<24), dev->parf + PCIE20_PARF_CONFIG_BITS);
/* ensure that hardware registers the PARF configuration */
wmb();
@@ -621,6 +621,8 @@
msm_pcie_dev.gpio = pdata->gpio;
msm_pcie_dev.wake_n = pdata->wake_n;
msm_pcie_dev.vreg_n = pdata->vreg_n;
+ msm_pcie_dev.parf_deemph = pdata->parf_deemph;
+ msm_pcie_dev.parf_swing = pdata->parf_swing;
msm_pcie_dev.vreg = msm_pcie_vreg_info;
msm_pcie_dev.clk = msm_pcie_clk_info;
msm_pcie_dev.res = msm_pcie_res_info;
diff --git a/arch/arm/mach-msm/pcie.h b/arch/arm/mach-msm/pcie.h
index 31371c2..051e475 100644
--- a/arch/arm/mach-msm/pcie.h
+++ b/arch/arm/mach-msm/pcie.h
@@ -71,6 +71,8 @@
uint32_t wake_n;
uint32_t vreg_n;
+ uint32_t parf_deemph;
+ uint32_t parf_swing;
};
extern uint32_t msm_pcie_irq_init(struct msm_pcie_dev_t *dev);
diff --git a/arch/arm/mach-msm/pil-q6v5-lpass.c b/arch/arm/mach-msm/pil-q6v5-lpass.c
index ef13c34..72253fd 100644
--- a/arch/arm/mach-msm/pil-q6v5-lpass.c
+++ b/arch/arm/mach-msm/pil-q6v5-lpass.c
@@ -47,7 +47,7 @@
void *ramdump_dev;
int wdog_irq;
struct work_struct work;
- void *riva_notif_hdle;
+ void *wcnss_notif_hdle;
void *modem_notif_hdle;
int crash_shutdown;
};
@@ -189,24 +189,19 @@
.shutdown = pil_lpass_shutdown_trusted,
};
-static int riva_notifier_cb(struct notifier_block *this, unsigned long code,
+static int wcnss_notifier_cb(struct notifier_block *this, unsigned long code,
void *ss_handle)
{
int ret;
- switch (code) {
- case SUBSYS_BEFORE_SHUTDOWN:
- pr_debug("%s: R-Notify: Shutdown started\n", __func__);
- ret = sysmon_send_event(SYSMON_SS_LPASS, "wcnss",
- SUBSYS_BEFORE_SHUTDOWN);
- if (ret < 0)
- pr_err("%s: sysmon_send_event error %d", __func__, ret);
- break;
- }
+ pr_debug("%s: W-Notify: event %lu\n", __func__, code);
+ ret = sysmon_send_event(SYSMON_SS_LPASS, "wcnss", code);
+ if (ret < 0)
+ pr_err("%s: sysmon_send_event error %d", __func__, ret);
return NOTIFY_DONE;
}
-static struct notifier_block rnb = {
- .notifier_call = riva_notifier_cb,
+static struct notifier_block wnb = {
+ .notifier_call = wcnss_notifier_cb,
};
static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
@@ -480,10 +475,10 @@
if (ret < 0)
goto err_smsm;
- drv->riva_notif_hdle = subsys_notif_register_notifier("riva", &rnb);
- if (IS_ERR(drv->riva_notif_hdle)) {
- ret = PTR_ERR(drv->riva_notif_hdle);
- goto err_notif_riva;
+ drv->wcnss_notif_hdle = subsys_notif_register_notifier("wcnss", &wnb);
+ if (IS_ERR(drv->wcnss_notif_hdle)) {
+ ret = PTR_ERR(drv->wcnss_notif_hdle);
+ goto err_notif_wcnss;
}
drv->modem_notif_hdle = subsys_notif_register_notifier("modem", &mnb);
@@ -510,8 +505,8 @@
err_kobj:
kobject_put(lpass_status);
err_notif_modem:
- subsys_notif_unregister_notifier(drv->riva_notif_hdle, &rnb);
-err_notif_riva:
+ subsys_notif_unregister_notifier(drv->wcnss_notif_hdle, &wnb);
+err_notif_wcnss:
smsm_state_cb_deregister(SMSM_Q6_STATE, SMSM_RESET,
adsp_smsm_state_cb, drv);
err_smsm:
@@ -527,7 +522,7 @@
static int __devexit pil_lpass_driver_exit(struct platform_device *pdev)
{
struct lpass_data *drv = platform_get_drvdata(pdev);
- subsys_notif_unregister_notifier(drv->riva_notif_hdle, &rnb);
+ subsys_notif_unregister_notifier(drv->wcnss_notif_hdle, &wnb);
subsys_notif_unregister_notifier(drv->modem_notif_hdle, &mnb);
smsm_state_cb_deregister(SMSM_Q6_STATE, SMSM_RESET,
adsp_smsm_state_cb, drv);
diff --git a/arch/arm/mach-msm/pil-q6v5-mss.c b/arch/arm/mach-msm/pil-q6v5-mss.c
index 06de8cc..cfd8daf 100644
--- a/arch/arm/mach-msm/pil-q6v5-mss.c
+++ b/arch/arm/mach-msm/pil-q6v5-mss.c
@@ -709,6 +709,15 @@
struct resource *res;
int ret;
+ int clk_ready = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,gpio-proxy-unvote", 0);
+ if (clk_ready < 0)
+ return clk_ready;
+
+ clk_ready = gpio_to_irq(clk_ready);
+ if (clk_ready < 0)
+ return clk_ready;
+
q6 = pil_q6v5_init(pdev);
if (IS_ERR(q6))
return PTR_ERR(q6);
@@ -718,6 +727,7 @@
q6_desc->ops = &pil_mss_ops;
q6_desc->owner = THIS_MODULE;
q6_desc->proxy_timeout = PROXY_TIMEOUT_MS;
+ q6_desc->proxy_unvote_irq = clk_ready;
drv->self_auth = of_property_read_bool(pdev->dev.of_node,
"qcom,pil-self-auth");
@@ -781,6 +791,7 @@
mba_desc->ops = &pil_mba_ops;
mba_desc->owner = THIS_MODULE;
mba_desc->proxy_timeout = PROXY_TIMEOUT_MS;
+ mba_desc->proxy_unvote_irq = clk_ready;
ret = pil_desc_init(mba_desc);
if (ret)
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
index 5d744a1..64ee880 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
@@ -769,13 +769,13 @@
static int audio_aio_ion_add(struct q6audio_aio *audio,
struct msm_audio_ion_info *info)
{
- ion_phys_addr_t paddr;
- size_t len;
+ ion_phys_addr_t paddr = 0;
+ size_t len = 0;
struct audio_aio_ion_region *region;
int rc = -EINVAL;
- struct ion_handle *handle;
+ struct ion_handle *handle = NULL;
unsigned long ionflag;
- void *kvaddr;
+ void *kvaddr = NULL;
pr_debug("%s[%p]:\n", __func__, audio);
region = kmalloc(sizeof(*region), GFP_KERNEL);
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
index 91ea1dc..d37a325 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -27,8 +27,8 @@
#include "usfcdev.h"
/* The driver version*/
-#define DRV_VERSION "1.4.1"
-#define USF_VERSION_ID 0x0141
+#define DRV_VERSION "1.4.2"
+#define USF_VERSION_ID 0x0142
/* Standard timeout in the asynchronous ops */
#define USF_TIMEOUT_JIFFIES (1*HZ) /* 1 sec */
@@ -430,12 +430,14 @@
{
int rc = 0;
uint16_t data_map_size = 0;
+ uint16_t min_map_size = 0;
if ((usf_xx == NULL) ||
(config == NULL))
return -EINVAL;
data_map_size = sizeof(usf_xx->encdec_cfg.cfg_common.data_map);
+ min_map_size = min(data_map_size, config->port_cnt);
if (config->client_name != NULL) {
if (strncpy_from_user(usf_xx->client_name,
@@ -454,20 +456,13 @@
__func__, config->buf_num, config->stream_format,
config->port_cnt, config->params_data_size);
- pr_debug("%s: p_id[0]=%d, p_id[1]=%d, p_id[2]=%d, p_id[3]=%d\n",
+ pr_debug("%s: id[0]=%d, id[1]=%d, id[2]=%d, id[3]=%d, id[4]=%d\n",
__func__,
config->port_id[0],
config->port_id[1],
config->port_id[2],
- config->port_id[3]);
-
- if (data_map_size < config->port_cnt) {
- pr_err("%s: number of supported ports:%d < requested:%d\n",
- __func__,
- data_map_size,
- config->port_cnt);
- return -EINVAL;
- }
+ config->port_id[3],
+ config->port_id[4]);
/* q6usm allocation & configuration */
usf_xx->buffer_size = config->buf_size;
@@ -481,7 +476,8 @@
usf_xx->encdec_cfg.cfg_common.ch_cfg = config->port_cnt;
memcpy((void *)&usf_xx->encdec_cfg.cfg_common.data_map,
(void *)config->port_id,
- config->port_cnt);
+ min_map_size);
+
if (rc) {
pr_err("%s: ports offsets copy failure\n", __func__);
return -EINVAL;
@@ -897,8 +893,10 @@
rc = q6usm_us_client_buf_alloc(OUT, usf_xx->usc,
usf_xx->buffer_size,
usf_xx->buffer_count);
- if (rc)
+ if (rc) {
+ (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE);
return rc;
+ }
rc = q6usm_enc_cfg_blk(usf_xx->usc,
&usf_xx->encdec_cfg);
@@ -908,7 +906,9 @@
&config_tx.input_info);
}
- if (!rc)
+ if (rc)
+ (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE);
+ else
usf_xx->usf_state = USF_CONFIGURED_STATE;
return rc;
@@ -948,13 +948,17 @@
usf_xx->usc,
usf_xx->buffer_size,
usf_xx->buffer_count);
- if (rc)
+ if (rc) {
+ (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE);
return rc;
+ }
}
rc = q6usm_dec_cfg_blk(usf_xx->usc,
&usf_xx->encdec_cfg);
- if (!rc) {
+ if (rc)
+ (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE);
+ else {
init_waitqueue_head(&usf_xx->wait);
usf_xx->usf_state = USF_CONFIGURED_STATE;
}
diff --git a/arch/arm/mach-msm/scm-pas.c b/arch/arm/mach-msm/scm-pas.c
index b7271bb..f1a7185 100644
--- a/arch/arm/mach-msm/scm-pas.c
+++ b/arch/arm/mach-msm/scm-pas.c
@@ -46,55 +46,6 @@
static struct clk *scm_clocks[NUM_CLKS];
-int pas_init_image(enum pas_id id, const u8 *metadata, size_t size)
-{
- int ret;
- struct pas_init_image_req {
- u32 proc;
- u32 image_addr;
- } request;
- u32 scm_ret = 0;
- /* Make memory physically contiguous */
- void *mdata_buf = kmemdup(metadata, size, GFP_KERNEL);
-
- if (!mdata_buf)
- return -ENOMEM;
-
- request.proc = id;
- request.image_addr = virt_to_phys(mdata_buf);
-
- ret = scm_call(SCM_SVC_PIL, PAS_INIT_IMAGE_CMD, &request,
- sizeof(request), &scm_ret, sizeof(scm_ret));
- kfree(mdata_buf);
-
- if (ret)
- return ret;
- return scm_ret;
-}
-EXPORT_SYMBOL(pas_init_image);
-
-int pas_mem_setup(enum pas_id id, u32 start_addr, u32 len)
-{
- int ret;
- struct pas_init_image_req {
- u32 proc;
- u32 start_addr;
- u32 len;
- } request;
- u32 scm_ret = 0;
-
- request.proc = id;
- request.start_addr = start_addr;
- request.len = len;
-
- ret = scm_call(SCM_SVC_PIL, PAS_MEM_SETUP_CMD, &request,
- sizeof(request), &scm_ret, sizeof(scm_ret));
- if (ret)
- return ret;
- return scm_ret;
-}
-EXPORT_SYMBOL(pas_mem_setup);
-
static struct msm_bus_paths scm_pas_bw_tbl[] = {
{
.vectors = (struct msm_bus_vectors[]){
@@ -176,18 +127,78 @@
mutex_unlock(&scm_pas_bw_mutex);
}
+int pas_init_image(enum pas_id id, const u8 *metadata, size_t size)
+{
+ int ret;
+ struct pas_init_image_req {
+ u32 proc;
+ u32 image_addr;
+ } request;
+ u32 scm_ret = 0;
+ void *mdata_buf;
+
+ ret = scm_pas_enable_bw();
+ if (ret)
+ return ret;
+
+ /* Make memory physically contiguous */
+ mdata_buf = kmemdup(metadata, size, GFP_KERNEL);
+
+ if (!mdata_buf)
+ return -ENOMEM;
+
+ request.proc = id;
+ request.image_addr = virt_to_phys(mdata_buf);
+
+ ret = scm_call(SCM_SVC_PIL, PAS_INIT_IMAGE_CMD, &request,
+ sizeof(request), &scm_ret, sizeof(scm_ret));
+
+ kfree(mdata_buf);
+ scm_pas_disable_bw();
+
+ if (ret)
+ return ret;
+ return scm_ret;
+}
+EXPORT_SYMBOL(pas_init_image);
+
+int pas_mem_setup(enum pas_id id, u32 start_addr, u32 len)
+{
+ int ret;
+ struct pas_init_image_req {
+ u32 proc;
+ u32 start_addr;
+ u32 len;
+ } request;
+ u32 scm_ret = 0;
+
+ request.proc = id;
+ request.start_addr = start_addr;
+ request.len = len;
+
+ ret = scm_call(SCM_SVC_PIL, PAS_MEM_SETUP_CMD, &request,
+ sizeof(request), &scm_ret, sizeof(scm_ret));
+ if (ret)
+ return ret;
+ return scm_ret;
+}
+EXPORT_SYMBOL(pas_mem_setup);
+
int pas_auth_and_reset(enum pas_id id)
{
- int ret, bus_ret;
+ int ret;
u32 proc = id, scm_ret = 0;
- bus_ret = scm_pas_enable_bw();
+ ret = scm_pas_enable_bw();
+ if (ret)
+ return ret;
+
ret = scm_call(SCM_SVC_PIL, PAS_AUTH_AND_RESET_CMD, &proc,
sizeof(proc), &scm_ret, sizeof(scm_ret));
if (ret)
scm_ret = ret;
- if (!bus_ret)
- scm_pas_disable_bw();
+
+ scm_pas_disable_bw();
return scm_ret;
}
diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c
index ee6dfbf..5158f8e 100644
--- a/arch/arm/mach-msm/socinfo.c
+++ b/arch/arm/mach-msm/socinfo.c
@@ -328,6 +328,7 @@
/* 8610 IDs */
[147] = MSM_CPU_8610,
+ [165] = MSM_CPU_8610,
/* 8064AB IDs */
[153] = MSM_CPU_8064AB,
@@ -849,10 +850,6 @@
dummy_socinfo.id = 146;
strlcpy(dummy_socinfo.build_id, "mpq8092 - ",
sizeof(dummy_socinfo.build_id));
- } else if (early_machine_is_msm8610()) {
- dummy_socinfo.id = 147;
- strlcpy(dummy_socinfo.build_id, "msm8610 - ",
- sizeof(dummy_socinfo.build_id));
} else if (early_machine_is_msmzinc()) {
dummy_socinfo.id = 178;
strlcpy(dummy_socinfo.build_id, "msmzinc - ",
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index ba0fd7c..739a753 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -610,7 +610,8 @@
/* write xts du size */
pce = cmdlistinfo->encr_xts_du_size;
if (use_pipe_key == true)
- pce->data = QCE_SECTOR_SIZE;
+ pce->data = min((unsigned int)QCE_SECTOR_SIZE,
+ creq->cryptlen);
else
pce->data = creq->cryptlen;
}
diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c
index ce25bfd..d3434d8 100644
--- a/drivers/gpu/ion/ion.c
+++ b/drivers/gpu/ion/ion.c
@@ -456,7 +456,7 @@
struct ion_handle *handle;
struct ion_device *dev = client->dev;
struct ion_buffer *buffer = NULL;
- unsigned long secure_allocation = flags & ION_SECURE;
+ unsigned long secure_allocation = flags & ION_FLAG_SECURE;
const unsigned int MAX_DBG_STR_LEN = 64;
char dbg_str[MAX_DBG_STR_LEN];
unsigned int dbg_str_idx = 0;
diff --git a/drivers/gpu/ion/ion_cma_secure_heap.c b/drivers/gpu/ion/ion_cma_secure_heap.c
index d7a5920..0fbcfbf 100644
--- a/drivers/gpu/ion/ion_cma_secure_heap.c
+++ b/drivers/gpu/ion/ion_cma_secure_heap.c
@@ -117,7 +117,7 @@
unsigned long len, unsigned long align,
unsigned long flags)
{
- unsigned long secure_allocation = flags & ION_SECURE;
+ unsigned long secure_allocation = flags & ION_FLAG_SECURE;
struct ion_secure_cma_buffer_info *buf = NULL;
if (!secure_allocation) {
diff --git a/drivers/gpu/ion/ion_cp_heap.c b/drivers/gpu/ion/ion_cp_heap.c
index 7bcae01..88addab 100644
--- a/drivers/gpu/ion/ion_cp_heap.c
+++ b/drivers/gpu/ion/ion_cp_heap.c
@@ -289,8 +289,8 @@
unsigned long flags)
{
unsigned long offset;
- unsigned long secure_allocation = flags & ION_SECURE;
- unsigned long force_contig = flags & ION_FORCE_CONTIGUOUS;
+ unsigned long secure_allocation = flags & ION_FLAG_SECURE;
+ unsigned long force_contig = flags & ION_FLAG_FORCE_CONTIGUOUS;
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
@@ -460,7 +460,7 @@
buf->want_delayed_unsecure = 0;
atomic_set(&buf->secure_cnt, 0);
mutex_init(&buf->lock);
- buf->is_secure = flags & ION_SECURE ? 1 : 0;
+ buf->is_secure = flags & ION_FLAG_SECURE ? 1 : 0;
buffer->priv_virt = buf;
return 0;
diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb/dvb-core/demux.h
index 1c15a41..aea3431 100644
--- a/drivers/media/dvb/dvb-core/demux.h
+++ b/drivers/media/dvb/dvb-core/demux.h
@@ -66,6 +66,8 @@
DMX_OK = 0, /* Received Ok */
DMX_OK_PES_END, /* Received OK, data reached end of PES packet */
DMX_OK_PCR, /* Received OK, data with new PCR/STC pair */
+ DMX_OK_EOS, /* Received OK, reached End-of-Stream (EOS) */
+ DMX_OK_MARKER, /* Received OK, reached a data Marker */
DMX_LENGTH_ERROR, /* Incorrect length */
DMX_OVERRUN_ERROR, /* Receiver ring buffer overrun */
DMX_CRC_ERROR, /* Incorrect CRC */
@@ -87,7 +89,7 @@
enum dmx_success status;
/*
- * data_length may be 0 in case of DMX_OK_PES_END
+ * data_length may be 0 in case of DMX_OK_PES_END or DMX_OK_EOS
* and in non-DMX_OK_XXX events. In DMX_OK_PES_END,
* data_length is for data comming after the end of PES.
*/
@@ -125,6 +127,10 @@
u32 ts_packets_num;
u32 ts_dropped_bytes;
} buf;
+
+ struct {
+ u64 id;
+ } marker;
};
};
@@ -232,6 +238,9 @@
enum dmx_tsp_format_t tsp_format);
int (*set_secure_mode)(struct dmx_ts_feed *feed,
struct dmx_secure_mode *sec_mode);
+ int (*oob_command) (struct dmx_ts_feed *feed,
+ struct dmx_oob_command *cmd);
+
};
/*--------------------------------------------------------------------------*/
@@ -280,6 +289,8 @@
u32 bytes_num);
int (*set_secure_mode)(struct dmx_section_feed *feed,
struct dmx_secure_mode *sec_mode);
+ int (*oob_command) (struct dmx_section_feed *feed,
+ struct dmx_oob_command *cmd);
};
/*--------------------------------------------------------------------------*/
@@ -413,6 +424,8 @@
int (*unmap_buffer) (struct dmx_demux *demux,
void *priv_handle);
+
+ int (*get_tsp_size) (struct dmx_demux *demux);
};
#endif /* #ifndef __DEMUX_H */
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index dce37e5..219970b 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -485,23 +485,56 @@
return NULL;
}
-static int dvr_input_thread_entry(void *arg)
+static void dvb_dvr_oob_cmd(struct dmxdev *dmxdev, struct dmx_oob_command *cmd)
{
- struct dmxdev *dmxdev = arg;
- struct dvb_ringbuffer *src = &dmxdev->dvr_input_buffer;
- int ret;
- size_t todo;
- int bytes_written;
- size_t split;
+ int i;
+ struct dmxdev_filter *filter;
+ struct dmxdev_feed *feed;
- while (1) {
+ for (i = 0; i < dmxdev->filternum; i++) {
+ filter = &dmxdev->filter[i];
+ if (!filter || filter->state != DMXDEV_STATE_GO)
+ continue;
+
+ switch (filter->type) {
+ case DMXDEV_TYPE_SEC:
+ filter->feed.sec.feed->oob_command(
+ filter->feed.sec.feed, cmd);
+ break;
+ case DMXDEV_TYPE_PES:
+ feed = list_first_entry(&filter->feed.ts,
+ struct dmxdev_feed, next);
+ feed->ts->oob_command(feed->ts, cmd);
+ break;
+ case DMXDEV_TYPE_NONE:
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static int dvb_dvr_feed_cmd(struct dmxdev *dmxdev, struct dvr_command *dvr_cmd)
+{
+ int ret = 0;
+ size_t todo;
+ int bytes_written = 0;
+ size_t split;
+ size_t tsp_size;
+ struct dvb_ringbuffer *src = &dmxdev->dvr_input_buffer;
+ todo = dvr_cmd->cmd.data_feed_count;
+
+ if (dmxdev->demux->get_tsp_size)
+ tsp_size = dmxdev->demux->get_tsp_size(dmxdev->demux);
+ else
+ tsp_size = 188;
+
+ while (todo >= tsp_size) {
/* wait for input */
ret = wait_event_interruptible(
src->queue,
- (!src->data) ||
- (dvb_ringbuffer_avail(src) > 188) ||
- (src->error != 0) ||
- dmxdev->dvr_in_exit);
+ (dvb_ringbuffer_avail(src) >= tsp_size) || (!src->data)
+ || (dmxdev->dvr_in_exit) || (src->error));
if (ret < 0)
break;
@@ -510,23 +543,21 @@
if (!src->data || dmxdev->exit || dmxdev->dvr_in_exit) {
spin_unlock(&dmxdev->dvr_in_lock);
+ ret = -ENODEV;
break;
}
if (src->error) {
spin_unlock(&dmxdev->dvr_in_lock);
wake_up_all(&src->queue);
+ ret = -EINVAL;
break;
}
dmxdev->dvr_processing_input = 1;
- ret = dvb_ringbuffer_avail(src);
- todo = ret;
-
- split = (src->pread + ret > src->size) ?
- src->size - src->pread :
- 0;
+ split = (src->pread + todo > src->size) ?
+ src->size - src->pread : 0;
/*
* In DVR PULL mode, write might block.
@@ -537,54 +568,128 @@
*/
if (split > 0) {
spin_unlock(&dmxdev->dvr_in_lock);
- bytes_written = dmxdev->demux->write(dmxdev->demux,
+ ret = dmxdev->demux->write(dmxdev->demux,
src->data + src->pread,
split);
- if (bytes_written < 0) {
+ if (ret < 0) {
printk(KERN_ERR "dmxdev: dvr write error %d\n",
- bytes_written);
+ ret);
continue;
}
- if (dmxdev->dvr_in_exit)
+ if (dmxdev->dvr_in_exit) {
+ ret = -ENODEV;
break;
+ }
spin_lock(&dmxdev->dvr_in_lock);
- todo -= bytes_written;
- DVB_RINGBUFFER_SKIP(src, bytes_written);
- if (bytes_written < split) {
+ todo -= ret;
+ bytes_written += ret;
+ DVB_RINGBUFFER_SKIP(src, ret);
+ if (ret < split) {
dmxdev->dvr_processing_input = 0;
spin_unlock(&dmxdev->dvr_in_lock);
wake_up_all(&src->queue);
continue;
}
-
}
spin_unlock(&dmxdev->dvr_in_lock);
- bytes_written = dmxdev->demux->write(dmxdev->demux,
- src->data + src->pread, todo);
+ ret = dmxdev->demux->write(dmxdev->demux,
+ src->data + src->pread, todo);
- if (bytes_written < 0) {
+ if (ret < 0) {
printk(KERN_ERR "dmxdev: dvr write error %d\n",
- bytes_written);
+ ret);
continue;
}
- if (dmxdev->dvr_in_exit)
+ if (dmxdev->dvr_in_exit) {
+ ret = -ENODEV;
break;
+ }
spin_lock(&dmxdev->dvr_in_lock);
- DVB_RINGBUFFER_SKIP(src, bytes_written);
+ todo -= ret;
+ bytes_written += ret;
+ DVB_RINGBUFFER_SKIP(src, ret);
dmxdev->dvr_processing_input = 0;
spin_unlock(&dmxdev->dvr_in_lock);
wake_up_all(&src->queue);
}
+ if (ret < 0)
+ return ret;
+
+ return bytes_written;
+}
+
+static int dvr_input_thread_entry(void *arg)
+{
+ struct dmxdev *dmxdev = arg;
+ struct dvb_ringbuffer *cmdbuf = &dmxdev->dvr_cmd_buffer;
+ struct dvr_command dvr_cmd;
+ int leftover = 0;
+ int ret;
+
+ while (1) {
+ /* wait for input */
+ ret = wait_event_interruptible(
+ cmdbuf->queue,
+ (!cmdbuf->data) ||
+ (dvb_ringbuffer_avail(cmdbuf) >= sizeof(dvr_cmd)) ||
+ (dmxdev->dvr_in_exit));
+
+ if (ret < 0)
+ break;
+
+ spin_lock(&dmxdev->dvr_in_lock);
+
+ if (!cmdbuf->data || dmxdev->exit || dmxdev->dvr_in_exit) {
+ spin_unlock(&dmxdev->dvr_in_lock);
+ break;
+ }
+
+ dvb_ringbuffer_read(cmdbuf, (u8 *)&dvr_cmd, sizeof(dvr_cmd));
+
+ spin_unlock(&dmxdev->dvr_in_lock);
+
+ if (dvr_cmd.type == DVR_DATA_FEED_CMD) {
+ dvr_cmd.cmd.data_feed_count += leftover;
+
+ ret = dvb_dvr_feed_cmd(dmxdev, &dvr_cmd);
+ if (ret < 0) {
+ printk(KERN_ERR
+ "%s: DVR data feed failed, ret=%d\n",
+ __func__, ret);
+ continue;
+ }
+
+ leftover = dvr_cmd.cmd.data_feed_count - ret;
+ } else {
+ /*
+ * For EOS, try to process leftover data in the input
+ * buffer.
+ */
+ if (dvr_cmd.cmd.oobcmd.type == DMX_OOB_CMD_EOS) {
+ struct dvr_command feed_cmd;
+
+ feed_cmd.type = DVR_DATA_FEED_CMD;
+ feed_cmd.cmd.data_feed_count =
+ dvb_ringbuffer_avail(
+ &dmxdev->dvr_input_buffer);
+
+ dvb_dvr_feed_cmd(dmxdev, &dvr_cmd);
+ }
+
+ dvb_dvr_oob_cmd(dmxdev, &dvr_cmd.cmd.oobcmd);
+ }
+ }
+
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
schedule();
@@ -675,6 +780,15 @@
dmxdev->demux->dvr_input.priv_handle = NULL;
dmxdev->demux->dvr_input.ringbuff = &dmxdev->dvr_input_buffer;
+ mem = vmalloc(DVR_CMDS_BUFFER_SIZE);
+ if (!mem) {
+ vfree(dmxdev->dvr_input_buffer.data);
+ dmxdev->dvr_input_buffer.data = NULL;
+ mutex_unlock(&dmxdev->mutex);
+ return -ENOMEM;
+ }
+ dvb_ringbuffer_init(&dmxdev->dvr_cmd_buffer, mem,
+ DVR_CMDS_BUFFER_SIZE);
dvbdev->writers--;
dmxdev->dvr_input_thread =
@@ -684,6 +798,10 @@
"dvr_input");
if (IS_ERR(dmxdev->dvr_input_thread)) {
+ vfree(dmxdev->dvr_input_buffer.data);
+ vfree(dmxdev->dvr_cmd_buffer.data);
+ dmxdev->dvr_input_buffer.data = NULL;
+ dmxdev->dvr_cmd_buffer.data = NULL;
mutex_unlock(&dmxdev->mutex);
return -ENOMEM;
}
@@ -725,7 +843,8 @@
int i;
dmxdev->dvr_in_exit = 1;
- wake_up_all(&dmxdev->dvr_input_buffer.queue);
+
+ wake_up_all(&dmxdev->dvr_cmd_buffer.queue);
/*
* There might be dmx filters reading now from DVR
@@ -776,6 +895,15 @@
dmxdev->demux->dvr_input.priv_handle);
dmxdev->demux->dvr_input.priv_handle = NULL;
}
+
+ if (dmxdev->dvr_cmd_buffer.data) {
+ void *mem = dmxdev->dvr_cmd_buffer.data;
+ mb();
+ spin_lock_irq(&dmxdev->dvr_in_lock);
+ dmxdev->dvr_cmd_buffer.data = NULL;
+ spin_unlock_irq(&dmxdev->dvr_in_lock);
+ vfree(mem);
+ }
}
/* TODO */
dvbdev->users--;
@@ -850,12 +978,57 @@
return ret;
}
+static void dvb_dvr_queue_data_feed(struct dmxdev *dmxdev, size_t count)
+{
+ struct dvb_ringbuffer *cmdbuf = &dmxdev->dvr_cmd_buffer;
+ struct dvr_command *dvr_cmd;
+ int last_dvr_cmd;
+
+ spin_lock(&dmxdev->dvr_in_lock);
+
+ /* Peek at the last DVR command queued, try to coalesce FEED commands */
+ if (dvb_ringbuffer_avail(cmdbuf) >= sizeof(*dvr_cmd)) {
+ last_dvr_cmd = cmdbuf->pwrite - sizeof(*dvr_cmd);
+ if (last_dvr_cmd < 0)
+ last_dvr_cmd += cmdbuf->size;
+
+ dvr_cmd = (struct dvr_command *)&cmdbuf->data[last_dvr_cmd];
+ if (dvr_cmd->type == DVR_DATA_FEED_CMD) {
+ dvr_cmd->cmd.data_feed_count += count;
+ spin_unlock(&dmxdev->dvr_in_lock);
+ return;
+ }
+ }
+
+ /*
+ * We assume command buffer is large enough so that overflow should not
+ * happen. Overflow to the command buffer means data previously written
+ * to the input buffer is 'orphan' - does not have a matching FEED
+ * command. Issue a warning if this ever happens.
+ * Orphan data might still be processed if EOS is issued.
+ */
+ if (dvb_ringbuffer_free(cmdbuf) < sizeof(*dvr_cmd)) {
+ printk(KERN_ERR "%s: DVR command buffer overflow\n", __func__);
+ spin_unlock(&dmxdev->dvr_in_lock);
+ return;
+ }
+
+ dvr_cmd = (struct dvr_command *)&cmdbuf->data[cmdbuf->pwrite];
+ dvr_cmd->type = DVR_DATA_FEED_CMD;
+ dvr_cmd->cmd.data_feed_count = count;
+ DVB_RINGBUFFER_PUSH(cmdbuf, sizeof(*dvr_cmd));
+ spin_unlock(&dmxdev->dvr_in_lock);
+
+ wake_up_all(&cmdbuf->queue);
+}
+
static ssize_t dvb_dvr_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct dvb_device *dvbdev = file->private_data;
struct dmxdev *dmxdev = dvbdev->priv;
struct dvb_ringbuffer *src = &dmxdev->dvr_input_buffer;
+ struct dvb_ringbuffer *cmdbuf = &dmxdev->dvr_cmd_buffer;
int ret;
size_t todo;
ssize_t free_space;
@@ -864,7 +1037,7 @@
return -EOPNOTSUPP;
if (((file->f_flags & O_ACCMODE) == O_RDONLY) ||
- (!src->data))
+ (!src->data) || (!cmdbuf->data))
return -EINVAL;
if ((file->f_flags & O_NONBLOCK) &&
@@ -874,10 +1047,9 @@
ret = 0;
for (todo = count; todo > 0; todo -= ret) {
ret = wait_event_interruptible(src->queue,
- (!src->data) ||
- (dvb_ringbuffer_free(src)) ||
- (src->error != 0) ||
- (dmxdev->dvr_in_exit));
+ (dvb_ringbuffer_free(src)) ||
+ (!src->data) || (!cmdbuf->data) ||
+ (src->error != 0) || (dmxdev->dvr_in_exit));
if (ret < 0)
return ret;
@@ -885,7 +1057,7 @@
if (mutex_lock_interruptible(&dmxdev->mutex))
return -ERESTARTSYS;
- if (!src->data) {
+ if ((!src->data) || (!cmdbuf->data)) {
mutex_unlock(&dmxdev->mutex);
return 0;
}
@@ -917,8 +1089,9 @@
buf += ret;
+ dvb_dvr_queue_data_feed(dmxdev, ret);
+
mutex_unlock(&dmxdev->mutex);
- wake_up_all(&src->queue);
}
return (count - todo) ? (count - todo) : ret;
@@ -968,6 +1141,34 @@
return res;
}
+/*
+ * dvb_dvr_push_oob_cmd
+ *
+ * Note: this function assume dmxdev->mutex was taken, so command buffer cannot
+ * be released during its operation.
+ */
+static int dvb_dvr_push_oob_cmd(struct dmxdev *dmxdev, unsigned int f_flags,
+ struct dmx_oob_command *cmd)
+{
+ struct dvb_ringbuffer *cmdbuf = &dmxdev->dvr_cmd_buffer;
+ struct dvr_command *dvr_cmd;
+
+ if ((f_flags & O_ACCMODE) == O_RDONLY ||
+ dmxdev->source < DMX_SOURCE_DVR0)
+ return -EPERM;
+
+ if (dvb_ringbuffer_free(cmdbuf) < sizeof(*dvr_cmd))
+ return -ENOMEM;
+
+ dvr_cmd = (struct dvr_command *)&cmdbuf->data[cmdbuf->pwrite];
+ dvr_cmd->type = DVR_OOB_CMD;
+ dvr_cmd->cmd.oobcmd = *cmd;
+ DVB_RINGBUFFER_PUSH(cmdbuf, sizeof(*dvr_cmd));
+ wake_up_all(&cmdbuf->queue);
+
+ return 0;
+}
+
static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev,
unsigned int f_flags,
unsigned long size)
@@ -1245,9 +1446,19 @@
return 0;
}
+/*
+ * dvb_dvr_feed_data - Notify new data in DVR input buffer
+ *
+ * @dmxdev - demux device instance
+ * @f_flags - demux device file flag (access mode)
+ * @bytes_count - how many bytes were written to the input buffer
+ *
+ * Note: this function assume dmxdev->mutex was taken, so buffer cannot
+ * be released during its operation.
+ */
static int dvb_dvr_feed_data(struct dmxdev *dmxdev,
- unsigned int f_flags,
- u32 bytes_count)
+ unsigned int f_flags,
+ u32 bytes_count)
{
ssize_t free_space;
struct dvb_ringbuffer *buffer = &dmxdev->dvr_input_buffer;
@@ -1263,10 +1474,9 @@
if (bytes_count > free_space)
return -EINVAL;
- buffer->pwrite =
- (buffer->pwrite + bytes_count) % buffer->size;
+ DVB_RINGBUFFER_PUSH(buffer, bytes_count);
- wake_up_all(&buffer->queue);
+ dvb_dvr_queue_data_feed(dmxdev, bytes_count);
return 0;
}
@@ -1825,8 +2035,10 @@
wake_up_all(&dmxdevfilter->buffer.queue);
return 0;
}
+
spin_lock(&dmxdevfilter->dev->lock);
- if (dmxdevfilter->state != DMXDEV_STATE_GO) {
+ if (dmxdevfilter->state != DMXDEV_STATE_GO ||
+ dmxdevfilter->eos_state) {
spin_unlock(&dmxdevfilter->dev->lock);
return 0;
}
@@ -1896,13 +2108,15 @@
int ret;
spin_lock(&dmxdevfilter->dev->lock);
- if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER) {
+
+ if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER ||
+ dmxdevfilter->state != DMXDEV_STATE_GO ||
+ dmxdevfilter->eos_state) {
spin_unlock(&dmxdevfilter->dev->lock);
return 0;
}
- if (dmxdevfilter->params.pes.output == DMX_OUT_TAP
- || dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP) {
+ if (dmxdevfilter->params.pes.output != DMX_OUT_TS_TAP) {
buffer = &dmxdevfilter->buffer;
events = &dmxdevfilter->events;
} else {
@@ -1916,11 +2130,6 @@
return 0;
}
- if (dmxdevfilter->state != DMXDEV_STATE_GO) {
- spin_unlock(&dmxdevfilter->dev->lock);
- return 0;
- }
-
if (dmxdevfilter->params.pes.output == DMX_OUT_TAP) {
if ((success == DMX_OK) &&
(!events->current_event_data_size)) {
@@ -2010,7 +2219,8 @@
spin_lock(&dmxdevfilter->dev->lock);
- if (dmxdevfilter->state != DMXDEV_STATE_GO) {
+ if (dmxdevfilter->state != DMXDEV_STATE_GO ||
+ dmxdevfilter->eos_state) {
spin_unlock(&dmxdevfilter->dev->lock);
return 0;
}
@@ -2023,6 +2233,17 @@
spin_unlock(&dmxdevfilter->dev->lock);
wake_up_all(&dmxdevfilter->buffer.queue);
+ } else if (dmx_data_ready->status == DMX_OK_EOS) {
+ event.type = DMX_EVENT_EOS;
+ dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&dmxdevfilter->buffer.queue);
+ } else if (dmx_data_ready->status == DMX_OK_MARKER) {
+ event.type = DMX_EVENT_MARKER;
+ event.params.marker.id = dmx_data_ready->marker.id;
+ dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&dmxdevfilter->buffer.queue);
} else {
spin_unlock(&dmxdevfilter->dev->lock);
}
@@ -2076,7 +2297,8 @@
spin_lock(&dmxdevfilter->dev->lock);
- if (dmxdevfilter->state != DMXDEV_STATE_GO) {
+ if (dmxdevfilter->state != DMXDEV_STATE_GO ||
+ dmxdevfilter->eos_state) {
spin_unlock(&dmxdevfilter->dev->lock);
return 0;
}
@@ -2089,6 +2311,27 @@
events = &dmxdevfilter->dev->dvr_output_events;
}
+ if (dmx_data_ready->status == DMX_OK_EOS) {
+ dmxdevfilter->eos_state = 1;
+ dprintk("dmxdev: DMX_OK_EOS - entering EOS state\n");
+ event.type = DMX_EVENT_EOS;
+ dvb_dmxdev_add_event(events, &event);
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&dmxdevfilter->buffer.queue);
+ return 0;
+ }
+
+ if (dmx_data_ready->status == DMX_OK_MARKER) {
+ dprintk("dmxdev: DMX_OK_MARKER - id=%llu\n",
+ dmx_data_ready->marker.id);
+ event.type = DMX_EVENT_MARKER;
+ event.params.marker.id = dmx_data_ready->marker.id;
+ dvb_dmxdev_add_event(events, &event);
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&dmxdevfilter->buffer.queue);
+ return 0;
+ }
+
if (dmx_data_ready->status == DMX_OK_PCR) {
dprintk("dmxdev: event callback DMX_OK_PCR\n");
event.type = DMX_EVENT_NEW_PCR;
@@ -2130,8 +2373,18 @@
return 0;
}
- if ((dmxdevfilter->params.pes.output == DMX_OUT_DECODER) ||
- (buffer->error)) {
+ if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER) {
+ if (DMX_OVERRUN_ERROR == dmx_data_ready->status) {
+ dprintk("dmxdev: buffer overflow\n");
+ event.type = DMX_EVENT_BUFFER_OVERFLOW;
+ dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
+ }
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&buffer->queue);
+ return 0;
+ }
+
+ if (buffer->error) {
spin_unlock(&dmxdevfilter->dev->lock);
wake_up_all(&buffer->queue);
return 0;
@@ -2505,6 +2758,8 @@
spin_unlock_irq(&filter->dev->lock);
}
+ filter->eos_state = 0;
+
spin_lock_irq(&filter->dev->lock);
dvb_dmxdev_flush_output(&filter->buffer, &filter->events);
spin_unlock_irq(&filter->dev->lock);
@@ -2535,7 +2790,7 @@
secfeed,
dvb_dmxdev_section_callback);
if (ret < 0) {
- printk("DVB (%s): could not alloc feed\n",
+ printk(KERN_ERR "DVB (%s): could not alloc feed\n",
__func__);
return ret;
}
@@ -2556,7 +2811,7 @@
ret = (*secfeed)->set(*secfeed, para->pid, 32768,
(para->flags & DMX_CHECK_CRC) ? 1 : 0);
if (ret < 0) {
- printk("DVB (%s): could not set feed\n",
+ printk(KERN_ERR "DVB (%s): could not set feed\n",
__func__);
dvb_dmxdev_feed_restart(filter);
return ret;
@@ -3033,6 +3288,12 @@
if (mutex_lock_interruptible(&dmxdevfilter->mutex))
return -ERESTARTSYS;
+ if (dmxdevfilter->eos_state &&
+ dvb_ringbuffer_empty(&dmxdevfilter->buffer)) {
+ mutex_unlock(&dmxdevfilter->mutex);
+ return 0;
+ }
+
if (dmxdevfilter->type == DMXDEV_TYPE_SEC)
ret = dvb_dmxdev_read_sec(dmxdevfilter, file, buf, count, ppos);
else
@@ -3494,6 +3755,10 @@
ret = dvb_dvr_get_event(dmxdev, file->f_flags, parg);
break;
+ case DMX_PUSH_OOB_COMMAND:
+ ret = dvb_dvr_push_oob_cmd(dmxdev, file->f_flags, parg);
+ break;
+
default:
ret = -EINVAL;
break;
diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h
index 1443de5..7845b75 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.h
+++ b/drivers/media/dvb/dvb-core/dmxdev.h
@@ -144,6 +144,9 @@
enum dmx_tsp_format_t dmx_tsp_format;
u32 rec_chunk_size;
+ /* End-of-stream indication has been received */
+ int eos_state;
+
/* only for sections */
struct timer_list timer;
int todo;
@@ -186,6 +189,8 @@
struct dvb_ringbuffer dvr_input_buffer;
enum dmx_buffer_mode dvr_input_buffer_mode;
struct task_struct *dvr_input_thread;
+ /* DVR commands (data feed / OOB command) queue */
+ struct dvb_ringbuffer dvr_cmd_buffer;
#define DVR_BUFFER_SIZE (10*188*1024)
@@ -194,6 +199,21 @@
spinlock_t dvr_in_lock;
};
+enum dvr_cmd {
+ DVR_DATA_FEED_CMD,
+ DVR_OOB_CMD
+};
+
+struct dvr_command {
+ enum dvr_cmd type;
+ union {
+ struct dmx_oob_command oobcmd;
+ size_t data_feed_count;
+ } cmd;
+};
+
+#define DVR_CMDS_BUFFER_SIZE (sizeof(struct dvr_command)*500)
+
int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *);
void dvb_dmxdev_release(struct dmxdev *dmxdev);
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index 0fef315..3f73c4d 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -376,6 +376,10 @@
else
ccok = ((feed->cc + 1) & 0x0f) == cc;
+ /* discard TS packets holding sections with TEI bit set */
+ if (buf[1] & 0x80)
+ return -EINVAL;
+
feed->first_cc = 0;
feed->cc = cc;
@@ -1250,6 +1254,93 @@
return 0;
}
+static int dvbdmx_ts_feed_oob_cmd(struct dmx_ts_feed *ts_feed,
+ struct dmx_oob_command *cmd)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+ struct dmx_data_ready data;
+ struct dvb_demux *dvbdmx = feed->demux;
+ int ret;
+
+ mutex_lock(&dvbdmx->mutex);
+
+ if (feed->state != DMX_STATE_GO) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -EINVAL;
+ }
+
+ /* Decoder feeds are handled by plug-in */
+ if (feed->ts_type & TS_DECODER) {
+ if (feed->demux->oob_command)
+ ret = feed->demux->oob_command(feed, cmd);
+ else
+ ret = 0;
+
+ mutex_unlock(&dvbdmx->mutex);
+ return ret;
+ }
+
+ data.data_length = 0;
+
+ switch (cmd->type) {
+ case DMX_OOB_CMD_EOS:
+ if (feed->ts_type & TS_PAYLOAD_ONLY) {
+ if (feed->secure_mode.is_secured) {
+ /* Secure feeds are handled by plug-in */
+ if (feed->demux->oob_command)
+ ret = feed->demux->oob_command(feed,
+ cmd);
+ else
+ ret = 0;
+ break;
+ }
+
+ /* Close last PES on non-secure feeds */
+ if (feed->pusi_seen) {
+ data.status = DMX_OK_PES_END;
+ data.pes_end.start_gap = 0;
+ data.pes_end.actual_length =
+ feed->peslen;
+ data.pes_end.disc_indicator_set = 0;
+ data.pes_end.pes_length_mismatch = 0;
+ data.pes_end.stc = 0;
+ data.pes_end.tei_counter =
+ feed->pes_tei_counter;
+ data.pes_end.cont_err_counter =
+ feed->pes_cont_err_counter;
+ data.pes_end.ts_packets_num =
+ feed->pes_ts_packets_num;
+
+ feed->peslen = 0;
+ feed->pes_tei_counter = 0;
+ feed->pes_ts_packets_num = 0;
+ feed->pes_cont_err_counter = 0;
+
+ ret = feed->data_ready_cb.ts(&feed->feed.ts,
+ &data);
+ if (ret)
+ break;
+ }
+ }
+ data.status = DMX_OK_EOS;
+ ret = feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ break;
+
+ case DMX_OOB_CMD_MARKER:
+ data.status = DMX_OK_MARKER;
+ data.marker.id = cmd->params.marker.id;
+ ret = feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&dvbdmx->mutex);
+ return ret;
+}
+
static int dmx_ts_set_tsp_out_format(
struct dmx_ts_feed *ts_feed,
enum dmx_tsp_format_t tsp_format)
@@ -1319,6 +1410,7 @@
(*ts_feed)->data_ready_cb = dmx_ts_feed_data_ready_cb;
(*ts_feed)->notify_data_read = NULL;
(*ts_feed)->set_secure_mode = dmx_ts_set_secure_mode;
+ (*ts_feed)->oob_command = dvbdmx_ts_feed_oob_cmd;
if (!(feed->filter = dvb_dmx_filter_alloc(demux))) {
feed->state = DMX_STATE_FREE;
@@ -1598,6 +1690,55 @@
return 0;
}
+static int dvbdmx_section_feed_oob_cmd(struct dmx_section_feed *section_feed,
+ struct dmx_oob_command *cmd)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)section_feed;
+ struct dvb_demux *dvbdmx = feed->demux;
+ struct dmx_data_ready data;
+ int ret;
+
+ data.data_length = 0;
+
+ mutex_lock(&dvbdmx->mutex);
+
+ if (feed->state != DMX_STATE_GO) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -EINVAL;
+ }
+
+ /* Secure section feeds are handled by the plug-in */
+ if (feed->secure_mode.is_secured) {
+ if (feed->demux->oob_command)
+ ret = feed->demux->oob_command(feed, cmd);
+ else
+ ret = 0;
+
+ mutex_unlock(&dvbdmx->mutex);
+ return ret;
+ }
+
+ switch (cmd->type) {
+ case DMX_OOB_CMD_EOS:
+ data.status = DMX_OK_EOS;
+ ret = feed->data_ready_cb.sec(&feed->filter->filter, &data);
+ break;
+
+ case DMX_OOB_CMD_MARKER:
+ data.status = DMX_OK_MARKER;
+ data.marker.id = cmd->params.marker.id;
+ ret = feed->data_ready_cb.sec(&feed->filter->filter, &data);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&dvbdmx->mutex);
+ return ret;
+}
+
static int dvbdmx_allocate_section_feed(struct dmx_demux *demux,
struct dmx_section_feed **feed,
dmx_section_cb callback)
@@ -1637,6 +1778,7 @@
(*feed)->data_ready_cb = dmx_section_feed_data_ready_cb;
(*feed)->notify_data_read = NULL;
(*feed)->set_secure_mode = dmx_section_set_secure_mode;
+ (*feed)->oob_command = dvbdmx_section_feed_oob_cmd;
mutex_unlock(&dvbdmx->mutex);
return 0;
@@ -1814,6 +1956,18 @@
return 0;
}
+static int dvbdmx_get_tsp_size(struct dmx_demux *demux)
+{
+ int tsp_size;
+ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+
+ mutex_lock(&dvbdemux->mutex);
+ tsp_size = dvbdemux->ts_packet_size;
+ mutex_unlock(&dvbdemux->mutex);
+
+ return tsp_size;
+}
+
static int dvbdmx_set_tsp_format(
struct dmx_demux *demux,
enum dmx_tsp_format_t tsp_format)
@@ -1944,6 +2098,7 @@
dmx->get_pes_pids = dvbdmx_get_pes_pids;
dmx->set_tsp_format = dvbdmx_set_tsp_format;
+ dmx->get_tsp_size = dvbdmx_get_tsp_size;
mutex_init(&dvbdemux->mutex);
spin_lock_init(&dvbdemux->lock);
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h
index f3dc4b8..fc04219 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.h
+++ b/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -139,6 +139,8 @@
const u8 *buf, size_t len);
void (*memcopy)(struct dvb_demux_feed *feed, u8 *dst,
const u8 *src, size_t len);
+ int (*oob_command)(struct dvb_demux_feed *feed,
+ struct dmx_oob_command *cmd);
int users;
#define MAX_DVB_DEMUX_USERS 10
diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c
index 32aa4ef..802349a 100644
--- a/drivers/media/platform/msm/camera_v2/camera/camera.c
+++ b/drivers/media/platform/msm/camera_v2/camera/camera.c
@@ -38,6 +38,7 @@
struct camera_v4l2_private {
struct v4l2_fh fh;
unsigned int stream_id;
+ unsigned int is_vb2_valid; /*0 if no vb2 buffers on stream, else 1*/
struct vb2_queue vb2_q;
};
@@ -314,6 +315,7 @@
rc = camera_check_event_status(&event);
if (rc < 0)
goto set_fmt_fail;
+ sp->is_vb2_valid = 1;
}
return rc;
@@ -550,8 +552,8 @@
{
int rc = 0;
struct camera_v4l2_private *sp = fh_to_private(filep->private_data);
-
- rc = vb2_poll(&sp->vb2_q, filep, wait);
+ if (sp->is_vb2_valid == 1)
+ rc = vb2_poll(&sp->vb2_q, filep, wait);
poll_wait(filep, &sp->fh.wait, wait);
if (v4l2_event_pending(&sp->fh))
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
index 31fa39e..d4f6a07 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
@@ -24,7 +24,7 @@
#define VFE32_BURST_LEN 3
#define VFE32_UB_SIZE 1024
-#define VFE32_EQUAL_SLICE_UB 117
+#define VFE32_EQUAL_SLICE_UB 204
#define VFE32_WM_BASE(idx) (0x4C + 0x18 * idx)
#define VFE32_RDI_BASE(idx) (idx ? 0x734 + 0x4 * (idx - 1) : 0x06FC)
#define VFE32_XBAR_BASE(idx) (0x40 + 0x4 * (idx / 4))
@@ -592,7 +592,7 @@
stream_cfg_cmd->plane_cfg[plane_idx].
output_stride) << 16 |
(stream_cfg_cmd->plane_cfg[plane_idx].
- output_height - 1) << 4 | VFE32_BURST_LEN >> 2;
+ output_height - 1) << 4 | VFE32_BURST_LEN;
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
} else {
msm_camera_io_w(0x2, vfe_dev->vfe_base + wm_base);
@@ -602,7 +602,7 @@
stream_cfg_cmd->plane_cfg[plane_idx].
output_width) << 16 |
(stream_cfg_cmd->plane_cfg[plane_idx].
- output_height - 1) << 4 | VFE32_BURST_LEN >> 2;
+ output_height - 1) << 4 | VFE32_BURST_LEN;
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
}
return;
@@ -945,7 +945,7 @@
}
struct msm_vfe_axi_hardware_info msm_vfe32_axi_hw_info = {
- .num_wm = 7,
+ .num_wm = 4,
.num_comp_mask = 3,
.num_rdi = 3,
.num_rdi_master = 3,
diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c
index e50ac3a..56ec259 100644
--- a/drivers/media/platform/msm/camera_v2/msm.c
+++ b/drivers/media/platform/msm/camera_v2/msm.c
@@ -90,6 +90,7 @@
/* real streams(either data or metadate) owned by one
* session struct msm_stream */
struct msm_queue_head stream_q;
+ struct mutex lock;
};
static struct v4l2_device *msm_v4l2_dev;
@@ -393,6 +394,7 @@
msm_init_queue(&session->command_ack_q);
msm_init_queue(&session->stream_q);
msm_enqueue(msm_session_q, &session->list);
+ mutex_init(&session->lock);
return 0;
}
@@ -408,10 +410,12 @@
list, __msm_queue_find_session, &session_id);
if (!session)
return -EINVAL;
-
+ mutex_lock(&session->lock);
cmd_ack = kzalloc(sizeof(*cmd_ack), GFP_KERNEL);
- if (!cmd_ack)
+ if (!cmd_ack) {
+ mutex_unlock(&session->lock);
return -ENOMEM;
+ }
msm_init_queue(&cmd_ack->command_q);
INIT_LIST_HEAD(&cmd_ack->list);
@@ -420,7 +424,7 @@
msm_enqueue(&session->command_ack_q, &cmd_ack->list);
session->command_ack_q.len++;
-
+ mutex_unlock(&session->lock);
return 0;
}
@@ -543,7 +547,7 @@
msm_destroy_session_streams(session);
msm_remove_session_cmd_ack_q(session);
-
+ mutex_destroy(&session->lock);
msm_delete_entry(msm_session_q, struct msm_session,
list, session);
@@ -683,33 +687,43 @@
list, __msm_queue_find_session, &session_id);
if (WARN_ON(!session))
return -EIO;
-
+ mutex_lock(&session->lock);
cmd_ack = msm_queue_find(&session->command_ack_q,
struct msm_command_ack, list,
__msm_queue_find_command_ack_q, &stream_id);
- if (WARN_ON(!cmd_ack))
+ if (WARN_ON(!cmd_ack)) {
+ mutex_unlock(&session->lock);
return -EIO;
+ }
v4l2_event_queue(vdev, event);
- if (timeout < 0)
+ if (timeout < 0) {
+ mutex_unlock(&session->lock);
return rc;
+ }
/* should wait on session based condition */
rc = wait_event_interruptible_timeout(cmd_ack->wait,
!list_empty_careful(&cmd_ack->command_q.list),
msecs_to_jiffies(timeout));
if (list_empty_careful(&cmd_ack->command_q.list)) {
- if (!rc)
+ if (!rc) {
+ pr_err("%s: Ankit Timed out\n", __func__);
rc = -ETIMEDOUT;
- if (rc < 0)
+ }
+ if (rc < 0) {
+ mutex_unlock(&session->lock);
return rc;
+ }
}
cmd = msm_dequeue(&cmd_ack->command_q,
struct msm_command, list);
- if (!cmd)
+ if (!cmd) {
+ mutex_unlock(&session->lock);
return -EINVAL;
+ }
event_data = (struct msm_v4l2_event_data *)cmd->event.u.data;
@@ -721,6 +735,7 @@
*event = cmd->event;
kzfree(cmd);
+ mutex_unlock(&session->lock);
return rc;
}
@@ -730,7 +745,7 @@
struct msm_v4l2_event_data *event_data =
(struct msm_v4l2_event_data *)&event.u.data[0];
struct msm_session *session = d1;
-
+ mutex_lock(&session->lock);
event.type = MSM_CAMERA_V4L2_EVENT_TYPE;
event.id = MSM_CAMERA_MSM_NOTIFY;
event_data->command = MSM_CAMERA_PRIV_SHUTDOWN;
@@ -739,7 +754,7 @@
msm_destroy_session_streams(session);
msm_remove_session_cmd_ack_q(session);
-
+ mutex_unlock(&session->lock);
return 0;
}
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
index 6775a23..1a481ce 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
@@ -622,8 +622,77 @@
mpq_demux->sdmx_process_time_max = process_time;
}
-/* Extend dvb-demux debugfs with HW statistics */
-void mpq_dmx_init_hw_statistics(struct mpq_demux *mpq_demux)
+static int mpq_sdmx_log_level_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t mpq_sdmx_log_level_read(struct file *fp,
+ char __user *user_buffer, size_t count, loff_t *position)
+{
+ char user_str[16];
+ struct mpq_demux *mpq_demux = fp->private_data;
+ int ret;
+
+ ret = scnprintf(user_str, 16, "%d", mpq_demux->sdmx_log_level);
+ ret = simple_read_from_buffer(user_buffer, count, position,
+ user_str, ret+1);
+
+ return ret;
+}
+
+static ssize_t mpq_sdmx_log_level_write(struct file *fp,
+ const char __user *user_buffer, size_t count, loff_t *position)
+{
+ char user_str[16];
+ int ret;
+ int ret_count;
+ int level;
+ struct mpq_demux *mpq_demux = fp->private_data;
+
+ if (count >= 16)
+ return -EINVAL;
+
+ ret_count = simple_write_to_buffer(user_str, 16, position, user_buffer,
+ count);
+ if (ret_count < 0)
+ return ret_count;
+
+ ret = sscanf(user_str, "%d", &level);
+ if (ret != 1)
+ return -EINVAL;
+
+ if (level < SDMX_LOG_NO_PRINT || level > SDMX_LOG_VERBOSE)
+ return -EINVAL;
+
+ mutex_lock(&mpq_demux->mutex);
+ mpq_demux->sdmx_log_level = level;
+ if (mpq_demux->sdmx_session_handle != SDMX_INVALID_SESSION_HANDLE) {
+ ret = sdmx_set_log_level(mpq_demux->sdmx_session_handle,
+ mpq_demux->sdmx_log_level);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Could not set sdmx log level. ret = %d\n",
+ __func__, ret);
+ mutex_unlock(&mpq_demux->mutex);
+ return -EINVAL;
+ }
+ }
+
+ mutex_unlock(&mpq_demux->mutex);
+ return ret_count;
+}
+
+static const struct file_operations sdmx_debug_fops = {
+ .open = mpq_sdmx_log_level_open,
+ .read = mpq_sdmx_log_level_read,
+ .write = mpq_sdmx_log_level_write,
+ .owner = THIS_MODULE,
+};
+
+/* Extend dvb-demux debugfs with common plug-in entries */
+void mpq_dmx_init_debugfs_entries(struct mpq_demux *mpq_demux)
{
/*
* Extend dvb-demux debugfs with HW statistics.
@@ -745,8 +814,14 @@
S_IRUGO | S_IWUSR | S_IWGRP,
mpq_demux->demux.dmx.debugfs_demux_dir,
&mpq_demux->sdmx_process_packets_min);
+
+ debugfs_create_file("sdmx_log_level",
+ S_IRUGO | S_IWUSR | S_IWGRP,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ mpq_demux,
+ &sdmx_debug_fops);
}
-EXPORT_SYMBOL(mpq_dmx_init_hw_statistics);
+EXPORT_SYMBOL(mpq_dmx_init_debugfs_entries);
/* Update dvb-demux debugfs with HW notification statistics */
void mpq_dmx_update_hw_statistics(struct mpq_demux *mpq_demux)
@@ -907,6 +982,8 @@
mpq_demux->num_active_feeds = 0;
mpq_demux->sdmx_filter_count = 0;
mpq_demux->sdmx_session_handle = SDMX_INVALID_SESSION_HANDLE;
+ mpq_demux->sdmx_eos = 0;
+ mpq_demux->sdmx_log_level = SDMX_LOG_NO_PRINT;
if (mpq_demux->demux.feednum > MPQ_MAX_DMX_FILES) {
MPQ_DVB_ERR_PRINT(
@@ -1127,7 +1204,7 @@
goto map_buffer_failed_free_buff;
}
- if (ionflag & ION_SECURE) {
+ if (ionflag & ION_FLAG_SECURE) {
MPQ_DVB_DBG_PRINT("%s: secured buffer\n", __func__);
*kernel_mem = NULL;
} else {
@@ -1207,7 +1284,7 @@
return -EINVAL;
}
- if (!(ionflag & ION_SECURE))
+ if (!(ionflag & ION_FLAG_SECURE))
ion_unmap_kernel(mpq_demux->ion_client, ion_handle);
ion_free(mpq_demux->ion_client, ion_handle);
@@ -2476,6 +2553,174 @@
feed_data->continuity_errs = 0;
}
+static int mpq_sdmx_dvr_buffer_desc(struct mpq_demux *mpq_demux,
+ struct sdmx_buff_descr *buf_desc)
+{
+ struct dvb_ringbuffer *rbuf = (struct dvb_ringbuffer *)
+ mpq_demux->demux.dmx.dvr_input.ringbuff;
+ struct ion_handle *ion_handle =
+ mpq_demux->demux.dmx.dvr_input.priv_handle;
+ ion_phys_addr_t phys_addr;
+ size_t len;
+ int ret;
+
+ ret = ion_phys(mpq_demux->ion_client, ion_handle, &phys_addr, &len);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Failed to obtain physical address of input buffer. ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ buf_desc->base_addr = (void *)phys_addr;
+ buf_desc->size = rbuf->size;
+
+ return 0;
+}
+
+/**
+ * mpq_dmx_decoder_frame_closure - Helper function to handle closing current
+ * pending frame upon reaching EOS.
+ *
+ * @mpq_demux - mpq demux instance
+ * @mpq_feed - mpq feed object
+ */
+static void mpq_dmx_decoder_frame_closure(struct mpq_demux *mpq_demux,
+ struct mpq_feed *mpq_feed)
+{
+ struct mpq_streambuffer_packet_header packet;
+ struct mpq_streambuffer *stream_buffer;
+ struct mpq_adapter_video_meta_data meta_data;
+ struct mpq_video_feed_info *feed_data;
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ struct dmx_data_ready data;
+
+ feed_data = &mpq_feed->video_info;
+
+ /*
+ * spin-lock is taken to protect against manipulation of video
+ * output buffer by the API (terminate video feed, re-use of video
+ * buffers).
+ */
+ spin_lock(&feed_data->video_buffer_lock);
+ stream_buffer = feed_data->video_buffer;
+
+ if (stream_buffer == NULL) {
+ MPQ_DVB_ERR_PRINT("%s: video_buffer released\n", __func__);
+ spin_unlock(&feed_data->video_buffer_lock);
+ return;
+ }
+
+ /* Report last pattern found */
+ if ((feed_data->pending_pattern_len) &&
+ mpq_dmx_is_video_frame(feed->indexing_params.standard,
+ feed_data->last_framing_match_type)) {
+ meta_data.packet_type = DMX_FRAMING_INFO_PACKET;
+ mpq_dmx_write_pts_dts(feed_data,
+ &(meta_data.info.framing.pts_dts_info));
+ mpq_dmx_save_pts_dts(feed_data);
+ packet.user_data_len =
+ sizeof(struct mpq_adapter_video_meta_data);
+ packet.raw_data_len = feed_data->pending_pattern_len;
+ packet.raw_data_offset = feed_data->frame_offset;
+ meta_data.info.framing.pattern_type =
+ feed_data->last_framing_match_type;
+
+ mpq_streambuffer_get_buffer_handle(stream_buffer,
+ 0, /* current write buffer handle */
+ &packet.raw_data_handle);
+
+ mpq_dmx_update_decoder_stat(mpq_demux);
+
+ /* Writing meta-data that includes the framing information */
+ if (mpq_streambuffer_pkt_write(stream_buffer, &packet,
+ (u8 *)&meta_data) < 0)
+ MPQ_DVB_ERR_PRINT("%s: Couldn't write packet\n",
+ __func__);
+
+ mpq_dmx_prepare_es_event_data(&packet, &meta_data, feed_data,
+ stream_buffer, &data);
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ }
+
+ spin_unlock(&feed_data->video_buffer_lock);
+}
+
+/**
+ * mpq_dmx_decoder_pes_closure - Helper function to handle closing current PES
+ * upon reaching EOS.
+ *
+ * @mpq_demux - mpq demux instance
+ * @mpq_feed - mpq feed object
+ */
+static void mpq_dmx_decoder_pes_closure(struct mpq_demux *mpq_demux,
+ struct mpq_feed *mpq_feed)
+{
+ struct mpq_streambuffer_packet_header packet;
+ struct mpq_streambuffer *stream_buffer;
+ struct mpq_adapter_video_meta_data meta_data;
+ struct mpq_video_feed_info *feed_data;
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ struct dmx_data_ready data;
+
+ feed_data = &mpq_feed->video_info;
+
+ /*
+ * spin-lock is taken to protect against manipulation of video
+ * output buffer by the API (terminate video feed, re-use of video
+ * buffers).
+ */
+ spin_lock(&feed_data->video_buffer_lock);
+ stream_buffer = feed_data->video_buffer;
+
+ if (stream_buffer == NULL) {
+ MPQ_DVB_ERR_PRINT("%s: video_buffer released\n", __func__);
+ spin_unlock(&feed_data->video_buffer_lock);
+ return;
+ }
+
+ /*
+ * Close previous PES.
+ * Push new packet to the meta-data buffer.
+ */
+ if ((feed->pusi_seen) && (0 == feed_data->pes_header_left_bytes)) {
+ packet.raw_data_len = feed->peslen;
+ mpq_streambuffer_get_buffer_handle(stream_buffer,
+ 0, /* current write buffer handle */
+ &packet.raw_data_handle);
+ packet.raw_data_offset = feed_data->frame_offset;
+ packet.user_data_len =
+ sizeof(struct mpq_adapter_video_meta_data);
+
+ mpq_dmx_write_pts_dts(feed_data,
+ &(meta_data.info.pes.pts_dts_info));
+ mpq_dmx_save_pts_dts(feed_data);
+
+ meta_data.packet_type = DMX_PES_PACKET;
+
+ mpq_dmx_update_decoder_stat(mpq_demux);
+
+ if (mpq_streambuffer_pkt_write(stream_buffer, &packet,
+ (u8 *)&meta_data) < 0)
+ MPQ_DVB_ERR_PRINT("%s: Couldn't write packet\n",
+ __func__);
+
+ /* Save write offset where new PES will begin */
+ mpq_streambuffer_get_data_rw_offset(stream_buffer, NULL,
+ &feed_data->frame_offset);
+
+ mpq_dmx_prepare_es_event_data(&packet, &meta_data, feed_data,
+ stream_buffer, &data);
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ }
+ /* Reset PES info */
+ feed->peslen = 0;
+ feed_data->pes_header_offset = 0;
+ feed_data->pes_header_left_bytes = PES_MANDATORY_FIELDS_LEN;
+
+ spin_unlock(&feed_data->video_buffer_lock);
+}
+
static int mpq_dmx_process_video_packet_framing(
struct dvb_demux_feed *feed,
const u8 *buf)
@@ -3234,6 +3479,34 @@
}
EXPORT_SYMBOL(mpq_dmx_process_pcr_packet);
+static int mpq_dmx_decoder_eos_cmd(struct mpq_feed *mpq_feed)
+{
+ struct mpq_video_feed_info *feed_data = &mpq_feed->video_info;
+ struct mpq_streambuffer *stream_buffer;
+ struct mpq_streambuffer_packet_header oob_packet;
+ struct mpq_adapter_video_meta_data oob_meta_data;
+ int ret;
+
+ spin_lock(&feed_data->video_buffer_lock);
+ stream_buffer = feed_data->video_buffer;
+
+ if (stream_buffer == NULL) {
+ MPQ_DVB_ERR_PRINT("%s: video_buffer released\n", __func__);
+ spin_unlock(&feed_data->video_buffer_lock);
+ return 0;
+ }
+
+ memset(&oob_packet, 0, sizeof(oob_packet));
+ oob_packet.user_data_len = sizeof(oob_meta_data);
+ oob_meta_data.packet_type = DMX_EOS_PACKET;
+
+ ret = mpq_streambuffer_pkt_write(stream_buffer, &oob_packet,
+ (u8 *)&oob_meta_data);
+
+ spin_unlock(&feed_data->video_buffer_lock);
+ return ret;
+}
+
int mpq_sdmx_open_session(struct mpq_demux *mpq_demux)
{
enum sdmx_status ret = SDMX_SUCCESS;
@@ -3296,6 +3569,15 @@
return -EINVAL;
}
+ ret = sdmx_set_log_level(mpq_demux->sdmx_session_handle,
+ mpq_demux->sdmx_log_level);
+ if (ret != SDMX_SUCCESS) {
+ MPQ_DVB_ERR_PRINT("%s: Could not set log level. ret=%d\n",
+ __func__, ret);
+ /* Don't fail open session if just log level setting failed */
+ ret = 0;
+ }
+
mpq_demux->sdmx_process_count = 0;
mpq_demux->sdmx_process_time_sum = 0;
mpq_demux->sdmx_process_time_average = 0;
@@ -3329,6 +3611,7 @@
__func__, status);
return -EINVAL;
}
+ mpq_demux->sdmx_eos = 0;
mpq_demux->sdmx_session_handle = SDMX_INVALID_SESSION_HANDLE;
}
@@ -4055,6 +4338,12 @@
data_event.data_length = 0;
feed->data_ready_cb.ts(&feed->feed.ts, &data_event);
}
+
+ if (sts->status_indicators & SDMX_FILTER_STATUS_EOS) {
+ data_event.data_length = 0;
+ data_event.status = DMX_OK_EOS;
+ feed->data_ready_cb.ts(&feed->feed.ts, &data_event);
+ }
}
static void mpq_sdmx_section_filter_results(struct mpq_demux *mpq_demux,
@@ -4080,7 +4369,7 @@
__func__);
if ((!sts->metadata_fill_count) && (!sts->data_fill_count))
- return;
+ goto section_filter_check_eos;
mpq_feed->metadata_buf.pwrite = sts->metadata_write_offset;
mpq_feed->sdmx_buf.pwrite = sts->data_write_offset;
@@ -4101,6 +4390,19 @@
DVB_RINGBUFFER_SKIP(&mpq_feed->sdmx_buf, header.payload_length);
}
+
+section_filter_check_eos:
+ if (sts->status_indicators & SDMX_FILTER_STATUS_EOS) {
+ event.data_length = 0;
+ event.status = DMX_OK_EOS;
+ f = feed->filter;
+
+ while (f && sec->is_filtering) {
+ feed->data_ready_cb.sec(&f->filter, &event);
+ f = f->next;
+ }
+ }
+
}
static void mpq_sdmx_decoder_filter_results(struct mpq_demux *mpq_demux,
@@ -4122,7 +4424,7 @@
struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
if ((!sts->metadata_fill_count) && (!sts->data_fill_count))
- goto decoder_filter_check_overflow;
+ goto decoder_filter_check_flags;
/* Update meta data buffer write pointer */
mpq_feed->metadata_buf.pwrite = sts->metadata_write_offset;
@@ -4220,7 +4522,8 @@
mpq_feed->video_info.ts_packets_num =
counters.pes_ts_count;
mpq_feed->video_info.ts_dropped_bytes =
- counters.drop_count * mpq_demux->demux.ts_packet_size;
+ counters.drop_count *
+ mpq_demux->demux.ts_packet_size;
sbuf = mpq_feed->video_info.video_buffer;
if (sbuf == NULL) {
@@ -4261,7 +4564,7 @@
spin_unlock(&mpq_feed->video_info.video_buffer_lock);
}
-decoder_filter_check_overflow:
+decoder_filter_check_flags:
if ((mpq_demux->demux.playback_mode == DMX_PB_MODE_PUSH) &&
(sts->error_indicators & SDMX_FILTER_ERR_D_LIN_BUFS_FULL)) {
MPQ_DVB_ERR_PRINT("%s: DMX_OVERRUN_ERROR\n", __func__);
@@ -4270,6 +4573,21 @@
mpq_feed->dvb_demux_feed->data_ready_cb.ts(
&mpq_feed->dvb_demux_feed->feed.ts, &data_event);
}
+
+ if (sts->status_indicators & SDMX_FILTER_STATUS_EOS) {
+ /* Notify decoder via the stream buffer */
+ ret = mpq_dmx_decoder_eos_cmd(mpq_feed);
+ if (ret)
+ MPQ_DVB_ERR_PRINT(
+ "%s: Failed to notify decoder on EOS, ret=%d\n",
+ __func__, ret);
+
+ /* Notify user filter */
+ data_event.data_length = 0;
+ data_event.status = DMX_OK_EOS;
+ mpq_feed->dvb_demux_feed->data_ready_cb.ts(
+ &mpq_feed->dvb_demux_feed->feed.ts, &data_event);
+ }
}
static void mpq_sdmx_pcr_filter_results(struct mpq_demux *mpq_demux,
@@ -4289,10 +4607,8 @@
MPQ_DVB_ERR_PRINT("%s: internal PCR buffer overflowed!\n",
__func__);
- /* MPQ_TODO: Parse rest of error indicators ? */
-
if ((!sts->metadata_fill_count) && (!sts->data_fill_count))
- return;
+ goto pcr_filter_check_eos;
if (DMX_TSP_FORMAT_192_TAIL == mpq_demux->demux.tsp_format)
stc_len = 4;
@@ -4338,6 +4654,13 @@
feed->data_ready_cb.ts(&feed->feed.ts, &data);
}
}
+
+pcr_filter_check_eos:
+ if (sts->status_indicators & SDMX_FILTER_STATUS_EOS) {
+ data.data_length = 0;
+ data.status = DMX_OK_EOS;
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ }
}
static void mpq_sdmx_raw_filter_results(struct mpq_demux *mpq_demux,
@@ -4352,7 +4675,7 @@
feed->feed.ts.buffer.ringbuff;
if ((!sts->metadata_fill_count) && (!sts->data_fill_count))
- goto raw_filter_check_overflow;
+ goto raw_filter_check_flags;
new_data = sts->data_write_offset -
buf->pwrite;
@@ -4374,7 +4697,7 @@
MPQ_DVB_DBG_PRINT("%s: Callback DMX_OK, size=%d\n",
__func__, data_event.data_length);
-raw_filter_check_overflow:
+raw_filter_check_flags:
if ((mpq_demux->demux.playback_mode == DMX_PB_MODE_PUSH) &&
(sts->error_indicators & SDMX_FILTER_ERR_D_BUF_FULL)) {
MPQ_DVB_DBG_PRINT("%s: DMX_OVERRUN_ERROR\n", __func__);
@@ -4382,6 +4705,13 @@
data_event.data_length = 0;
feed->data_ready_cb.ts(&feed->feed.ts, &data_event);
}
+
+ if (sts->status_indicators & SDMX_FILTER_STATUS_EOS) {
+ data_event.data_length = 0;
+ data_event.status = DMX_OK_EOS;
+ feed->data_ready_cb.ts(&feed->feed.ts, &data_event);
+ }
+
}
static void mpq_sdmx_process_results(struct mpq_demux *mpq_demux)
@@ -4457,8 +4787,7 @@
{
struct sdmx_filter_status *sts;
struct mpq_feed *mpq_feed;
- /* MPQ_TODO: EOS handling */
- u8 flags = mpq_sdmx_debug ? SDMX_INPUT_FLAG_DBG_ENABLE : 0;
+ u8 flags = 0;
u32 errors;
u32 status;
u32 prev_read_offset;
@@ -4485,6 +4814,12 @@
return 0;
}
+ /* Set input flags */
+ if (mpq_demux->sdmx_eos)
+ flags |= SDMX_INPUT_FLAG_EOS;
+ if (mpq_sdmx_debug)
+ flags |= SDMX_INPUT_FLAG_DBG_ENABLE;
+
/* Build up to date filter status array */
for (i = 0; i < MPQ_MAX_DMX_FILES; i++) {
mpq_feed = &mpq_demux->feeds[i];
@@ -4590,11 +4925,7 @@
size_t count)
{
struct sdmx_buff_descr buf_desc;
- struct dvb_ringbuffer *rbuf = (struct dvb_ringbuffer *)
- mpq_demux->demux.dmx.dvr_input.ringbuff;
- ion_phys_addr_t phys_addr;
u32 read_offset;
- size_t len;
int ret;
if (mpq_demux == NULL || input_handle == NULL) {
@@ -4602,17 +4933,14 @@
return -EINVAL;
}
- ret = ion_phys(mpq_demux->ion_client, input_handle, &phys_addr, &len);
+ ret = mpq_sdmx_dvr_buffer_desc(mpq_demux, &buf_desc);
if (ret) {
MPQ_DVB_ERR_PRINT(
- "%s: Failed to obtain physical address of input buffer. ret = %d\n",
+ "%s: Failed to init input buffer descriptor. ret = %d\n",
__func__, ret);
return ret;
}
-
- buf_desc.base_addr = (void *)phys_addr;
- buf_desc.size = rbuf->size;
- read_offset = rbuf->pread;
+ read_offset = mpq_demux->demux.dmx.dvr_input.ringbuff->pread;
return mpq_sdmx_process(mpq_demux, &buf_desc, count, read_offset);
}
@@ -4649,10 +4977,9 @@
* process managed to consume, unless some sdmx error occurred, for
* which should process the whole buffer
*/
- if (mpq_demux->num_active_feeds > mpq_demux->num_secure_feeds) {
+ if (mpq_demux->num_active_feeds > mpq_demux->num_secure_feeds)
dvb_dmx_swfilter_format(dvb_demux, buf, ret,
dvb_demux->tsp_format);
- }
if (signal_pending(current))
return -EINTR;
@@ -4676,3 +5003,78 @@
return mpq_dmx_info.secure_demux_app_loaded;
}
EXPORT_SYMBOL(mpq_sdmx_is_loaded);
+
+int mpq_dmx_oob_command(struct dvb_demux_feed *feed,
+ struct dmx_oob_command *cmd)
+{
+ struct mpq_feed *mpq_feed = feed->priv;
+ struct mpq_demux *mpq_demux = mpq_feed->mpq_demux;
+ struct dmx_data_ready event;
+ int ret = 0;
+
+ mutex_lock(&mpq_demux->mutex);
+ mpq_feed = feed->priv;
+
+ event.data_length = 0;
+
+ switch (cmd->type) {
+ case DMX_OOB_CMD_EOS:
+ event.status = DMX_OK_EOS;
+ if (!feed->secure_mode.is_secured) {
+ if (dvb_dmx_is_video_feed(feed)) {
+ if (mpq_dmx_info.decoder_framing)
+ mpq_dmx_decoder_pes_closure(mpq_demux,
+ mpq_feed);
+ else
+ mpq_dmx_decoder_frame_closure(mpq_demux,
+ mpq_feed);
+ ret = mpq_dmx_decoder_eos_cmd(mpq_feed);
+ if (ret)
+ MPQ_DVB_ERR_PRINT(
+ "%s: Couldn't write oob eos packet\n",
+ __func__);
+ }
+ ret = feed->data_ready_cb.ts(&feed->feed.ts, &event);
+ } else if (!mpq_demux->sdmx_eos) {
+ struct sdmx_buff_descr buf_desc;
+
+ mpq_demux->sdmx_eos = 1;
+ ret = mpq_sdmx_dvr_buffer_desc(mpq_demux, &buf_desc);
+ if (!ret) {
+ mutex_unlock(&mpq_demux->mutex);
+ mpq_sdmx_process_buffer(mpq_demux, &buf_desc,
+ 0, 0);
+ return 0;
+ }
+ }
+ break;
+ case DMX_OOB_CMD_MARKER:
+ event.status = DMX_OK_MARKER;
+ event.marker.id = cmd->params.marker.id;
+
+ if (feed->type == DMX_TYPE_SEC) {
+ struct dvb_demux_filter *f = feed->filter;
+ struct dmx_section_feed *sec = &feed->feed.sec;
+
+ while (f && sec->is_filtering) {
+ ret = feed->data_ready_cb.sec(&f->filter,
+ &event);
+ if (ret)
+ break;
+ f = f->next;
+ }
+ } else {
+ /* MPQ_TODO: Notify decoder via the stream buffer */
+ ret = feed->data_ready_cb.ts(&feed->feed.ts, &event);
+ }
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&mpq_demux->mutex);
+ return ret;
+}
+EXPORT_SYMBOL(mpq_dmx_oob_command);
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h
index 7affcc6..4c2a9f4 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h
@@ -242,7 +242,7 @@
* @pes_header: Used for feeds that output data to decoder,
* holds PES header of current processed PES.
* @pes_header_left_bytes: Used for feeds that output data to decoder,
- * holds remainning PES header bytes of current processed PES.
+ * holds remaining PES header bytes of current processed PES.
* @pes_header_offset: Holds the offset within the current processed
* pes header.
* @fullness_wait_cancel: Flag used to signal to abort waiting for
@@ -369,6 +369,7 @@
* Used before each call to sdmx_process() to build up to date state.
* @sdmx_session_handle: Secure demux open session handle
* @sdmx_filter_count: Number of active secure demux filters
+ * @sdmx_eos: End-of-stream indication flag for current sdmx session
* @plugin_priv: Underlying plugin's own private data
* @hw_notification_interval: Notification interval in msec,
* exposed in debugfs.
@@ -415,6 +416,7 @@
int sdmx_session_handle;
int sdmx_session_ref_count;
int sdmx_filter_count;
+ int sdmx_eos;
void *plugin_priv;
/* debug-fs */
@@ -436,6 +438,7 @@
u32 sdmx_process_packets_sum;
u32 sdmx_process_packets_average;
u32 sdmx_process_packets_min;
+ enum sdmx_log_level sdmx_log_level;
struct timespec decoder_out_last_time;
struct timespec last_notification_time;
@@ -620,12 +623,13 @@
int mpq_dmx_process_pcr_packet(struct dvb_demux_feed *feed, const u8 *buf);
/**
- * mpq_dmx_init_hw_statistics -
- * Extend dvb-demux debugfs with HW statistics.
+ * mpq_dmx_init_debugfs_entries -
+ * Extend dvb-demux debugfs with mpq related entries (HW statistics and secure
+ * demux log level).
*
* @mpq_demux: The mpq_demux device to initialize.
*/
-void mpq_dmx_init_hw_statistics(struct mpq_demux *mpq_demux);
+void mpq_dmx_init_debugfs_entries(struct mpq_demux *mpq_demux);
/**
* mpq_dmx_update_hw_statistics -
@@ -725,6 +729,22 @@
*/
int mpq_sdmx_is_loaded(void);
+/**
+ * mpq_dmx_oob_command - Handles OOB command from dvb-demux.
+ *
+ * OOB marker commands trigger callback to the dmxdev.
+ * Handling of EOS command may trigger current (last on stream) PES/Frame to
+ * be reported, in addition to callback to the dmxdev.
+ * In case secure demux is active for the feed, EOS command is passed to the
+ * secure demux for handling.
+ *
+ * @feed: dvb demux feed object
+ * @cmd: oob command data
+ *
+ * returns 0 on success or error
+ */
+int mpq_dmx_oob_command(struct dvb_demux_feed *feed,
+ struct dmx_oob_command *cmd);
#endif /* _MPQ_DMX_PLUGIN_COMMON_H */
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tsif.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tsif.c
index 3d48441..0d40520 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tsif.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tsif.c
@@ -687,6 +687,7 @@
mpq_demux->demux.decoder_buffer_status = mpq_dmx_decoder_buffer_status;
mpq_demux->demux.reuse_decoder_buffer = mpq_dmx_reuse_decoder_buffer;
mpq_demux->demux.set_secure_mode = NULL;
+ mpq_demux->demux.oob_command = mpq_dmx_oob_command;
/* Initialize dvb_demux object */
result = dvb_dmx_init(&mpq_demux->demux);
@@ -718,7 +719,7 @@
}
/* Extend dvb-demux debugfs with TSIF statistics. */
- mpq_dmx_init_hw_statistics(mpq_demux);
+ mpq_dmx_init_debugfs_entries(mpq_demux);
return 0;
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c
index beb4cce..f0f8fa9 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c
@@ -1742,6 +1742,7 @@
mpq_demux->demux.decoder_buffer_status = mpq_dmx_decoder_buffer_status;
mpq_demux->demux.reuse_decoder_buffer = mpq_dmx_reuse_decoder_buffer;
mpq_demux->demux.set_secure_mode = mpq_dmx_set_secure_mode;
+ mpq_demux->demux.oob_command = mpq_dmx_oob_command;
/* Initialize dvb_demux object */
result = dvb_dmx_init(&mpq_demux->demux);
@@ -1773,7 +1774,7 @@
}
/* Extend dvb-demux debugfs with TSPP statistics. */
- mpq_dmx_init_hw_statistics(mpq_demux);
+ mpq_dmx_init_debugfs_entries(mpq_demux);
return 0;
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c
index 60ce9e5..81a2a93 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c
@@ -127,6 +127,7 @@
mpq_demux->demux.decoder_buffer_status = NULL;
mpq_demux->demux.reuse_decoder_buffer = NULL;
mpq_demux->demux.set_secure_mode = NULL;
+ mpq_demux->demux.oob_command = NULL;
/* Initialize dvb_demux object */
result = dvb_dmx_init(&mpq_demux->demux);
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_sdmx.c b/drivers/media/platform/msm/dvb/demux/mpq_sdmx.c
index 946b055..14d3a39 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_sdmx.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_sdmx.c
@@ -38,7 +38,9 @@
SDMX_PROCESS_CMD,
SDMX_GET_DBG_COUNTERS_CMD,
SDMX_RESET_DBG_COUNTERS_CMD,
- SDMX_GET_VERSION_CMD
+ SDMX_GET_VERSION_CMD,
+ SDMX_INVALIDATE_KL_CMD,
+ SDMX_SET_LOG_LEVEL_CMD
};
struct sdmx_proc_req {
@@ -184,6 +186,15 @@
int32_t version;
};
+struct sdmx_set_log_level_req {
+ enum sdmx_cmd_id cmd_id;
+ enum sdmx_log_level level;
+ u32 session_handle;
+};
+
+struct sdmx_set_log_level_rsp {
+ enum sdmx_status ret;
+};
static void get_cmd_rsp_buffers(int handle_index,
void **cmd,
int *cmd_len,
@@ -935,3 +946,48 @@
return ret;
}
EXPORT_SYMBOL(sdmx_reset_dbg_counters);
+
+/*
+ * Set debug log verbosity level
+ *
+ * @session_handle: secure demux instance
+ * @level: requested log level
+ *
+ * Return error code
+ */
+int sdmx_set_log_level(int session_handle, enum sdmx_log_level level)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_set_log_level_req *cmd;
+ struct sdmx_set_log_level_rsp *rsp;
+ enum sdmx_status ret;
+
+ cmd_len = sizeof(struct sdmx_set_log_level_req);
+ rsp_len = sizeof(struct sdmx_set_log_level_rsp);
+
+ /* Get command and response buffers */
+ get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_SET_LOG_LEVEL_CMD;
+ cmd->session_handle = session_handle;
+ cmd->level = level;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+ ret = rsp->ret;
+
+ /* Unlock */
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return ret;
+}
+
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_sdmx.h b/drivers/media/platform/msm/dvb/demux/mpq_sdmx.h
index f9d85aa..6b669e4 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_sdmx.h
+++ b/drivers/media/platform/msm/dvb/demux/mpq_sdmx.h
@@ -78,6 +78,13 @@
SDMX_195_BYTE_PKT = 195,
};
+enum sdmx_log_level {
+ SDMX_LOG_NO_PRINT,
+ SDMX_LOG_MSG_ERROR,
+ SDMX_LOG_DEBUG,
+ SDMX_LOG_VERBOSE
+};
+
enum sdmx_status {
SDMX_SUCCESS = 0,
SDMX_STATUS_GENERAL_FAILURE = -1,
@@ -250,4 +257,6 @@
int sdmx_reset_dbg_counters(int session_handle);
+int sdmx_set_log_level(int session_handle, enum sdmx_log_level level);
+
#endif /* _MPQ_SDMX_H */
diff --git a/drivers/media/platform/msm/dvb/include/mpq_adapter.h b/drivers/media/platform/msm/dvb/include/mpq_adapter.h
index 23121b2..b55f367 100644
--- a/drivers/media/platform/msm/dvb/include/mpq_adapter.h
+++ b/drivers/media/platform/msm/dvb/include/mpq_adapter.h
@@ -62,10 +62,10 @@
};
enum dmx_packet_type {
- DMX_PADDING_PACKET,
DMX_PES_PACKET,
DMX_FRAMING_INFO_PACKET,
- DMX_EOS_PACKET
+ DMX_EOS_PACKET,
+ DMX_MARKER_PACKET
};
struct dmx_pts_dts_info {
@@ -94,6 +94,11 @@
struct dmx_pts_dts_info pts_dts_info;
};
+struct dmx_marker_info {
+ /* marker id */
+ u64 id;
+};
+
/** The meta-data used for video interface */
struct mpq_adapter_video_meta_data {
/** meta-data packet type */
@@ -103,6 +108,7 @@
union {
struct dmx_framing_packet_info framing;
struct dmx_pes_packet_info pes;
+ struct dmx_marker_info marker;
} info;
} __packed;
diff --git a/drivers/media/platform/msm/dvb/video/mpq_dvb_video.c b/drivers/media/platform/msm/dvb/video/mpq_dvb_video.c
index 45a9dd5..3f33535 100644
--- a/drivers/media/platform/msm/dvb/video/mpq_dvb_video.c
+++ b/drivers/media/platform/msm/dvb/video/mpq_dvb_video.c
@@ -186,7 +186,9 @@
case DMX_EOS_PACKET:
break;
case DMX_PES_PACKET:
- case DMX_PADDING_PACKET:
+ case DMX_MARKER_PACKET:
+ break;
+ default:
break;
}
} while (!frame_found);
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index addd235..f46abcf 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -896,6 +896,19 @@
pkt->size += sizeof(u32) + sizeof(struct hfi_bitrate);
break;
}
+ case HAL_CONFIG_VENC_MAX_BITRATE:
+ {
+ struct hfi_bitrate *hfi;
+
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE;
+ hfi = (struct hfi_bitrate *) &pkt->rg_property_data[1];
+ hfi->bit_rate = ((struct hal_bitrate *)pdata)->bit_rate;
+ hfi->layer_id = ((struct hal_bitrate *)pdata)->layer_id;
+
+ pkt->size += sizeof(u32) + sizeof(struct hfi_bitrate);
+ break;
+ }
case HAL_PARAM_PROFILE_LEVEL_CURRENT:
{
struct hfi_profile_level *hfi;
diff --git a/drivers/media/platform/msm/vidc/msm_smem.h b/drivers/media/platform/msm/vidc/msm_smem.h
index b80d63e..4425909 100644
--- a/drivers/media/platform/msm/vidc/msm_smem.h
+++ b/drivers/media/platform/msm/vidc/msm_smem.h
@@ -25,7 +25,7 @@
enum smem_prop {
SMEM_CACHED = ION_FLAG_CACHED,
- SMEM_SECURE = ION_SECURE,
+ SMEM_SECURE = ION_FLAG_SECURE,
};
enum hal_buffer {
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index 6cf2572..eca8091 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -24,7 +24,7 @@
#define MAX_NUM_OUTPUT_BUFFERS 6
enum msm_vdec_ctrl_cluster {
- MSM_VDEC_CTRL_CLUSTER_MAX = 1,
+ MSM_VDEC_CTRL_CLUSTER_MAX = 1 << 0,
};
static const char *const mpeg_video_vidc_divx_format[] = {
@@ -1357,7 +1357,7 @@
return NULL;
for (c = 0; c < NUM_CTRLS; c++) {
- if (msm_vdec_ctrls[c].cluster == type) {
+ if (msm_vdec_ctrls[c].cluster & type) {
cluster[sz] = msm_vdec_ctrls[c].priv;
++sz;
}
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index bf29a95..160d450 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -107,16 +107,17 @@
};
enum msm_venc_ctrl_cluster {
- MSM_VENC_CTRL_CLUSTER_QP = 1,
- MSM_VENC_CTRL_CLUSTER_INTRA_PERIOD,
- MSM_VENC_CTRL_CLUSTER_H264_PROFILE_LEVEL,
- MSM_VENC_CTRL_CLUSTER_MPEG_PROFILE_LEVEL,
- MSM_VENC_CTRL_CLUSTER_H263_PROFILE_LEVEL,
- MSM_VENC_CTRL_CLUSTER_H264_ENTROPY,
- MSM_VENC_CTRL_CLUSTER_SLICING,
- MSM_VENC_CTRL_CLUSTER_INTRA_REFRESH,
- MSM_VENC_CTRL_CLUSTER_BITRATE,
- MSM_VENC_CTRL_CLUSTER_MAX,
+ MSM_VENC_CTRL_CLUSTER_QP = 1 << 0,
+ MSM_VENC_CTRL_CLUSTER_INTRA_PERIOD = 1 << 1,
+ MSM_VENC_CTRL_CLUSTER_H264_PROFILE_LEVEL = 1 << 2,
+ MSM_VENC_CTRL_CLUSTER_MPEG_PROFILE_LEVEL = 1 << 3,
+ MSM_VENC_CTRL_CLUSTER_H263_PROFILE_LEVEL = 1 << 4,
+ MSM_VENC_CTRL_CLUSTER_H264_ENTROPY = 1 << 5,
+ MSM_VENC_CTRL_CLUSTER_SLICING = 1 << 6,
+ MSM_VENC_CTRL_CLUSTER_INTRA_REFRESH = 1 << 7,
+ MSM_VENC_CTRL_CLUSTER_BITRATE = 1 << 8,
+ MSM_VENC_CTRL_CLUSTER_TIMING = 1 << 9,
+ MSM_VENC_CTRL_CLUSTER_MAX = 1 << 10,
};
static struct msm_vidc_ctrl msm_venc_ctrls[] = {
@@ -238,6 +239,18 @@
.cluster = MSM_VENC_CTRL_CLUSTER_BITRATE,
},
{
+ .id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+ .name = "Peak Bit Rate",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = MIN_BIT_RATE,
+ .maximum = MAX_BIT_RATE,
+ .default_value = DEFAULT_BIT_RATE,
+ .step = BIT_RATE_STEP,
+ .menu_skip_mask = 0,
+ .qmenu = NULL,
+ .cluster = MSM_VENC_CTRL_CLUSTER_BITRATE,
+ },
+ {
.id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE,
.name = "Entropy Mode",
.type = V4L2_CTRL_TYPE_MENU,
@@ -1255,6 +1268,29 @@
bitrate.layer_id = 0;
pdata = &bitrate;
break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ {
+ struct v4l2_ctrl *avg_bitrate = TRY_GET_CTRL(
+ V4L2_CID_MPEG_VIDEO_BITRATE);
+
+ if (ctrl->val < avg_bitrate->val) {
+ dprintk(VIDC_ERR,
+ "Peak bitrate (%d) is lower than average bitrate (%d)",
+ ctrl->val, avg_bitrate->val);
+ rc = -EINVAL;
+ break;
+ } else if (ctrl->val < avg_bitrate->val * 2) {
+ dprintk(VIDC_WARN,
+ "Peak bitrate (%d) ideally should be twice the average bitrate (%d)",
+ ctrl->val, avg_bitrate->val);
+ }
+
+ property_id = HAL_CONFIG_VENC_MAX_BITRATE;
+ bitrate.bit_rate = ctrl->val;
+ bitrate.layer_id = 0;
+ pdata = &bitrate;
+ break;
+ }
case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
temp_ctrl = TRY_GET_CTRL(
V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL);
@@ -1979,7 +2015,7 @@
{
int rc = 0;
int i;
- struct vidc_buffer_addr_info buffer_info;
+ struct vidc_buffer_addr_info buffer_info = {0};
struct hfi_device *hdev;
int extra_idx = 0;
@@ -2042,7 +2078,7 @@
struct v4l2_buffer *b)
{
int i, rc = 0, extra_idx = 0;
- struct vidc_buffer_addr_info buffer_info;
+ struct vidc_buffer_addr_info buffer_info = {0};
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
@@ -2187,7 +2223,7 @@
return NULL;
for (c = 0; c < NUM_CTRLS; c++) {
- if (msm_venc_ctrls[c].cluster == type) {
+ if (msm_venc_ctrls[c].cluster & type) {
cluster[sz] = msm_venc_ctrls[c].priv;
++sz;
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index adf6dec..dc08c64 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -373,7 +373,6 @@
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
- struct v4l2_control control = {0};
struct msm_vidc_cb_event *event_notify;
int event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
int rc = 0;
@@ -382,15 +381,7 @@
event_notify = (struct msm_vidc_cb_event *) response->data;
switch (event_notify->hal_event_type) {
case HAL_EVENT_SEQ_CHANGED_SUFFICIENT_RESOURCES:
- event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
- control.id =
- V4L2_CID_MPEG_VIDC_VIDEO_CONTINUE_DATA_TRANSFER;
- rc = v4l2_g_ctrl(&inst->ctrl_handler, &control);
- if (rc)
- dprintk(VIDC_WARN,
- "Failed to get Smooth streamng flag\n");
- if (!rc && control.value == true)
- event = V4L2_EVENT_SEQ_CHANGED_SUFFICIENT;
+ event = V4L2_EVENT_SEQ_CHANGED_SUFFICIENT;
break;
case HAL_EVENT_SEQ_CHANGED_INSUFFICIENT_RESOURCES:
event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index e20348d..3b82666 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -166,6 +166,7 @@
HAL_PARAM_VENC_SYNC_FRAME_SEQUENCE_HEADER,
HAL_PARAM_VDEC_SYNC_FRAME_DECODE,
HAL_PARAM_VENC_H264_ENTROPY_CABAC_MODEL,
+ HAL_CONFIG_VENC_MAX_BITRATE,
};
enum hal_domain {
diff --git a/drivers/media/platform/msm/wfd/enc-mfc-subdev.c b/drivers/media/platform/msm/wfd/enc-mfc-subdev.c
index fee7b47..ceb0149 100644
--- a/drivers/media/platform/msm/wfd/enc-mfc-subdev.c
+++ b/drivers/media/platform/msm/wfd/enc-mfc-subdev.c
@@ -2040,7 +2040,7 @@
}
heap_mask = ION_HEAP(ION_CP_MM_HEAP_ID);
heap_mask |= inst->secure ? 0 : ION_HEAP(ION_IOMMU_HEAP_ID);
- ion_flags |= inst->secure ? ION_SECURE : 0;
+ ion_flags |= inst->secure ? ION_FLAG_SECURE : 0;
if (vcd_get_ion_status()) {
for (i = 0; i < 4; ++i) {
diff --git a/drivers/media/platform/msm/wfd/wfd-ioctl.c b/drivers/media/platform/msm/wfd/wfd-ioctl.c
index e589878..1d3c9f55 100644
--- a/drivers/media/platform/msm/wfd/wfd-ioctl.c
+++ b/drivers/media/platform/msm/wfd/wfd-ioctl.c
@@ -168,7 +168,7 @@
if (secure) {
alloc_regions = ION_HEAP(ION_CP_MM_HEAP_ID);
- ion_flags = ION_SECURE;
+ ion_flags = ION_FLAG_SECURE;
align = SZ_1M;
} else {
alloc_regions = ION_HEAP(ION_IOMMU_HEAP_ID);
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
index 7c73d6c..e011d8f 100644
--- a/drivers/mfd/wcd9xxx-core.c
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -15,6 +15,7 @@
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
+#include <linux/ratelimit.h>
#include <linux/mfd/core.h>
#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
#include <linux/mfd/wcd9xxx/core.h>
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 73e2e0f..188ac32 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -2839,6 +2839,7 @@
ret = qseecom_unload_app(data);
break;
case QSEECOM_SECURE_SERVICE:
+ case QSEECOM_GENERIC:
ret = qseecom_unmap_ion_allocated_memory(data);
if (ret) {
pr_err("Close failed\n");
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index fc7c399..d339d81 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -45,6 +45,9 @@
#include "sd_ops.h"
#include "sdio_ops.h"
+#define CREATE_TRACE_POINTS
+#include <trace/events/mmc.h>
+
static void mmc_clk_scaling(struct mmc_host *host, bool from_wq);
/* If the device is not responding */
@@ -1486,6 +1489,19 @@
if (ios->clock > 0)
mmc_set_ungated(host);
host->ops->set_ios(host, ios);
+ if (ios->old_rate != ios->clock) {
+ if (likely(ios->clk_ts)) {
+ char trace_info[80];
+ snprintf(trace_info, 80,
+ "%s: freq_KHz %d --> %d | t = %d",
+ mmc_hostname(host), ios->old_rate / 1000,
+ ios->clock / 1000, jiffies_to_msecs(
+ (long)jiffies - (long)ios->clk_ts));
+ trace_mmc_clk(trace_info);
+ }
+ ios->old_rate = ios->clock;
+ ios->clk_ts = jiffies;
+ }
}
EXPORT_SYMBOL(mmc_set_ios);
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 4a063fd..9c30cd1 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -4959,6 +4959,10 @@
bam.callback = msmsdcc_sps_bam_global_irq_cb;
bam.user = (void *)host;
+ /* bam reset messages will be limited to 5 times */
+ bam.constrained_logging = true;
+ bam.logging_number = 5;
+
pr_info("%s: bam physical base=0x%x\n", mmc_hostname(host->mmc),
(u32)bam.phys_addr);
pr_info("%s: bam virtual base=0x%x\n", mmc_hostname(host->mmc),
diff --git a/drivers/net/ethernet/msm/ecm_ipa.c b/drivers/net/ethernet/msm/ecm_ipa.c
index ed67df4..114b23d 100644
--- a/drivers/net/ethernet/msm/ecm_ipa.c
+++ b/drivers/net/ethernet/msm/ecm_ipa.c
@@ -18,7 +18,7 @@
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/sched.h>
-#include <linux/spinlock.h>
+#include <linux/atomic.h>
#include <mach/ecm_ipa.h>
#define DRIVER_NAME "ecm_ipa"
@@ -27,6 +27,9 @@
#define ECM_IPA_IPV6_HDR_NAME "ecm_eth_ipv6"
#define IPA_TO_USB_CLIENT IPA_CLIENT_USB_CONS
#define INACTIVITY_MSEC_DELAY 100
+#define DEFAULT_OUTSTANDING_HIGH 64
+#define DEFAULT_OUTSTANDING_LOW 32
+
#define ECM_IPA_ERROR(fmt, args...) \
pr_err(DRIVER_NAME "@%s@%d@ctx:%s: "\
fmt, __func__, __LINE__, current->comm, ## args)
@@ -45,8 +48,6 @@
/**
* struct ecm_ipa_dev - main driver context parameters
- * @ack_spinlock: protect last sent skb
- * @last_out_skb: last sent skb saved until Tx notify is received from IPA
* @net: network interface struct implemented by this driver
* @folder: debugfs folder for various debuging switches
* @tx_enable: flag that enable/disable Tx path to continue to IPA
@@ -56,15 +57,19 @@
* @tx_file: saved debugfs entry to allow cleanup
* @rx_file: saved debugfs entry to allow cleanup
* @rm_file: saved debugfs entry to allow cleanup
+ * @outstanding_high_file saved debugfs entry to allow cleanup
+ * @outstanding_low_file saved debugfs entry to allow cleanup
* @dma_file: saved debugfs entry to allow cleanup
* @eth_ipv4_hdr_hdl: saved handle for ipv4 header-insertion table
* @eth_ipv6_hdr_hdl: saved handle for ipv6 header-insertion table
* @usb_to_ipa_hdl: save handle for IPA pipe operations
* @ipa_to_usb_hdl: save handle for IPA pipe operations
+ * @outstanding_pkts: number of packets sent to IPA without TX complete ACKed
+ * @outstanding_high: number of outstanding packets allowed
+ * @outstanding_low: number of outstanding packets which shall cause
+ * to netdev queue start (after stopped due to outstanding_high reached)
*/
struct ecm_ipa_dev {
- spinlock_t ack_spinlock;
- struct sk_buff *last_out_skb;
struct net_device *net;
bool tx_enable;
bool rx_enable;
@@ -74,11 +79,16 @@
struct dentry *tx_file;
struct dentry *rx_file;
struct dentry *rm_file;
+ struct dentry *outstanding_high_file;
+ struct dentry *outstanding_low_file;
struct dentry *dma_file;
uint32_t eth_ipv4_hdr_hdl;
uint32_t eth_ipv6_hdr_hdl;
u32 usb_to_ipa_hdl;
u32 ipa_to_usb_hdl;
+ atomic_t outstanding_pkts;
+ u8 outstanding_high;
+ u8 outstanding_low;
};
/**
@@ -104,7 +114,7 @@
static void ecm_ipa_rm_notify(void *user_data, enum ipa_rm_event event,
unsigned long data);
static int ecm_ipa_create_rm_resource(struct ecm_ipa_dev *dev);
-static void ecm_ipa_destory_rm_resource(void);
+static void ecm_ipa_destory_rm_resource(struct ecm_ipa_dev *dev);
static bool rx_filter(struct sk_buff *skb);
static bool tx_filter(struct sk_buff *skb);
static bool rm_enabled(struct ecm_ipa_dev *dev);
@@ -200,7 +210,9 @@
memset(dev, 0, sizeof(*dev));
dev->tx_enable = true;
dev->rx_enable = true;
- spin_lock_init(&dev->ack_spinlock);
+ atomic_set(&dev->outstanding_pkts, 0);
+ dev->outstanding_high = DEFAULT_OUTSTANDING_HIGH;
+ dev->outstanding_low = DEFAULT_OUTSTANDING_LOW;
dev->net = net;
ecm_ipa_ctx = dev;
*priv = (void *)dev;
@@ -448,7 +460,7 @@
fail_register_tx:
fail_set_device_ethernet:
ecm_ipa_rules_destroy(dev);
- ecm_ipa_destory_rm_resource();
+ ecm_ipa_destory_rm_resource(dev);
free_netdev(net);
return result;
}
@@ -517,6 +529,10 @@
struct ipa_rm_create_params create_params = {0};
int result;
ECM_IPA_LOG_ENTRY();
+ if (!dev->rm_enable) {
+ pr_debug("RM feature not used\n");
+ return 0;
+ }
create_params.name = IPA_RM_RESOURCE_STD_ECM_PROD;
create_params.reg_params.user_data = dev;
create_params.reg_params.notify_cb = ecm_ipa_rm_notify;
@@ -550,10 +566,11 @@
return result;
}
-static void ecm_ipa_destory_rm_resource(void)
+static void ecm_ipa_destory_rm_resource(struct ecm_ipa_dev *dev)
{
ECM_IPA_LOG_ENTRY();
-
+ if (!dev->rm_enable)
+ return;
ipa_rm_delete_dependency(IPA_RM_RESOURCE_STD_ECM_PROD,
IPA_RM_RESOURCE_USB_CONS);
ipa_rm_inactivity_timer_destroy(IPA_RM_RESOURCE_STD_ECM_PROD);
@@ -609,10 +626,10 @@
ECM_IPA_ERROR("dev NULL pointer\n");
return;
}
- if (rm_enabled(dev)) {
- ecm_ipa_destory_rm_resource();
- ecm_ipa_debugfs_destroy(dev);
- }
+
+ ecm_ipa_destory_rm_resource(dev);
+ ecm_ipa_debugfs_destroy(dev);
+
if (!dev->net) {
unregister_netdev(dev->net);
free_netdev(dev->net);
@@ -662,7 +679,6 @@
int ret;
netdev_tx_t status = NETDEV_TX_BUSY;
struct ecm_ipa_dev *dev = netdev_priv(net);
- unsigned long flags;
if (unlikely(netif_queue_stopped(net))) {
ECM_IPA_ERROR("interface queue is stopped\n");
@@ -682,23 +698,24 @@
goto resource_busy;
}
- spin_lock_irqsave(&dev->ack_spinlock, flags);
- if (dev->last_out_skb) {
- pr_debug("No Tx-ack received for previous packet\n");
- spin_unlock_irqrestore(&dev->ack_spinlock, flags);
+ pr_debug("Before sending packet the outstanding packets counter is %d\n",
+ atomic_read(&dev->outstanding_pkts));
+
+ if (atomic_read(&dev->outstanding_pkts) >= dev->outstanding_high) {
+ pr_debug("Outstanding high boundary reached (%d)- stopping queue\n",
+ dev->outstanding_high);
netif_stop_queue(net);
status = -NETDEV_TX_BUSY;
goto out;
- } else {
- dev->last_out_skb = skb;
}
- spin_unlock_irqrestore(&dev->ack_spinlock, flags);
ret = ipa_tx_dp(IPA_TO_USB_CLIENT, skb, NULL);
if (ret) {
ECM_IPA_ERROR("ipa transmit failed (%d)\n", ret);
goto fail_tx_packet;
}
+
+ atomic_inc(&dev->outstanding_pkts);
net->stats.tx_packets++;
net->stats.tx_bytes += skb->len;
status = NETDEV_TX_OK;
@@ -766,7 +783,6 @@
{
struct sk_buff *skb = (struct sk_buff *)data;
struct ecm_ipa_dev *dev = priv;
- unsigned long flags;
if (!dev) {
ECM_IPA_ERROR("dev is NULL pointer\n");
@@ -776,15 +792,16 @@
ECM_IPA_ERROR("unsupported event on Tx callback\n");
return;
}
- spin_lock_irqsave(&dev->ack_spinlock, flags);
- if (skb != dev->last_out_skb)
- ECM_IPA_ERROR("ACKed/Sent not the same(FIFO expected)\n");
- dev->last_out_skb = NULL;
- spin_unlock_irqrestore(&dev->ack_spinlock, flags);
- if (netif_queue_stopped(dev->net)) {
- pr_debug("waking up queue\n");
+ atomic_dec(&dev->outstanding_pkts);
+ if (netif_queue_stopped(dev->net) &&
+ atomic_read(&dev->outstanding_pkts) < (dev->outstanding_low)) {
+ pr_debug("Outstanding low boundary reached (%d) - waking up queue\n",
+ dev->outstanding_low);
netif_wake_queue(dev->net);
}
+ pr_debug("After Tx-complete the outstanding packets counter is %d\n",
+ atomic_read(&dev->outstanding_pkts));
+
dev_kfree_skb_any(skb);
return;
}
@@ -889,8 +906,8 @@
static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *dev)
{
- const mode_t flags = S_IRUSR | S_IRGRP | S_IROTH |
- S_IWUSR | S_IWGRP | S_IWOTH;
+ const mode_t flags = S_IRUGO | S_IWUGO;
+
int ret = -EINVAL;
ECM_IPA_LOG_ENTRY();
if (!dev)
@@ -929,6 +946,22 @@
ret = -EFAULT;
goto fail_file;
}
+
+ dev->outstanding_high_file = debugfs_create_u8("outstanding_high",
+ flags, dev->folder, &dev->outstanding_high);
+ if (!dev->outstanding_high_file) {
+ ECM_IPA_ERROR("could not create outstanding_high file\n");
+ ret = -EFAULT;
+ goto fail_file;
+ }
+ dev->outstanding_low_file = debugfs_create_u8("outstanding_low",
+ flags, dev->folder, &dev->outstanding_low);
+ if (!dev->outstanding_low_file) {
+ ECM_IPA_ERROR("could not create outstanding_low file\n");
+ ret = -EFAULT;
+ goto fail_file;
+ }
+
ECM_IPA_LOG_EXIT();
return 0;
fail_file:
diff --git a/drivers/net/ethernet/msm/msm_rmnet_wwan.c b/drivers/net/ethernet/msm/msm_rmnet_wwan.c
index fe1ac46..f90ee3d 100644
--- a/drivers/net/ethernet/msm/msm_rmnet_wwan.c
+++ b/drivers/net/ethernet/msm/msm_rmnet_wwan.c
@@ -34,6 +34,7 @@
#include <mach/ipa.h>
#define WWAN_DEV_NAME "rmnet%d"
+#define WWAN_METADATA_SHFT 16
#define WWAN_METADATA_MASK 0x00FF0000
#define IPA_RM_INACTIVITY_TIMER 1000
#define WWAN_DEVICE_COUNT (8)
@@ -304,13 +305,15 @@
rx_ipv4_property = &rx_properties.prop[0];
rx_ipv4_property->ip = IPA_IP_v4;
rx_ipv4_property->attrib.attrib_mask |= IPA_FLT_META_DATA;
- rx_ipv4_property->attrib.meta_data = wwan_ptr->ch_id;
+ rx_ipv4_property->attrib.meta_data =
+ wwan_ptr->ch_id << WWAN_METADATA_SHFT;
rx_ipv4_property->attrib.meta_data_mask = WWAN_METADATA_MASK;
rx_ipv4_property->src_pipe = IPA_CLIENT_A2_EMBEDDED_PROD;
rx_ipv6_property = &rx_properties.prop[1];
rx_ipv6_property->ip = IPA_IP_v6;
rx_ipv6_property->attrib.attrib_mask |= IPA_FLT_META_DATA;
- rx_ipv6_property->attrib.meta_data = wwan_ptr->ch_id;
+ rx_ipv6_property->attrib.meta_data =
+ wwan_ptr->ch_id << WWAN_METADATA_SHFT;
rx_ipv6_property->attrib.meta_data_mask = WWAN_METADATA_MASK;
rx_ipv6_property->src_pipe = IPA_CLIENT_A2_EMBEDDED_PROD;
rx_properties.num_props = 2;
diff --git a/drivers/platform/msm/sps/sps.c b/drivers/platform/msm/sps/sps.c
index 6f2e2a4..cda1717 100644
--- a/drivers/platform/msm/sps/sps.c
+++ b/drivers/platform/msm/sps/sps.c
@@ -2434,7 +2434,7 @@
static int __devinit msm_sps_probe(struct platform_device *pdev)
{
- int ret;
+ int ret = -ENODEV;
SPS_DBG2("sps:%s.", __func__);
@@ -2471,7 +2471,10 @@
sps->dfab_clk = clk_get(sps->dev, "dfab_clk");
if (IS_ERR(sps->dfab_clk)) {
- SPS_ERR("sps:fail to get dfab_clk.");
+ if (IS_ERR(sps->dfab_clk) == -EPROBE_DEFER)
+ ret = -EPROBE_DEFER;
+ else
+ SPS_ERR("sps:fail to get dfab_clk.");
goto clk_err;
} else {
ret = clk_set_rate(sps->dfab_clk, 64000000);
@@ -2485,7 +2488,10 @@
if (!d_type) {
sps->pmem_clk = clk_get(sps->dev, "mem_clk");
if (IS_ERR(sps->pmem_clk)) {
- SPS_ERR("sps:fail to get pmem_clk.");
+ if (IS_ERR(sps->pmem_clk) == -EPROBE_DEFER)
+ ret = -EPROBE_DEFER;
+ else
+ SPS_ERR("sps:fail to get pmem_clk.");
goto clk_err;
} else {
ret = clk_prepare_enable(sps->pmem_clk);
@@ -2499,7 +2505,10 @@
#ifdef CONFIG_SPS_SUPPORT_BAMDMA
sps->bamdma_clk = clk_get(sps->dev, "dma_bam_pclk");
if (IS_ERR(sps->bamdma_clk)) {
- SPS_ERR("sps:fail to get bamdma_clk.");
+ if (IS_ERR(sps->bamdma_clk) == -EPROBE_DEFER)
+ ret = -EPROBE_DEFER;
+ else
+ SPS_ERR("sps:fail to get bamdma_clk.");
goto clk_err;
} else {
ret = clk_prepare_enable(sps->bamdma_clk);
@@ -2539,7 +2548,7 @@
alloc_chrdev_region_err:
class_destroy(sps->dev_class);
- return -ENODEV;
+ return ret;
}
static int __devexit msm_sps_remove(struct platform_device *pdev)
diff --git a/drivers/platform/msm/sps/sps_bam.c b/drivers/platform/msm/sps/sps_bam.c
index 3ebb1cd..a84d99e 100644
--- a/drivers/platform/msm/sps/sps_bam.c
+++ b/drivers/platform/msm/sps/sps_bam.c
@@ -105,7 +105,7 @@
for (n = 0; n < ARRAY_SIZE(opt_event_table); n++) {
if ((u32)opt_event_table[n].option !=
(u32)opt_event_table[n].pipe_irq) {
- SPS_ERR("sps:SPS_O 0x%x != HAL IRQ 0x%x",
+ SPS_ERR("sps:SPS_O 0x%x != HAL IRQ 0x%x\n",
opt_event_table[n].option,
opt_event_table[n].pipe_irq);
return SPS_ERROR;
@@ -141,11 +141,11 @@
source = bam_check_irq_source(dev->base, dev->props.ee,
mask, &cb_case);
- SPS_DBG1("sps:bam_isr:bam=0x%x;source=0x%x;mask=0x%x.",
+ SPS_DBG1("sps:bam_isr:bam=0x%x;source=0x%x;mask=0x%x.\n",
BAM_ID(dev), source, mask);
if ((source & (1UL << 31)) && (dev->props.callback)) {
- SPS_INFO("sps:bam_isr:bam=0x%x;callback for case %d.",
+ SPS_INFO("sps:bam_isr:bam=0x%x;callback for case %d.\n",
BAM_ID(dev), cb_case);
dev->props.callback(cb_case, dev->props.user);
}
@@ -156,7 +156,7 @@
/* If MTIs are used, must poll each active pipe */
source = dev->pipe_active_mask;
- SPS_DBG1("sps:bam_isr for MTI:bam=0x%x;source=0x%x.",
+ SPS_DBG1("sps:bam_isr for MTI:bam=0x%x;source=0x%x.\n",
BAM_ID(dev), source);
}
@@ -177,7 +177,7 @@
/* Process any inactive pipe sources */
if (source) {
- SPS_ERR("sps:IRQ from BAM 0x%x inactive pipe(s) 0x%x",
+ SPS_ERR("sps:IRQ from BAM 0x%x inactive pipe(s) 0x%x\n",
BAM_ID(dev), source);
dev->irq_from_disabled_pipe++;
}
@@ -204,7 +204,7 @@
/* Is there any access to this BAM? */
if ((dev->props.manage & SPS_BAM_MGR_ACCESS_MASK) == SPS_BAM_MGR_NONE) {
- SPS_ERR("sps:No local access to BAM 0x%x", BAM_ID(dev));
+ SPS_ERR("sps:No local access to BAM 0x%x\n", BAM_ID(dev));
return SPS_ERROR;
}
@@ -222,7 +222,7 @@
IRQF_TRIGGER_HIGH, "sps", dev);
if (result) {
- SPS_ERR("sps:Failed to enable BAM 0x%x IRQ %d",
+ SPS_ERR("sps:Failed to enable BAM 0x%x IRQ %d\n",
BAM_ID(dev), dev->props.irq);
return SPS_ERROR;
}
@@ -236,13 +236,13 @@
result = enable_irq_wake(dev->props.irq);
if (result) {
- SPS_ERR("sps:Fail to enable wakeup irq "
- "BAM 0x%x IRQ %d",
+ SPS_ERR(
+ "sps:Fail to enable wakeup irq for BAM 0x%x IRQ %d\n",
BAM_ID(dev), dev->props.irq);
return SPS_ERROR;
} else
- SPS_DBG2("sps:Enable wakeup irq for "
- "BAM 0x%x IRQ %d",
+ SPS_DBG2(
+ "sps:Enable wakeup irq for BAM 0x%x IRQ %d\n",
BAM_ID(dev), dev->props.irq);
}
}
@@ -262,7 +262,7 @@
rc = bam_check(dev->base, &dev->version, &num_pipes);
if (rc) {
- SPS_ERR("sps:Fail to init BAM 0x%x IRQ %d",
+ SPS_ERR("sps:Fail to init BAM 0x%x IRQ %d\n",
BAM_ID(dev), dev->props.irq);
return SPS_ERROR;
}
@@ -281,7 +281,7 @@
* must use MTI. Thus, force EE index to a non-zero value to
* insure that EE zero globals can't be modified.
*/
- SPS_ERR("sps:EE for satellite BAM must be set to non-zero.");
+ SPS_ERR("sps:EE for satellite BAM must be set to non-zero.\n");
return SPS_ERROR;
}
@@ -295,8 +295,9 @@
MTIenabled) {
if (dev->props.irq_gen_addr == 0 ||
dev->props.irq_gen_addr == SPS_ADDR_INVALID) {
- SPS_ERR("sps:MTI destination address not specified "
- "for BAM 0x%x", BAM_ID(dev));
+ SPS_ERR(
+ "sps:MTI destination address not specified for BAM 0x%x\n",
+ BAM_ID(dev));
return SPS_ERROR;
}
dev->state |= BAM_STATE_MTI;
@@ -304,13 +305,13 @@
if (num_pipes) {
dev->props.num_pipes = num_pipes;
- SPS_DBG1("sps:BAM 0x%x number of pipes reported by hw: %d",
+ SPS_DBG1("sps:BAM 0x%x number of pipes reported by hw: %d\n",
BAM_ID(dev), dev->props.num_pipes);
}
/* Check EE index */
if (!MTIenabled && dev->props.ee >= SPS_BAM_NUM_EES) {
- SPS_ERR("sps:Invalid EE BAM 0x%x: %d", BAM_ID(dev),
+ SPS_ERR("sps:Invalid EE BAM 0x%x: %d\n", BAM_ID(dev),
dev->props.ee);
return SPS_ERROR;
}
@@ -323,8 +324,9 @@
struct sps_bam_sec_config_props *p_sec =
dev->props.p_sec_config_props;
if (p_sec == NULL) {
- SPS_ERR("sps:EE config table is not specified for "
- "BAM 0x%x", BAM_ID(dev));
+ SPS_ERR(
+ "sps:EE config table is not specified for BAM 0x%x\n",
+ BAM_ID(dev));
return SPS_ERROR;
}
@@ -351,9 +353,8 @@
for (i = n + 1; i < SPS_BAM_NUM_EES; i++) {
if ((p_sec->ees[n].pipe_mask &
p_sec->ees[i].pipe_mask) != 0) {
- SPS_ERR("sps:Overlapping pipe "
- "assignments for BAM "
- "0x%x: EEs %d and %d",
+ SPS_ERR(
+ "sps:Overlapping pipe assignments for BAM 0x%x: EEs %d and %d\n",
BAM_ID(dev), n, i);
return SPS_ERROR;
}
@@ -403,9 +404,21 @@
}
dev->state |= BAM_STATE_ENABLED;
- SPS_INFO("sps:BAM 0x%x (va:0x%x) enabled: ver:0x%x, number of pipes:%d",
- BAM_ID(dev), (u32) dev->base, dev->version,
- dev->props.num_pipes);
+
+ if (!dev->props.constrained_logging ||
+ (dev->props.constrained_logging && dev->props.logging_number)) {
+ if (dev->props.logging_number > 0)
+ dev->props.logging_number--;
+ SPS_INFO(
+ "sps:BAM 0x%x (va:0x%x) enabled: ver:0x%x, number of pipes:%d\n",
+ BAM_ID(dev), (u32) dev->base, dev->version,
+ dev->props.num_pipes);
+ } else
+ SPS_DBG2(
+ "sps:BAM 0x%x (va:0x%x) enabled: ver:0x%x, number of pipes:%d\n",
+ BAM_ID(dev), (u32) dev->base, dev->version,
+ dev->props.num_pipes);
+
return 0;
}
@@ -420,7 +433,7 @@
/* Is there any access to this BAM? */
if ((dev->props.manage & SPS_BAM_MGR_ACCESS_MASK) == SPS_BAM_MGR_NONE) {
- SPS_ERR("sps:No local access to BAM 0x%x", BAM_ID(dev));
+ SPS_ERR("sps:No local access to BAM 0x%x\n", BAM_ID(dev));
return SPS_ERROR;
}
@@ -444,7 +457,7 @@
dev->state &= ~BAM_STATE_ENABLED;
- SPS_DBG2("sps:BAM 0x%x disabled", BAM_ID(dev));
+ SPS_DBG2("sps:BAM 0x%x disabled\n", BAM_ID(dev));
return 0;
}
@@ -455,7 +468,7 @@
int sps_bam_device_init(struct sps_bam *dev)
{
if (dev->props.virt_addr == NULL) {
- SPS_ERR("sps:NULL BAM virtual address");
+ SPS_ERR("sps:NULL BAM virtual address\n");
return SPS_ERROR;
}
dev->base = (void *) dev->props.virt_addr;
@@ -463,7 +476,7 @@
if (dev->props.num_pipes == 0) {
/* Assume max number of pipes until BAM registers can be read */
dev->props.num_pipes = BAM_MAX_PIPES;
- SPS_DBG2("sps:BAM 0x%x: assuming max number of pipes: %d",
+ SPS_DBG2("sps:BAM 0x%x: assuming max number of pipes: %d\n",
BAM_ID(dev), dev->props.num_pipes);
}
@@ -479,11 +492,11 @@
if ((dev->props.options & SPS_BAM_OPT_ENABLE_AT_BOOT))
if (sps_bam_enable(dev)) {
- SPS_ERR("sps:Fail to enable bam device");
+ SPS_ERR("sps:Fail to enable bam device\n");
return SPS_ERROR;
}
- SPS_DBG2("sps:BAM device: phys 0x%x IRQ %d",
+ SPS_DBG2("sps:BAM device: phys 0x%x IRQ %d\n",
BAM_ID(dev), dev->props.irq);
return 0;
@@ -497,7 +510,7 @@
{
int result;
- SPS_DBG2("sps:BAM device DEINIT: phys 0x%x IRQ %d",
+ SPS_DBG2("sps:BAM device DEINIT: phys 0x%x IRQ %d\n",
BAM_ID(dev), dev->props.irq);
result = sps_bam_disable(dev);
@@ -515,7 +528,7 @@
u32 pipe_index;
int result;
- SPS_DBG2("sps:BAM device RESET: phys 0x%x IRQ %d",
+ SPS_DBG2("sps:BAM device RESET: phys 0x%x IRQ %d\n",
BAM_ID(dev), dev->props.irq);
/* If BAM is enabled, then disable */
@@ -526,8 +539,8 @@
pipe_index++) {
pipe = dev->pipes[pipe_index];
if (BAM_PIPE_IS_ASSIGNED(pipe)) {
- SPS_ERR("sps:BAM device 0x%x RESET failed: "
- "pipe %d in use",
+ SPS_ERR(
+ "sps:BAM device 0x%x RESET failed: pipe %d in use\n",
BAM_ID(dev), pipe_index);
result = SPS_ERROR;
break;
@@ -579,8 +592,9 @@
if (pipe_index == SPS_BAM_PIPE_INVALID) {
/* Allocate a pipe from the BAM */
if ((dev->props.manage & SPS_BAM_MGR_PIPE_NO_ALLOC)) {
- SPS_ERR("sps:Restricted from allocating pipes "
- "on BAM 0x%x", BAM_ID(dev));
+ SPS_ERR(
+ "sps:Restricted from allocating pipes on BAM 0x%x\n",
+ BAM_ID(dev));
return SPS_BAM_PIPE_INVALID;
}
for (pipe_index = 0, pipe_mask = 1;
@@ -593,24 +607,25 @@
break; /* Found an available pipe */
}
if (pipe_index >= dev->props.num_pipes) {
- SPS_ERR("sps:Fail to allocate pipe on BAM 0x%x",
+ SPS_ERR("sps:Fail to allocate pipe on BAM 0x%x\n",
BAM_ID(dev));
return SPS_BAM_PIPE_INVALID;
}
} else {
/* Check that client-specified pipe is available */
if (pipe_index >= dev->props.num_pipes) {
- SPS_ERR("sps:Invalid pipe %d for allocate on BAM 0x%x",
+ SPS_ERR(
+ "sps:Invalid pipe %d for allocate on BAM 0x%x\n",
pipe_index, BAM_ID(dev));
return SPS_BAM_PIPE_INVALID;
}
if ((dev->props.restricted_pipes & (1UL << pipe_index))) {
- SPS_ERR("sps:BAM 0x%x pipe %d is not local",
+ SPS_ERR("sps:BAM 0x%x pipe %d is not local\n",
BAM_ID(dev), pipe_index);
return SPS_BAM_PIPE_INVALID;
}
if (dev->pipes[pipe_index] != NULL) {
- SPS_ERR("sps:Pipe %d already allocated on BAM 0x%x",
+ SPS_ERR("sps:Pipe %d already allocated on BAM 0x%x\n",
pipe_index, BAM_ID(dev));
return SPS_BAM_PIPE_INVALID;
}
@@ -631,7 +646,7 @@
struct sps_pipe *pipe;
if (pipe_index >= dev->props.num_pipes) {
- SPS_ERR("sps:Invalid BAM 0x%x pipe: %d", BAM_ID(dev),
+ SPS_ERR("sps:Invalid BAM 0x%x pipe: %d\n", BAM_ID(dev),
pipe_index);
return;
}
@@ -642,8 +657,8 @@
/* Is the pipe currently allocated? */
if (pipe == NULL) {
- SPS_ERR("sps:Attempt to free unallocated pipe %d on "
- "BAM 0x%x", pipe_index, BAM_ID(dev));
+ SPS_ERR("sps:Attempt to free unallocated pipe %d on BAM 0x%x\n",
+ pipe_index, BAM_ID(dev));
return;
}
@@ -654,7 +669,7 @@
if (!list_empty(&pipe->sys.events_q)) {
struct sps_q_event *sps_event;
- SPS_ERR("sps:Disconnect BAM 0x%x pipe %d with events pending",
+ SPS_ERR("sps:Disconnect BAM 0x%x pipe %d with events pending\n",
BAM_ID(dev), pipe_index);
sps_event = list_entry((&pipe->sys.events_q)->next,
@@ -718,7 +733,7 @@
dev = map_pipe->bam;
pipe_index = map_pipe->pipe_index;
if (pipe_index >= dev->props.num_pipes) {
- SPS_ERR("sps:Invalid BAM 0x%x pipe: %d", BAM_ID(dev),
+ SPS_ERR("sps:Invalid BAM 0x%x pipe: %d\n", BAM_ID(dev),
pipe_index);
return SPS_ERROR;
}
@@ -729,14 +744,14 @@
/* Verify that control of this pipe is allowed */
if ((dev->props.manage & SPS_BAM_MGR_PIPE_NO_CTRL) ||
(dev->props.restricted_pipes & (1UL << pipe_index))) {
- SPS_ERR("sps:BAM 0x%x pipe %d is not local",
+ SPS_ERR("sps:BAM 0x%x pipe %d is not local\n",
BAM_ID(dev), pipe_index);
return SPS_ERROR;
}
/* Control without configuration permission is not supported yet */
if ((dev->props.manage & SPS_BAM_MGR_PIPE_NO_CONFIG)) {
- SPS_ERR("sps:BAM 0x%x pipe %d remote config is not supported",
+ SPS_ERR("sps:BAM 0x%x pipe %d remote config is not supported\n",
BAM_ID(dev), pipe_index);
return SPS_ERROR;
}
@@ -754,8 +769,9 @@
if (map->desc.phys_base == SPS_ADDR_INVALID ||
map->data.phys_base == SPS_ADDR_INVALID ||
map->desc.size == 0 || map->data.size == 0) {
- SPS_ERR("sps:FIFO buffers are not allocated for BAM "
- "0x%x pipe %d.", BAM_ID(dev), pipe_index);
+ SPS_ERR(
+ "sps:FIFO buffers are not allocated for BAM 0x%x pipe %d.\n",
+ BAM_ID(dev), pipe_index);
return SPS_ERROR;
}
hw_params.data_base = map->data.phys_base;
@@ -787,8 +803,8 @@
/* Get virtual address for descriptor FIFO */
if (map->desc.phys_base != SPS_ADDR_INVALID) {
if (map->desc.size < (2 * sizeof(struct sps_iovec))) {
- SPS_ERR("sps:Invalid descriptor FIFO size "
- "for BAM 0x%x pipe %d: %d",
+ SPS_ERR(
+ "sps:Invalid descriptor FIFO size for BAM 0x%x pipe %d: %d\n",
BAM_ID(dev), pipe_index, map->desc.size);
return SPS_ERROR;
}
@@ -821,7 +837,7 @@
/* Check pipe allocation */
if (dev->pipes[pipe_index] != BAM_PIPE_UNASSIGNED) {
- SPS_ERR("sps:Invalid pipe %d on BAM 0x%x for connect",
+ SPS_ERR("sps:Invalid pipe %d on BAM 0x%x for connect\n",
pipe_index, BAM_ID(dev));
return SPS_ERROR;
}
@@ -838,7 +854,7 @@
}
if (bam_pipe_init(dev->base, pipe_index, &hw_params, dev->props.ee)) {
- SPS_ERR("sps:BAM 0x%x pipe %d init error",
+ SPS_ERR("sps:BAM 0x%x pipe %d init error\n",
BAM_ID(dev), pipe_index);
goto exit_err;
}
@@ -913,7 +929,7 @@
int result;
if (pipe_index >= dev->props.num_pipes) {
- SPS_ERR("sps:Invalid BAM 0x%x pipe: %d", BAM_ID(dev),
+ SPS_ERR("sps:Invalid BAM 0x%x pipe: %d\n", BAM_ID(dev),
pipe_index);
return SPS_ERROR;
}
@@ -947,7 +963,7 @@
}
if (result)
- SPS_ERR("sps:BAM 0x%x pipe %d already disconnected",
+ SPS_ERR("sps:BAM 0x%x pipe %d already disconnected\n",
BAM_ID(dev), pipe_index);
return result;
@@ -990,7 +1006,7 @@
irq_enable = BAM_DISABLE;
pipe->polled = true;
if (poll == 0 && pipe->irq_mask)
- SPS_DBG2("sps:BAM 0x%x pipe %d forced to use polling",
+ SPS_DBG2("sps:BAM 0x%x pipe %d forced to use polling\n",
BAM_ID(dev), pipe_index);
}
if ((pipe->state & BAM_STATE_MTI) == 0)
@@ -1038,8 +1054,8 @@
if (pipe->sys.desc_wr_count > 0 &&
(no_queue != pipe->sys.no_queue
|| ack_xfers != pipe->sys.ack_xfers)) {
- SPS_ERR("sps:Queue/ack mode change after transfer: "
- "BAM 0x%x pipe %d opt 0x%x",
+ SPS_ERR(
+ "sps:Queue/ack mode change after transfer: BAM 0x%x pipe %d opt 0x%x\n",
BAM_ID(dev), pipe_index, options);
return SPS_ERROR;
}
@@ -1048,8 +1064,9 @@
/* Is client setting invalid options for a BAM-to-BAM connection? */
if ((pipe->state & BAM_STATE_BAM2BAM) &&
(options & BAM2BAM_O_INVALID)) {
- SPS_ERR("sps:Invalid option for BAM-to-BAM: BAM 0x%x pipe %d "
- "opt 0x%x", BAM_ID(dev), pipe_index, options);
+ SPS_ERR(
+ "sps:Invalid option for BAM-to-BAM: BAM 0x%x pipe %d opt 0x%x\n",
+ BAM_ID(dev), pipe_index, options);
return SPS_ERROR;
}
@@ -1067,7 +1084,8 @@
vmalloc(pipe->desc_size + size);
if (pipe->sys.desc_cache == NULL) {
- SPS_ERR("sps:No memory for pipe %d of BAM 0x%x",
+ SPS_ERR(
+ "sps:No memory for pipe %d of BAM 0x%x\n",
pipe_index, BAM_ID(dev));
return -ENOMEM;
}
@@ -1077,7 +1095,7 @@
if (pipe->sys.desc_cache == NULL) {
/*** MUST BE LAST POINT OF FAILURE (see below) *****/
- SPS_ERR("sps:Desc cache error: BAM 0x%x pipe %d: %d",
+ SPS_ERR("sps:Desc cache error: BAM 0x%x pipe %d: %d\n",
BAM_ID(dev), pipe_index,
pipe->desc_size + size);
return SPS_ERROR;
@@ -1153,8 +1171,8 @@
if (pipe->sys.no_queue && reg->xfer_done != NULL &&
reg->mode != SPS_TRIGGER_CALLBACK) {
- SPS_ERR("sps:Only callback events support for NO_Q: "
- "BAM 0x%x pipe %d mode %d",
+ SPS_ERR(
+ "sps:Only callback events support for NO_Q: BAM 0x%x pipe %d mode %d\n",
BAM_ID(dev), pipe_index, reg->mode);
return SPS_ERROR;
}
@@ -1168,9 +1186,9 @@
index = SPS_EVENT_INDEX(opt_event_table[n].event_id);
if (index < 0)
- SPS_ERR("sps:Negative event index: "
- "BAM 0x%x pipe %d mode %d",
- BAM_ID(dev), pipe_index, reg->mode);
+ SPS_ERR(
+ "sps:Negative event index: BAM 0x%x pipe %d mode %d\n",
+ BAM_ID(dev), pipe_index, reg->mode);
else {
event_reg = &pipe->sys.event_regs[index];
event_reg->xfer_done = reg->xfer_done;
@@ -1199,7 +1217,7 @@
/* Is this a BAM-to-BAM or satellite connection? */
if ((pipe->state & (BAM_STATE_BAM2BAM | BAM_STATE_REMOTE))) {
- SPS_ERR("sps:Transfer on BAM-to-BAM: BAM 0x%x pipe %d",
+ SPS_ERR("sps:Transfer on BAM-to-BAM: BAM 0x%x pipe %d\n",
BAM_ID(dev), pipe_index);
return SPS_ERROR;
}
@@ -1209,7 +1227,7 @@
* SPS_O_NO_Q option.
*/
if (pipe->sys.no_queue && user != NULL) {
- SPS_ERR("sps:User pointer arg non-NULL: BAM 0x%x pipe %d",
+ SPS_ERR("sps:User pointer arg non-NULL: BAM 0x%x pipe %d\n",
BAM_ID(dev), pipe_index);
return SPS_ERROR;
}
@@ -1230,24 +1248,27 @@
if (next_write == pipe->sys.acked_offset) {
if (!show_recom) {
show_recom = true;
- SPS_ERR("sps:Client of BAM 0x%x pipe %d is recommended to have flow control",
+ SPS_ERR(
+ "sps:Client of BAM 0x%x pipe %d is recommended to have flow control\n",
BAM_ID(dev), pipe_index);
}
- SPS_DBG2("sps:Descriptor FIFO is full for BAM "
- "0x%x pipe %d after pipe_handler_eot",
+ SPS_DBG2(
+ "sps:Descriptor FIFO is full for BAM 0x%x pipe %d after pipe_handler_eot\n",
BAM_ID(dev), pipe_index);
return SPS_ERROR;
}
} else {
if (!show_recom) {
show_recom = true;
- SPS_ERR("sps:Client of BAM 0x%x pipe %d is recommended to have flow control.",
+ SPS_ERR(
+ "sps:Client of BAM 0x%x pipe %d is recommended to have flow control.\n",
BAM_ID(dev), pipe_index);
}
- SPS_DBG2("sps:Descriptor FIFO is full for "
- "BAM 0x%x pipe %d", BAM_ID(dev), pipe_index);
+ SPS_DBG2(
+ "sps:Descriptor FIFO is full for BAM 0x%x pipe %d\n",
+ BAM_ID(dev), pipe_index);
return SPS_ERROR;
}
}
@@ -1323,14 +1344,14 @@
int result;
if (transfer->iovec_count == 0) {
- SPS_ERR("sps:iovec count zero: BAM 0x%x pipe %d",
+ SPS_ERR("sps:iovec count zero: BAM 0x%x pipe %d\n",
BAM_ID(dev), pipe_index);
return SPS_ERROR;
}
sps_bam_get_free_count(dev, pipe_index, &count);
if (count < transfer->iovec_count) {
- SPS_ERR("sps:Insufficient free desc: BAM 0x%x pipe %d: %d",
+ SPS_ERR("sps:Insufficient free desc: BAM 0x%x pipe %d: %d\n",
BAM_ID(dev), pipe_index, count);
return SPS_ERROR;
}
@@ -1401,18 +1422,18 @@
struct sps_q_event *sps_event)
{
if (sps_event == NULL) {
- SPS_DBG("sps:trigger_event.sps_event is NULL.");
+ SPS_DBG("sps:trigger_event.sps_event is NULL.\n");
return;
}
if (event_reg->xfer_done) {
complete(event_reg->xfer_done);
- SPS_DBG("sps:trigger_event.done=%d.",
+ SPS_DBG("sps:trigger_event.done=%d.\n",
event_reg->xfer_done->done);
}
if (event_reg->callback) {
- SPS_DBG("sps:trigger_event.using callback.");
+ SPS_DBG("sps:trigger_event.using callback.\n");
event_reg->callback(&sps_event->notify);
}
@@ -1684,7 +1705,7 @@
pipe_index = pipe->pipe_index;
status = bam_pipe_get_and_clear_irq_status(dev->base, pipe_index);
- SPS_DBG("sps:pipe_handler.bam 0x%x.pipe %d.status=0x%x.",
+ SPS_DBG("sps:pipe_handler.bam 0x%x.pipe %d.status=0x%x.\n",
BAM_ID(dev), pipe_index, status);
/* Check for enabled interrupt sources */
@@ -1756,8 +1777,8 @@
struct sps_q_event *event_queue;
if (pipe->sys.no_queue) {
- SPS_ERR("sps:Invalid connection for event: "
- "BAM 0x%x pipe %d context 0x%x",
+ SPS_ERR(
+ "sps:Invalid connection for event: BAM 0x%x pipe %d context 0x%x\n",
BAM_ID(dev), pipe_index, (u32) pipe);
notify->event_id = SPS_EVENT_INVALID;
return SPS_ERROR;
@@ -1770,9 +1791,10 @@
/* Pull an event off the synchronous event queue */
if (list_empty(&pipe->sys.events_q)) {
event_queue = NULL;
- SPS_DBG("sps:events_q of bam 0x%x is empty.", BAM_ID(dev));
+ SPS_DBG("sps:events_q of bam 0x%x is empty.\n", BAM_ID(dev));
} else {
- SPS_DBG("sps:events_q of bam 0x%x is not empty.", BAM_ID(dev));
+ SPS_DBG("sps:events_q of bam 0x%x is not empty.\n",
+ BAM_ID(dev));
event_queue =
list_first_entry(&pipe->sys.events_q, struct sps_q_event,
list);
@@ -1861,7 +1883,7 @@
/* Is this a satellite connection? */
if ((pipe->state & BAM_STATE_REMOTE)) {
- SPS_ERR("sps:Is empty on remote: BAM 0x%x pipe %d",
+ SPS_ERR("sps:Is empty on remote: BAM 0x%x pipe %d\n",
BAM_ID(dev), pipe_index);
return SPS_ERROR;
}
@@ -1900,8 +1922,9 @@
/* Is this a BAM-to-BAM or satellite connection? */
if ((pipe->state & (BAM_STATE_BAM2BAM | BAM_STATE_REMOTE))) {
- SPS_ERR("sps:Free count on BAM-to-BAM or remote: BAM "
- "0x%x pipe %d", BAM_ID(dev), pipe_index);
+ SPS_ERR(
+ "sps:Free count on BAM-to-BAM or remote: BAM 0x%x pipe %d\n",
+ BAM_ID(dev), pipe_index);
*count = 0;
return SPS_ERROR;
}
@@ -1936,14 +1959,15 @@
*/
if ((dev->props.manage & SPS_BAM_MGR_MULTI_EE) == 0 ||
(dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE)) {
- SPS_ERR("sps:Cannot grant satellite control to BAM 0x%x "
- "pipe %d", BAM_ID(dev), pipe_index);
+ SPS_ERR(
+ "sps:Cannot grant satellite control to BAM 0x%x pipe %d\n",
+ BAM_ID(dev), pipe_index);
return SPS_ERROR;
}
/* Is this pipe locally controlled? */
if ((dev->pipe_active_mask & (1UL << pipe_index)) == 0) {
- SPS_ERR("sps:BAM 0x%x pipe %d not local and active",
+ SPS_ERR("sps:BAM 0x%x pipe %d not local and active\n",
BAM_ID(dev), pipe_index);
return SPS_ERROR;
}
@@ -1991,7 +2015,7 @@
/* Is this pipe locally controlled? */
if ((dev->pipe_active_mask & (1UL << pipe_index)) == 0) {
- SPS_ERR("sps:BAM 0x%x pipe %d not local and active",
+ SPS_ERR("sps:BAM 0x%x pipe %d not local and active\n",
BAM_ID(dev), pipe_index);
return SPS_ERROR;
}
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index 210964e..fd42c47 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -125,7 +125,6 @@
int r_conn_mohm;
int shutdown_soc_valid_limit;
int adjust_soc_low_threshold;
- int adjust_soc_high_threshold;
int chg_term_ua;
enum battery_type batt_type;
unsigned int fcc;
@@ -176,6 +175,7 @@
struct timespec t_soc_queried;
int last_soc;
int last_soc_est;
+ int last_soc_unbound;
int charge_time_us;
int catch_up_time_us;
@@ -195,6 +195,10 @@
bool use_voltage_soc;
int prev_batt_terminal_uv;
+ int high_ocv_correction_limit_uv;
+ int low_ocv_correction_limit_uv;
+ int flat_ocv_threshold_uv;
+ int hold_soc_est;
int ocv_high_threshold_uv;
int ocv_low_threshold_uv;
@@ -1363,6 +1367,7 @@
}
}
+#define NO_ADJUST_HIGH_SOC_THRESHOLD 90
static int adjust_soc(struct qpnp_bms_chip *chip, struct soc_params *params,
int soc, int batt_temp)
{
@@ -1376,6 +1381,7 @@
int slope = 0;
int rc = 0;
int delta_ocv_uv_limit = 0;
+ int correction_limit_uv = 0;
rc = get_simultaneous_batt_v_and_i(chip, &ibat_ua, &vbat_uv);
if (rc < 0) {
@@ -1411,18 +1417,15 @@
/*
* do not adjust
- * if soc is same as what bms calculated
- * if soc_est is between 45 and 25, this is the flat portion of the
- * curve where soc_est is not so accurate. We generally don't want to
- * adjust when soc_est is inaccurate except for the cases when soc is
- * way far off (higher than 50 or lesser than 20).
- * Also don't adjust soc if it is above 90 becuase it might be pulled
- * low and cause a bad user experience
+ * if soc_est is same as what bms calculated
+ * OR if soc_est > adjust_soc_low_threshold
+ * OR if soc is above 90
+ * because we might pull it low
+ * and cause a bad user experience
*/
if (soc_est == soc
- || (is_between(45, chip->adjust_soc_low_threshold, soc_est)
- && is_between(50, chip->adjust_soc_low_threshold - 5, soc))
- || soc >= 90)
+ || soc_est > chip->adjust_soc_low_threshold
+ || soc >= NO_ADJUST_HIGH_SOC_THRESHOLD)
goto out;
if (chip->last_soc_est == -EINVAL)
@@ -1467,6 +1470,21 @@
pr_debug("new delta ocv = %d\n", delta_ocv_uv);
}
+ if (chip->last_ocv_uv > chip->flat_ocv_threshold_uv)
+ correction_limit_uv = chip->high_ocv_correction_limit_uv;
+ else
+ correction_limit_uv = chip->low_ocv_correction_limit_uv;
+
+ if (abs(delta_ocv_uv) > correction_limit_uv) {
+ pr_debug("limiting delta ocv %d limit = %d\n",
+ delta_ocv_uv, correction_limit_uv);
+ if (delta_ocv_uv > 0)
+ delta_ocv_uv = correction_limit_uv;
+ else
+ delta_ocv_uv = -correction_limit_uv;
+ pr_debug("new delta ocv = %d\n", delta_ocv_uv);
+ }
+
chip->last_ocv_uv -= delta_ocv_uv;
if (chip->last_ocv_uv >= chip->max_voltage_uv)
@@ -1481,9 +1499,9 @@
/*
* if soc_new is ZERO force it higher so that phone doesnt report soc=0
- * soc = 0 should happen only when soc_est == 0
+ * soc = 0 should happen only when soc_est is above a set value
*/
- if (soc_new == 0 && soc_est != 0)
+ if (soc_new == 0 && soc_est >= chip->hold_soc_est)
soc_new = 1;
soc = soc_new;
@@ -1881,9 +1899,18 @@
soc = scale_soc_while_chg(chip, delta_time_us,
soc, chip->last_soc);
+ if (chip->last_soc_unbound)
+ chip->last_soc_unbound = false;
+ else if (chip->last_soc != -EINVAL) {
+ if (soc < chip->last_soc && soc != 0)
+ soc = chip->last_soc - 1;
+ if (soc > chip->last_soc && soc != 100)
+ soc = chip->last_soc + 1;
+ }
+
pr_debug("last_soc = %d, calculated_soc = %d, soc = %d\n",
chip->last_soc, chip->calculated_soc, soc);
- chip->last_soc = soc;
+ chip->last_soc = bound_soc(soc);
backup_soc_and_iavg(chip, batt_temp, chip->last_soc);
pr_debug("Reported SOC = %d\n", chip->last_soc);
chip->t_soc_queried = now;
@@ -2150,6 +2177,7 @@
chip->rbatt_sf_lut = batt_data->rbatt_sf_lut;
chip->default_rbatt_mohm = batt_data->default_rbatt_mohm;
chip->rbatt_capacitive_mohm = batt_data->rbatt_capacitive_mohm;
+ chip->flat_ocv_threshold_uv = batt_data->flat_ocv_threshold_uv;
if (chip->pc_temp_ocv_lut == NULL) {
pr_err("temp ocv lut table is NULL\n");
@@ -2181,8 +2209,6 @@
SPMI_PROP_READ(chg_term_ua, "chg-term-ua", rc);
SPMI_PROP_READ(shutdown_soc_valid_limit,
"shutdown-soc-valid-limit", rc);
- SPMI_PROP_READ(adjust_soc_high_threshold,
- "adjust-soc-high-threshold", rc);
SPMI_PROP_READ(adjust_soc_low_threshold,
"adjust-soc-low-threshold", rc);
SPMI_PROP_READ(batt_type, "batt-type", rc);
@@ -2202,6 +2228,12 @@
chip->use_ocv_thresholds = of_property_read_bool(
chip->spmi->dev.of_node,
"qcom,use-ocv-thresholds");
+ SPMI_PROP_READ(high_ocv_correction_limit_uv,
+ "high-ocv-correction-limit-uv", rc);
+ SPMI_PROP_READ(low_ocv_correction_limit_uv,
+ "low-ocv-correction-limit-uv", rc);
+ SPMI_PROP_READ(hold_soc_est,
+ "hold-soc-est", rc);
SPMI_PROP_READ(ocv_high_threshold_uv,
"ocv-voltage-high-threshold-uv", rc);
SPMI_PROP_READ(ocv_low_threshold_uv,
@@ -2217,8 +2249,8 @@
pr_debug("r_conn:%d, shutdown_soc: %d, adjust_soc_low:%d\n",
chip->r_conn_mohm, chip->shutdown_soc_valid_limit,
chip->adjust_soc_low_threshold);
- pr_debug("adjust_soc_high:%d, chg_term_ua:%d, batt_type:%d\n",
- chip->adjust_soc_high_threshold, chip->chg_term_ua,
+ pr_debug("chg_term_ua:%d, batt_type:%d\n",
+ chip->chg_term_ua,
chip->batt_type);
pr_debug("ignore_shutdown_soc:%d, use_voltage_soc:%d\n",
chip->ignore_shutdown_soc, chip->use_voltage_soc);
@@ -2549,6 +2581,11 @@
if (rc) {
pr_err("Could not read current time: %d\n", rc);
} else if (tm_now_sec > chip->last_recalc_time) {
+ /*
+ * unbind the last soc so that the next
+ * recalculation is not limited to changing by 1%
+ */
+ chip->last_soc_unbound = true;
time_since_last_recalc = tm_now_sec - chip->last_recalc_time;
pr_debug("Time since last recalc: %lu\n",
time_since_last_recalc);
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index f4efa756..331c7f1 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -157,6 +157,11 @@
/* smbb_misc_interrupts */
#define TFTWDOG_IRQ BIT(0)
+/* SMBB types */
+#define SMBB BIT(1)
+#define SMBBP BIT(2)
+#define SMBCL BIT(3)
+
/* Workaround flags */
#define CHG_FLAGS_VCP_WA BIT(0)
@@ -194,6 +199,8 @@
* @warm_bat_decidegc Warm battery temperature in degree Celsius
* @cool_bat_decidegc Cool battery temperature in degree Celsius
* @revision: PMIC revision
+ * @type: SMBB type
+ * @tchg_mins maximum allowed software initiated charge time
* @thermal_levels amount of thermal mitigation levels
* @thermal_mitigation thermal mitigation level values
* @therm_lvl_sel thermal mitigation level selection
@@ -247,6 +254,8 @@
unsigned int cool_bat_decidegc;
unsigned int safe_current;
unsigned int revision;
+ unsigned int type;
+ unsigned int tchg_mins;
unsigned int thermal_levels;
unsigned int therm_lvl_sel;
unsigned int *thermal_mitigation;
@@ -256,6 +265,7 @@
struct power_supply batt_psy;
uint32_t flags;
struct qpnp_adc_tm_btm_param adc_param;
+ struct work_struct adc_measure_work;
};
static struct of_device_id qpnp_charger_match_table[] = {
@@ -508,6 +518,15 @@
enable ? USB_SUSPEND_BIT : 0, 1);
}
+static void qpnp_bat_if_adc_measure_work(struct work_struct *work)
+{
+ struct qpnp_chg_chip *chip = container_of(work,
+ struct qpnp_chg_chip, adc_measure_work);
+
+ if (qpnp_adc_tm_channel_measure(&chip->adc_param))
+ pr_err("request ADC error\n");
+}
+
#define ENUM_T_STOP_BIT BIT(0)
static irqreturn_t
qpnp_chg_usb_usbin_valid_irq_handler(int irq, void *_chip)
@@ -550,8 +569,7 @@
if (chip->cool_bat_decidegc && chip->warm_bat_decidegc
&& batt_present) {
- if (qpnp_adc_tm_channel_measure(&chip->adc_param))
- pr_err("request ADC error\n");
+ schedule_work(&chip->adc_measure_work);
}
}
@@ -773,6 +791,7 @@
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_TEMP,
@@ -813,16 +832,16 @@
int rc = 0;
struct qpnp_vadc_result results;
- if (chip->revision > 0) {
+ if (chip->revision == 0 && chip->type == SMBB) {
+ pr_err("vbat reading not supported for 1.0 rc=%d\n", rc);
+ return 0;
+ } else {
rc = qpnp_vadc_read(VBAT_SNS, &results);
if (rc) {
pr_err("Unable to read vbat rc=%d\n", rc);
return 0;
}
return results.physical;
- } else {
- pr_err("vbat reading not supported for 1.0 rc=%d\n", rc);
- return 0;
}
}
@@ -912,6 +931,21 @@
return POWER_SUPPLY_STATUS_DISCHARGING;
}
+static int
+get_prop_current_max(struct qpnp_chg_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (chip->bms_psy) {
+ chip->bms_psy->get_property(chip->bms_psy,
+ POWER_SUPPLY_PROP_CURRENT_MAX, &ret);
+ return ret.intval;
+ } else {
+ pr_debug("No BMS supply registered return 0\n");
+ }
+
+ return 0;
+}
static int
get_prop_current_now(struct qpnp_chg_chip *chip)
@@ -1090,6 +1124,9 @@
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = get_prop_capacity(chip);
break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ val->intval = get_prop_current_max(chip);
+ break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = get_prop_current_now(chip);
break;
@@ -1421,7 +1458,7 @@
static void
qpnp_chg_setup_flags(struct qpnp_chg_chip *chip)
{
- if (chip->revision > 0)
+ if (chip->revision > 0 && chip->type == SMBB)
chip->flags |= CHG_FLAGS_VCP_WA;
}
@@ -1651,7 +1688,9 @@
case SMBBP_BOOST_SUBTYPE:
break;
case SMBB_MISC_SUBTYPE:
+ chip->type = SMBB;
case SMBBP_MISC_SUBTYPE:
+ chip->type = SMBBP;
pr_debug("Setting BOOT_DONE\n");
rc = qpnp_chg_masked_write(chip,
chip->misc_base + CHGR_MISC_BOOT_DONE,
@@ -2002,6 +2041,8 @@
pr_err("batt failed to register rc = %d\n", rc);
goto fail_chg_enable;
}
+ INIT_WORK(&chip->adc_measure_work,
+ qpnp_bat_if_adc_measure_work);
}
if (chip->dc_chgpth_base) {
@@ -2082,6 +2123,8 @@
&& chip->batt_present) {
qpnp_adc_tm_disable_chan_meas(&chip->adc_param);
}
+ cancel_work_sync(&chip->adc_measure_work);
+
dev_set_drvdata(&spmi->dev, NULL);
kfree(chip);
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index 10c69c3..63d3750 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -761,7 +761,13 @@
pr_info("ADSP P.C. CTRL state:%d NGD not enumerated:0x%x",
dev->state, laddr);
}
-
+ /* ADSP SSR scenario, need to disconnect pipe before connecting */
+ if (dev->use_rx_msgqs == MSM_MSGQ_DOWN) {
+ struct msm_slim_endp *endpoint = &dev->rx_msgq;
+ sps_disconnect(endpoint->sps);
+ sps_free_endpoint(endpoint->sps);
+ dev->use_rx_msgqs = MSM_MSGQ_RESET;
+ }
/*
* ADSP power collapse case (OR SSR), where HW was reset
* BAM programming will happen when capability message is received
@@ -911,6 +917,8 @@
ngd_slim_enable(dev, false);
/* disconnect BAM pipes */
+ if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED)
+ dev->use_rx_msgqs = MSM_MSGQ_DOWN;
msm_slim_sps_exit(dev, false);
mutex_lock(&ctrl->m_ctrl);
/* device up should be called again after SSR */
diff --git a/drivers/slimbus/slim-msm.c b/drivers/slimbus/slim-msm.c
index 3e19f9b..30341e2 100644
--- a/drivers/slimbus/slim-msm.c
+++ b/drivers/slimbus/slim-msm.c
@@ -581,7 +581,7 @@
void msm_slim_sps_exit(struct msm_slim_ctrl *dev, bool dereg)
{
- if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED) {
+ if (dev->use_rx_msgqs >= MSM_MSGQ_ENABLED) {
struct msm_slim_endp *endpoint = &dev->rx_msgq;
struct sps_connect *config = &endpoint->config;
struct sps_mem_buffer *descr = &config->desc;
@@ -590,10 +590,12 @@
memset(&sps_event, 0x00, sizeof(sps_event));
msm_slim_sps_mem_free(dev, mem);
sps_register_event(endpoint->sps, &sps_event);
- sps_disconnect(endpoint->sps);
+ if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED) {
+ sps_disconnect(endpoint->sps);
+ msm_slim_free_endpoint(endpoint);
+ dev->use_rx_msgqs = MSM_MSGQ_RESET;
+ }
msm_slim_sps_mem_free(dev, descr);
- msm_slim_free_endpoint(endpoint);
- dev->use_rx_msgqs = MSM_MSGQ_RESET;
}
if (dereg) {
sps_deregister_bam_device(dev->bam.hdl);
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index 6e329b3..6ff3f19 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -159,6 +159,7 @@
MSM_MSGQ_DISABLED,
MSM_MSGQ_RESET,
MSM_MSGQ_ENABLED,
+ MSM_MSGQ_DOWN,
};
struct msm_slim_sps_bam {
diff --git a/drivers/thermal/msm8960_tsens.c b/drivers/thermal/msm8960_tsens.c
index 67e0181..837ac21 100644
--- a/drivers/thermal/msm8960_tsens.c
+++ b/drivers/thermal/msm8960_tsens.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -243,6 +243,17 @@
}
EXPORT_SYMBOL(tsens_get_temp);
+int tsens_get_max_sensor_num(uint32_t *tsens_num_sensors)
+{
+ if (!tmdev)
+ return -ENODEV;
+
+ *tsens_num_sensors = tmdev->tsens_num_sensor;
+
+ return 0;
+}
+EXPORT_SYMBOL(tsens_get_max_sensor_num);
+
static int tsens_tz_get_mode(struct thermal_zone_device *thermal,
enum thermal_device_mode *mode)
{
diff --git a/drivers/thermal/msm8974-tsens.c b/drivers/thermal/msm8974-tsens.c
index f7e5eee..ee80975 100644
--- a/drivers/thermal/msm8974-tsens.c
+++ b/drivers/thermal/msm8974-tsens.c
@@ -280,10 +280,13 @@
num = ((adc_code * tmdev->tsens_factor) -
tmdev->sensor[sensor_num].offset);
den = (int) tmdev->sensor[sensor_num].slope_mul_tsens_factor;
- degc = num/den;
- if ((degc >= 0) && (num % den != 0))
- degc++;
+ if (num > 0)
+ degc = ((num + (den/2))/den);
+ else if (num < 0)
+ degc = ((num - (den/2))/den);
+ else
+ degc = num/den;
return degc;
}
@@ -344,6 +347,17 @@
}
EXPORT_SYMBOL(tsens_get_temp);
+int tsens_get_max_sensor_num(uint32_t *tsens_num_sensors)
+{
+ if (!tmdev)
+ return -ENODEV;
+
+ *tsens_num_sensors = tmdev->tsens_num_sensor;
+
+ return 0;
+}
+EXPORT_SYMBOL(tsens_get_max_sensor_num);
+
static int tsens_tz_get_mode(struct thermal_zone_device *thermal,
enum thermal_device_mode *mode)
{
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 8806004..867e218 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -1920,6 +1920,22 @@
return IRQ_HANDLED;
}
+/*
+ * Find UART device port using its port index value.
+ */
+struct uart_port *msm_hs_get_uart_port(int port_index)
+{
+ int i;
+
+ for (i = 0; i < UARTDM_NR; i++) {
+ if (q_uart_port[i].uport.line == port_index)
+ return &q_uart_port[i].uport;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(msm_hs_get_uart_port);
+
/* request to turn off uart clock once pending TX is flushed */
void msm_hs_request_clock_off(struct uart_port *uport) {
unsigned long flags;
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 435ef3b..f83794c 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -2434,10 +2434,14 @@
dev_err(&pdev->dev, "irqreq IDINT failed\n");
goto disable_hs_ldo;
}
+
local_irq_save(flags);
/* Update initial ID state */
- msm->id_state = msm->ext_xceiv.id =
+ msm->id_state =
!!irq_read_line(msm->pmic_id_irq);
+ if (msm->id_state == DWC3_ID_GROUND)
+ queue_work(system_nrt_wq,
+ &msm->id_work);
local_irq_restore(flags);
enable_irq_wake(msm->pmic_id_irq);
}
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index e0255ce..6fca910 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -66,7 +66,8 @@
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
#include <linux/usb/msm_hsusb.h>
-
+#include <linux/tracepoint.h>
+#include <mach/usb_trace.h>
#include "ci13xxx_udc.h"
/* Turns on streaming. overrides CI13XXX_DISABLE_STREAMING */
@@ -139,6 +140,21 @@
return n ? n-1 : 32;
}
+struct ci13xxx_ebi_err_entry {
+ u32 *usb_req_buf;
+ u32 usb_req_length;
+ u32 ep_info;
+ struct ci13xxx_ebi_err_entry *next;
+};
+
+struct ci13xxx_ebi_err_data {
+ u32 ebi_err_addr;
+ u32 apkt0;
+ u32 apkt1;
+ struct ci13xxx_ebi_err_entry *ebi_err_entry;
+};
+static struct ci13xxx_ebi_err_data *ebi_err_data;
+
/******************************************************************************
* HW block
*****************************************************************************/
@@ -1738,6 +1754,72 @@
return 0;
}
+static void dump_usb_info(void *ignore, unsigned int ebi_addr,
+ unsigned int ebi_apacket0, unsigned int ebi_apacket1)
+{
+ struct ci13xxx *udc = _udc;
+ unsigned long flags;
+ struct list_head *ptr = NULL;
+ struct ci13xxx_req *req = NULL;
+ struct ci13xxx_ep *mEp;
+ unsigned i;
+ struct ci13xxx_ebi_err_entry *temp_dump;
+ static int count;
+ u32 epdir = 0;
+
+ if (count)
+ return;
+ count++;
+
+ pr_info("%s: USB EBI error detected\n", __func__);
+
+ ebi_err_data = kmalloc(sizeof(struct ci13xxx_ebi_err_data),
+ GFP_ATOMIC);
+ if (!ebi_err_data) {
+ pr_err("%s: memory alloc failed for ebi_err_data\n", __func__);
+ return;
+ }
+
+ ebi_err_data->ebi_err_entry = kmalloc(
+ sizeof(struct ci13xxx_ebi_err_entry),
+ GFP_ATOMIC);
+ if (!ebi_err_data->ebi_err_entry) {
+ kfree(ebi_err_data);
+ pr_err("%s: memory alloc failed for ebi_err_entry\n", __func__);
+ return;
+ }
+
+ ebi_err_data->ebi_err_addr = ebi_addr;
+ ebi_err_data->apkt0 = ebi_apacket0;
+ ebi_err_data->apkt1 = ebi_apacket1;
+
+ temp_dump = ebi_err_data->ebi_err_entry;
+ pr_info("\n DUMPING USB Requests Information\n");
+ spin_lock_irqsave(udc->lock, flags);
+ for (i = 0; i < hw_ep_max; i++) {
+ list_for_each(ptr, &udc->ci13xxx_ep[i].qh.queue) {
+ mEp = &udc->ci13xxx_ep[i];
+ req = list_entry(ptr, struct ci13xxx_req, queue);
+
+ temp_dump->usb_req_buf = req->req.buf;
+ temp_dump->usb_req_length = req->req.length;
+ epdir = mEp->dir;
+ temp_dump->ep_info = mEp->num | (epdir << 15);
+
+ temp_dump->next = kmalloc(
+ sizeof(struct ci13xxx_ebi_err_entry),
+ GFP_ATOMIC);
+ if (!temp_dump->next) {
+ pr_err("%s: memory alloc failed\n", __func__);
+ spin_unlock_irqrestore(udc->lock, flags);
+ return;
+ }
+ temp_dump = temp_dump->next;
+ }
+ }
+ spin_unlock_irqrestore(udc->lock, flags);
+}
+
/******************************************************************************
* UTIL block
*****************************************************************************/
@@ -3692,6 +3774,11 @@
pm_runtime_no_callbacks(&udc->gadget.dev);
pm_runtime_enable(&udc->gadget.dev);
+ retval = register_trace_usb_daytona_invalid_access(dump_usb_info,
+ NULL);
+ if (retval)
+ pr_err("Registering trace failed\n");
+
_udc = udc;
return retval;
@@ -3725,11 +3812,17 @@
static void udc_remove(void)
{
struct ci13xxx *udc = _udc;
+ int retval;
if (udc == NULL) {
err("EINVAL");
return;
}
+ retval = unregister_trace_usb_daytona_invalid_access(dump_usb_info,
+ NULL);
+ if (retval)
+ pr_err("Unregistering trace failed\n");
+
usb_del_gadget_udc(&udc->gadget);
if (udc->transceiver) {
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index e415a95..e8d7489 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -3273,10 +3273,10 @@
pr_debug("pipe->flags 0x%x\n", pipe->flags);
if (pipe->flags & MDP_SECURE_OVERLAY_SESSION) {
mfd->mem_hid &= ~BIT(ION_IOMMU_HEAP_ID);
- mfd->mem_hid |= ION_SECURE;
+ mfd->mem_hid |= ION_FLAG_SECURE;
} else {
mfd->mem_hid |= BIT(ION_IOMMU_HEAP_ID);
- mfd->mem_hid &= ~ION_SECURE;
+ mfd->mem_hid &= ~ION_FLAG_SECURE;
}
}
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
index 2423de5..f8b7f2f 100644
--- a/drivers/video/msm/mdp4_util.c
+++ b/drivers/video/msm/mdp4_util.c
@@ -2314,7 +2314,7 @@
pr_err("ion_map_iommu() read failed\n");
return -ENOMEM;
}
- if (mfd->mem_hid & ION_SECURE) {
+ if (mfd->mem_hid & ION_FLAG_SECURE) {
if (ion_phys(mfd->iclient, buf->ihdl,
&addr, (size_t *)&len)) {
pr_err("%s:%d: ion_phys map failed\n",
@@ -2377,7 +2377,7 @@
if (!IS_ERR_OR_NULL(mfd->iclient)) {
if (!IS_ERR_OR_NULL(buf->ihdl)) {
if (mdp_iommu_split_domain) {
- if (!(mfd->mem_hid & ION_SECURE))
+ if (!(mfd->mem_hid & ION_FLAG_SECURE))
ion_unmap_iommu(mfd->iclient, buf->ihdl,
DISPLAY_WRITE_DOMAIN, GEN_POOL);
ion_unmap_iommu(mfd->iclient, buf->ihdl,
diff --git a/drivers/video/msm/mdss/Makefile b/drivers/video/msm/mdss/Makefile
index d09c03a..2c58e49 100644
--- a/drivers/video/msm/mdss/Makefile
+++ b/drivers/video/msm/mdss/Makefile
@@ -15,6 +15,9 @@
obj-$(CONFIG_DEBUG_FS) += mdss_debug.o
endif
+dsi-v2-objs = dsi_v2.o dsi_host_v2.o dsi_io_v2.o dsi_panel_v2.o
+obj-$(CONFIG_FB_MSM_MDSS) += dsi-v2.o
+
mdss-dsi-objs := mdss_dsi.o mdss_dsi_host.o
mdss-dsi-objs += mdss_dsi_panel.o
mdss-dsi-objs += msm_mdss_io_8974.o
@@ -25,8 +28,9 @@
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_tx.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_util.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_edid.o
-obj-$(CONFIG_FB_MSM_MDSS_HDMI_MHL_SII8334) += mhl_sii8334.o mhl_msc.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_hdcp.o
+obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_cec.o
+obj-$(CONFIG_FB_MSM_MDSS_HDMI_MHL_SII8334) += mhl_sii8334.o mhl_msc.o
obj-$(CONFIG_FB_MSM_MDSS_WRITEBACK) += mdss_wb.o
diff --git a/drivers/video/msm/mdss/dsi_host_v2.c b/drivers/video/msm/mdss/dsi_host_v2.c
new file mode 100644
index 0000000..453cbaa
--- /dev/null
+++ b/drivers/video/msm/mdss/dsi_host_v2.c
@@ -0,0 +1,1034 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/iopoll.h>
+#include <linux/interrupt.h>
+#include <linux/of_device.h>
+
+#include "dsi_v2.h"
+#include "dsi_io_v2.h"
+#include "dsi_host_v2.h"
+
+#define DSI_POLL_SLEEP_US 1000
+#define DSI_POLL_TIMEOUT_US 16000
+#define DSI_ESC_CLK_RATE 19200000
+
+struct dsi_host_v2_private {
+ struct completion dma_comp;
+ int irq_enabled;
+ spinlock_t irq_lock;
+ spinlock_t mdp_lock;
+ int mdp_busy;
+ int irq_no;
+ unsigned char *dsi_base;
+ struct device dis_dev;
+};
+
+static struct dsi_host_v2_private *dsi_host_private;
+
+int msm_dsi_init(void)
+{
+ if (!dsi_host_private) {
+ dsi_host_private = kzalloc(sizeof(struct dsi_host_v2_private),
+ GFP_KERNEL);
+ if (!dsi_host_private) {
+ pr_err("fail to alloc dsi host private data\n");
+ return -ENOMEM;
+ }
+ }
+
+ init_completion(&dsi_host_private->dma_comp);
+ spin_lock_init(&dsi_host_private->irq_lock);
+ spin_lock_init(&dsi_host_private->mdp_lock);
+ return 0;
+}
+
+void msm_dsi_deinit(void)
+{
+ kfree(dsi_host_private);
+ dsi_host_private = NULL;
+}
+
+void msm_dsi_ack_err_status(unsigned char *ctrl_base)
+{
+ u32 status;
+
+ status = MIPI_INP(ctrl_base + DSI_ACK_ERR_STATUS);
+
+ if (status) {
+ MIPI_OUTP(ctrl_base + DSI_ACK_ERR_STATUS, status);
+ pr_debug("%s: status=%x\n", __func__, status);
+ }
+}
+
+void msm_dsi_timeout_status(unsigned char *ctrl_base)
+{
+ u32 status;
+
+ status = MIPI_INP(ctrl_base + DSI_TIMEOUT_STATUS);
+ if (status & 0x0111) {
+ MIPI_OUTP(ctrl_base + DSI_TIMEOUT_STATUS, status);
+ pr_debug("%s: status=%x\n", __func__, status);
+ }
+}
+
+void msm_dsi_dln0_phy_err(unsigned char *ctrl_base)
+{
+ u32 status;
+
+ status = MIPI_INP(ctrl_base + DSI_DLN0_PHY_ERR);
+
+ if (status & 0x011111) {
+ MIPI_OUTP(ctrl_base + DSI_DLN0_PHY_ERR, status);
+ pr_debug("%s: status=%x\n", __func__, status);
+ }
+}
+
+void msm_dsi_fifo_status(unsigned char *ctrl_base)
+{
+ u32 status;
+
+ status = MIPI_INP(ctrl_base + DSI_FIFO_STATUS);
+
+ if (status & 0x44444489) {
+ MIPI_OUTP(ctrl_base + DSI_FIFO_STATUS, status);
+ pr_debug("%s: status=%x\n", __func__, status);
+ }
+}
+
+void msm_dsi_status(unsigned char *ctrl_base)
+{
+ u32 status;
+
+ status = MIPI_INP(ctrl_base + DSI_STATUS);
+
+ if (status & 0x80000000) {
+ MIPI_OUTP(ctrl_base + DSI_STATUS, status);
+ pr_debug("%s: status=%x\n", __func__, status);
+ }
+}
+
+void msm_dsi_error(unsigned char *ctrl_base)
+{
+ msm_dsi_ack_err_status(ctrl_base);
+ msm_dsi_timeout_status(ctrl_base);
+ msm_dsi_fifo_status(ctrl_base);
+ msm_dsi_status(ctrl_base);
+ msm_dsi_dln0_phy_err(ctrl_base);
+}
+
+void msm_dsi_enable_irq(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dsi_host_private->irq_lock, flags);
+ if (dsi_host_private->irq_enabled) {
+ pr_debug("%s: IRQ aleady enabled\n", __func__);
+ spin_unlock_irqrestore(&dsi_host_private->irq_lock, flags);
+ return;
+ }
+
+ enable_irq(dsi_host_private->irq_no);
+ dsi_host_private->irq_enabled = 1;
+ spin_unlock_irqrestore(&dsi_host_private->irq_lock, flags);
+}
+
+void msm_dsi_disable_irq(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dsi_host_private->irq_lock, flags);
+ if (dsi_host_private->irq_enabled == 0) {
+ pr_debug("%s: IRQ already disabled\n", __func__);
+ spin_unlock_irqrestore(&dsi_host_private->irq_lock, flags);
+ return;
+ }
+ disable_irq(dsi_host_private->irq_no);
+ dsi_host_private->irq_enabled = 0;
+ spin_unlock_irqrestore(&dsi_host_private->irq_lock, flags);
+}
+
+void msm_dsi_disable_irq_nosync(void)
+{
+ spin_lock(&dsi_host_private->irq_lock);
+ if (dsi_host_private->irq_enabled == 0) {
+ pr_debug("%s: IRQ cannot be disabled\n", __func__);
+ spin_unlock(&dsi_host_private->irq_lock);
+ return;
+ }
+ disable_irq_nosync(dsi_host_private->irq_no);
+ dsi_host_private->irq_enabled = 0;
+ spin_unlock(&dsi_host_private->irq_lock);
+}
+
+irqreturn_t msm_dsi_isr(int irq, void *ptr)
+{
+ u32 isr;
+
+ isr = MIPI_INP(dsi_host_private->dsi_base + DSI_INT_CTRL);
+ MIPI_OUTP(dsi_host_private->dsi_base + DSI_INT_CTRL, isr);
+
+ if (isr & DSI_INTR_ERROR)
+ msm_dsi_error(dsi_host_private->dsi_base);
+
+ if (isr & DSI_INTR_CMD_DMA_DONE)
+ complete(&dsi_host_private->dma_comp);
+
+ if (isr & DSI_INTR_CMD_MDP_DONE) {
+ spin_lock(&dsi_host_private->mdp_lock);
+ dsi_host_private->mdp_busy = false;
+ msm_dsi_disable_irq_nosync();
+ spin_unlock(&dsi_host_private->mdp_lock);
+ }
+
+ return IRQ_HANDLED;
+}
+
+int msm_dsi_irq_init(struct device *dev, int irq_no)
+{
+ int ret;
+
+ ret = devm_request_irq(dev, irq_no, msm_dsi_isr,
+ IRQF_DISABLED, "DSI", NULL);
+ if (ret) {
+ pr_err("msm_dsi_irq_init request_irq() failed!\n");
+ return ret;
+ }
+ dsi_host_private->irq_no = irq_no;
+ disable_irq(irq_no);
+ return 0;
+}
+
+void msm_dsi_host_init(struct mipi_panel_info *pinfo)
+{
+ u32 dsi_ctrl, intr_ctrl, data;
+ unsigned char *ctrl_base = dsi_host_private->dsi_base;
+
+ pr_debug("msm_dsi_host_init\n");
+ pinfo->rgb_swap = DSI_RGB_SWAP_RGB;
+
+ if (pinfo->mode == DSI_VIDEO_MODE) {
+ data = 0;
+ if (pinfo->pulse_mode_hsa_he)
+ data |= BIT(28);
+ if (pinfo->hfp_power_stop)
+ data |= BIT(24);
+ if (pinfo->hbp_power_stop)
+ data |= BIT(20);
+ if (pinfo->hsa_power_stop)
+ data |= BIT(16);
+ if (pinfo->eof_bllp_power_stop)
+ data |= BIT(15);
+ if (pinfo->bllp_power_stop)
+ data |= BIT(12);
+ data |= ((pinfo->traffic_mode & 0x03) << 8);
+ data |= ((pinfo->dst_format & 0x03) << 4); /* 2 bits */
+ data |= (pinfo->vc & 0x03);
+ MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_CTRL, data);
+
+ data = 0;
+ data |= ((pinfo->rgb_swap & 0x07) << 12);
+ if (pinfo->b_sel)
+ data |= BIT(8);
+ if (pinfo->g_sel)
+ data |= BIT(4);
+ if (pinfo->r_sel)
+ data |= BIT(0);
+ MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_DATA_CTRL, data);
+ } else if (pinfo->mode == DSI_CMD_MODE) {
+ data = 0;
+ data |= ((pinfo->interleave_max & 0x0f) << 20);
+ data |= ((pinfo->rgb_swap & 0x07) << 16);
+ if (pinfo->b_sel)
+ data |= BIT(12);
+ if (pinfo->g_sel)
+ data |= BIT(8);
+ if (pinfo->r_sel)
+ data |= BIT(4);
+ data |= (pinfo->dst_format & 0x0f); /* 4 bits */
+ MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_MDP_CTRL, data);
+
+ /* DSI_COMMAND_MODE_MDP_DCS_CMD_CTRL */
+ data = pinfo->wr_mem_continue & 0x0ff;
+ data <<= 8;
+ data |= (pinfo->wr_mem_start & 0x0ff);
+ if (pinfo->insert_dcs_cmd)
+ data |= BIT(16);
+ MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_MDP_DCS_CMD_CTRL,
+ data);
+ } else
+ pr_err("%s: Unknown DSI mode=%d\n", __func__, pinfo->mode);
+
+ dsi_ctrl = BIT(8) | BIT(2); /* clock enable & cmd mode */
+ intr_ctrl = 0;
+ intr_ctrl = (DSI_INTR_CMD_DMA_DONE_MASK | DSI_INTR_CMD_MDP_DONE_MASK);
+
+ if (pinfo->crc_check)
+ dsi_ctrl |= BIT(24);
+ if (pinfo->ecc_check)
+ dsi_ctrl |= BIT(20);
+ if (pinfo->data_lane3)
+ dsi_ctrl |= BIT(7);
+ if (pinfo->data_lane2)
+ dsi_ctrl |= BIT(6);
+ if (pinfo->data_lane1)
+ dsi_ctrl |= BIT(5);
+ if (pinfo->data_lane0)
+ dsi_ctrl |= BIT(4);
+
+ /* from frame buffer, low power mode */
+ /* DSI_COMMAND_MODE_DMA_CTRL */
+ MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_DMA_CTRL, 0x14000000);
+
+ data = 0;
+ if (pinfo->te_sel)
+ data |= BIT(31);
+ data |= pinfo->mdp_trigger << 4;/* cmd mdp trigger */
+ data |= pinfo->dma_trigger; /* cmd dma trigger */
+ data |= (pinfo->stream & 0x01) << 8;
+ MIPI_OUTP(ctrl_base + DSI_TRIG_CTRL, data);
+
+ /* DSI_LAN_SWAP_CTRL */
+ MIPI_OUTP(ctrl_base + DSI_LANE_SWAP_CTRL, pinfo->dlane_swap);
+
+ /* clock out ctrl */
+ data = pinfo->t_clk_post & 0x3f; /* 6 bits */
+ data <<= 8;
+ data |= pinfo->t_clk_pre & 0x3f; /* 6 bits */
+ /* DSI_CLKOUT_TIMING_CTRL */
+ MIPI_OUTP(ctrl_base + DSI_CLKOUT_TIMING_CTRL, data);
+
+ data = 0;
+ if (pinfo->rx_eot_ignore)
+ data |= BIT(4);
+ if (pinfo->tx_eot_append)
+ data |= BIT(0);
+ MIPI_OUTP(ctrl_base + DSI_EOT_PACKET_CTRL, data);
+
+
+ /* allow only ack-err-status to generate interrupt */
+ /* DSI_ERR_INT_MASK0 */
+ MIPI_OUTP(ctrl_base + DSI_ERR_INT_MASK0, 0x13ff3fe0);
+
+ intr_ctrl |= DSI_INTR_ERROR_MASK;
+ MIPI_OUTP(ctrl_base + DSI_INT_CTRL, intr_ctrl);
+
+ /* turn esc, byte, dsi, pclk, sclk, hclk on */
+ MIPI_OUTP(ctrl_base + DSI_CLK_CTRL, 0x23f);
+
+ dsi_ctrl |= BIT(0); /* enable dsi */
+ MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl);
+
+ wmb();
+}
+
+void msm_dsi_set_tx_power_mode(int mode)
+{
+ u32 data;
+ unsigned char *ctrl_base = dsi_host_private->dsi_base;
+
+ data = MIPI_INP(ctrl_base + DSI_COMMAND_MODE_DMA_CTRL);
+
+ if (mode == 0)
+ data &= ~BIT(26);
+ else
+ data |= BIT(26);
+
+ MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_DMA_CTRL, data);
+}
+
+void msm_dsi_sw_reset(void)
+{
+ u32 dsi_ctrl;
+ unsigned char *ctrl_base = dsi_host_private->dsi_base;
+
+ pr_debug("msm_dsi_sw_reset\n");
+
+ dsi_ctrl = MIPI_INP(ctrl_base + DSI_CTRL);
+ dsi_ctrl &= ~0x01;
+ MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl);
+ wmb();
+
+ /* turn esc, byte, dsi, pclk, sclk, hclk on */
+ MIPI_OUTP(ctrl_base + DSI_CLK_CTRL, 0x23f);
+ wmb();
+
+ MIPI_OUTP(ctrl_base + DSI_SOFT_RESET, 0x01);
+ wmb();
+ MIPI_OUTP(ctrl_base + DSI_SOFT_RESET, 0x00);
+ wmb();
+}
+
+void msm_dsi_controller_cfg(int enable)
+{
+ u32 dsi_ctrl, status;
+ unsigned char *ctrl_base = dsi_host_private->dsi_base;
+
+ pr_debug("msm_dsi_controller_cfg\n");
+
+ /* Check for CMD_MODE_DMA_BUSY */
+ if (readl_poll_timeout((ctrl_base + DSI_STATUS),
+ status,
+ ((status & 0x02) == 0),
+ DSI_POLL_SLEEP_US, DSI_POLL_TIMEOUT_US))
+ pr_err("%s: DSI status=%x failed\n", __func__, status);
+
+ /* Check for x_HS_FIFO_EMPTY */
+ if (readl_poll_timeout((ctrl_base + DSI_FIFO_STATUS),
+ status,
+ ((status & 0x11111000) == 0x11111000),
+ DSI_POLL_SLEEP_US, DSI_POLL_TIMEOUT_US))
+ pr_err("%s: FIFO status=%x failed\n", __func__, status);
+
+ /* Check for VIDEO_MODE_ENGINE_BUSY */
+ if (readl_poll_timeout((ctrl_base + DSI_STATUS),
+ status,
+ ((status & 0x08) == 0),
+ DSI_POLL_SLEEP_US, DSI_POLL_TIMEOUT_US)) {
+ pr_err("%s: DSI status=%x\n", __func__, status);
+ pr_err("%s: Doing sw reset\n", __func__);
+ msm_dsi_sw_reset();
+ }
+
+ dsi_ctrl = MIPI_INP(ctrl_base + DSI_CTRL);
+ if (enable)
+ dsi_ctrl |= 0x01;
+ else
+ dsi_ctrl &= ~0x01;
+
+ MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl);
+ wmb();
+}
+
+void msm_dsi_op_mode_config(int mode, struct mdss_panel_data *pdata)
+{
+ u32 dsi_ctrl, intr_ctrl;
+ unsigned char *ctrl_base = dsi_host_private->dsi_base;
+
+ pr_debug("msm_dsi_op_mode_config\n");
+
+ dsi_ctrl = MIPI_INP(ctrl_base + DSI_CTRL);
+ /*If Video enabled, Keep Video and Cmd mode ON */
+ if (dsi_ctrl & 0x02)
+ dsi_ctrl &= ~0x05;
+ else
+ dsi_ctrl &= ~0x07;
+
+ if (mode == DSI_VIDEO_MODE) {
+ dsi_ctrl |= 0x03;
+ intr_ctrl = DSI_INTR_CMD_DMA_DONE_MASK;
+ } else { /* command mode */
+ dsi_ctrl |= 0x05;
+ if (pdata->panel_info.type == MIPI_VIDEO_PANEL)
+ dsi_ctrl |= 0x02;
+
+ intr_ctrl = DSI_INTR_CMD_DMA_DONE_MASK | DSI_INTR_ERROR_MASK |
+ DSI_INTR_CMD_MDP_DONE_MASK;
+ }
+
+ pr_debug("%s: dsi_ctrl=%x intr=%x\n", __func__, dsi_ctrl, intr_ctrl);
+
+ MIPI_OUTP(ctrl_base + DSI_INT_CTRL, intr_ctrl);
+ MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl);
+ wmb();
+}
+
+void msm_dsi_cmd_mdp_start(void)
+{
+ unsigned long flag;
+
+ spin_lock_irqsave(&dsi_host_private->mdp_lock, flag);
+ msm_dsi_enable_irq();
+ dsi_host_private->mdp_busy = true;
+ spin_unlock_irqrestore(&dsi_host_private->mdp_lock, flag);
+}
+
+int msm_dsi_cmd_reg_tx(u32 data)
+{
+ unsigned char *ctrl_base = dsi_host_private->dsi_base;
+
+ MIPI_OUTP(ctrl_base + DSI_TRIG_CTRL, 0x04);/* sw trigger */
+ MIPI_OUTP(ctrl_base + DSI_CTRL, 0x135);
+ wmb();
+
+ MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_DMA_CTRL, data);
+ wmb();
+ MIPI_OUTP(ctrl_base + DSI_CMD_MODE_DMA_SW_TRIGGER, 0x01);
+ wmb();
+
+ udelay(300); /*per spec*/
+
+ return 0;
+}
+
+int msm_dsi_cmd_dma_tx(struct dsi_buf *tp)
+{
+ int len;
+ unsigned long size, addr;
+ unsigned char *ctrl_base = dsi_host_private->dsi_base;
+
+ len = ALIGN(tp->len, 4);
+ size = ALIGN(tp->len, SZ_4K);
+
+ tp->dmap = dma_map_single(&dsi_host_private->dis_dev, tp->data, size,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(&dsi_host_private->dis_dev, tp->dmap)) {
+ pr_err("%s: dmap mapp failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ addr = tp->dmap;
+
+ INIT_COMPLETION(dsi_host_private->dma_comp);
+
+ MIPI_OUTP(ctrl_base + DSI_DMA_CMD_OFFSET, addr);
+ MIPI_OUTP(ctrl_base + DSI_DMA_CMD_LENGTH, len);
+ wmb();
+
+ MIPI_OUTP(ctrl_base + DSI_CMD_MODE_DMA_SW_TRIGGER, 0x01);
+ wmb();
+
+ wait_for_completion_interruptible(&dsi_host_private->dma_comp);
+
+ dma_unmap_single(&dsi_host_private->dis_dev, tp->dmap, size,
+ DMA_TO_DEVICE);
+ tp->dmap = 0;
+ return 0;
+}
+
+int msm_dsi_cmd_dma_rx(struct dsi_buf *rp, int rlen)
+{
+ u32 *lp, data;
+ int i, off, cnt;
+ unsigned char *ctrl_base = dsi_host_private->dsi_base;
+
+ lp = (u32 *)rp->data;
+ cnt = rlen;
+ cnt += 3;
+ cnt >>= 2;
+
+ if (cnt > 4)
+ cnt = 4; /* 4 x 32 bits registers only */
+
+ off = DSI_RDBK_DATA0;
+ off += ((cnt - 1) * 4);
+
+ for (i = 0; i < cnt; i++) {
+ data = (u32)MIPI_INP(ctrl_base + off);
+ *lp++ = ntohl(data); /* to network byte order */
+ pr_debug("%s: data = 0x%x and ntohl(data) = 0x%x\n",
+ __func__, data, ntohl(data));
+ off -= 4;
+ rp->len += sizeof(*lp);
+ }
+
+ return 0;
+}
+
+int msm_dsi_cmds_tx(struct mdss_panel_data *pdata,
+ struct dsi_buf *tp, struct dsi_cmd_desc *cmds, int cnt)
+{
+ struct dsi_cmd_desc *cm;
+ u32 dsi_ctrl, ctrl;
+ int i, video_mode;
+ unsigned long flag;
+ unsigned char *ctrl_base = dsi_host_private->dsi_base;
+
+ /* turn on cmd mode
+ * for video mode, do not send cmds more than
+ * one pixel line, since it only transmit it
+ * during BLLP.
+ */
+ dsi_ctrl = MIPI_INP(ctrl_base + DSI_CTRL);
+ video_mode = dsi_ctrl & 0x02; /* VIDEO_MODE_EN */
+ if (video_mode) {
+ ctrl = dsi_ctrl | 0x04; /* CMD_MODE_EN */
+ MIPI_OUTP(ctrl_base + DSI_CTRL, ctrl);
+ }
+
+ spin_lock_irqsave(&dsi_host_private->mdp_lock, flag);
+ msm_dsi_enable_irq();
+ dsi_host_private->mdp_busy = true;
+ spin_unlock_irqrestore(&dsi_host_private->mdp_lock, flag);
+
+ cm = cmds;
+ dsi_buf_init(tp);
+ for (i = 0; i < cnt; i++) {
+ dsi_buf_init(tp);
+ dsi_cmd_dma_add(tp, cm);
+ msm_dsi_cmd_dma_tx(tp);
+ if (cm->wait)
+ msleep(cm->wait);
+ cm++;
+ }
+
+ spin_lock_irqsave(&dsi_host_private->mdp_lock, flag);
+ dsi_host_private->mdp_busy = false;
+ msm_dsi_disable_irq();
+ spin_unlock_irqrestore(&dsi_host_private->mdp_lock, flag);
+
+ if (video_mode)
+ MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl);
+ return 0;
+}
+
+/* MDSS_DSI_MRPS, Maximum Return Packet Size */
+static char max_pktsize[2] = {0x00, 0x00}; /* LSB tx first, 10 bytes */
+
+static struct dsi_cmd_desc pkt_size_cmd[] = {
+ {DTYPE_MAX_PKTSIZE, 1, 0, 0, 0,
+ sizeof(max_pktsize), max_pktsize}
+};
+
+/*
+ * DSI panel reply with MAX_RETURN_PACKET_SIZE bytes of data
+ * plus DCS header, ECC and CRC for DCS long read response
+ * mdss_dsi_controller only have 4x32 bits register ( 16 bytes) to
+ * hold data per transaction.
+ * MDSS_DSI_LEN equal to 8
+ * len should be either 4 or 8
+ * any return data more than MDSS_DSI_LEN need to be break down
+ * to multiple transactions.
+ *
+ * ov_mutex need to be acquired before call this function.
+ */
+int msm_dsi_cmds_rx(struct mdss_panel_data *pdata,
+ struct dsi_buf *tp, struct dsi_buf *rp,
+ struct dsi_cmd_desc *cmds, int rlen)
+{
+ int cnt, len, diff, pkt_size;
+ unsigned long flag;
+ char cmd;
+
+ if (pdata->panel_info.mipi.no_max_pkt_size)
+ rlen = ALIGN(rlen, 4); /* Only support rlen = 4*n */
+
+ len = rlen;
+ diff = 0;
+
+ if (len <= 2) {
+ cnt = 4; /* short read */
+ } else {
+ if (len > DSI_LEN)
+ len = DSI_LEN; /* 8 bytes at most */
+
+ len = ALIGN(len, 4); /* len 4 bytes align */
+ diff = len - rlen;
+ /*
+ * add extra 2 bytes to len to have overall
+ * packet size is multipe by 4. This also make
+ * sure 4 bytes dcs headerlocates within a
+ * 32 bits register after shift in.
+ * after all, len should be either 6 or 10.
+ */
+ len += 2;
+ cnt = len + 6; /* 4 bytes header + 2 bytes crc */
+ }
+
+ spin_lock_irqsave(&dsi_host_private->mdp_lock, flag);
+ msm_dsi_enable_irq();
+ dsi_host_private->mdp_busy = true;
+ spin_unlock_irqrestore(&dsi_host_private->mdp_lock, flag);
+
+ if (!pdata->panel_info.mipi.no_max_pkt_size) {
+ /* packet size need to be set at every read */
+ pkt_size = len;
+ max_pktsize[0] = pkt_size;
+ dsi_buf_init(tp);
+ dsi_cmd_dma_add(tp, pkt_size_cmd);
+ msm_dsi_cmd_dma_tx(tp);
+ pr_debug("%s: Max packet size sent\n", __func__);
+ }
+
+ dsi_buf_init(tp);
+ dsi_cmd_dma_add(tp, cmds);
+
+ /* transmit read comamnd to client */
+ msm_dsi_cmd_dma_tx(tp);
+ /*
+ * once cmd_dma_done interrupt received,
+ * return data from client is ready and stored
+ * at RDBK_DATA register already
+ */
+ dsi_buf_init(rp);
+ if (pdata->panel_info.mipi.no_max_pkt_size) {
+ /*
+ * expect rlen = n * 4
+ * short alignement for start addr
+ */
+ rp->data += 2;
+ }
+
+ msm_dsi_cmd_dma_rx(rp, cnt);
+
+ spin_lock_irqsave(&dsi_host_private->mdp_lock, flag);
+ dsi_host_private->mdp_busy = false;
+ msm_dsi_disable_irq();
+ spin_unlock_irqrestore(&dsi_host_private->mdp_lock, flag);
+
+ if (pdata->panel_info.mipi.no_max_pkt_size) {
+ /*
+ * remove extra 2 bytes from previous
+ * rx transaction at shift register
+ * which was inserted during copy
+ * shift registers to rx buffer
+ * rx payload start from long alignment addr
+ */
+ rp->data += 2;
+ }
+
+ cmd = rp->data[0];
+ switch (cmd) {
+ case DTYPE_ACK_ERR_RESP:
+ pr_debug("%s: rx ACK_ERR_PACLAGE\n", __func__);
+ break;
+ case DTYPE_GEN_READ1_RESP:
+ case DTYPE_DCS_READ1_RESP:
+ dsi_short_read1_resp(rp);
+ break;
+ case DTYPE_GEN_READ2_RESP:
+ case DTYPE_DCS_READ2_RESP:
+ dsi_short_read2_resp(rp);
+ break;
+ case DTYPE_GEN_LREAD_RESP:
+ case DTYPE_DCS_LREAD_RESP:
+ dsi_long_read_resp(rp);
+ rp->len -= 2; /* extra 2 bytes added */
+ rp->len -= diff; /* align bytes */
+ break;
+ default:
+ pr_debug("%s: Unknown cmd received\n", __func__);
+ break;
+ }
+
+ return rp->len;
+}
+
+static int msm_dsi_cal_clk_rate(struct mdss_panel_data *pdata,
+ u32 *bitclk_rate,
+ u32 *byteclk_rate,
+ u32 *pclk_rate)
+{
+ struct mdss_panel_info *pinfo;
+ struct mipi_panel_info *mipi;
+ u32 hbp, hfp, vbp, vfp, hspw, vspw, width, height;
+ int lanes;
+
+ pinfo = &pdata->panel_info;
+ mipi = &pdata->panel_info.mipi;
+
+ hbp = pdata->panel_info.lcdc.h_back_porch;
+ hfp = pdata->panel_info.lcdc.h_front_porch;
+ vbp = pdata->panel_info.lcdc.v_back_porch;
+ vfp = pdata->panel_info.lcdc.v_front_porch;
+ hspw = pdata->panel_info.lcdc.h_pulse_width;
+ vspw = pdata->panel_info.lcdc.v_pulse_width;
+ width = pdata->panel_info.xres;
+ height = pdata->panel_info.yres;
+
+ lanes = 0;
+ if (mipi->data_lane0)
+ lanes++;
+ if (mipi->data_lane1)
+ lanes++;
+ if (mipi->data_lane2)
+ lanes++;
+ if (mipi->data_lane3)
+ lanes++;
+ if (lanes == 0)
+ return -EINVAL;
+
+ *bitclk_rate = (width + hbp + hfp + hspw) * (height + vbp + vfp + vspw);
+ *bitclk_rate *= mipi->frame_rate;
+ *bitclk_rate *= pdata->panel_info.bpp;
+ *bitclk_rate /= lanes;
+
+ *byteclk_rate = *bitclk_rate / 8;
+ *pclk_rate = *byteclk_rate * lanes * 8 / pdata->panel_info.bpp;
+
+ pr_debug("bitclk=%u, byteclk=%u, pck_=%u\n",
+ *bitclk_rate, *byteclk_rate, *pclk_rate);
+ return 0;
+}
+
+static int msm_dsi_on(struct mdss_panel_data *pdata)
+{
+ int ret = 0;
+ u32 clk_rate;
+ struct mdss_panel_info *pinfo;
+ struct mipi_panel_info *mipi;
+ u32 hbp, hfp, vbp, vfp, hspw, vspw, width, height;
+ u32 ystride, bpp, data;
+ u32 dummy_xres, dummy_yres;
+ u32 bitclk_rate = 0, byteclk_rate = 0, pclk_rate = 0;
+ unsigned char *ctrl_base = dsi_host_private->dsi_base;
+
+ pr_debug("msm_dsi_on\n");
+
+ pinfo = &pdata->panel_info;
+
+ ret = msm_dsi_regulator_enable();
+ if (ret) {
+ pr_err("%s: DSI power on failed\n", __func__);
+ return ret;
+ }
+
+ msm_dsi_ahb_ctrl(1);
+ msm_dsi_phy_sw_reset(dsi_host_private->dsi_base);
+ msm_dsi_phy_init(dsi_host_private->dsi_base, pdata);
+
+ msm_dsi_cal_clk_rate(pdata, &bitclk_rate, &byteclk_rate, &pclk_rate);
+ msm_dsi_clk_set_rate(DSI_ESC_CLK_RATE, byteclk_rate, pclk_rate);
+ msm_dsi_prepare_clocks();
+ msm_dsi_clk_enable();
+
+ clk_rate = pdata->panel_info.clk_rate;
+ clk_rate = min(clk_rate, pdata->panel_info.clk_max);
+
+ hbp = pdata->panel_info.lcdc.h_back_porch;
+ hfp = pdata->panel_info.lcdc.h_front_porch;
+ vbp = pdata->panel_info.lcdc.v_back_porch;
+ vfp = pdata->panel_info.lcdc.v_front_porch;
+ hspw = pdata->panel_info.lcdc.h_pulse_width;
+ vspw = pdata->panel_info.lcdc.v_pulse_width;
+ width = pdata->panel_info.xres;
+ height = pdata->panel_info.yres;
+
+ mipi = &pdata->panel_info.mipi;
+ if (pdata->panel_info.type == MIPI_VIDEO_PANEL) {
+ dummy_xres = pdata->panel_info.lcdc.xres_pad;
+ dummy_yres = pdata->panel_info.lcdc.yres_pad;
+
+ MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_ACTIVE_H,
+ ((hspw + hbp + width + dummy_xres) << 16 |
+ (hspw + hbp)));
+ MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_ACTIVE_V,
+ ((vspw + vbp + height + dummy_yres) << 16 |
+ (vspw + vbp)));
+ MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_TOTAL,
+ (vspw + vbp + height + dummy_yres +
+ vfp - 1) << 16 | (hspw + hbp +
+ width + dummy_xres + hfp - 1));
+
+ MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_HSYNC, (hspw << 16));
+ MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_VSYNC, 0);
+ MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_VSYNC_VPOS,
+ (vspw << 16));
+
+ } else { /* command mode */
+ if (mipi->dst_format == DSI_CMD_DST_FORMAT_RGB888)
+ bpp = 3;
+ else if (mipi->dst_format == DSI_CMD_DST_FORMAT_RGB666)
+ bpp = 3;
+ else if (mipi->dst_format == DSI_CMD_DST_FORMAT_RGB565)
+ bpp = 2;
+ else
+ bpp = 3; /* Default format set to RGB888 */
+
+ ystride = width * bpp + 1;
+
+ data = (ystride << 16) | (mipi->vc << 8) | DTYPE_DCS_LWRITE;
+ MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_MDP_STREAM0_CTRL,
+ data);
+ MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_MDP_STREAM1_CTRL,
+ data);
+
+ data = height << 16 | width;
+ MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_MDP_STREAM1_TOTAL,
+ data);
+ MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_MDP_STREAM0_TOTAL,
+ data);
+ }
+
+ msm_dsi_sw_reset();
+ msm_dsi_host_init(mipi);
+
+ if (mipi->force_clk_lane_hs) {
+ u32 tmp;
+
+ tmp = MIPI_INP(ctrl_base + DSI_LANE_CTRL);
+ tmp |= (1<<28);
+ MIPI_OUTP(ctrl_base + DSI_LANE_CTRL, tmp);
+ wmb();
+ }
+
+ msm_dsi_op_mode_config(mipi->mode, pdata);
+
+ return ret;
+}
+
+static int msm_dsi_off(struct mdss_panel_data *pdata)
+{
+ int ret = 0;
+
+ pr_debug("msm_dsi_off\n");
+ msm_dsi_clk_set_rate(0, 0, 0);
+ msm_dsi_clk_disable();
+ msm_dsi_unprepare_clocks();
+
+ /* disable DSI controller */
+ msm_dsi_controller_cfg(0);
+ msm_dsi_ahb_ctrl(0);
+
+ ret = msm_dsi_regulator_disable();
+ if (ret) {
+ pr_err("%s: Panel power off failed\n", __func__);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int __devinit msm_dsi_probe(struct platform_device *pdev)
+{
+ struct dsi_interface intf;
+ int rc = 0;
+
+ pr_debug("%s\n", __func__);
+
+ rc = msm_dsi_init();
+ if (rc)
+ return rc;
+
+ if (pdev->dev.of_node) {
+ struct resource *mdss_dsi_mres;
+ pdev->id = 0;
+ mdss_dsi_mres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mdss_dsi_mres) {
+ pr_err("%s:%d unable to get the MDSS reg resources",
+ __func__, __LINE__);
+ return -ENOMEM;
+ } else {
+ dsi_host_private->dsi_base = ioremap(
+ mdss_dsi_mres->start,
+ resource_size(mdss_dsi_mres));
+ if (!dsi_host_private->dsi_base) {
+ pr_err("%s:%d unable to remap dsi resources",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+ }
+
+ mdss_dsi_mres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!mdss_dsi_mres || mdss_dsi_mres->start == 0) {
+ pr_err("%s:%d unable to get the MDSS irq resources",
+ __func__, __LINE__);
+ rc = -ENODEV;
+ goto dsi_probe_error;
+ } else {
+ rc = msm_dsi_irq_init(&pdev->dev, mdss_dsi_mres->start);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: failed to init irq, rc=%d\n",
+ __func__, rc);
+ goto dsi_probe_error;
+ }
+ }
+
+ rc = msm_dsi_io_init(pdev);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: failed to init DSI IO, rc=%d\n",
+ __func__, rc);
+ goto dsi_probe_error;
+ }
+
+ rc = of_platform_populate(pdev->dev.of_node,
+ NULL, NULL, &pdev->dev);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: failed to add child nodes, rc=%d\n",
+ __func__, rc);
+ goto dsi_probe_error;
+ }
+
+ }
+
+ dsi_host_private->dis_dev = pdev->dev;
+ intf.on = msm_dsi_on;
+ intf.off = msm_dsi_off;
+ intf.op_mode_config = msm_dsi_op_mode_config;
+ intf.tx = msm_dsi_cmds_tx;
+ intf.rx = msm_dsi_cmds_rx;
+ intf.index = 0;
+ intf.private = NULL;
+ dsi_register_interface(&intf);
+ pr_debug("%s success\n", __func__);
+ return 0;
+dsi_probe_error:
+ if (dsi_host_private->dsi_base) {
+ iounmap(dsi_host_private->dsi_base);
+ dsi_host_private->dsi_base = NULL;
+ }
+ msm_dsi_io_deinit();
+ msm_dsi_deinit();
+ return rc;
+}
+
+static int __devexit msm_dsi_remove(struct platform_device *pdev)
+{
+ msm_dsi_disable_irq();
+ msm_dsi_io_deinit();
+ iounmap(dsi_host_private->dsi_base);
+ dsi_host_private->dsi_base = NULL;
+ msm_dsi_deinit();
+ return 0;
+}
+
+static const struct of_device_id msm_dsi_v2_dt_match[] = {
+ {.compatible = "qcom,msm-dsi-v2"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_dsi_v2_dt_match);
+
+static struct platform_driver msm_dsi_v2_driver = {
+ .probe = msm_dsi_probe,
+ .remove = __devexit_p(msm_dsi_remove),
+ .shutdown = NULL,
+ .driver = {
+ .name = "msm_dsi_v2",
+ .of_match_table = msm_dsi_v2_dt_match,
+ },
+};
+
+static int msm_dsi_v2_register_driver(void)
+{
+ return platform_driver_register(&msm_dsi_v2_driver);
+}
+
+static int __init msm_dsi_v2_driver_init(void)
+{
+ int ret;
+
+ ret = msm_dsi_v2_register_driver();
+ if (ret) {
+ pr_err("msm_dsi_v2_register_driver() failed!\n");
+ return ret;
+ }
+
+ return ret;
+}
+module_init(msm_dsi_v2_driver_init);
+
+static void __exit msm_dsi_v2_driver_cleanup(void)
+{
+ platform_driver_unregister(&msm_dsi_v2_driver);
+}
+module_exit(msm_dsi_v2_driver_cleanup);
diff --git a/drivers/video/msm/mdss/dsi_host_v2.h b/drivers/video/msm/mdss/dsi_host_v2.h
new file mode 100644
index 0000000..cec9774
--- /dev/null
+++ b/drivers/video/msm/mdss/dsi_host_v2.h
@@ -0,0 +1,169 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef DSI_HOST_V2_H
+#define DSI_HOST_V2_H
+
+#include <linux/bitops.h>
+
+#define DSI_INTR_ERROR_MASK BIT(25)
+#define DSI_INTR_ERROR BIT(24)
+#define DSI_INTR_VIDEO_DONE_MASK BIT(17)
+#define DSI_INTR_VIDEO_DONE BIT(16)
+#define DSI_INTR_CMD_MDP_DONE_MASK BIT(9)
+#define DSI_INTR_CMD_MDP_DONE BIT(8)
+#define DSI_INTR_CMD_DMA_DONE_MASK BIT(1)
+#define DSI_INTR_CMD_DMA_DONE BIT(0)
+
+#define DSI_CTRL 0x0000
+#define DSI_STATUS 0x0004
+#define DSI_FIFO_STATUS 0x0008
+#define DSI_VIDEO_MODE_CTRL 0x000C
+#define DSI_VIDEO_MODE_DATA_CTRL 0x001C
+#define DSI_VIDEO_MODE_ACTIVE_H 0x0020
+#define DSI_VIDEO_MODE_ACTIVE_V 0x0024
+#define DSI_VIDEO_MODE_TOTAL 0x0028
+#define DSI_VIDEO_MODE_HSYNC 0x002C
+#define DSI_VIDEO_MODE_VSYNC 0x0030
+#define DSI_VIDEO_MODE_VSYNC_VPOS 0x0034
+#define DSI_COMMAND_MODE_DMA_CTRL 0x0038
+#define DSI_COMMAND_MODE_MDP_CTRL 0x003C
+#define DSI_COMMAND_MODE_MDP_DCS_CMD_CTRL 0x0040
+#define DSI_DMA_CMD_OFFSET 0x0044
+#define DSI_DMA_CMD_LENGTH 0x0048
+#define DSI_DMA_FIFO_CTRL 0x004C
+#define DSI_COMMAND_MODE_MDP_STREAM0_CTRL 0x0054
+#define DSI_COMMAND_MODE_MDP_STREAM0_TOTAL 0x0058
+#define DSI_COMMAND_MODE_MDP_STREAM1_CTRL 0x005C
+#define DSI_COMMAND_MODE_MDP_STREAM1_TOTAL 0x0060
+#define DSI_ACK_ERR_STATUS 0x0064
+#define DSI_RDBK_DATA0 0x0068
+#define DSI_RDBK_DATA1 0x006C
+#define DSI_RDBK_DATA2 0x0070
+#define DSI_RDBK_DATA3 0x0074
+#define DSI_RDBK_DATATYPE0 0x0078
+#define DSI_RDBK_DATATYPE1 0x007C
+#define DSI_TRIG_CTRL 0x0080
+#define DSI_EXT_MUX 0x0084
+#define DSI_EXT_TE_PULSE_DETECT_CTRL 0x0088
+#define DSI_CMD_MODE_DMA_SW_TRIGGER 0x008C
+#define DSI_CMD_MODE_MDP_SW_TRIGGER 0x0090
+#define DSI_CMD_MODE_BTA_SW_TRIGGER 0x0094
+#define DSI_RESET_SW_TRIGGER 0x0098
+#define DSI_LANE_CTRL 0x00A8
+#define DSI_LANE_SWAP_CTRL 0x00AC
+#define DSI_DLN0_PHY_ERR 0x00B0
+#define DSI_TIMEOUT_STATUS 0x00BC
+#define DSI_CLKOUT_TIMING_CTRL 0x00C0
+#define DSI_EOT_PACKET 0x00C4
+#define DSI_EOT_PACKET_CTRL 0x00C8
+#define DSI_ERR_INT_MASK0 0x0108
+#define DSI_INT_CTRL 0x010c
+#define DSI_SOFT_RESET 0x0114
+#define DSI_CLK_CTRL 0x0118
+#define DSI_CLK_STATUS 0x011C
+#define DSI_PHY_SW_RESET 0x0128
+#define DSI_COMMAND_MODE_MDP_IDLE_CTRL 0x0190
+#define DSI_VERSION 0x01F0
+
+#define DSI_DSIPHY_PLL_CTRL_0 0x0200
+#define DSI_DSIPHY_PLL_CTRL_1 0x0204
+#define DSI_DSIPHY_PLL_CTRL_2 0x0208
+#define DSI_DSIPHY_PLL_CTRL_3 0x020C
+#define DSI_DSIPHY_PLL_CTRL_4 0x0210
+#define DSI_DSIPHY_PLL_CTRL_5 0x0214
+#define DSI_DSIPHY_PLL_CTRL_6 0x0218
+#define DSI_DSIPHY_PLL_CTRL_7 0x021C
+#define DSI_DSIPHY_PLL_CTRL_8 0x0220
+#define DSI_DSIPHY_PLL_CTRL_9 0x0224
+#define DSI_DSIPHY_PLL_CTRL_10 0x0228
+#define DSI_DSIPHY_PLL_CTRL_11 0x022C
+#define DSI_DSIPHY_PLL_CTRL_12 0x0230
+#define DSI_DSIPHY_PLL_CTRL_13 0x0234
+#define DSI_DSIPHY_PLL_CTRL_14 0x0238
+#define DSI_DSIPHY_PLL_CTRL_15 0x023C
+#define DSI_DSIPHY_PLL_CTRL_16 0x0240
+#define DSI_DSIPHY_PLL_CTRL_17 0x0244
+#define DSI_DSIPHY_PLL_CTRL_18 0x0248
+#define DSI_DSIPHY_PLL_CTRL_19 0x024C
+#define DSI_DSIPHY_ANA_CTRL0 0x0260
+#define DSI_DSIPHY_ANA_CTRL1 0x0264
+#define DSI_DSIPHY_ANA_CTRL2 0x0268
+#define DSI_DSIPHY_ANA_CTRL3 0x026C
+#define DSI_DSIPHY_ANA_CTRL4 0x0270
+#define DSI_DSIPHY_ANA_CTRL5 0x0274
+#define DSI_DSIPHY_ANA_CTRL6 0x0278
+#define DSI_DSIPHY_ANA_CTRL7 0x027C
+#define DSI_DSIPHY_PLL_RDY 0x0280
+#define DSI_DSIPHY_PLL_ANA_STATUS0 0x0294
+#define DSI_DSIPHY_PLL_ANA_STATUS1 0x0298
+#define DSI_DSIPHY_PLL_ANA_STATUS2 0x029C
+#define DSI_DSIPHY_LN0_CFG0 0x0300
+#define DSI_DSIPHY_LN0_CFG1 0x0304
+#define DSI_DSIPHY_LN0_CFG2 0x0308
+#define DSI_DSIPHY_LN1_CFG0 0x0340
+#define DSI_DSIPHY_LN1_CFG1 0x0344
+#define DSI_DSIPHY_LN1_CFG2 0x0348
+#define DSI_DSIPHY_LN2_CFG0 0x0380
+#define DSI_DSIPHY_LN2_CFG1 0x0384
+#define DSI_DSIPHY_LN2_CFG2 0x0388
+#define DSI_DSIPHY_LN3_CFG0 0x03C0
+#define DSI_DSIPHY_LN3_CFG1 0x03C4
+#define DSI_DSIPHY_LN3_CFG2 0x03C8
+#define DSI_DSIPHY_LNCK_CFG0 0x0400
+#define DSI_DSIPHY_LNCK_CFG1 0x0404
+#define DSI_DSIPHY_LNCK_CFG2 0x0408
+#define DSI_DSIPHY_TIMING_CTRL_0 0x0440
+#define DSI_DSIPHY_TIMING_CTRL_1 0x0444
+#define DSI_DSIPHY_TIMING_CTRL_2 0x0448
+#define DSI_DSIPHY_TIMING_CTRL_3 0x044C
+#define DSI_DSIPHY_TIMING_CTRL_4 0x0450
+#define DSI_DSIPHY_TIMING_CTRL_5 0x0454
+#define DSI_DSIPHY_TIMING_CTRL_6 0x0458
+#define DSI_DSIPHY_TIMING_CTRL_7 0x045C
+#define DSI_DSIPHY_TIMING_CTRL_8 0x0460
+#define DSI_DSIPHY_TIMING_CTRL_9 0x0464
+#define DSI_DSIPHY_TIMING_CTRL_10 0x0468
+#define DSI_DSIPHY_TIMING_CTRL_11 0x046C
+#define DSI_DSIPHY_CTRL_0 0x0470
+#define DSI_DSIPHY_CTRL_1 0x0474
+#define DSI_DSIPHY_CTRL_2 0x0478
+#define DSI_DSIPHY_CTRL_3 0x047C
+#define DSI_DSIPHY_STRENGTH_CTRL_0 0x0480
+#define DSI_DSIPHY_STRENGTH_CTRL_1 0x0484
+#define DSI_DSIPHY_STRENGTH_CTRL_2 0x0488
+#define DSI_DSIPHY_LDO_CNTRL 0x04B0
+#define DSI_DSIPHY_REGULATOR_CTRL_0 0x0500
+#define DSI_DSIPHY_REGULATOR_CTRL_1 0x0504
+#define DSI_DSIPHY_REGULATOR_CTRL_2 0x0508
+#define DSI_DSIPHY_REGULATOR_CTRL_3 0x050C
+#define DSI_DSIPHY_REGULATOR_CTRL_4 0x0510
+#define DSI_DSIPHY_REGULATOR_TEST 0x0514
+#define DSI_DSIPHY_REGULATOR_CAL_PWR_CFG 0x0518
+#define DSI_DSIPHY_CAL_HW_TRIGGER 0x0528
+#define DSI_DSIPHY_CAL_SW_CFG0 0x052C
+#define DSI_DSIPHY_CAL_SW_CFG1 0x0530
+#define DSI_DSIPHY_CAL_SW_CFG2 0x0534
+#define DSI_DSIPHY_CAL_HW_CFG0 0x0538
+#define DSI_DSIPHY_CAL_HW_CFG1 0x053C
+#define DSI_DSIPHY_CAL_HW_CFG2 0x0540
+#define DSI_DSIPHY_CAL_HW_CFG3 0x0544
+#define DSI_DSIPHY_CAL_HW_CFG4 0x0548
+#define DSI_DSIPHY_REGULATOR_CAL_STATUS0 0x0550
+#define DSI_DSIPHY_BIST_CTRL0 0x048C
+#define DSI_DSIPHY_BIST_CTRL1 0x0490
+#define DSI_DSIPHY_BIST_CTRL2 0x0494
+#define DSI_DSIPHY_BIST_CTRL3 0x0498
+#define DSI_DSIPHY_BIST_CTRL4 0x049C
+#define DSI_DSIPHY_BIST_CTRL5 0x04A0
+
+#endif /* DSI_HOST_V2_H */
diff --git a/drivers/video/msm/mdss/dsi_io_v2.c b/drivers/video/msm/mdss/dsi_io_v2.c
new file mode 100644
index 0000000..0486c4c
--- /dev/null
+++ b/drivers/video/msm/mdss/dsi_io_v2.c
@@ -0,0 +1,426 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+
+#include <mach/clk.h>
+
+#include "dsi_v2.h"
+#include "dsi_io_v2.h"
+#include "dsi_host_v2.h"
+
+struct msm_dsi_io_private {
+ struct regulator *vdda_vreg;
+ struct clk *dsi_byte_clk;
+ struct clk *dsi_esc_clk;
+ struct clk *dsi_pixel_clk;
+ struct clk *dsi_ahb_clk;
+ int msm_dsi_clk_on;
+ int msm_dsi_ahb_clk_on;
+};
+
+static struct msm_dsi_io_private *dsi_io_private;
+
+#define DSI_VDDA_VOLTAGE 1200000
+
+void msm_dsi_ahb_ctrl(int enable)
+{
+ if (enable) {
+ if (dsi_io_private->msm_dsi_ahb_clk_on) {
+ pr_debug("ahb clks already ON\n");
+ return;
+ }
+ clk_enable(dsi_io_private->dsi_ahb_clk);
+ dsi_io_private->msm_dsi_ahb_clk_on = 1;
+ } else {
+ if (dsi_io_private->msm_dsi_ahb_clk_on == 0) {
+ pr_debug("ahb clk already OFF\n");
+ return;
+ }
+ clk_disable(dsi_io_private->dsi_ahb_clk);
+ dsi_io_private->msm_dsi_ahb_clk_on = 0;
+ }
+}
+
+int msm_dsi_io_init(struct platform_device *dev)
+{
+ int rc;
+
+ if (!dsi_io_private) {
+ dsi_io_private = kzalloc(sizeof(struct msm_dsi_io_private),
+ GFP_KERNEL);
+ if (!dsi_io_private) {
+ pr_err("fail to alloc dsi io private data structure\n");
+ return -ENOMEM;
+ }
+ }
+
+ rc = msm_dsi_clk_init(dev);
+ if (rc) {
+ pr_err("fail to initialize DSI clock\n");
+ return rc;
+ }
+
+ rc = msm_dsi_regulator_init(dev);
+ if (rc) {
+ pr_err("fail to initialize DSI regulator\n");
+ return rc;
+ }
+ return 0;
+}
+
+void msm_dsi_io_deinit(void)
+{
+ if (dsi_io_private) {
+ msm_dsi_clk_deinit();
+ msm_dsi_regulator_deinit();
+ kfree(dsi_io_private);
+ dsi_io_private = NULL;
+ }
+}
+
+int msm_dsi_clk_init(struct platform_device *dev)
+{
+ int rc = 0;
+
+ dsi_io_private->dsi_byte_clk = clk_get(&dev->dev, "byte_clk");
+ if (IS_ERR(dsi_io_private->dsi_byte_clk)) {
+ pr_err("can't find dsi byte_clk\n");
+ rc = PTR_ERR(dsi_io_private->dsi_byte_clk);
+ dsi_io_private->dsi_byte_clk = NULL;
+ return rc;
+ }
+
+ dsi_io_private->dsi_esc_clk = clk_get(&dev->dev, "esc_clk");
+ if (IS_ERR(dsi_io_private->dsi_esc_clk)) {
+ pr_err("can't find dsi esc_clk\n");
+ rc = PTR_ERR(dsi_io_private->dsi_esc_clk);
+ dsi_io_private->dsi_esc_clk = NULL;
+ return rc;
+ }
+
+ dsi_io_private->dsi_pixel_clk = clk_get(&dev->dev, "pixel_clk");
+ if (IS_ERR(dsi_io_private->dsi_pixel_clk)) {
+ pr_err("can't find dsi pixel\n");
+ rc = PTR_ERR(dsi_io_private->dsi_pixel_clk);
+ dsi_io_private->dsi_pixel_clk = NULL;
+ return rc;
+ }
+
+ dsi_io_private->dsi_ahb_clk = clk_get(&dev->dev, "iface_clk");
+ if (IS_ERR(dsi_io_private->dsi_ahb_clk)) {
+ pr_err("can't find dsi iface_clk\n");
+ rc = PTR_ERR(dsi_io_private->dsi_ahb_clk);
+ dsi_io_private->dsi_ahb_clk = NULL;
+ return rc;
+ }
+ clk_prepare(dsi_io_private->dsi_ahb_clk);
+
+ return 0;
+}
+
+void msm_dsi_clk_deinit(void)
+{
+ if (dsi_io_private->dsi_byte_clk) {
+ clk_put(dsi_io_private->dsi_byte_clk);
+ dsi_io_private->dsi_byte_clk = NULL;
+ }
+ if (dsi_io_private->dsi_esc_clk) {
+ clk_put(dsi_io_private->dsi_esc_clk);
+ dsi_io_private->dsi_esc_clk = NULL;
+ }
+ if (dsi_io_private->dsi_pixel_clk) {
+ clk_put(dsi_io_private->dsi_pixel_clk);
+ dsi_io_private->dsi_pixel_clk = NULL;
+ }
+ if (dsi_io_private->dsi_ahb_clk) {
+ clk_unprepare(dsi_io_private->dsi_ahb_clk);
+ clk_put(dsi_io_private->dsi_ahb_clk);
+ dsi_io_private->dsi_ahb_clk = NULL;
+ }
+}
+
+int msm_dsi_prepare_clocks(void)
+{
+ clk_prepare(dsi_io_private->dsi_byte_clk);
+ clk_prepare(dsi_io_private->dsi_esc_clk);
+ clk_prepare(dsi_io_private->dsi_pixel_clk);
+ return 0;
+}
+
+int msm_dsi_unprepare_clocks(void)
+{
+ clk_unprepare(dsi_io_private->dsi_esc_clk);
+ clk_unprepare(dsi_io_private->dsi_byte_clk);
+ clk_unprepare(dsi_io_private->dsi_pixel_clk);
+ return 0;
+}
+
+int msm_dsi_clk_set_rate(unsigned long esc_rate, unsigned long byte_rate,
+ unsigned long pixel_rate)
+{
+ int rc;
+
+ rc = clk_set_rate(dsi_io_private->dsi_esc_clk, esc_rate);
+ if (rc) {
+ pr_err("dsi_esc_clk - clk_set_rate failed =%d\n", rc);
+ return rc;
+ }
+
+ rc = clk_set_rate(dsi_io_private->dsi_byte_clk, byte_rate);
+ if (rc) {
+ pr_err("dsi_byte_clk - clk_set_rate faile = %dd\n", rc);
+ return rc;
+ }
+
+ rc = clk_set_rate(dsi_io_private->dsi_pixel_clk, pixel_rate);
+ if (rc) {
+ pr_err("dsi_pixel_clk - clk_set_rate failed = %d\n", rc);
+ return rc;
+ }
+ return 0;
+}
+
+int msm_dsi_clk_enable(void)
+{
+ if (dsi_io_private->msm_dsi_clk_on) {
+ pr_debug("dsi_clks on already\n");
+ return 0;
+ }
+
+ clk_enable(dsi_io_private->dsi_esc_clk);
+ clk_enable(dsi_io_private->dsi_byte_clk);
+ clk_enable(dsi_io_private->dsi_pixel_clk);
+
+ dsi_io_private->msm_dsi_clk_on = 1;
+ return 0;
+}
+
+int msm_dsi_clk_disable(void)
+{
+ if (dsi_io_private->msm_dsi_clk_on == 0) {
+ pr_debug("mdss_dsi_clks already OFF\n");
+ return 0;
+ }
+
+ clk_disable(dsi_io_private->dsi_byte_clk);
+ clk_disable(dsi_io_private->dsi_esc_clk);
+ clk_disable(dsi_io_private->dsi_pixel_clk);
+
+ dsi_io_private->msm_dsi_clk_on = 0;
+ return 0;
+}
+
+int msm_dsi_regulator_init(struct platform_device *dev)
+{
+ int ret = 0;
+
+ dsi_io_private->vdda_vreg = devm_regulator_get(&dev->dev, "vdda");
+ if (IS_ERR(dsi_io_private->vdda_vreg)) {
+ ret = PTR_ERR(dsi_io_private->vdda_vreg);
+ pr_err("could not get vdda 8110_l4, ret=%d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_set_voltage(dsi_io_private->vdda_vreg, DSI_VDDA_VOLTAGE,
+ DSI_VDDA_VOLTAGE);
+ if (ret)
+ pr_err("vdd_io_vreg->set_voltage failed, ret=%d\n", ret);
+
+ return ret;
+}
+
+void msm_dsi_regulator_deinit(void)
+{
+ if (dsi_io_private->vdda_vreg) {
+ devm_regulator_put(dsi_io_private->vdda_vreg);
+ dsi_io_private->vdda_vreg = NULL;
+ }
+}
+
+int msm_dsi_regulator_enable(void)
+{
+ int ret;
+
+ ret = regulator_enable(dsi_io_private->vdda_vreg);
+ if (ret) {
+ pr_err("%s: Failed to enable regulator.\n", __func__);
+ return ret;
+ }
+ msleep(20); /*per DSI controller spec*/
+ return ret;
+}
+
+int msm_dsi_regulator_disable(void)
+{
+ int ret;
+
+ ret = regulator_disable(dsi_io_private->vdda_vreg);
+ if (ret) {
+ pr_err("%s: Failed to disable regulator.\n", __func__);
+ return ret;
+ }
+ wmb();
+ msleep(20); /*per DSI controller spec*/
+
+ return ret;
+}
+
+static void msm_dsi_phy_strength_init(unsigned char *ctrl_base,
+ struct mdss_dsi_phy_ctrl *pd)
+{
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_STRENGTH_CTRL_0, pd->strength[0]);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_STRENGTH_CTRL_2, pd->strength[1]);
+}
+
+static void msm_dsi_phy_ctrl_init(unsigned char *ctrl_base,
+ struct mdss_panel_data *pdata)
+{
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_CTRL_0, 0x5f);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_CTRL_3, 0x10);
+}
+
+static void msm_dsi_phy_regulator_init(unsigned char *ctrl_base,
+ struct mdss_dsi_phy_ctrl *pd)
+{
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_LDO_CNTRL, 0x04);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CTRL_0, pd->regulator[0]);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CTRL_1, pd->regulator[1]);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CTRL_2, pd->regulator[2]);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CTRL_3, pd->regulator[3]);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CTRL_4, pd->regulator[4]);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CAL_PWR_CFG,
+ pd->regulator[5]);
+
+}
+
+static int msm_dsi_phy_calibration(unsigned char *ctrl_base)
+{
+ int i = 0, term_cnt = 5000, ret = 0, cal_busy;
+
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_SW_CFG2, 0x0);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_HW_CFG1, 0x5a);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_HW_CFG3, 0x10);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_HW_CFG4, 0x01);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_HW_CFG0, 0x01);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_HW_TRIGGER, 0x01);
+ usleep_range(5000, 5000); /*per DSI controller spec*/
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_HW_TRIGGER, 0x00);
+
+ cal_busy = MIPI_INP(ctrl_base + DSI_DSIPHY_REGULATOR_CAL_STATUS0);
+ while (cal_busy & 0x10) {
+ i++;
+ if (i > term_cnt) {
+ ret = -EINVAL;
+ pr_err("msm_dsi_phy_calibration error\n");
+ break;
+ }
+ cal_busy = MIPI_INP(ctrl_base +
+ DSI_DSIPHY_REGULATOR_CAL_STATUS0);
+ }
+
+ return ret;
+}
+
+static void msm_dsi_phy_lane_init(unsigned char *ctrl_base,
+ struct mdss_dsi_phy_ctrl *pd)
+{
+ int ln, index;
+
+ /*CFG0, CFG1, CFG2, TEST_DATAPATH, TEST_STR0, TEST_STR1*/
+ for (ln = 0; ln < 5; ln++) {
+ unsigned char *off = ctrl_base + 0x0300 + (ln * 0x40);
+ index = ln * 6;
+ MIPI_OUTP(off, pd->laneCfg[index]);
+ MIPI_OUTP(off + 4, pd->laneCfg[index + 1]);
+ MIPI_OUTP(off + 8, pd->laneCfg[index + 2]);
+ MIPI_OUTP(off + 12, pd->laneCfg[index + 3]);
+ MIPI_OUTP(off + 20, pd->laneCfg[index + 4]);
+ MIPI_OUTP(off + 24, pd->laneCfg[index + 5]);
+ }
+ wmb();
+}
+
+static void msm_dsi_phy_timing_init(unsigned char *ctrl_base,
+ struct mdss_dsi_phy_ctrl *pd)
+{
+ int i, off = DSI_DSIPHY_TIMING_CTRL_0;
+ for (i = 0; i < 12; i++) {
+ MIPI_OUTP(ctrl_base + off, pd->timing[i]);
+ off += 4;
+ }
+ wmb();
+}
+
+static void msm_dsi_phy_bist_init(unsigned char *ctrl_base,
+ struct mdss_dsi_phy_ctrl *pd)
+{
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_BIST_CTRL4, pd->bistCtrl[4]);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_BIST_CTRL1, pd->bistCtrl[1]);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_BIST_CTRL0, pd->bistCtrl[0]);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_BIST_CTRL4, 0);
+ wmb();
+}
+
+int msm_dsi_phy_init(unsigned char *ctrl_base,
+ struct mdss_panel_data *pdata)
+{
+ struct mdss_dsi_phy_ctrl *pd;
+
+ pd = pdata->panel_info.mipi.dsi_phy_db;
+
+ msm_dsi_phy_strength_init(ctrl_base, pd);
+
+ msm_dsi_phy_ctrl_init(ctrl_base, pdata);
+
+ msm_dsi_phy_regulator_init(ctrl_base, pd);
+
+ msm_dsi_phy_calibration(ctrl_base);
+
+ msm_dsi_phy_lane_init(ctrl_base, pd);
+
+ msm_dsi_phy_timing_init(ctrl_base, pd);
+
+ msm_dsi_phy_bist_init(ctrl_base, pd);
+
+ return 0;
+}
+
+void msm_dsi_phy_sw_reset(unsigned char *ctrl_base)
+{
+ /* start phy sw reset */
+ MIPI_OUTP(ctrl_base + DSI_PHY_SW_RESET, 0x0001);
+ udelay(1000); /*per DSI controller spec*/
+ wmb();
+ /* end phy sw reset */
+ MIPI_OUTP(ctrl_base + DSI_PHY_SW_RESET, 0x0000);
+ udelay(100); /*per DSI controller spec*/
+ wmb();
+}
+
+void msm_dsi_phy_enable(unsigned char *ctrl_base, int on)
+{
+ if (on) {
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_PLL_CTRL_5, 0x050);
+ } else {
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_PLL_CTRL_5, 0x05f);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CTRL_0, 0x02);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_CTRL_0, 0x00);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_CTRL_1, 0x7f);
+ MIPI_OUTP(ctrl_base + DSI_CLK_CTRL, 0);
+ }
+}
diff --git a/drivers/video/msm/mdss/dsi_io_v2.h b/drivers/video/msm/mdss/dsi_io_v2.h
new file mode 100644
index 0000000..25ecd7f
--- /dev/null
+++ b/drivers/video/msm/mdss/dsi_io_v2.h
@@ -0,0 +1,52 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef DSI_IO_V2_H
+#define DSI_IO_V2_H
+
+#include "mdss_panel.h"
+
+void msm_dsi_ahb_ctrl(int enable);
+
+int msm_dsi_io_init(struct platform_device *dev);
+
+void msm_dsi_io_deinit(void);
+
+int msm_dsi_clk_init(struct platform_device *dev);
+
+void msm_dsi_clk_deinit(void);
+
+int msm_dsi_prepare_clocks(void);
+
+int msm_dsi_unprepare_clocks(void);
+
+int msm_dsi_clk_set_rate(unsigned long esc_rate, unsigned long byte_rate,
+ unsigned long pixel_rate);
+
+int msm_dsi_clk_enable(void);
+
+int msm_dsi_clk_disable(void);
+
+int msm_dsi_regulator_init(struct platform_device *dev);
+
+void msm_dsi_regulator_deinit(void);
+
+int msm_dsi_regulator_enable(void);
+
+int msm_dsi_regulator_disable(void);
+
+int msm_dsi_phy_init(unsigned char *ctrl_base,
+ struct mdss_panel_data *pdata);
+
+void msm_dsi_phy_sw_reset(unsigned char *ctrl_base);
+
+#endif /* DSI_IO_V2_H */
diff --git a/drivers/video/msm/mdss/dsi_panel_v2.c b/drivers/video/msm/mdss/dsi_panel_v2.c
new file mode 100644
index 0000000..6686de3
--- /dev/null
+++ b/drivers/video/msm/mdss/dsi_panel_v2.c
@@ -0,0 +1,753 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+#include <linux/qpnp/pin.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
+#include <linux/regulator/consumer.h>
+
+#include "dsi_v2.h"
+
+#define DT_CMD_HDR 6
+
+struct dsi_panel_private {
+ struct dsi_buf dsi_panel_tx_buf;
+ struct dsi_buf dsi_panel_rx_buf;
+
+ int rst_gpio;
+ int disp_en_gpio;
+ char bl_ctrl;
+
+ struct regulator *vddio_vreg;
+ struct regulator *vdda_vreg;
+
+ struct dsi_panel_cmds_list *on_cmds_list;
+ struct dsi_panel_cmds_list *off_cmds_list;
+ struct mdss_dsi_phy_ctrl phy_params;
+};
+
+static struct dsi_panel_private *panel_private;
+
+DEFINE_LED_TRIGGER(bl_led_trigger);
+
+int dsi_panel_init(void)
+{
+ int rc;
+
+ if (!panel_private) {
+ panel_private = kzalloc(sizeof(struct dsi_panel_private),
+ GFP_KERNEL);
+ if (!panel_private) {
+ pr_err("fail to alloc dsi panel private data\n");
+ return -ENOMEM;
+ }
+ }
+
+ rc = dsi_buf_alloc(&panel_private->dsi_panel_tx_buf,
+ ALIGN(DSI_BUF_SIZE,
+ SZ_4K));
+ if (rc)
+ return rc;
+
+ rc = dsi_buf_alloc(&panel_private->dsi_panel_rx_buf,
+ ALIGN(DSI_BUF_SIZE,
+ SZ_4K));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+void dsi_panel_deinit(void)
+{
+ if (!panel_private)
+ return;
+
+ kfree(panel_private->dsi_panel_tx_buf.start);
+ kfree(panel_private->dsi_panel_rx_buf.start);
+
+ if (panel_private->vddio_vreg)
+ devm_regulator_put(panel_private->vddio_vreg);
+
+ if (panel_private->vdda_vreg)
+ devm_regulator_put(panel_private->vddio_vreg);
+
+ kfree(panel_private);
+ panel_private = NULL;
+}
+
+void dsi_panel_reset(struct mdss_panel_data *pdata, int enable)
+{
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return;
+ }
+
+ if (!gpio_is_valid(panel_private->disp_en_gpio)) {
+ pr_debug("%s:%d, reset line not configured\n",
+ __func__, __LINE__);
+ }
+
+ if (!gpio_is_valid(panel_private->rst_gpio)) {
+ pr_debug("%s:%d, reset line not configured\n",
+ __func__, __LINE__);
+ return;
+ }
+
+ pr_debug("%s: enable = %d\n", __func__, enable);
+
+ if (enable) {
+ gpio_set_value(panel_private->rst_gpio, 1);
+ /*
+ * these delay values are by experiments currently, will need
+ * to move to device tree late
+ */
+ msleep(20);
+ gpio_set_value(panel_private->rst_gpio, 0);
+ udelay(200);
+ gpio_set_value(panel_private->rst_gpio, 1);
+ msleep(20);
+ if (gpio_is_valid(panel_private->disp_en_gpio))
+ gpio_set_value(panel_private->disp_en_gpio, 1);
+ } else {
+ gpio_set_value(panel_private->rst_gpio, 0);
+ if (gpio_is_valid(panel_private->disp_en_gpio))
+ gpio_set_value(panel_private->disp_en_gpio, 0);
+ }
+}
+
+static void dsi_panel_bl_ctrl(struct mdss_panel_data *pdata,
+ u32 bl_level)
+{
+ if (panel_private->bl_ctrl) {
+ switch (panel_private->bl_ctrl) {
+ case BL_WLED:
+ led_trigger_event(bl_led_trigger, bl_level);
+ break;
+
+ default:
+ pr_err("%s: Unknown bl_ctrl configuration\n",
+ __func__);
+ break;
+ }
+ } else
+ pr_err("%s:%d, bl_ctrl not configured", __func__, __LINE__);
+}
+
+static int dsi_panel_on(struct mdss_panel_data *pdata)
+{
+ struct mipi_panel_info *mipi;
+
+ mipi = &pdata->panel_info.mipi;
+
+ pr_debug("%s:%d, debug info (mode) : %d\n", __func__, __LINE__,
+ mipi->mode);
+
+ if (mipi->mode == DSI_VIDEO_MODE) {
+ dsi_cmds_tx_v2(pdata, &panel_private->dsi_panel_tx_buf,
+ panel_private->on_cmds_list->buf,
+ panel_private->on_cmds_list->size);
+ } else {
+ pr_err("%s:%d, CMD MODE NOT SUPPORTED", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dsi_panel_off(struct mdss_panel_data *pdata)
+{
+ struct mipi_panel_info *mipi;
+ mipi = &pdata->panel_info.mipi;
+
+ pr_debug("%s:%d, debug info\n", __func__, __LINE__);
+
+ if (mipi->mode == DSI_VIDEO_MODE) {
+ dsi_cmds_tx_v2(pdata, &panel_private->dsi_panel_tx_buf,
+ panel_private->off_cmds_list->buf,
+ panel_private->off_cmds_list->size);
+ } else {
+ pr_debug("%s:%d, CMD mode not supported", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dsi_panel_parse_gpio(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ panel_private->disp_en_gpio = of_get_named_gpio(np,
+ "qcom,enable-gpio", 0);
+ panel_private->rst_gpio = of_get_named_gpio(np, "qcom,rst-gpio", 0);
+ return 0;
+}
+
+static int dsi_panel_parse_regulator(struct platform_device *pdev)
+{
+ panel_private->vddio_vreg = devm_regulator_get(&pdev->dev, "vddio");
+ panel_private->vdda_vreg = devm_regulator_get(&pdev->dev, "vdda");
+ return 0;
+}
+
+static int dsi_panel_parse_timing(struct platform_device *pdev,
+ struct dsi_panel_common_pdata *panel_data)
+{
+ struct device_node *np = pdev->dev.of_node;
+ u32 res[6], tmp;
+ int rc;
+
+ rc = of_property_read_u32_array(np, "qcom,mdss-pan-res", res, 2);
+ if (rc) {
+ pr_err("%s:%d, panel resolution not specified\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ panel_data->panel_info.xres = (!rc ? res[0] : 480);
+ panel_data->panel_info.yres = (!rc ? res[1] : 800);
+
+ rc = of_property_read_u32_array(np, "qcom,mdss-pan-active-res", res, 2);
+ if (rc == 0) {
+ panel_data->panel_info.lcdc.xres_pad =
+ panel_data->panel_info.xres - res[0];
+ panel_data->panel_info.lcdc.yres_pad =
+ panel_data->panel_info.yres - res[1];
+ }
+
+ rc = of_property_read_u32(np, "qcom,mdss-pan-bpp", &tmp);
+ if (rc) {
+ pr_err("%s:%d, panel bpp not specified\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ panel_data->panel_info.bpp = (!rc ? tmp : 24);
+
+ rc = of_property_read_u32_array(np,
+ "qcom,mdss-pan-porch-values", res, 6);
+ if (rc) {
+ pr_err("%s:%d, panel porch not specified\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ panel_data->panel_info.lcdc.h_back_porch = (!rc ? res[0] : 6);
+ panel_data->panel_info.lcdc.h_pulse_width = (!rc ? res[1] : 2);
+ panel_data->panel_info.lcdc.h_front_porch = (!rc ? res[2] : 6);
+ panel_data->panel_info.lcdc.v_back_porch = (!rc ? res[3] : 6);
+ panel_data->panel_info.lcdc.v_pulse_width = (!rc ? res[4] : 2);
+ panel_data->panel_info.lcdc.v_front_porch = (!rc ? res[5] : 6);
+
+ return 0;
+}
+
+static int dsi_panel_parse_phy(struct platform_device *pdev,
+ struct dsi_panel_common_pdata *panel_data)
+{
+ struct device_node *np = pdev->dev.of_node;
+ u32 res[6], tmp;
+ int i, len, rc;
+ const char *data;
+
+ rc = of_property_read_u32(np, "qcom,mdss-pan-dsi-mode", &tmp);
+ panel_data->panel_info.mipi.mode = (!rc ? tmp : DSI_VIDEO_MODE);
+
+ rc = of_property_read_u32(np,
+ "qcom,mdss-pan-dsi-h-pulse-mode", &tmp);
+ panel_data->panel_info.mipi.pulse_mode_hsa_he = (!rc ? tmp : false);
+
+ rc = of_property_read_u32_array(np,
+ "qcom,mdss-pan-dsi-h-power-stop", res, 3);
+ panel_data->panel_info.mipi.hbp_power_stop = (!rc ? res[0] : false);
+ panel_data->panel_info.mipi.hsa_power_stop = (!rc ? res[1] : false);
+ panel_data->panel_info.mipi.hfp_power_stop = (!rc ? res[2] : false);
+
+ rc = of_property_read_u32_array(np,
+ "qcom,mdss-pan-dsi-bllp-power-stop", res, 2);
+ panel_data->panel_info.mipi.bllp_power_stop =
+ (!rc ? res[0] : false);
+ panel_data->panel_info.mipi.eof_bllp_power_stop =
+ (!rc ? res[1] : false);
+
+ rc = of_property_read_u32(np,
+ "qcom,mdss-pan-dsi-traffic-mode", &tmp);
+ panel_data->panel_info.mipi.traffic_mode =
+ (!rc ? tmp : DSI_NON_BURST_SYNCH_PULSE);
+
+ rc = of_property_read_u32(np,
+ "qcom,mdss-pan-dsi-dst-format", &tmp);
+ panel_data->panel_info.mipi.dst_format =
+ (!rc ? tmp : DSI_VIDEO_DST_FORMAT_RGB888);
+
+ rc = of_property_read_u32(np, "qcom,mdss-pan-dsi-vc", &tmp);
+ panel_data->panel_info.mipi.vc = (!rc ? tmp : 0);
+
+ rc = of_property_read_u32(np, "qcom,mdss-pan-dsi-rgb-swap", &tmp);
+ panel_data->panel_info.mipi.rgb_swap = (!rc ? tmp : DSI_RGB_SWAP_RGB);
+
+ rc = of_property_read_u32_array(np,
+ "qcom,mdss-pan-dsi-data-lanes", res, 4);
+ panel_data->panel_info.mipi.data_lane0 = (!rc ? res[0] : true);
+ panel_data->panel_info.mipi.data_lane1 = (!rc ? res[1] : false);
+ panel_data->panel_info.mipi.data_lane2 = (!rc ? res[2] : false);
+ panel_data->panel_info.mipi.data_lane3 = (!rc ? res[3] : false);
+
+ rc = of_property_read_u32(np, "qcom,mdss-pan-dsi-dlane-swap", &tmp);
+ panel_data->panel_info.mipi.dlane_swap = (!rc ? tmp : 0);
+
+ rc = of_property_read_u32_array(np, "qcom,mdss-pan-dsi-t-clk", res, 2);
+ panel_data->panel_info.mipi.t_clk_pre = (!rc ? res[0] : 0x24);
+ panel_data->panel_info.mipi.t_clk_post = (!rc ? res[1] : 0x03);
+
+ rc = of_property_read_u32(np, "qcom,mdss-pan-dsi-stream", &tmp);
+ panel_data->panel_info.mipi.stream = (!rc ? tmp : 0);
+
+ rc = of_property_read_u32(np, "qcom,mdss-pan-dsi-mdp-tr", &tmp);
+ panel_data->panel_info.mipi.mdp_trigger =
+ (!rc ? tmp : DSI_CMD_TRIGGER_SW);
+ if (panel_data->panel_info.mipi.mdp_trigger > 6) {
+ pr_err("%s:%d, Invalid mdp trigger. Forcing to sw trigger",
+ __func__, __LINE__);
+ panel_data->panel_info.mipi.mdp_trigger =
+ DSI_CMD_TRIGGER_SW;
+ }
+
+ rc = of_property_read_u32(np, "qcom,mdss-pan-dsi-dma-tr", &tmp);
+ panel_data->panel_info.mipi.dma_trigger =
+ (!rc ? tmp : DSI_CMD_TRIGGER_SW);
+ if (panel_data->panel_info.mipi.dma_trigger > 6) {
+ pr_err("%s:%d, Invalid dma trigger. Forcing to sw trigger",
+ __func__, __LINE__);
+ panel_data->panel_info.mipi.dma_trigger =
+ DSI_CMD_TRIGGER_SW;
+ }
+
+ rc = of_property_read_u32(np, "qcom,mdss-pan-dsi-frame-rate", &tmp);
+ panel_data->panel_info.mipi.frame_rate = (!rc ? tmp : 60);
+
+ data = of_get_property(np, "qcom,panel-phy-regulatorSettings", &len);
+ if ((!data) || (len != 6)) {
+ pr_err("%s:%d, Unable to read Phy regulator settings",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ for (i = 0; i < len; i++)
+ panel_private->phy_params.regulator[i] = data[i];
+
+ data = of_get_property(np, "qcom,panel-phy-timingSettings", &len);
+ if ((!data) || (len != 12)) {
+ pr_err("%s:%d, Unable to read Phy timing settings",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ for (i = 0; i < len; i++)
+ panel_private->phy_params.timing[i] = data[i];
+
+ data = of_get_property(np, "qcom,panel-phy-strengthCtrl", &len);
+ if ((!data) || (len != 2)) {
+ pr_err("%s:%d, Unable to read Phy Strength ctrl settings",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ panel_private->phy_params.strength[0] = data[0];
+ panel_private->phy_params.strength[1] = data[1];
+
+ data = of_get_property(np, "qcom,panel-phy-bistCtrl", &len);
+ if ((!data) || (len != 6)) {
+ pr_err("%s:%d, Unable to read Phy Bist Ctrl settings",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ for (i = 0; i < len; i++)
+ panel_private->phy_params.bistCtrl[i] = data[i];
+
+ data = of_get_property(np, "qcom,panel-phy-laneConfig", &len);
+ if ((!data) || (len != 30)) {
+ pr_err("%s:%d, Unable to read Phy lane configure settings",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ for (i = 0; i < len; i++)
+ panel_private->phy_params.laneCfg[i] = data[i];
+
+ panel_data->panel_info.mipi.dsi_phy_db = &panel_private->phy_params;
+ return 0;
+}
+
+static int dsi_panel_parse_init_cmds(struct platform_device *pdev,
+ struct dsi_panel_common_pdata *panel_data)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int i, len;
+ int cmd_plen, data_offset;
+ const char *data;
+ const char *on_cmds_state, *off_cmds_state;
+ char *on_cmds = NULL, *off_cmds = NULL;
+ int num_of_on_cmds = 0, num_of_off_cmds = 0;
+
+ data = of_get_property(np, "qcom,panel-on-cmds", &len);
+ if (!data) {
+ pr_err("%s:%d, Unable to read ON cmds", __func__, __LINE__);
+ goto parse_init_cmds_error;
+ }
+
+ on_cmds = kzalloc(sizeof(char) * len, GFP_KERNEL);
+ if (!on_cmds)
+ goto parse_init_cmds_error;
+
+ memcpy(on_cmds, data, len);
+
+ data_offset = 0;
+ cmd_plen = 0;
+ while ((len - data_offset) >= DT_CMD_HDR) {
+ data_offset += (DT_CMD_HDR - 1);
+ cmd_plen = on_cmds[data_offset++];
+ data_offset += cmd_plen;
+ num_of_on_cmds++;
+ }
+ if (!num_of_on_cmds) {
+ pr_err("%s:%d, No ON cmds specified", __func__, __LINE__);
+ goto parse_init_cmds_error;
+ }
+
+ panel_data->dsi_panel_on_cmds =
+ kzalloc(sizeof(struct dsi_panel_cmds_list), GFP_KERNEL);
+ if (!panel_data->dsi_panel_on_cmds)
+ goto parse_init_cmds_error;
+
+ (panel_data->dsi_panel_on_cmds)->buf =
+ kzalloc((num_of_on_cmds * sizeof(struct dsi_cmd_desc)),
+ GFP_KERNEL);
+ if (!(panel_data->dsi_panel_on_cmds)->buf)
+ goto parse_init_cmds_error;
+
+ data_offset = 0;
+ for (i = 0; i < num_of_on_cmds; i++) {
+ panel_data->dsi_panel_on_cmds->buf[i].dtype =
+ on_cmds[data_offset++];
+ panel_data->dsi_panel_on_cmds->buf[i].last =
+ on_cmds[data_offset++];
+ panel_data->dsi_panel_on_cmds->buf[i].vc =
+ on_cmds[data_offset++];
+ panel_data->dsi_panel_on_cmds->buf[i].ack =
+ on_cmds[data_offset++];
+ panel_data->dsi_panel_on_cmds->buf[i].wait =
+ on_cmds[data_offset++];
+ panel_data->dsi_panel_on_cmds->buf[i].dlen =
+ on_cmds[data_offset++];
+ panel_data->dsi_panel_on_cmds->buf[i].payload =
+ &on_cmds[data_offset];
+ data_offset += (panel_data->dsi_panel_on_cmds->buf[i].dlen);
+ }
+
+ if (data_offset != len) {
+ pr_err("%s:%d, Incorrect ON command entries",
+ __func__, __LINE__);
+ goto parse_init_cmds_error;
+ }
+
+ (panel_data->dsi_panel_on_cmds)->size = num_of_on_cmds;
+
+ on_cmds_state = of_get_property(pdev->dev.of_node,
+ "qcom,on-cmds-dsi-state", NULL);
+ if (!strncmp(on_cmds_state, "DSI_LP_MODE", 11)) {
+ (panel_data->dsi_panel_on_cmds)->ctrl_state = DSI_LP_MODE;
+ } else if (!strncmp(on_cmds_state, "DSI_HS_MODE", 11)) {
+ (panel_data->dsi_panel_on_cmds)->ctrl_state = DSI_HS_MODE;
+ } else {
+ pr_debug("%s: ON cmds state not specified. Set Default\n",
+ __func__);
+ (panel_data->dsi_panel_on_cmds)->ctrl_state = DSI_LP_MODE;
+ }
+
+ panel_private->on_cmds_list = panel_data->dsi_panel_on_cmds;
+ data = of_get_property(np, "qcom,panel-off-cmds", &len);
+ if (!data) {
+ pr_err("%s:%d, Unable to read OFF cmds", __func__, __LINE__);
+ goto parse_init_cmds_error;
+ }
+
+ off_cmds = kzalloc(sizeof(char) * len, GFP_KERNEL);
+ if (!off_cmds)
+ goto parse_init_cmds_error;
+
+ memcpy(off_cmds, data, len);
+
+ data_offset = 0;
+ cmd_plen = 0;
+ while ((len - data_offset) >= DT_CMD_HDR) {
+ data_offset += (DT_CMD_HDR - 1);
+ cmd_plen = off_cmds[data_offset++];
+ data_offset += cmd_plen;
+ num_of_off_cmds++;
+ }
+ if (!num_of_off_cmds) {
+ pr_err("%s:%d, No OFF cmds specified", __func__, __LINE__);
+ goto parse_init_cmds_error;
+ }
+
+ panel_data->dsi_panel_off_cmds =
+ kzalloc(sizeof(struct dsi_panel_cmds_list), GFP_KERNEL);
+ if (!panel_data->dsi_panel_off_cmds)
+ goto parse_init_cmds_error;
+
+ (panel_data->dsi_panel_off_cmds)->buf = kzalloc(num_of_off_cmds
+ * sizeof(struct dsi_cmd_desc),
+ GFP_KERNEL);
+ if (!(panel_data->dsi_panel_off_cmds)->buf)
+ goto parse_init_cmds_error;
+
+ data_offset = 0;
+ for (i = 0; i < num_of_off_cmds; i++) {
+ panel_data->dsi_panel_off_cmds->buf[i].dtype =
+ off_cmds[data_offset++];
+ panel_data->dsi_panel_off_cmds->buf[i].last =
+ off_cmds[data_offset++];
+ panel_data->dsi_panel_off_cmds->buf[i].vc =
+ off_cmds[data_offset++];
+ panel_data->dsi_panel_off_cmds->buf[i].ack =
+ off_cmds[data_offset++];
+ panel_data->dsi_panel_off_cmds->buf[i].wait =
+ off_cmds[data_offset++];
+ panel_data->dsi_panel_off_cmds->buf[i].dlen =
+ off_cmds[data_offset++];
+ panel_data->dsi_panel_off_cmds->buf[i].payload =
+ &off_cmds[data_offset];
+ data_offset += (panel_data->dsi_panel_off_cmds->buf[i].dlen);
+ }
+
+ if (data_offset != len) {
+ pr_err("%s:%d, Incorrect OFF command entries",
+ __func__, __LINE__);
+ goto parse_init_cmds_error;
+ }
+
+ (panel_data->dsi_panel_off_cmds)->size = num_of_off_cmds;
+ off_cmds_state = of_get_property(pdev->dev.of_node,
+ "qcom,off-cmds-dsi-state", NULL);
+ if (!strncmp(off_cmds_state, "DSI_LP_MODE", 11)) {
+ (panel_data->dsi_panel_off_cmds)->ctrl_state =
+ DSI_LP_MODE;
+ } else if (!strncmp(off_cmds_state, "DSI_HS_MODE", 11)) {
+ (panel_data->dsi_panel_off_cmds)->ctrl_state = DSI_HS_MODE;
+ } else {
+ pr_debug("%s: ON cmds state not specified. Set Default\n",
+ __func__);
+ (panel_data->dsi_panel_off_cmds)->ctrl_state = DSI_LP_MODE;
+ }
+
+ panel_private->off_cmds_list = panel_data->dsi_panel_on_cmds;
+ kfree(on_cmds);
+ kfree(off_cmds);
+
+ return 0;
+parse_init_cmds_error:
+ if (panel_data->dsi_panel_on_cmds) {
+ kfree((panel_data->dsi_panel_on_cmds)->buf);
+ kfree(panel_data->dsi_panel_on_cmds);
+ panel_data->dsi_panel_on_cmds = NULL;
+ }
+ if (panel_data->dsi_panel_off_cmds) {
+ kfree((panel_data->dsi_panel_off_cmds)->buf);
+ kfree(panel_data->dsi_panel_off_cmds);
+ panel_data->dsi_panel_off_cmds = NULL;
+ }
+
+ kfree(on_cmds);
+ kfree(off_cmds);
+ return -EINVAL;
+}
+
+static int dsi_panel_parse_backlight(struct platform_device *pdev,
+ struct dsi_panel_common_pdata *panel_data,
+ char *bl_ctrl)
+{
+ int rc;
+ u32 res[6];
+ static const char *bl_ctrl_type;
+
+ bl_ctrl_type = of_get_property(pdev->dev.of_node,
+ "qcom,mdss-pan-bl-ctrl", NULL);
+ if ((bl_ctrl_type) && (!strncmp(bl_ctrl_type, "bl_ctrl_wled", 12))) {
+ led_trigger_register_simple("bkl-trigger", &bl_led_trigger);
+ pr_debug("%s: SUCCESS-> WLED TRIGGER register\n", __func__);
+ *bl_ctrl = BL_WLED;
+ }
+
+ rc = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,mdss-pan-bl-levels", res, 2);
+ panel_data->panel_info.bl_min = (!rc ? res[0] : 0);
+ panel_data->panel_info.bl_max = (!rc ? res[1] : 255);
+ return rc;
+}
+
+static int dsi_panel_parse_other(struct platform_device *pdev,
+ struct dsi_panel_common_pdata *panel_data)
+{
+ const char *pdest;
+ u32 tmp;
+ int rc;
+
+ pdest = of_get_property(pdev->dev.of_node,
+ "qcom,mdss-pan-dest", NULL);
+ if (strlen(pdest) != 9) {
+ pr_err("%s: Unknown pdest specified\n", __func__);
+ return -EINVAL;
+ }
+ if (!strncmp(pdest, "display_1", 9)) {
+ panel_data->panel_info.pdest = DISPLAY_1;
+ } else if (!strncmp(pdest, "display_2", 9)) {
+ panel_data->panel_info.pdest = DISPLAY_2;
+ } else {
+ pr_debug("%s: pdest not specified. Set Default\n",
+ __func__);
+ panel_data->panel_info.pdest = DISPLAY_1;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mdss-pan-underflow-clr", &tmp);
+ panel_data->panel_info.lcdc.underflow_clr = (!rc ? tmp : 0xff);
+
+ return rc;
+}
+
+static int dsi_panel_parse_dt(struct platform_device *pdev,
+ struct dsi_panel_common_pdata *panel_data,
+ char *bl_ctrl)
+{
+ int rc;
+
+ rc = dsi_panel_parse_gpio(pdev);
+ if (rc) {
+ pr_err("fail to parse panel GPIOs\n");
+ return rc;
+ }
+
+ rc = dsi_panel_parse_regulator(pdev);
+ if (rc) {
+ pr_err("fail to parse panel regulators\n");
+ return rc;
+ }
+
+ rc = dsi_panel_parse_timing(pdev, panel_data);
+ if (rc) {
+ pr_err("fail to parse panel timing\n");
+ return rc;
+ }
+
+ rc = dsi_panel_parse_phy(pdev, panel_data);
+ if (rc) {
+ pr_err("fail to parse DSI PHY settings\n");
+ return rc;
+ }
+
+ rc = dsi_panel_parse_backlight(pdev, panel_data, bl_ctrl);
+ if (rc) {
+ pr_err("fail to parse DSI backlight\n");
+ return rc;
+ }
+
+ rc = dsi_panel_parse_other(pdev, panel_data);
+ if (rc) {
+ pr_err("fail to parse DSI panel destination\n");
+ return rc;
+ }
+
+ rc = dsi_panel_parse_init_cmds(pdev, panel_data);
+ if (rc) {
+ pr_err("fail to parse DSI init commands\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static int __devinit dsi_panel_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ static struct dsi_panel_common_pdata vendor_pdata;
+ static const char *panel_name;
+
+ pr_debug("%s:%d, debug info id=%d", __func__, __LINE__, pdev->id);
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ panel_name = of_get_property(pdev->dev.of_node, "label", NULL);
+ if (!panel_name)
+ pr_debug("%s:%d, panel name not specified\n",
+ __func__, __LINE__);
+ else
+ pr_debug("%s: Panel Name = %s\n", __func__, panel_name);
+
+ rc = dsi_panel_init();
+ if (rc) {
+ pr_err("dsi_panel_init failed %d\n", rc);
+ goto dsi_panel_probe_error;
+
+ }
+ rc = dsi_panel_parse_dt(pdev, &vendor_pdata, &panel_private->bl_ctrl);
+ if (rc) {
+ pr_err("dsi_panel_parse_dt failed %d\n", rc);
+ goto dsi_panel_probe_error;
+ }
+
+ vendor_pdata.on = dsi_panel_on;
+ vendor_pdata.off = dsi_panel_off;
+ vendor_pdata.bl_fnc = dsi_panel_bl_ctrl;
+
+ rc = dsi_panel_device_register_v2(pdev, &vendor_pdata,
+ panel_private->bl_ctrl);
+
+ if (rc) {
+ pr_err("dsi_panel_device_register_v2 failed %d\n", rc);
+ goto dsi_panel_probe_error;
+ }
+
+ return 0;
+dsi_panel_probe_error:
+ dsi_panel_deinit();
+ return rc;
+}
+
+static int __devexit dsi_panel_remove(struct platform_device *pdev)
+{
+ dsi_panel_deinit();
+ return 0;
+}
+
+
+static const struct of_device_id dsi_panel_match[] = {
+ {.compatible = "qcom,dsi-panel-v2"},
+ {}
+};
+
+static struct platform_driver this_driver = {
+ .probe = dsi_panel_probe,
+ .remove = __devexit_p(dsi_panel_remove),
+ .driver = {
+ .name = "dsi_v2_panel",
+ .of_match_table = dsi_panel_match,
+ },
+};
+
+static int __init dsi_panel_module_init(void)
+{
+ return platform_driver_register(&this_driver);
+}
+module_init(dsi_panel_module_init);
diff --git a/drivers/video/msm/mdss/dsi_v2.c b/drivers/video/msm/mdss/dsi_v2.c
new file mode 100644
index 0000000..5e46bf5
--- /dev/null
+++ b/drivers/video/msm/mdss/dsi_v2.c
@@ -0,0 +1,789 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/iopoll.h>
+#include <linux/of_device.h>
+
+#include "mdss_panel.h"
+#include "dsi_v2.h"
+
+static struct dsi_panel_common_pdata *panel_common_data;
+static struct dsi_interface dsi_intf;
+
+static int dsi_off(struct mdss_panel_data *pdata)
+{
+ int rc = 0;
+ if (!panel_common_data || !pdata)
+ return -ENODEV;
+
+ if (dsi_intf.off)
+ rc = dsi_intf.off(pdata);
+
+ if (rc) {
+ pr_err("mdss_dsi_off DSI failed %d\n", rc);
+ return rc;
+ }
+
+ pr_debug("dsi_off reset\n");
+ if (panel_common_data->off)
+ panel_common_data->off(pdata);
+
+ return rc;
+}
+
+static int dsi_on(struct mdss_panel_data *pdata)
+{
+ int rc = 0;
+
+ pr_debug("dsi_on\n");
+
+ if (!panel_common_data || !pdata)
+ return -ENODEV;
+
+ if (panel_common_data->reset)
+ panel_common_data->reset(1);
+
+ pr_debug("dsi_on DSI controller ont\n");
+ if (dsi_intf.on)
+ rc = dsi_intf.on(pdata);
+
+ if (rc) {
+ pr_err("mdss_dsi_on DSI failed %d\n", rc);
+ return rc;
+ }
+
+ pr_debug("dsi_on DSI panel ont\n");
+ if (panel_common_data->on)
+ rc = panel_common_data->on(pdata);
+
+ if (rc) {
+ pr_err("mdss_dsi_on panel failed %d\n", rc);
+ return rc;
+ }
+ return rc;
+}
+
+static int dsi_event_handler(struct mdss_panel_data *pdata,
+ int event, void *arg)
+{
+ int rc = 0;
+
+ if (!pdata || !panel_common_data) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -ENODEV;
+ }
+
+ switch (event) {
+ case MDSS_EVENT_PANEL_ON:
+ rc = dsi_on(pdata);
+ break;
+ case MDSS_EVENT_PANEL_OFF:
+ rc = dsi_off(pdata);
+ break;
+ default:
+ pr_debug("%s: unhandled event=%d\n", __func__, event);
+ break;
+ }
+ return rc;
+}
+
+static struct platform_device *get_dsi_platform_device(
+ struct platform_device *dev)
+{
+ struct device_node *dsi_ctrl_np;
+ struct platform_device *ctrl_pdev;
+
+ dsi_ctrl_np = of_parse_phandle(dev->dev.of_node,
+ "qcom,dsi-ctrl-phandle", 0);
+
+ if (!dsi_ctrl_np)
+ return NULL;
+
+ ctrl_pdev = of_find_device_by_node(dsi_ctrl_np);
+ if (!ctrl_pdev)
+ return NULL;
+
+ return ctrl_pdev;
+}
+
+int dsi_panel_device_register_v2(struct platform_device *dev,
+ struct dsi_panel_common_pdata *panel_data,
+ char backlight_ctrl)
+{
+ struct mipi_panel_info *mipi;
+ struct platform_device *ctrl_pdev;
+ int rc;
+ u8 lanes = 0, bpp;
+ u32 h_period, v_period;
+ static struct mdss_panel_data dsi_panel_data;
+
+ h_period = ((panel_data->panel_info.lcdc.h_pulse_width)
+ + (panel_data->panel_info.lcdc.h_back_porch)
+ + (panel_data->panel_info.xres)
+ + (panel_data->panel_info.lcdc.h_front_porch));
+
+ v_period = ((panel_data->panel_info.lcdc.v_pulse_width)
+ + (panel_data->panel_info.lcdc.v_back_porch)
+ + (panel_data->panel_info.yres)
+ + (panel_data->panel_info.lcdc.v_front_porch));
+
+ mipi = &panel_data->panel_info.mipi;
+
+ panel_data->panel_info.type =
+ ((mipi->mode == DSI_VIDEO_MODE)
+ ? MIPI_VIDEO_PANEL : MIPI_CMD_PANEL);
+
+ if (mipi->data_lane3)
+ lanes += 1;
+ if (mipi->data_lane2)
+ lanes += 1;
+ if (mipi->data_lane1)
+ lanes += 1;
+ if (mipi->data_lane0)
+ lanes += 1;
+
+
+ if ((mipi->dst_format == DSI_CMD_DST_FORMAT_RGB888)
+ || (mipi->dst_format == DSI_VIDEO_DST_FORMAT_RGB888)
+ || (mipi->dst_format == DSI_VIDEO_DST_FORMAT_RGB666_LOOSE))
+ bpp = 3;
+ else if ((mipi->dst_format == DSI_CMD_DST_FORMAT_RGB565)
+ || (mipi->dst_format == DSI_VIDEO_DST_FORMAT_RGB565))
+ bpp = 2;
+ else
+ bpp = 3; /* Default format set to RGB888 */
+
+ if (panel_data->panel_info.type == MIPI_VIDEO_PANEL &&
+ !panel_data->panel_info.clk_rate) {
+ h_period += panel_data->panel_info.lcdc.xres_pad;
+ v_period += panel_data->panel_info.lcdc.yres_pad;
+
+ if (lanes > 0) {
+ panel_data->panel_info.clk_rate =
+ ((h_period * v_period * (mipi->frame_rate) * bpp * 8)
+ / lanes);
+ } else {
+ pr_err("%s: forcing mdss_dsi lanes to 1\n", __func__);
+ panel_data->panel_info.clk_rate =
+ (h_period * v_period
+ * (mipi->frame_rate) * bpp * 8);
+ }
+ }
+
+ ctrl_pdev = get_dsi_platform_device(dev);
+ if (!ctrl_pdev)
+ return -EPROBE_DEFER;
+
+ dsi_panel_data.event_handler = dsi_event_handler;
+
+ dsi_panel_data.panel_info = panel_data->panel_info;
+
+ dsi_panel_data.set_backlight = panel_data->bl_fnc;
+ panel_common_data = panel_data;
+ /*
+ * register in mdp driver
+ */
+ rc = mdss_register_panel(ctrl_pdev, &dsi_panel_data);
+ if (rc) {
+ dev_err(&dev->dev, "unable to register MIPI DSI panel\n");
+ return rc;
+ }
+
+ pr_debug("%s: Panal data initialized\n", __func__);
+ return 0;
+}
+
+void dsi_register_interface(struct dsi_interface *intf)
+{
+ dsi_intf = *intf;
+}
+
+int dsi_cmds_tx_v2(struct mdss_panel_data *pdata,
+ struct dsi_buf *tp, struct dsi_cmd_desc *cmds,
+ int cnt)
+{
+ int rc = 0;
+
+ if (!dsi_intf.tx)
+ return -EINVAL;
+
+ rc = dsi_intf.tx(pdata, tp, cmds, cnt);
+ return rc;
+}
+
+int dsi_cmds_rx_v2(struct mdss_panel_data *pdata,
+ struct dsi_buf *tp, struct dsi_buf *rp,
+ struct dsi_cmd_desc *cmds, int rlen)
+{
+ int rc = 0;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!dsi_intf.rx)
+ return -EINVAL;
+
+ rc = dsi_intf.rx(pdata, tp, rp, cmds, rlen);
+ return rc;
+}
+
+static char *dsi_buf_reserve(struct dsi_buf *dp, int len)
+{
+ dp->data += len;
+ return dp->data;
+}
+
+
+static char *dsi_buf_push(struct dsi_buf *dp, int len)
+{
+ dp->data -= len;
+ dp->len += len;
+ return dp->data;
+}
+
+static char *dsi_buf_reserve_hdr(struct dsi_buf *dp, int hlen)
+{
+ dp->hdr = (u32 *)dp->data;
+ return dsi_buf_reserve(dp, hlen);
+}
+
+char *dsi_buf_init(struct dsi_buf *dp)
+{
+ int off;
+
+ dp->data = dp->start;
+ off = (int)dp->data;
+ /* 8 byte align */
+ off &= 0x07;
+ if (off)
+ off = 8 - off;
+ dp->data += off;
+ dp->len = 0;
+ return dp->data;
+}
+
+int dsi_buf_alloc(struct dsi_buf *dp, int size)
+{
+ dp->start = kmalloc(size, GFP_KERNEL);
+ if (dp->start == NULL) {
+ pr_err("%s:%u\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ dp->end = dp->start + size;
+ dp->size = size;
+
+ if ((int)dp->start & 0x07) {
+ pr_err("%s: buf NOT 8 bytes aligned\n", __func__);
+ return -EINVAL;
+ }
+
+ dp->data = dp->start;
+ dp->len = 0;
+ return 0;
+}
+
+/*
+ * mipi dsi generic long write
+ */
+static int dsi_generic_lwrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+ char *bp;
+ u32 *hp;
+ int i, len;
+
+ bp = dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+
+ /* fill up payload */
+ if (cm->payload) {
+ len = cm->dlen;
+ len += 3;
+ len &= ~0x03; /* multipled by 4 */
+ for (i = 0; i < cm->dlen; i++)
+ *bp++ = cm->payload[i];
+
+ /* append 0xff to the end */
+ for (; i < len; i++)
+ *bp++ = 0xff;
+
+ dp->len += len;
+ }
+
+ /* fill up header */
+ hp = dp->hdr;
+ *hp = 0;
+ *hp = DSI_HDR_WC(cm->dlen);
+ *hp |= DSI_HDR_VC(cm->vc);
+ *hp |= DSI_HDR_LONG_PKT;
+ *hp |= DSI_HDR_DTYPE(DTYPE_GEN_LWRITE);
+ if (cm->last)
+ *hp |= DSI_HDR_LAST;
+
+ dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+ return dp->len;
+}
+
+/*
+ * mipi dsi generic short write with 0, 1 2 parameters
+ */
+static int dsi_generic_swrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+ u32 *hp;
+ int len;
+
+ if (cm->dlen && cm->payload == 0) {
+ pr_err("%s: NO payload error\n", __func__);
+ return 0;
+ }
+
+ dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+ hp = dp->hdr;
+ *hp = 0;
+ *hp |= DSI_HDR_VC(cm->vc);
+ if (cm->last)
+ *hp |= DSI_HDR_LAST;
+
+ len = (cm->dlen > 2) ? 2 : cm->dlen;
+
+ if (len == 1) {
+ *hp |= DSI_HDR_DTYPE(DTYPE_GEN_WRITE1);
+ *hp |= DSI_HDR_DATA1(cm->payload[0]);
+ *hp |= DSI_HDR_DATA2(0);
+ } else if (len == 2) {
+ *hp |= DSI_HDR_DTYPE(DTYPE_GEN_WRITE2);
+ *hp |= DSI_HDR_DATA1(cm->payload[0]);
+ *hp |= DSI_HDR_DATA2(cm->payload[1]);
+ } else {
+ *hp |= DSI_HDR_DTYPE(DTYPE_GEN_WRITE);
+ *hp |= DSI_HDR_DATA1(0);
+ *hp |= DSI_HDR_DATA2(0);
+ }
+
+ dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+ return dp->len;
+}
+
+/*
+ * mipi dsi gerneric read with 0, 1 2 parameters
+ */
+static int dsi_generic_read(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+ u32 *hp;
+ int len;
+
+ if (cm->dlen && cm->payload == 0) {
+ pr_err("%s: NO payload error\n", __func__);
+ return 0;
+ }
+
+ dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+ hp = dp->hdr;
+ *hp = 0;
+ *hp |= DSI_HDR_VC(cm->vc);
+ *hp |= DSI_HDR_BTA;
+ if (cm->last)
+ *hp |= DSI_HDR_LAST;
+
+ len = (cm->dlen > 2) ? 2 : cm->dlen;
+
+ if (len == 1) {
+ *hp |= DSI_HDR_DTYPE(DTYPE_GEN_READ1);
+ *hp |= DSI_HDR_DATA1(cm->payload[0]);
+ *hp |= DSI_HDR_DATA2(0);
+ } else if (len == 2) {
+ *hp |= DSI_HDR_DTYPE(DTYPE_GEN_READ2);
+ *hp |= DSI_HDR_DATA1(cm->payload[0]);
+ *hp |= DSI_HDR_DATA2(cm->payload[1]);
+ } else {
+ *hp |= DSI_HDR_DTYPE(DTYPE_GEN_READ);
+ *hp |= DSI_HDR_DATA1(0);
+ *hp |= DSI_HDR_DATA2(0);
+ }
+
+ dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+ return dp->len;
+}
+
+/*
+ * mipi dsi dcs long write
+ */
+static int dsi_dcs_lwrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+ char *bp;
+ u32 *hp;
+ int i, len;
+
+ bp = dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+
+ /*
+ * fill up payload
+ * dcs command byte (first byte) followed by payload
+ */
+ if (cm->payload) {
+ len = cm->dlen;
+ len += 3;
+ len &= ~0x03; /* multipled by 4 */
+ for (i = 0; i < cm->dlen; i++)
+ *bp++ = cm->payload[i];
+
+ /* append 0xff to the end */
+ for (; i < len; i++)
+ *bp++ = 0xff;
+
+ dp->len += len;
+ }
+
+ /* fill up header */
+ hp = dp->hdr;
+ *hp = 0;
+ *hp = DSI_HDR_WC(cm->dlen);
+ *hp |= DSI_HDR_VC(cm->vc);
+ *hp |= DSI_HDR_LONG_PKT;
+ *hp |= DSI_HDR_DTYPE(DTYPE_DCS_LWRITE);
+ if (cm->last)
+ *hp |= DSI_HDR_LAST;
+
+ dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+ return dp->len;
+}
+
+/*
+ * mipi dsi dcs short write with 0 parameters
+ */
+static int dsi_dcs_swrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+ u32 *hp;
+ int len;
+
+ if (cm->payload == 0) {
+ pr_err("%s: NO payload error\n", __func__);
+ return -EINVAL;
+ }
+
+ dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+ hp = dp->hdr;
+ *hp = 0;
+ *hp |= DSI_HDR_VC(cm->vc);
+ if (cm->ack)
+ *hp |= DSI_HDR_BTA;
+ if (cm->last)
+ *hp |= DSI_HDR_LAST;
+
+ len = (cm->dlen > 1) ? 1 : cm->dlen;
+
+ *hp |= DSI_HDR_DTYPE(DTYPE_DCS_WRITE);
+ *hp |= DSI_HDR_DATA1(cm->payload[0]); /* dcs command byte */
+ *hp |= DSI_HDR_DATA2(0);
+
+ dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+ return dp->len;
+}
+
+/*
+ * mipi dsi dcs short write with 1 parameters
+ */
+static int dsi_dcs_swrite1(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+ u32 *hp;
+
+ if (cm->dlen < 2 || cm->payload == 0) {
+ pr_err("%s: NO payload error\n", __func__);
+ return -EINVAL;
+ }
+
+ dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+ hp = dp->hdr;
+ *hp = 0;
+ *hp |= DSI_HDR_VC(cm->vc);
+ if (cm->ack)
+ *hp |= DSI_HDR_BTA;
+ if (cm->last)
+ *hp |= DSI_HDR_LAST;
+
+ *hp |= DSI_HDR_DTYPE(DTYPE_DCS_WRITE1);
+ *hp |= DSI_HDR_DATA1(cm->payload[0]); /* dcs comamnd byte */
+ *hp |= DSI_HDR_DATA2(cm->payload[1]); /* parameter */
+
+ dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+ return dp->len;
+}
+
+/*
+ * mipi dsi dcs read with 0 parameters
+ */
+static int dsi_dcs_read(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+ u32 *hp;
+
+ if (cm->payload == 0) {
+ pr_err("%s: NO payload error\n", __func__);
+ return -EINVAL;
+ }
+
+ dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+ hp = dp->hdr;
+ *hp = 0;
+ *hp |= DSI_HDR_VC(cm->vc);
+ *hp |= DSI_HDR_BTA;
+ *hp |= DSI_HDR_DTYPE(DTYPE_DCS_READ);
+ if (cm->last)
+ *hp |= DSI_HDR_LAST;
+
+ *hp |= DSI_HDR_DATA1(cm->payload[0]); /* dcs command byte */
+ *hp |= DSI_HDR_DATA2(0);
+
+ dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+ return dp->len; /* 4 bytes */
+}
+
+static int dsi_cm_on(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+ u32 *hp;
+
+ dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+ hp = dp->hdr;
+ *hp = 0;
+ *hp |= DSI_HDR_VC(cm->vc);
+ *hp |= DSI_HDR_DTYPE(DTYPE_CM_ON);
+ if (cm->last)
+ *hp |= DSI_HDR_LAST;
+
+ dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+ return dp->len; /* 4 bytes */
+}
+
+static int dsi_cm_off(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+ u32 *hp;
+
+ dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+ hp = dp->hdr;
+ *hp = 0;
+ *hp |= DSI_HDR_VC(cm->vc);
+ *hp |= DSI_HDR_DTYPE(DTYPE_CM_OFF);
+ if (cm->last)
+ *hp |= DSI_HDR_LAST;
+
+ dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+ return dp->len; /* 4 bytes */
+}
+
+static int dsi_peripheral_on(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+ u32 *hp;
+
+ dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+ hp = dp->hdr;
+ *hp = 0;
+ *hp |= DSI_HDR_VC(cm->vc);
+ *hp |= DSI_HDR_DTYPE(DTYPE_PERIPHERAL_ON);
+ if (cm->last)
+ *hp |= DSI_HDR_LAST;
+
+ dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+ return dp->len; /* 4 bytes */
+}
+
+static int dsi_peripheral_off(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+ u32 *hp;
+
+ dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+ hp = dp->hdr;
+ *hp = 0;
+ *hp |= DSI_HDR_VC(cm->vc);
+ *hp |= DSI_HDR_DTYPE(DTYPE_PERIPHERAL_OFF);
+ if (cm->last)
+ *hp |= DSI_HDR_LAST;
+
+ dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+ return dp->len; /* 4 bytes */
+}
+
+static int dsi_set_max_pktsize(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+ u32 *hp;
+
+ if (cm->payload == 0) {
+ pr_err("%s: NO payload error\n", __func__);
+ return 0;
+ }
+
+ dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+ hp = dp->hdr;
+ *hp = 0;
+ *hp |= DSI_HDR_VC(cm->vc);
+ *hp |= DSI_HDR_DTYPE(DTYPE_MAX_PKTSIZE);
+ if (cm->last)
+ *hp |= DSI_HDR_LAST;
+
+ *hp |= DSI_HDR_DATA1(cm->payload[0]);
+ *hp |= DSI_HDR_DATA2(cm->payload[1]);
+
+ dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+ return dp->len; /* 4 bytes */
+}
+
+static int dsi_null_pkt(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+ u32 *hp;
+
+ dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+ hp = dp->hdr;
+ *hp = 0;
+ *hp = DSI_HDR_WC(cm->dlen);
+ *hp |= DSI_HDR_LONG_PKT;
+ *hp |= DSI_HDR_VC(cm->vc);
+ *hp |= DSI_HDR_DTYPE(DTYPE_NULL_PKT);
+ if (cm->last)
+ *hp |= DSI_HDR_LAST;
+
+ dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+ return dp->len; /* 4 bytes */
+}
+
+static int dsi_blank_pkt(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+ u32 *hp;
+
+ dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+ hp = dp->hdr;
+ *hp = 0;
+ *hp = DSI_HDR_WC(cm->dlen);
+ *hp |= DSI_HDR_LONG_PKT;
+ *hp |= DSI_HDR_VC(cm->vc);
+ *hp |= DSI_HDR_DTYPE(DTYPE_BLANK_PKT);
+ if (cm->last)
+ *hp |= DSI_HDR_LAST;
+
+ dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+ return dp->len; /* 4 bytes */
+}
+
+/*
+ * prepare cmd buffer to be txed
+ */
+int dsi_cmd_dma_add(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+ int len = 0;
+
+ switch (cm->dtype) {
+ case DTYPE_GEN_WRITE:
+ case DTYPE_GEN_WRITE1:
+ case DTYPE_GEN_WRITE2:
+ len = dsi_generic_swrite(dp, cm);
+ break;
+ case DTYPE_GEN_LWRITE:
+ len = dsi_generic_lwrite(dp, cm);
+ break;
+ case DTYPE_GEN_READ:
+ case DTYPE_GEN_READ1:
+ case DTYPE_GEN_READ2:
+ len = dsi_generic_read(dp, cm);
+ break;
+ case DTYPE_DCS_LWRITE:
+ len = dsi_dcs_lwrite(dp, cm);
+ break;
+ case DTYPE_DCS_WRITE:
+ len = dsi_dcs_swrite(dp, cm);
+ break;
+ case DTYPE_DCS_WRITE1:
+ len = dsi_dcs_swrite1(dp, cm);
+ break;
+ case DTYPE_DCS_READ:
+ len = dsi_dcs_read(dp, cm);
+ break;
+ case DTYPE_MAX_PKTSIZE:
+ len = dsi_set_max_pktsize(dp, cm);
+ break;
+ case DTYPE_NULL_PKT:
+ len = dsi_null_pkt(dp, cm);
+ break;
+ case DTYPE_BLANK_PKT:
+ len = dsi_blank_pkt(dp, cm);
+ break;
+ case DTYPE_CM_ON:
+ len = dsi_cm_on(dp, cm);
+ break;
+ case DTYPE_CM_OFF:
+ len = dsi_cm_off(dp, cm);
+ break;
+ case DTYPE_PERIPHERAL_ON:
+ len = dsi_peripheral_on(dp, cm);
+ break;
+ case DTYPE_PERIPHERAL_OFF:
+ len = dsi_peripheral_off(dp, cm);
+ break;
+ default:
+ pr_debug("%s: dtype=%x NOT supported\n",
+ __func__, cm->dtype);
+ break;
+
+ }
+
+ return len;
+}
+
+/*
+ * mdss_dsi_short_read1_resp: 1 parameter
+ */
+int dsi_short_read1_resp(struct dsi_buf *rp)
+{
+ /* strip out dcs type */
+ rp->data++;
+ rp->len = 1;
+ return rp->len;
+}
+
+/*
+ * mdss_dsi_short_read2_resp: 2 parameter
+ */
+int dsi_short_read2_resp(struct dsi_buf *rp)
+{
+ /* strip out dcs type */
+ rp->data++;
+ rp->len = 2;
+ return rp->len;
+}
+
+int dsi_long_read_resp(struct dsi_buf *rp)
+{
+ short len;
+
+ len = rp->data[2];
+ len <<= 8;
+ len |= rp->data[1];
+ /* strip out dcs header */
+ rp->data += 4;
+ rp->len -= 4;
+ /* strip out 2 bytes of checksum */
+ rp->len -= 2;
+ return len;
+}
diff --git a/drivers/video/msm/mdss/dsi_v2.h b/drivers/video/msm/mdss/dsi_v2.h
new file mode 100644
index 0000000..fa868ab
--- /dev/null
+++ b/drivers/video/msm/mdss/dsi_v2.h
@@ -0,0 +1,237 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MDSS_DSI_H
+#define MDSS_DSI_H
+
+#include <linux/list.h>
+#include <mach/scm-io.h>
+
+#include "mdss_panel.h"
+
+#define MIPI_OUTP(addr, data) writel_relaxed((data), (addr))
+#define MIPI_INP(addr) readl_relaxed(addr)
+
+#define MIPI_DSI_PRIM 1
+#define MIPI_DSI_SECD 2
+
+#define MIPI_DSI_PANEL_VGA 0
+#define MIPI_DSI_PANEL_WVGA 1
+#define MIPI_DSI_PANEL_WVGA_PT 2
+#define MIPI_DSI_PANEL_FWVGA_PT 3
+#define MIPI_DSI_PANEL_WSVGA_PT 4
+#define MIPI_DSI_PANEL_QHD_PT 5
+#define MIPI_DSI_PANEL_WXGA 6
+#define MIPI_DSI_PANEL_WUXGA 7
+#define MIPI_DSI_PANEL_720P_PT 8
+#define DSI_PANEL_MAX 8
+
+enum {
+ DSI_VIDEO_MODE,
+ DSI_CMD_MODE,
+};
+
+enum {
+ ST_DSI_CLK_OFF,
+ ST_DSI_SUSPEND,
+ ST_DSI_RESUME,
+ ST_DSI_PLAYING,
+ ST_DSI_NUM
+};
+
+enum {
+ EV_DSI_UPDATE,
+ EV_DSI_DONE,
+ EV_DSI_TOUT,
+ EV_DSI_NUM
+};
+
+enum {
+ LANDSCAPE = 1,
+ PORTRAIT = 2,
+};
+
+enum {
+ DSI_CMD_MODE_DMA,
+ DSI_CMD_MODE_MDP,
+};
+
+enum {
+ BL_PWM,
+ BL_WLED,
+ BL_DCS_CMD,
+ UNKNOWN_CTRL,
+};
+
+enum {
+ DSI_LP_MODE,
+ DSI_HS_MODE,
+};
+
+#define DSI_NON_BURST_SYNCH_PULSE 0
+#define DSI_NON_BURST_SYNCH_EVENT 1
+#define DSI_BURST_MODE 2
+
+#define DSI_RGB_SWAP_RGB 0
+#define DSI_RGB_SWAP_RBG 1
+#define DSI_RGB_SWAP_BGR 2
+#define DSI_RGB_SWAP_BRG 3
+#define DSI_RGB_SWAP_GRB 4
+#define DSI_RGB_SWAP_GBR 5
+
+#define DSI_VIDEO_DST_FORMAT_RGB565 0
+#define DSI_VIDEO_DST_FORMAT_RGB666 1
+#define DSI_VIDEO_DST_FORMAT_RGB666_LOOSE 2
+#define DSI_VIDEO_DST_FORMAT_RGB888 3
+
+#define DSI_CMD_DST_FORMAT_RGB111 0
+#define DSI_CMD_DST_FORMAT_RGB332 3
+#define DSI_CMD_DST_FORMAT_RGB444 4
+#define DSI_CMD_DST_FORMAT_RGB565 6
+#define DSI_CMD_DST_FORMAT_RGB666 7
+#define DSI_CMD_DST_FORMAT_RGB888 8
+
+#define DSI_CMD_TRIGGER_NONE 0x0 /* mdp trigger */
+#define DSI_CMD_TRIGGER_TE 0x02
+#define DSI_CMD_TRIGGER_SW 0x04
+#define DSI_CMD_TRIGGER_SW_SEOF 0x05 /* cmd dma only */
+#define DSI_CMD_TRIGGER_SW_TE 0x06
+
+#define DSI_HOST_HDR_SIZE 4
+#define DSI_HDR_LAST BIT(31)
+#define DSI_HDR_LONG_PKT BIT(30)
+#define DSI_HDR_BTA BIT(29)
+#define DSI_HDR_VC(vc) (((vc) & 0x03) << 22)
+#define DSI_HDR_DTYPE(dtype) (((dtype) & 0x03f) << 16)
+#define DSI_HDR_DATA2(data) (((data) & 0x0ff) << 8)
+#define DSI_HDR_DATA1(data) ((data) & 0x0ff)
+#define DSI_HDR_WC(wc) ((wc) & 0x0ffff)
+
+#define DSI_BUF_SIZE 1024
+#define DSI_MRPS 0x04 /* Maximum Return Packet Size */
+
+#define DSI_LEN 8 /* 4 x 4 - 6 - 2, bytes dcs header+crc-align */
+
+struct dsi_buf {
+ u32 *hdr; /* dsi host header */
+ char *start; /* buffer start addr */
+ char *end; /* buffer end addr */
+ int size; /* size of buffer */
+ char *data; /* buffer */
+ int len; /* data length */
+ dma_addr_t dmap; /* mapped dma addr */
+};
+
+/* dcs read/write */
+#define DTYPE_DCS_WRITE 0x05 /* short write, 0 parameter */
+#define DTYPE_DCS_WRITE1 0x15 /* short write, 1 parameter */
+#define DTYPE_DCS_READ 0x06 /* read */
+#define DTYPE_DCS_LWRITE 0x39 /* long write */
+
+/* generic read/write */
+#define DTYPE_GEN_WRITE 0x03 /* short write, 0 parameter */
+#define DTYPE_GEN_WRITE1 0x13 /* short write, 1 parameter */
+#define DTYPE_GEN_WRITE2 0x23 /* short write, 2 parameter */
+#define DTYPE_GEN_LWRITE 0x29 /* long write */
+#define DTYPE_GEN_READ 0x04 /* long read, 0 parameter */
+#define DTYPE_GEN_READ1 0x14 /* long read, 1 parameter */
+#define DTYPE_GEN_READ2 0x24 /* long read, 2 parameter */
+
+#define DTYPE_TEAR_ON 0x35 /* set tear on */
+#define DTYPE_MAX_PKTSIZE 0x37 /* set max packet size */
+#define DTYPE_NULL_PKT 0x09 /* null packet, no data */
+#define DTYPE_BLANK_PKT 0x19 /* blankiing packet, no data */
+
+#define DTYPE_CM_ON 0x02 /* color mode off */
+#define DTYPE_CM_OFF 0x12 /* color mode on */
+#define DTYPE_PERIPHERAL_OFF 0x22
+#define DTYPE_PERIPHERAL_ON 0x32
+
+/*
+ * dcs response
+ */
+#define DTYPE_ACK_ERR_RESP 0x02
+#define DTYPE_EOT_RESP 0x08 /* end of tx */
+#define DTYPE_GEN_READ1_RESP 0x11 /* 1 parameter, short */
+#define DTYPE_GEN_READ2_RESP 0x12 /* 2 parameter, short */
+#define DTYPE_GEN_LREAD_RESP 0x1a
+#define DTYPE_DCS_LREAD_RESP 0x1c
+#define DTYPE_DCS_READ1_RESP 0x21 /* 1 parameter, short */
+#define DTYPE_DCS_READ2_RESP 0x22 /* 2 parameter, short */
+
+struct dsi_cmd_desc {
+ int dtype;
+ int last;
+ int vc;
+ int ack; /* ask ACK from peripheral */
+ int wait;
+ int dlen;
+ char *payload;
+};
+
+struct dsi_panel_cmds_list {
+ struct dsi_cmd_desc *buf;
+ char size;
+ char ctrl_state;
+};
+
+struct dsi_panel_common_pdata {
+ struct mdss_panel_info panel_info;
+ int (*on) (struct mdss_panel_data *pdata);
+ int (*off) (struct mdss_panel_data *pdata);
+ void (*reset)(int enable);
+ void (*bl_fnc) (struct mdss_panel_data *pdata, u32 bl_level);
+ struct dsi_panel_cmds_list *dsi_panel_on_cmds;
+ struct dsi_panel_cmds_list *dsi_panel_off_cmds;
+};
+
+struct dsi_interface {
+ int (*on)(struct mdss_panel_data *pdata);
+ int (*off)(struct mdss_panel_data *pdata);
+ void (*op_mode_config)(int mode, struct mdss_panel_data *pdata);
+ int (*tx)(struct mdss_panel_data *pdata,
+ struct dsi_buf *tp, struct dsi_cmd_desc *cmds, int cnt);
+ int (*rx)(struct mdss_panel_data *pdata,
+ struct dsi_buf *tp, struct dsi_buf *rp,
+ struct dsi_cmd_desc *cmds, int len);
+ int index;
+ void *private;
+};
+
+int dsi_panel_device_register_v2(struct platform_device *pdev,
+ struct dsi_panel_common_pdata *panel_data,
+ char bl_ctrl);
+
+void dsi_register_interface(struct dsi_interface *intf);
+
+int dsi_cmds_rx_v2(struct mdss_panel_data *pdata,
+ struct dsi_buf *tp, struct dsi_buf *rp,
+ struct dsi_cmd_desc *cmds, int len);
+
+int dsi_cmds_tx_v2(struct mdss_panel_data *pdata,
+ struct dsi_buf *tp, struct dsi_cmd_desc *cmds,
+ int cnt);
+
+char *dsi_buf_init(struct dsi_buf *dp);
+
+int dsi_buf_alloc(struct dsi_buf *dp, int size);
+
+int dsi_cmd_dma_add(struct dsi_buf *dp, struct dsi_cmd_desc *cm);
+
+int dsi_short_read1_resp(struct dsi_buf *rp);
+
+int dsi_short_read2_resp(struct dsi_buf *rp);
+
+int dsi_long_read_resp(struct dsi_buf *rp);
+
+#endif /* MDSS_DSI_H */
diff --git a/drivers/video/msm/mdss/mdss_hdmi_cec.c b/drivers/video/msm/mdss/mdss_hdmi_cec.c
new file mode 100644
index 0000000..694fcde
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_hdmi_cec.c
@@ -0,0 +1,949 @@
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <mach/board.h>
+
+#include "mdss_hdmi_cec.h"
+
+#define CEC_STATUS_WR_ERROR BIT(0)
+#define CEC_STATUS_WR_DONE BIT(1)
+
+/* Reference: HDMI 1.4a Specification section 7.1 */
+#define RETRANSMIT_MAX_NUM 5
+
+/*
+ * Ref. HDMI 1.4a: Supplement-1 CEC Section 6, 7
+ */
+struct hdmi_cec_msg {
+ u8 sender_id;
+ u8 recvr_id;
+ u8 opcode;
+ u8 operand[15];
+ u8 frame_size;
+ u8 retransmit;
+};
+
+struct hdmi_cec_msg_node {
+ struct hdmi_cec_msg msg;
+ struct list_head list;
+};
+
+struct hdmi_cec_ctrl {
+ bool cec_enabled;
+ bool compliance_response_enabled;
+ bool cec_engine_configed;
+
+ u8 cec_logical_addr;
+ u32 cec_msg_wr_status;
+
+ spinlock_t lock;
+ struct list_head msg_head;
+ struct work_struct cec_read_work;
+ struct completion cec_msg_wr_done;
+ struct hdmi_cec_init_data init_data;
+};
+
+static int hdmi_cec_msg_send(struct hdmi_cec_ctrl *cec_ctrl,
+ struct hdmi_cec_msg *msg);
+
+static void hdmi_cec_dump_msg(struct hdmi_cec_ctrl *cec_ctrl,
+ struct hdmi_cec_msg *msg)
+{
+ int i;
+ unsigned long flags;
+
+ if (!cec_ctrl || !msg) {
+ DEV_ERR("%pS->%s: invalid input\n",
+ __builtin_return_address(0), __func__);
+ return;
+ }
+
+ spin_lock_irqsave(&cec_ctrl->lock, flags);
+ DEV_DBG("=================%pS dump start =====================\n",
+ __builtin_return_address(0));
+
+ DEV_DBG("sender_id : %d", msg->sender_id);
+ DEV_DBG("recvr_id : %d", msg->recvr_id);
+
+ if (msg->frame_size < 2) {
+ DEV_DBG("polling message");
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+ return;
+ }
+
+ DEV_DBG("opcode : %02x", msg->opcode);
+ for (i = 0; i < msg->frame_size - 2; i++)
+ DEV_DBG("operand(%2d) : %02x", i + 1, msg->operand[i]);
+
+ DEV_DBG("=================%pS dump end =====================\n",
+ __builtin_return_address(0));
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+} /* hdmi_cec_dump_msg */
+
+static inline void hdmi_cec_write_logical_addr(struct hdmi_cec_ctrl *cec_ctrl,
+ u8 addr)
+{
+ if (!cec_ctrl || !cec_ctrl->init_data.io) {
+ DEV_ERR("%s: Invalid input\n", __func__);
+ return;
+ }
+
+ DSS_REG_W(cec_ctrl->init_data.io, HDMI_CEC_ADDR, addr & 0xF);
+} /* hdmi_cec_write_logical_addr */
+
+static void hdmi_cec_disable(struct hdmi_cec_ctrl *cec_ctrl)
+{
+ u32 reg_val;
+ unsigned long flags;
+ struct dss_io_data *io = NULL;
+ struct hdmi_cec_msg_node *msg_node, *tmp;
+
+ if (!cec_ctrl || !cec_ctrl->init_data.io) {
+ DEV_ERR("%s: Invalid input\n", __func__);
+ return;
+ }
+
+ io = cec_ctrl->init_data.io;
+
+ /* Disable Engine */
+ DSS_REG_W(io, HDMI_CEC_CTRL, 0);
+
+ /* Disable CEC interrupts */
+ reg_val = DSS_REG_R(io, HDMI_CEC_INT);
+ DSS_REG_W(io, HDMI_CEC_INT, reg_val & !BIT(1) & !BIT(3) & !BIT(7));
+
+ spin_lock_irqsave(&cec_ctrl->lock, flags);
+ list_for_each_entry_safe(msg_node, tmp, &cec_ctrl->msg_head, list) {
+ list_del(&msg_node->list);
+ kfree(msg_node);
+ }
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+} /* hdmi_cec_disable */
+
+static void hdmi_cec_enable(struct hdmi_cec_ctrl *cec_ctrl)
+{
+ struct dss_io_data *io = NULL;
+
+ if (!cec_ctrl || !cec_ctrl->init_data.io) {
+ DEV_ERR("%s: Invalid input\n", __func__);
+ return;
+ }
+
+ io = cec_ctrl->init_data.io;
+
+ INIT_LIST_HEAD(&cec_ctrl->msg_head);
+
+ /* Enable CEC interrupts */
+ DSS_REG_W(io, HDMI_CEC_INT, BIT(1) | BIT(3) | BIT(7));
+
+ /* Enable Engine */
+ DSS_REG_W(io, HDMI_CEC_CTRL, BIT(0));
+} /* hdmi_cec_enable */
+
+static int hdmi_cec_send_abort_opcode(struct hdmi_cec_ctrl *cec_ctrl,
+ struct hdmi_cec_msg *in_msg, u8 reason_operand)
+{
+ int i = 0;
+ struct hdmi_cec_msg out_msg;
+
+ if (!cec_ctrl || !in_msg) {
+ DEV_ERR("%s: Invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ out_msg.sender_id = 0x4;
+ out_msg.recvr_id = in_msg->sender_id;
+ out_msg.opcode = 0x0; /* opcode for feature abort */
+ out_msg.operand[i++] = in_msg->opcode;
+ out_msg.operand[i++] = reason_operand;
+ out_msg.frame_size = i + 2;
+
+ return hdmi_cec_msg_send(cec_ctrl, &out_msg);
+} /* hdmi_cec_send_abort_opcode */
+
+static int hdmi_cec_msg_parser(struct hdmi_cec_ctrl *cec_ctrl,
+ struct hdmi_cec_msg *in_msg)
+{
+ int rc = 0, i = 0;
+ struct hdmi_cec_msg out_msg;
+
+ if (!cec_ctrl || !in_msg) {
+ DEV_ERR("%s: Invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ DEV_DBG("%s: in_msg->opcode = 0x%x\n", __func__, in_msg->opcode);
+ switch (in_msg->opcode) {
+ case 0x64:
+ /* Set OSD String */
+ DEV_INFO("%s: Recvd OSD Str=[0x%x]\n", __func__,
+ in_msg->operand[3]);
+ break;
+ case 0x83:
+ /* Give Phy Addr */
+ DEV_INFO("%s: Recvd a Give Phy Addr cmd\n", __func__);
+
+ out_msg.sender_id = 0x4;
+ out_msg.recvr_id = 0xF; /* Broadcast */
+ out_msg.opcode = 0x84;
+ out_msg.operand[i++] = 0x10;
+ out_msg.operand[i++] = 0x0;
+ out_msg.operand[i++] = 0x04;
+ out_msg.frame_size = i + 2;
+
+ rc = hdmi_cec_msg_send(cec_ctrl, &out_msg);
+ break;
+ case 0xFF:
+ /* Abort */
+ DEV_INFO("%s: Recvd an abort cmd.\n", __func__);
+
+ /* reason = "Refused" */
+ rc = hdmi_cec_send_abort_opcode(cec_ctrl, in_msg, 0x04);
+ break;
+ case 0x46:
+ /* Give OSD name */
+ DEV_INFO("%s: Recvd 'Give OSD name' cmd.\n", __func__);
+
+ out_msg.sender_id = 0x4;
+ out_msg.recvr_id = in_msg->sender_id;
+ out_msg.opcode = 0x47; /* OSD Name */
+ /* Display control byte */
+ out_msg.operand[i++] = 0x0;
+ out_msg.operand[i++] = 'H';
+ out_msg.operand[i++] = 'e';
+ out_msg.operand[i++] = 'l';
+ out_msg.operand[i++] = 'l';
+ out_msg.operand[i++] = 'o';
+ out_msg.operand[i++] = ' ';
+ out_msg.operand[i++] = 'W';
+ out_msg.operand[i++] = 'o';
+ out_msg.operand[i++] = 'r';
+ out_msg.operand[i++] = 'l';
+ out_msg.operand[i++] = 'd';
+ out_msg.frame_size = i + 2;
+
+ rc = hdmi_cec_msg_send(cec_ctrl, &out_msg);
+ break;
+ case 0x8F:
+ /* Give Device Power status */
+ DEV_INFO("%s: Recvd a Power status message\n", __func__);
+
+ out_msg.sender_id = 0x4;
+ out_msg.recvr_id = in_msg->sender_id;
+ out_msg.opcode = 0x90; /* OSD String */
+ out_msg.operand[i++] = 'H';
+ out_msg.operand[i++] = 'e';
+ out_msg.operand[i++] = 'l';
+ out_msg.operand[i++] = 'l';
+ out_msg.operand[i++] = 'o';
+ out_msg.operand[i++] = ' ';
+ out_msg.operand[i++] = 'W';
+ out_msg.operand[i++] = 'o';
+ out_msg.operand[i++] = 'r';
+ out_msg.operand[i++] = 'l';
+ out_msg.operand[i++] = 'd';
+ out_msg.frame_size = i + 2;
+
+ rc = hdmi_cec_msg_send(cec_ctrl, &out_msg);
+ break;
+ case 0x80:
+ /* Routing Change cmd */
+ case 0x86:
+ /* Set Stream Path */
+ DEV_INFO("%s: Recvd Set Stream or Routing Change cmd\n",
+ __func__);
+
+ out_msg.sender_id = 0x4;
+ out_msg.recvr_id = 0xF; /* broadcast this message */
+ out_msg.opcode = 0x82; /* Active Source */
+ out_msg.operand[i++] = 0x10;
+ out_msg.operand[i++] = 0x0;
+ out_msg.frame_size = i + 2;
+
+ rc = hdmi_cec_msg_send(cec_ctrl, &out_msg);
+
+ /* todo: check if need to wait for msg response from sink */
+
+ /* sending <Image View On> message */
+ memset(&out_msg, 0x0, sizeof(struct hdmi_cec_msg));
+ i = 0;
+ out_msg.sender_id = 0x4;
+ out_msg.recvr_id = in_msg->sender_id;
+ out_msg.opcode = 0x04; /* opcode for Image View On */
+ out_msg.frame_size = i + 2;
+
+ rc = hdmi_cec_msg_send(cec_ctrl, &out_msg);
+ break;
+ case 0x44:
+ /* User Control Pressed */
+ DEV_INFO("%s: User Control Pressed\n", __func__);
+ break;
+ case 0x45:
+ /* User Control Released */
+ DEV_INFO("%s: User Control Released\n", __func__);
+ break;
+ default:
+ DEV_INFO("%s: Recvd an unknown cmd = [%u]\n", __func__,
+ in_msg->opcode);
+
+ /* reason = "Unrecognized opcode" */
+ rc = hdmi_cec_send_abort_opcode(cec_ctrl, in_msg, 0x0);
+ break;
+ }
+
+ return rc;
+} /* hdmi_cec_msg_parser */
+
+static int hdmi_cec_msg_send(struct hdmi_cec_ctrl *cec_ctrl,
+ struct hdmi_cec_msg *msg)
+{
+ int i, line_check_retry = 10;
+ u32 frame_retransmit = RETRANSMIT_MAX_NUM;
+ bool frame_type;
+ unsigned long flags;
+ struct dss_io_data *io = NULL;
+
+ if (!cec_ctrl || !cec_ctrl->init_data.io || !msg) {
+ DEV_ERR("%s: Invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ io = cec_ctrl->init_data.io;
+
+ INIT_COMPLETION(cec_ctrl->cec_msg_wr_done);
+ cec_ctrl->cec_msg_wr_status = 0;
+ frame_type = (msg->recvr_id == 15 ? BIT(0) : 0);
+ if (msg->retransmit > 0 && msg->retransmit < RETRANSMIT_MAX_NUM)
+ frame_retransmit = msg->retransmit;
+
+ /* toggle cec in order to flush out bad hw state, if any */
+ DSS_REG_W(io, HDMI_CEC_CTRL, 0);
+ DSS_REG_W(io, HDMI_CEC_CTRL, BIT(0));
+
+ frame_retransmit = (frame_retransmit & 0xF) << 4;
+ DSS_REG_W(io, HDMI_CEC_RETRANSMIT, BIT(0) | frame_retransmit);
+
+ /* header block */
+ DSS_REG_W_ND(io, HDMI_CEC_WR_DATA,
+ (((msg->sender_id << 4) | msg->recvr_id) << 8) | frame_type);
+
+ /* data block 0 : opcode */
+ DSS_REG_W_ND(io, HDMI_CEC_WR_DATA,
+ ((msg->frame_size < 2 ? 0 : msg->opcode) << 8) | frame_type);
+
+ /* data block 1-14 : operand 0-13 */
+ for (i = 0; i < msg->frame_size - 2; i++)
+ DSS_REG_W_ND(io, HDMI_CEC_WR_DATA,
+ (msg->operand[i] << 8) | frame_type);
+
+ while ((DSS_REG_R(io, HDMI_CEC_STATUS) & BIT(0)) &&
+ line_check_retry--) {
+ DEV_DBG("%s: CEC line is busy(%d)\n", __func__,
+ line_check_retry);
+ schedule();
+ }
+
+ if (!line_check_retry && (DSS_REG_R(io, HDMI_CEC_STATUS) & BIT(0))) {
+ DEV_ERR("%s: CEC line is busy. Retry\n", __func__);
+ return -EAGAIN;
+ }
+
+ /* start transmission */
+ DSS_REG_W(io, HDMI_CEC_CTRL, BIT(0) | BIT(1) |
+ ((msg->frame_size & 0x1F) << 4) | BIT(9));
+
+ if (!wait_for_completion_interruptible_timeout(
+ &cec_ctrl->cec_msg_wr_done, HZ)) {
+ DEV_ERR("%s: timedout", __func__);
+ hdmi_cec_dump_msg(cec_ctrl, msg);
+ return -ETIMEDOUT;
+ }
+
+ spin_lock_irqsave(&cec_ctrl->lock, flags);
+ if (cec_ctrl->cec_msg_wr_status == CEC_STATUS_WR_ERROR)
+ DEV_ERR("%s: msg write failed.\n", __func__);
+ else
+ DEV_DBG("%s: CEC write frame done (frame len=%d)", __func__,
+ msg->frame_size);
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+ hdmi_cec_dump_msg(cec_ctrl, msg);
+
+ return 0;
+} /* hdmi_cec_msg_send */
+
+static void hdmi_cec_msg_recv(struct work_struct *work)
+{
+ int i;
+ u32 data;
+ unsigned long flags;
+ struct hdmi_cec_ctrl *cec_ctrl = NULL;
+ struct dss_io_data *io = NULL;
+ struct hdmi_cec_msg_node *msg_node = NULL;
+
+ cec_ctrl = container_of(work, struct hdmi_cec_ctrl, cec_read_work);
+ if (!cec_ctrl || !cec_ctrl->cec_enabled || !cec_ctrl->init_data.io) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ io = cec_ctrl->init_data.io;
+
+ msg_node = kzalloc(sizeof(*msg_node), GFP_KERNEL);
+ if (!msg_node) {
+ DEV_ERR("%s: FAILED: out of memory\n", __func__);
+ return;
+ }
+
+ data = DSS_REG_R(io, HDMI_CEC_RD_DATA);
+
+ msg_node->msg.recvr_id = (data & 0x000F);
+ msg_node->msg.sender_id = (data & 0x00F0) >> 4;
+ msg_node->msg.frame_size = (data & 0x1F00) >> 8;
+ DEV_DBG("%s: Recvd init=[%u] dest=[%u] size=[%u]\n", __func__,
+ msg_node->msg.sender_id, msg_node->msg.recvr_id,
+ msg_node->msg.frame_size);
+
+ if (msg_node->msg.frame_size < 1) {
+ DEV_ERR("%s: invalid message (frame length = %d)",
+ __func__, msg_node->msg.frame_size);
+ kfree(msg_node);
+ return;
+ } else if (msg_node->msg.frame_size == 1) {
+ DEV_DBG("%s: polling message (dest[%x] <- init[%x])", __func__,
+ msg_node->msg.recvr_id, msg_node->msg.sender_id);
+ kfree(msg_node);
+ return;
+ }
+
+ /* data block 0 : opcode */
+ data = DSS_REG_R_ND(io, HDMI_CEC_RD_DATA);
+ msg_node->msg.opcode = data & 0xFF;
+
+ /* data block 1-14 : operand 0-13 */
+ for (i = 0; i < msg_node->msg.frame_size - 2; i++) {
+ data = DSS_REG_R_ND(io, HDMI_CEC_RD_DATA);
+ msg_node->msg.operand[i] = data & 0xFF;
+ }
+
+ for (; i < 14; i++)
+ msg_node->msg.operand[i] = 0;
+
+ DEV_DBG("%s: CEC read frame done\n", __func__);
+ hdmi_cec_dump_msg(cec_ctrl, &msg_node->msg);
+
+ spin_lock_irqsave(&cec_ctrl->lock, flags);
+ if (cec_ctrl->compliance_response_enabled) {
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+
+ if (hdmi_cec_msg_parser(cec_ctrl, &msg_node->msg) != 0) {
+ DEV_ERR("%s: cec_msg_parser fail. Sending abort msg\n",
+ __func__);
+ /* reason = "Unrecognized opcode" */
+ hdmi_cec_send_abort_opcode(cec_ctrl,
+ &msg_node->msg, 0x0);
+ }
+ kfree(msg_node);
+ } else {
+ list_add_tail(&msg_node->list, &cec_ctrl->msg_head);
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+
+ /* wake-up sysfs read_msg context */
+ sysfs_notify(cec_ctrl->init_data.sysfs_kobj, "cec", "rd_msg");
+ }
+} /* hdmi_cec_msg_recv*/
+
+static ssize_t hdmi_rda_cec_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t ret;
+ unsigned long flags;
+ struct hdmi_cec_ctrl *cec_ctrl =
+ hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_CEC);
+
+ if (!cec_ctrl || !cec_ctrl->init_data.io) {
+ DEV_ERR("%s: Invalid input\n", __func__);
+ return -EPERM;
+ }
+
+ spin_lock_irqsave(&cec_ctrl->lock, flags);
+ if (cec_ctrl->cec_enabled && cec_ctrl->cec_engine_configed) {
+ DEV_DBG("%s: cec is enabled\n", __func__);
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", 1);
+ } else if (cec_ctrl->cec_enabled && !cec_ctrl->cec_engine_configed) {
+ DEV_ERR("%s: CEC will be enabled when HDMI mirroring is on\n",
+ __func__);
+ ret = -EPERM;
+ } else {
+ DEV_DBG("%s: cec is disabled\n", __func__);
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", 0);
+ }
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+
+ return ret;
+} /* hdmi_rda_cec_enable */
+
+static ssize_t hdmi_wta_cec_enable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int val;
+ bool cec_en;
+ unsigned long flags;
+ ssize_t ret = strnlen(buf, PAGE_SIZE);
+ struct hdmi_cec_ctrl *cec_ctrl =
+ hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_CEC);
+
+ if (!cec_ctrl) {
+ DEV_ERR("%s: Invalid input\n", __func__);
+ return -EPERM;
+ }
+
+ if (kstrtoint(buf, 10, &val)) {
+ DEV_ERR("%s: kstrtoint failed.\n", __func__);
+ return -EPERM;
+ }
+ cec_en = (val == 1) ? true : false;
+
+ spin_lock_irqsave(&cec_ctrl->lock, flags);
+ if (cec_ctrl->cec_enabled == cec_en) {
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+ DEV_INFO("%s: cec is already %s\n", __func__,
+ cec_en ? "enabled" : "disabled");
+ return ret;
+ }
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+
+ if (!cec_en) {
+ spin_lock_irqsave(&cec_ctrl->lock, flags);
+ if (!cec_ctrl->cec_engine_configed) {
+ DEV_DBG("%s: hdmi is already off. disable cec\n",
+ __func__);
+ cec_ctrl->cec_enabled = false;
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+ return ret;
+ }
+ cec_ctrl->cec_enabled = false;
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+
+ hdmi_cec_disable(cec_ctrl);
+ return ret;
+ } else {
+ spin_lock_irqsave(&cec_ctrl->lock, flags);
+ if (!cec_ctrl->cec_engine_configed) {
+ DEV_DBG("%s: CEC will be enabled on mirroring\n",
+ __func__);
+ cec_ctrl->cec_enabled = true;
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+ return ret;
+ }
+ cec_ctrl->cec_enabled = true;
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+
+ hdmi_cec_enable(cec_ctrl);
+
+ return ret;
+ }
+} /* hdmi_wta_cec_enable */
+
+static ssize_t hdmi_rda_cec_enable_compliance(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned long flags;
+ ssize_t ret;
+ struct hdmi_cec_ctrl *cec_ctrl =
+ hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_CEC);
+
+ if (!cec_ctrl) {
+ DEV_ERR("%s: Invalid cec_ctrl\n", __func__);
+ return -EPERM;
+ }
+
+ spin_lock_irqsave(&cec_ctrl->lock, flags);
+ ret = snprintf(buf, PAGE_SIZE, "%d\n",
+ cec_ctrl->compliance_response_enabled);
+
+ cec_ctrl->cec_logical_addr = 0x4;
+ hdmi_cec_write_logical_addr(cec_ctrl, cec_ctrl->cec_logical_addr);
+
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+
+ return ret;
+} /* hdmi_rda_cec_enable_compliance */
+
+static ssize_t hdmi_wta_cec_enable_compliance(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int val;
+ unsigned long flags;
+ ssize_t ret = strnlen(buf, PAGE_SIZE);
+ struct hdmi_cec_ctrl *cec_ctrl =
+ hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_CEC);
+
+ if (!cec_ctrl) {
+ DEV_ERR("%s: Invalid cec_ctrl\n", __func__);
+ return -EPERM;
+ }
+
+ spin_lock_irqsave(&cec_ctrl->lock, flags);
+ if (cec_ctrl->cec_enabled && cec_ctrl->cec_engine_configed) {
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+ DEV_ERR("%s: Cannot en/dis compliance when CEC session is on\n",
+ __func__);
+ return -EPERM;
+ } else {
+ if (kstrtoint(buf, 10, &val)) {
+ DEV_ERR("%s: kstrtoint failed.\n", __func__);
+ return -EPERM;
+ }
+ cec_ctrl->compliance_response_enabled =
+ (val == 1) ? true : false;
+ }
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+
+ return ret;
+} /* hdmi_wta_cec_enable_compliance */
+
+static ssize_t hdmi_rda_cec_logical_addr(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned long flags;
+ ssize_t ret = strnlen(buf, PAGE_SIZE);
+ struct hdmi_cec_ctrl *cec_ctrl =
+ hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_CEC);
+
+ if (!cec_ctrl) {
+ DEV_ERR("%s: Invalid cec_ctrl\n", __func__);
+ return -EPERM;
+ }
+
+ spin_lock_irqsave(&cec_ctrl->lock, flags);
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", cec_ctrl->cec_logical_addr);
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+
+ return ret;
+} /* hdmi_rda_cec_logical_addr */
+
+static ssize_t hdmi_wta_cec_logical_addr(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int logical_addr;
+ unsigned long flags;
+ ssize_t ret = strnlen(buf, PAGE_SIZE);
+ struct hdmi_cec_ctrl *cec_ctrl =
+ hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_CEC);
+
+ if (!cec_ctrl) {
+ DEV_ERR("%s: Invalid cec_ctrl\n", __func__);
+ return -EPERM;
+ }
+
+ if (kstrtoint(buf, 10, &logical_addr)) {
+ DEV_ERR("%s: kstrtoint failed\n", __func__);
+ return -EPERM;
+ }
+
+ if (logical_addr < 0 || logical_addr > 15) {
+ DEV_ERR("%s: Invalid logical address\n", __func__);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&cec_ctrl->lock, flags);
+ cec_ctrl->cec_logical_addr = (u8)logical_addr;
+ if (cec_ctrl->cec_enabled && cec_ctrl->cec_engine_configed)
+ hdmi_cec_write_logical_addr(cec_ctrl,
+ cec_ctrl->cec_logical_addr);
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+
+ return ret;
+} /* hdmi_wta_cec_logical_addr */
+
+static ssize_t hdmi_rda_cec_msg(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i = 0;
+ unsigned long flags;
+ struct hdmi_cec_msg_node *msg_node, *tmp;
+ struct hdmi_cec_ctrl *cec_ctrl =
+ hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_CEC);
+
+ if (!cec_ctrl) {
+ DEV_ERR("%s: Invalid cec_ctrl\n", __func__);
+ return -EPERM;
+ }
+
+ spin_lock_irqsave(&cec_ctrl->lock, flags);
+
+ if (cec_ctrl->compliance_response_enabled) {
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+ DEV_ERR("%s: Read is disabled coz compliance response is on\n",
+ __func__);
+ return -EPERM;
+ }
+
+ if (list_empty_careful(&cec_ctrl->msg_head)) {
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+ DEV_ERR("%s: CEC message queue is empty\n", __func__);
+ return -EPERM;
+ }
+
+ list_for_each_entry_safe(msg_node, tmp, &cec_ctrl->msg_head, list) {
+ if ((i+1) * sizeof(struct hdmi_cec_msg) > PAGE_SIZE) {
+ DEV_DBG("%s: Overflowing PAGE_SIZE.\n", __func__);
+ break;
+ }
+
+ memcpy(buf + (i * sizeof(struct hdmi_cec_msg)), &msg_node->msg,
+ sizeof(struct hdmi_cec_msg));
+ list_del(&msg_node->list);
+ kfree(msg_node);
+ i++;
+ }
+
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+
+ return i * sizeof(struct hdmi_cec_msg);
+} /* hdmi_rda_cec_msg */
+
+static ssize_t hdmi_wta_cec_msg(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc;
+ unsigned long flags;
+ struct hdmi_cec_msg *msg = (struct hdmi_cec_msg *)buf;
+ struct hdmi_cec_ctrl *cec_ctrl =
+ hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_CEC);
+
+ if (!cec_ctrl) {
+ DEV_ERR("%s: Invalid cec_ctrl\n", __func__);
+ return -EPERM;
+ }
+
+ spin_lock_irqsave(&cec_ctrl->lock, flags);
+ if (cec_ctrl->compliance_response_enabled) {
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+ DEV_ERR("%s: Write disabled coz compliance response is on.\n",
+ __func__);
+ return -EPERM;
+ }
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+
+ rc = hdmi_cec_msg_send(cec_ctrl, msg);
+ if (rc) {
+ DEV_ERR("%s: hdmi_cec_msg_send failed\n", __func__);
+ return rc;
+ } else {
+ return sizeof(struct hdmi_cec_msg);
+ }
+} /* hdmi_wta_cec_msg */
+
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, hdmi_rda_cec_enable,
+ hdmi_wta_cec_enable);
+static DEVICE_ATTR(enable_compliance, S_IRUGO | S_IWUSR,
+ hdmi_rda_cec_enable_compliance, hdmi_wta_cec_enable_compliance);
+static DEVICE_ATTR(logical_addr, S_IRUGO | S_IWUSR,
+ hdmi_rda_cec_logical_addr, hdmi_wta_cec_logical_addr);
+static DEVICE_ATTR(rd_msg, S_IRUGO, hdmi_rda_cec_msg, NULL);
+static DEVICE_ATTR(wr_msg, S_IWUSR, NULL, hdmi_wta_cec_msg);
+
+static struct attribute *hdmi_cec_fs_attrs[] = {
+ &dev_attr_enable.attr,
+ &dev_attr_enable_compliance.attr,
+ &dev_attr_logical_addr.attr,
+ &dev_attr_rd_msg.attr,
+ &dev_attr_wr_msg.attr,
+ NULL,
+};
+
+static struct attribute_group hdmi_cec_fs_attr_group = {
+ .name = "cec",
+ .attrs = hdmi_cec_fs_attrs,
+};
+
+int hdmi_cec_isr(void *input)
+{
+ int rc = 0;
+ u32 cec_intr, cec_status;
+ unsigned long flags;
+ struct dss_io_data *io = NULL;
+ struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)input;
+
+ if (!cec_ctrl || !cec_ctrl->init_data.io) {
+ DEV_ERR("%s: Invalid input\n", __func__);
+ return -EPERM;
+ }
+
+ io = cec_ctrl->init_data.io;
+
+ if (!cec_ctrl->cec_enabled)
+ return 0;
+
+ cec_intr = DSS_REG_R_ND(io, HDMI_CEC_INT);
+ DEV_DBG("%s: cec interrupt status is [0x%x]\n", __func__, cec_intr);
+
+ cec_status = DSS_REG_R_ND(io, HDMI_CEC_STATUS);
+ DEV_DBG("%s: cec status is [0x%x]\n", __func__, cec_status);
+
+ if ((cec_intr & BIT(0)) && (cec_intr & BIT(1))) {
+ DEV_DBG("%s: CEC_IRQ_FRAME_WR_DONE\n", __func__);
+ DSS_REG_W(io, HDMI_CEC_INT, cec_intr | BIT(0));
+
+ spin_lock_irqsave(&cec_ctrl->lock, flags);
+ cec_ctrl->cec_msg_wr_status |= CEC_STATUS_WR_DONE;
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+
+ if (!completion_done(&cec_ctrl->cec_msg_wr_done))
+ complete_all(&cec_ctrl->cec_msg_wr_done);
+ }
+
+ if ((cec_intr & BIT(2)) && (cec_intr & BIT(3))) {
+ DEV_DBG("%s: CEC_IRQ_FRAME_ERROR\n", __func__);
+ DSS_REG_W(io, HDMI_CEC_INT, cec_intr | BIT(2));
+
+ spin_lock_irqsave(&cec_ctrl->lock, flags);
+ cec_ctrl->cec_msg_wr_status |= CEC_STATUS_WR_ERROR;
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+
+ if (!completion_done(&cec_ctrl->cec_msg_wr_done))
+ complete_all(&cec_ctrl->cec_msg_wr_done);
+ }
+
+ if ((cec_intr & BIT(6)) && (cec_intr & BIT(7))) {
+ DEV_DBG("%s: CEC_IRQ_FRAME_RD_DONE\n", __func__);
+
+ DSS_REG_W(io, HDMI_CEC_INT, cec_intr | BIT(6));
+ queue_work(cec_ctrl->init_data.workq, &cec_ctrl->cec_read_work);
+ }
+
+ return rc;
+} /* hdmi_cec_isr */
+
+int hdmi_cec_deconfig(void *input)
+{
+ unsigned long flags;
+ struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)input;
+
+ if (!cec_ctrl) {
+ DEV_ERR("%s: Invalid input\n", __func__);
+ return -EPERM;
+ }
+
+ hdmi_cec_disable(cec_ctrl);
+
+ spin_lock_irqsave(&cec_ctrl->lock, flags);
+ cec_ctrl->cec_engine_configed = false;
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+
+ return 0;
+} /* hdmi_cec_deconfig */
+
+int hdmi_cec_config(void *input)
+{
+ unsigned long flags;
+ u32 hdmi_hw_version;
+ struct dss_io_data *io = NULL;
+ struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)input;
+
+ if (!cec_ctrl || !cec_ctrl->init_data.io) {
+ DEV_ERR("%s: Invalid input\n", __func__);
+ return -EPERM;
+ }
+
+ io = cec_ctrl->init_data.io;
+
+ /* 19.2Mhz * 0.00005 us = 950 = 0x3B6 */
+ DSS_REG_W(io, HDMI_CEC_REFTIMER, (0x3B6 & 0xFFF) | BIT(16));
+
+ hdmi_hw_version = DSS_REG_R(io, HDMI_VERSION);
+ if (hdmi_hw_version == 0x30000001) {
+ DSS_REG_W(io, HDMI_CEC_RD_RANGE, 0x30AB9888);
+ DSS_REG_W(io, HDMI_CEC_WR_RANGE, 0x888AA888);
+
+ DSS_REG_W(io, HDMI_CEC_RD_START_RANGE, 0x88888888);
+ DSS_REG_W(io, HDMI_CEC_RD_TOTAL_RANGE, 0x99);
+ DSS_REG_W(io, HDMI_CEC_COMPL_CTL, 0xF);
+ DSS_REG_W(io, HDMI_CEC_WR_CHECK_CONFIG, 0x4);
+ } else {
+ DEV_INFO("%s: CEC is not supported on %d HDMI HW version.\n",
+ __func__, hdmi_hw_version);
+ return -EPERM;
+ }
+
+ DSS_REG_W(io, HDMI_CEC_RD_FILTER, BIT(0) | (0x7FF << 4));
+ DSS_REG_W(io, HDMI_CEC_TIME, BIT(0) | ((7 * 0x30) << 7));
+
+ if (cec_ctrl->cec_enabled)
+ hdmi_cec_enable(cec_ctrl);
+
+ spin_lock_irqsave(&cec_ctrl->lock, flags);
+ cec_ctrl->cec_engine_configed = true;
+ spin_unlock_irqrestore(&cec_ctrl->lock, flags);
+
+ return 0;
+} /* hdmi_cec_config */
+
+void hdmi_cec_deinit(void *input)
+{
+ struct hdmi_cec_msg_node *msg_node, *tmp;
+ struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)input;
+
+ if (cec_ctrl) {
+ list_for_each_entry_safe(msg_node, tmp, &cec_ctrl->msg_head,
+ list) {
+ list_del(&msg_node->list);
+ kfree(msg_node);
+ }
+
+ sysfs_remove_group(cec_ctrl->init_data.sysfs_kobj,
+ &hdmi_cec_fs_attr_group);
+
+ kfree(cec_ctrl);
+ }
+} /* hdmi_cec_deinit */
+
+void *hdmi_cec_init(struct hdmi_cec_init_data *init_data)
+{
+ struct hdmi_cec_ctrl *cec_ctrl = NULL;
+
+ if (!init_data) {
+ DEV_ERR("%s: Invalid input\n", __func__);
+ goto error;
+ }
+
+ cec_ctrl = kzalloc(sizeof(*cec_ctrl), GFP_KERNEL);
+ if (!cec_ctrl) {
+ DEV_ERR("%s: FAILED: out of memory\n", __func__);
+ goto error;
+ }
+
+ cec_ctrl->init_data = *init_data;
+
+ if (sysfs_create_group(init_data->sysfs_kobj,
+ &hdmi_cec_fs_attr_group)) {
+ DEV_ERR("%s: cec sysfs group creation failed\n", __func__);
+ goto error;
+ }
+
+ spin_lock_init(&cec_ctrl->lock);
+ INIT_LIST_HEAD(&cec_ctrl->msg_head);
+ INIT_WORK(&cec_ctrl->cec_read_work, hdmi_cec_msg_recv);
+ init_completion(&cec_ctrl->cec_msg_wr_done);
+
+ goto exit;
+
+error:
+ kfree(cec_ctrl);
+ cec_ctrl = NULL;
+exit:
+ return (void *)cec_ctrl;
+} /* hdmi_cec_init */
diff --git a/drivers/video/msm/mdss/mdss_hdmi_cec.h b/drivers/video/msm/mdss/mdss_hdmi_cec.h
new file mode 100644
index 0000000..a554507
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_hdmi_cec.h
@@ -0,0 +1,29 @@
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MDSS_HDMI_CEC_H__
+#define __MDSS_HDMI_CEC_H__
+
+#include "mdss_hdmi_util.h"
+
+struct hdmi_cec_init_data {
+ struct workqueue_struct *workq;
+ struct kobject *sysfs_kobj;
+ struct dss_io_data *io;
+};
+
+int hdmi_cec_deconfig(void *cec_ctrl);
+int hdmi_cec_config(void *cec_ctrl);
+int hdmi_cec_isr(void *cec_ctrl);
+void hdmi_cec_deinit(void *cec_ctrl);
+void *hdmi_cec_init(struct hdmi_cec_init_data *init_data);
+#endif /* __MDSS_HDMI_CEC_H__ */
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.c b/drivers/video/msm/mdss/mdss_hdmi_tx.c
index 66936e5..e28a4e9 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.c
@@ -26,9 +26,10 @@
#include "mdss_debug.h"
#include "mdss_fb.h"
-#include "mdss_hdmi_tx.h"
+#include "mdss_hdmi_cec.h"
#include "mdss_hdmi_edid.h"
#include "mdss_hdmi_hdcp.h"
+#include "mdss_hdmi_tx.h"
#include "mdss.h"
#include "mdss_panel.h"
#include "mdss_hdmi_mhl.h"
@@ -644,12 +645,14 @@
{
struct hdmi_edid_init_data edid_init_data;
struct hdmi_hdcp_init_data hdcp_init_data;
+ struct hdmi_cec_init_data cec_init_data;
if (!hdmi_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
return -EINVAL;
}
+ /* Initialize EDID feature */
edid_init_data.io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
edid_init_data.mutex = &hdmi_ctrl->mutex;
edid_init_data.sysfs_kobj = hdmi_ctrl->kobj;
@@ -688,6 +691,17 @@
DEV_DBG("%s: HDCP feature initialized\n", __func__);
}
+
+ /* Initialize CEC feature */
+ cec_init_data.io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
+ cec_init_data.sysfs_kobj = hdmi_ctrl->kobj;
+ cec_init_data.workq = hdmi_ctrl->workq;
+
+ hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC] =
+ hdmi_cec_init(&cec_init_data);
+ if (!hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC])
+ DEV_WARN("%s: hdmi_cec_init failed\n", __func__);
+
return 0;
} /* hdmi_tx_init_features */
@@ -2169,6 +2183,8 @@
hdmi_ctrl->hpd_off_pending = false;
}
+ hdmi_cec_deconfig(hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC]);
+
mutex_lock(&hdmi_ctrl->mutex);
hdmi_ctrl->panel_power_on = false;
mutex_unlock(&hdmi_ctrl->mutex);
@@ -2253,10 +2269,12 @@
mutex_lock(&hdmi_ctrl->mutex);
hdmi_ctrl->panel_power_on = true;
+ mutex_unlock(&hdmi_ctrl->mutex);
+
+ hdmi_cec_config(hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC]);
if (hdmi_ctrl->hpd_state) {
DEV_DBG("%s: Turning HDMI on\n", __func__);
- mutex_unlock(&hdmi_ctrl->mutex);
rc = hdmi_tx_start(hdmi_ctrl);
if (rc) {
DEV_ERR("%s: hdmi_tx_start failed. rc=%d\n",
@@ -2264,8 +2282,6 @@
hdmi_tx_power_off(panel_data);
return rc;
}
- } else {
- mutex_unlock(&hdmi_ctrl->mutex);
}
dss_reg_dump(io->base, io->len, "HDMI-ON: ", REG_DUMP);
@@ -2345,6 +2361,10 @@
hdmi_ctrl->hpd_initialized = true;
+ DEV_INFO("%s: HDMI HW version = 0x%x\n", __func__,
+ DSS_REG_R_ND(&hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO],
+ HDMI_VERSION));
+
/* set timeout to 4.1ms (max) for hardware debounce */
reg_val = DSS_REG_R(io, HDMI_HPD_CTRL) | 0x1FFF;
@@ -2415,12 +2435,15 @@
queue_work(hdmi_ctrl->workq, &hdmi_ctrl->hpd_int_work);
}
- if (hdmi_ddc_isr(&hdmi_ctrl->ddc_ctrl) < 0)
+ if (hdmi_ddc_isr(&hdmi_ctrl->ddc_ctrl))
DEV_ERR("%s: hdmi_ddc_isr failed\n", __func__);
+ if (hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC])
+ if (hdmi_cec_isr(hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC]))
+ DEV_ERR("%s: hdmi_cec_isr failed\n", __func__);
+
if (hdmi_ctrl->feature_data[HDMI_TX_FEAT_HDCP])
- if (hdmi_hdcp_isr(
- hdmi_ctrl->feature_data[HDMI_TX_FEAT_HDCP]) < 0)
+ if (hdmi_hdcp_isr(hdmi_ctrl->feature_data[HDMI_TX_FEAT_HDCP]))
DEV_ERR("%s: hdmi_hdcp_isr failed\n", __func__);
return IRQ_HANDLED;
@@ -2433,13 +2456,20 @@
return;
}
+ if (hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC]) {
+ hdmi_cec_deinit(hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC]);
+ hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC] = NULL;
+ }
+
if (hdmi_ctrl->feature_data[HDMI_TX_FEAT_HDCP]) {
hdmi_hdcp_deinit(hdmi_ctrl->feature_data[HDMI_TX_FEAT_HDCP]);
hdmi_ctrl->feature_data[HDMI_TX_FEAT_HDCP] = NULL;
}
- if (hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID])
+ if (hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID]) {
hdmi_edid_deinit(hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID]);
+ hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID] = NULL;
+ }
switch_dev_unregister(&hdmi_ctrl->audio_sdev);
switch_dev_unregister(&hdmi_ctrl->sdev);
diff --git a/drivers/video/msm/mdss/mdss_hdmi_util.h b/drivers/video/msm/mdss/mdss_hdmi_util.h
index cf42346..e99e549 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_util.h
+++ b/drivers/video/msm/mdss/mdss_hdmi_util.h
@@ -201,6 +201,11 @@
#define HDMI_TPG_INITIAL_VALUE (0x00000354)
#define HDMI_TPG_BLK_WHT_PATTERN_FRAMES (0x00000358)
#define HDMI_TPG_RGB_MAPPING (0x0000035C)
+#define HDMI_CEC_COMPL_CTL (0x00000360)
+#define HDMI_CEC_RD_START_RANGE (0x00000364)
+#define HDMI_CEC_RD_TOTAL_RANGE (0x00000368)
+#define HDMI_CEC_RD_ERR_RESP_LO (0x0000036C)
+#define HDMI_CEC_WR_CHECK_CONFIG (0x00000370)
/* HDMI PHY Registers */
#define HDMI_PHY_ANA_CFG0 (0x00000000)
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index 1ced200..fa53656 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -986,6 +986,19 @@
if (ret) {
pr_warn("error powering off intf ctl=%d\n", ctl->num);
} else {
+ mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_TOP, 0);
+ if (sctl)
+ mdss_mdp_ctl_write(sctl, MDSS_MDP_REG_CTL_TOP, 0);
+
+ if (ctl->mixer_left) {
+ mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_LAYER(
+ ctl->mixer_left->num), 0);
+ }
+ if (ctl->mixer_right) {
+ mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_LAYER(
+ ctl->mixer_right->num), 0);
+ }
+
ctl->power_on = false;
ctl->play_cnt = 0;
ctl->clk_rate = 0;
diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c
index 75fb7d6..2c0d5e0 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pp.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pp.c
@@ -524,7 +524,7 @@
struct mdss_data_type *mdata;
mdata = mdss_mdp_get_mdata();
- if (mdata->mdp_rev >= MDSS_MDP_HW_REV_102)
+ if (mdata->mdp_rev >= MDSS_MDP_HW_REV_102 && pipe->src_fmt->is_yuv)
filter_mode = MDSS_MDP_SCALE_FILTER_CA;
else
filter_mode = MDSS_MDP_SCALE_FILTER_BIL;
diff --git a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
index 5c15d9a..f5d7947 100644
--- a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
+++ b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
@@ -800,9 +800,9 @@
if (resource_context.vidc_platform_data->enable_ion) {
if (res_trk_check_for_sec_session()) {
if (resource_context.res_mem_type != DDL_FW_MEM)
- flags |= ION_SECURE;
+ flags |= ION_FLAG_SECURE;
else if (res_trk_is_cp_enabled())
- flags |= ION_SECURE;
+ flags |= ION_FLAG_SECURE;
}
}
return flags;
diff --git a/include/linux/dvb/dmx.h b/include/linux/dvb/dmx.h
index 6e50578..19face8 100644
--- a/include/linux/dvb/dmx.h
+++ b/include/linux/dvb/dmx.h
@@ -153,7 +153,7 @@
* DMX_EVENT_NEW_REC_CHUNK will be triggered.
* When new recorded data is received with size
* equal or larger than this value a new event
- * will be triggered. This is relevent when
+ * will be triggered. This is relevant when
* output is DMX_OUT_TS_TAP or DMX_OUT_TSDEMUX_TAP,
* size must be at least DMX_REC_BUFF_CHUNK_MIN_SIZE
* and smaller than buffer size.
@@ -210,7 +210,18 @@
DMX_EVENT_EOS = 0x00000040,
/* New Elementary Stream data is ready */
- DMX_EVENT_NEW_ES_DATA = 0x00000080
+ DMX_EVENT_NEW_ES_DATA = 0x00000080,
+
+ /* Data markers */
+ DMX_EVENT_MARKER = 0x00000100
+};
+
+enum dmx_oob_cmd {
+ /* End-of-stream, no more data from this filter */
+ DMX_OOB_CMD_EOS,
+
+ /* Data markers */
+ DMX_OOB_CMD_MARKER,
};
/* Flags passed in filter events */
@@ -360,6 +371,12 @@
__u32 ts_dropped_bytes;
};
+/* Marker details associated with DMX_EVENT_MARKER event */
+struct dmx_marker_event_info {
+ /* Marker id */
+ __u64 id;
+};
+
/*
* Filter's event returned through DMX_GET_EVENT.
* poll with POLLPRI would block until events are available.
@@ -373,6 +390,7 @@
struct dmx_rec_chunk_event_info recording_chunk;
struct dmx_pcr_event_info pcr;
struct dmx_es_data_event_info es_data;
+ struct dmx_marker_event_info marker;
} params;
};
@@ -406,6 +424,15 @@
#define DMX_BUFFER_LINEAR_GROUP_SUPPORT 0x10
};
+/* Out-of-band (OOB) command */
+struct dmx_oob_command {
+ enum dmx_oob_cmd type;
+
+ union {
+ struct dmx_marker_event_info marker;
+ } params;
+};
+
typedef struct dmx_caps {
__u32 caps;
@@ -641,6 +668,6 @@
#define DMX_SET_SECURE_MODE _IOW('o', 65, struct dmx_secure_mode)
#define DMX_SET_EVENTS_MASK _IOW('o', 66, struct dmx_events_mask)
#define DMX_GET_EVENTS_MASK _IOR('o', 67, struct dmx_events_mask)
-
+#define DMX_PUSH_OOB_COMMAND _IOW('o', 68, struct dmx_oob_command)
#endif /*_DVBDMX_H_*/
diff --git a/include/linux/mfd/pm8xxx/batterydata-lib.h b/include/linux/mfd/pm8xxx/batterydata-lib.h
index f27ceca..df9569b 100644
--- a/include/linux/mfd/pm8xxx/batterydata-lib.h
+++ b/include/linux/mfd/pm8xxx/batterydata-lib.h
@@ -91,6 +91,8 @@
* compensate for battery capacitance.
* @rbatt_capacitve_mohm: the resistance to be added to compensate for
* battery capacitance
+ * @flat_ocv_threshold_uv: the voltage where the battery's discharge curve
+ * starts flattening out.
*/
struct bms_battery_data {
@@ -103,6 +105,7 @@
int default_rbatt_mohm;
int delta_rbatt_mohm;
int rbatt_capacitive_mohm;
+ int flat_ocv_threshold_uv;
};
#if defined(CONFIG_PM8921_BMS) || \
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 05271ba..3b5742e 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -21,6 +21,8 @@
struct mmc_ios {
unsigned int clock; /* clock rate */
+ unsigned int old_rate; /* saved clock rate */
+ unsigned long clk_ts; /* time stamp of last updated clock */
unsigned short vdd;
/* vdd stores the bit number of the selected voltage range from below. */
diff --git a/include/linux/msm_ion.h b/include/linux/msm_ion.h
index 37c935d..3c3c7a9 100644
--- a/include/linux/msm_ion.h
+++ b/include/linux/msm_ion.h
@@ -38,7 +38,7 @@
ION_MM_FIRMWARE_HEAP_ID = 29,
ION_SYSTEM_HEAP_ID = 30,
- ION_HEAP_ID_RESERVED = 31 /** Bit reserved for ION_SECURE flag */
+ ION_HEAP_ID_RESERVED = 31 /** Bit reserved for ION_FLAG_SECURE flag */
};
enum ion_fixed_position {
diff --git a/include/linux/msm_tsens.h b/include/linux/msm_tsens.h
index 5837094..8aa7c17 100644
--- a/include/linux/msm_tsens.h
+++ b/include/linux/msm_tsens.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -41,5 +41,10 @@
int32_t tsens_get_temp(struct tsens_device *dev, unsigned long *temp);
int msm_tsens_early_init(struct tsens_platform_data *pdata);
-
+#if defined(CONFIG_THERMAL_TSENS8974) || defined(CONFIG_THERMAL_TSENS8960)
+int tsens_get_max_sensor_num(uint32_t *tsens_num_sensors);
+#else
+static inline int tsens_get_max_sensor_num(uint32_t *tsens_num_sensors)
+{ return -ENXIO; }
+#endif
#endif /*MSM_TSENS_H */
diff --git a/include/linux/regulator/cpr-regulator.h b/include/linux/regulator/cpr-regulator.h
index 538ad15..b6fc091 100644
--- a/include/linux/regulator/cpr-regulator.h
+++ b/include/linux/regulator/cpr-regulator.h
@@ -54,6 +54,21 @@
NUM_APC_PVS,
};
+/**
+ * enum vdd_mx_vmin_method - Method to determine vmin for vdd-mx
+ * %VDD_MX_VMIN_APC: Equal to APC voltage
+ * %VDD_MX_VMIN_APC_CORNER_CEILING: Equal to PVS corner ceiling voltage
+ * %VDD_MX_VMIN_APC_SLOW_CORNER_CEILING:
+ * Equal to slow speed corner ceiling
+ * %VDD_MX_VMIN_MX_VMAX: Equal to specified vdd-mx-vmax voltage
+ */
+enum vdd_mx_vmin_method {
+ VDD_MX_VMIN_APC,
+ VDD_MX_VMIN_APC_CORNER_CEILING,
+ VDD_MX_VMIN_APC_SLOW_CORNER_CEILING,
+ VDD_MX_VMIN_MX_VMAX,
+};
+
#ifdef CONFIG_MSM_CPR_REGULATOR
int __init cpr_regulator_init(void);
diff --git a/include/media/Kbuild b/include/media/Kbuild
index 16786a9..3992250 100644
--- a/include/media/Kbuild
+++ b/include/media/Kbuild
@@ -1,4 +1,5 @@
header-y += tavarua.h
+header-y += radio-iris-commands.h
header-y += msm_camera.h
header-y += vcap_fmt.h
diff --git a/include/media/radio-iris-commands.h b/include/media/radio-iris-commands.h
new file mode 100644
index 0000000..d41baa9
--- /dev/null
+++ b/include/media/radio-iris-commands.h
@@ -0,0 +1,105 @@
+#ifndef __RADIO_IRIS_COMMANDS_H
+#define __RADIO_IRIS_COMMANDS_H
+
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/atomic.h>
+
+enum v4l2_cid_private_iris_t {
+ V4L2_CID_PRIVATE_IRIS_SRCHMODE = (0x08000000 + 1),
+ V4L2_CID_PRIVATE_IRIS_SCANDWELL,
+ V4L2_CID_PRIVATE_IRIS_SRCHON,
+ V4L2_CID_PRIVATE_IRIS_STATE,
+ V4L2_CID_PRIVATE_IRIS_TRANSMIT_MODE,
+ V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK,
+ V4L2_CID_PRIVATE_IRIS_REGION,
+ V4L2_CID_PRIVATE_IRIS_SIGNAL_TH,
+ V4L2_CID_PRIVATE_IRIS_SRCH_PTY,
+ V4L2_CID_PRIVATE_IRIS_SRCH_PI,
+ V4L2_CID_PRIVATE_IRIS_SRCH_CNT,
+ V4L2_CID_PRIVATE_IRIS_EMPHASIS,
+ V4L2_CID_PRIVATE_IRIS_RDS_STD,
+ V4L2_CID_PRIVATE_IRIS_SPACING,
+ V4L2_CID_PRIVATE_IRIS_RDSON,
+ V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC,
+ V4L2_CID_PRIVATE_IRIS_LP_MODE,
+ V4L2_CID_PRIVATE_IRIS_ANTENNA,
+ V4L2_CID_PRIVATE_IRIS_RDSD_BUF,
+ V4L2_CID_PRIVATE_IRIS_PSALL, /*0x8000014*/
+
+ /*v4l2 Tx controls*/
+ V4L2_CID_PRIVATE_IRIS_TX_SETPSREPEATCOUNT,
+ V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_PS_NAME,
+ V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_RT,
+ V4L2_CID_PRIVATE_IRIS_IOVERC,
+ V4L2_CID_PRIVATE_IRIS_INTDET,
+ V4L2_CID_PRIVATE_IRIS_MPX_DCC,
+ V4L2_CID_PRIVATE_IRIS_AF_JUMP,
+ V4L2_CID_PRIVATE_IRIS_RSSI_DELTA,
+ V4L2_CID_PRIVATE_IRIS_HLSI, /*0x800001d*/
+
+ /*Diagnostic commands*/
+ V4L2_CID_PRIVATE_IRIS_SOFT_MUTE,
+ V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_ADDR,
+ V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_LEN,
+ V4L2_CID_PRIVATE_IRIS_RIVA_PEEK,
+ V4L2_CID_PRIVATE_IRIS_RIVA_POKE,
+ V4L2_CID_PRIVATE_IRIS_SSBI_ACCS_ADDR,
+ V4L2_CID_PRIVATE_IRIS_SSBI_PEEK,
+ V4L2_CID_PRIVATE_IRIS_SSBI_POKE,
+ V4L2_CID_PRIVATE_IRIS_TX_TONE,
+ V4L2_CID_PRIVATE_IRIS_RDS_GRP_COUNTERS,
+ V4L2_CID_PRIVATE_IRIS_SET_NOTCH_FILTER, /* 0x8000028 */
+ V4L2_CID_PRIVATE_IRIS_SET_AUDIO_PATH, /* TAVARUA specific command */
+ V4L2_CID_PRIVATE_IRIS_DO_CALIBRATION,
+ V4L2_CID_PRIVATE_IRIS_SRCH_ALGORITHM, /* TAVARUA specific command */
+ V4L2_CID_PRIVATE_IRIS_GET_SINR,
+ V4L2_CID_PRIVATE_INTF_LOW_THRESHOLD,
+ V4L2_CID_PRIVATE_INTF_HIGH_THRESHOLD,
+ V4L2_CID_PRIVATE_SINR_THRESHOLD,
+ V4L2_CID_PRIVATE_SINR_SAMPLES,
+ V4L2_CID_PRIVATE_SPUR_FREQ,
+ V4L2_CID_PRIVATE_SPUR_FREQ_RMSSI,
+ V4L2_CID_PRIVATE_SPUR_SELECTION,
+ V4L2_CID_PRIVATE_UPDATE_SPUR_TABLE,
+ V4L2_CID_PRIVATE_VALID_CHANNEL,
+ V4L2_CID_PRIVATE_AF_RMSSI_TH,
+ V4L2_CID_PRIVATE_AF_RMSSI_SAMPLES,
+ V4L2_CID_PRIVATE_GOOD_CH_RMSSI_TH,
+ V4L2_CID_PRIVATE_SRCHALGOTYPE,
+ V4L2_CID_PRIVATE_CF0TH12,
+ V4L2_CID_PRIVATE_SINRFIRSTSTAGE,
+ V4L2_CID_PRIVATE_RMSSIFIRSTSTAGE,
+
+ /*using private CIDs under userclass*/
+ V4L2_CID_PRIVATE_IRIS_READ_DEFAULT = 0x00980928,
+ V4L2_CID_PRIVATE_IRIS_WRITE_DEFAULT,
+ V4L2_CID_PRIVATE_IRIS_SET_CALIBRATION,
+};
+
+enum iris_evt_t {
+ IRIS_EVT_RADIO_READY,
+ IRIS_EVT_TUNE_SUCC,
+ IRIS_EVT_SEEK_COMPLETE,
+ IRIS_EVT_SCAN_NEXT,
+ IRIS_EVT_NEW_RAW_RDS,
+ IRIS_EVT_NEW_RT_RDS,
+ IRIS_EVT_NEW_PS_RDS,
+ IRIS_EVT_ERROR,
+ IRIS_EVT_BELOW_TH,
+ IRIS_EVT_ABOVE_TH,
+ IRIS_EVT_STEREO,
+ IRIS_EVT_MONO,
+ IRIS_EVT_RDS_AVAIL,
+ IRIS_EVT_RDS_NOT_AVAIL,
+ IRIS_EVT_NEW_SRCH_LIST,
+ IRIS_EVT_NEW_AF_LIST,
+ IRIS_EVT_TXRDSDAT,
+ IRIS_EVT_TXRDSDONE,
+ IRIS_EVT_RADIO_DISABLED,
+ IRIS_EVT_NEW_ODA,
+ IRIS_EVT_NEW_RT_PLUS,
+ IRIS_EVT_NEW_ERT,
+};
+#endif /* __RADIO_IRIS_COMMANDS_H */
diff --git a/include/media/radio-iris.h b/include/media/radio-iris.h
index 84789f1..53602c5 100644
--- a/include/media/radio-iris.h
+++ b/include/media/radio-iris.h
@@ -1,6 +1,6 @@
/*
*
- * Copyright (c) 2011-2012 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2013 The Linux Foundation. All rights reserved.
*
* This file is based on include/net/bluetooth/hci_core.h
*
@@ -31,6 +31,7 @@
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/atomic.h>
+#include "radio-iris-commands.h"
/* ---- HCI Packet structures ---- */
#define RADIO_HCI_COMMAND_HDR_SIZE sizeof(struct radio_hci_command_hdr)
@@ -621,104 +622,6 @@
FM_CALIB
};
-enum v4l2_cid_private_iris_t {
- V4L2_CID_PRIVATE_IRIS_SRCHMODE = (0x08000000 + 1),
- V4L2_CID_PRIVATE_IRIS_SCANDWELL,
- V4L2_CID_PRIVATE_IRIS_SRCHON,
- V4L2_CID_PRIVATE_IRIS_STATE,
- V4L2_CID_PRIVATE_IRIS_TRANSMIT_MODE,
- V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK,
- V4L2_CID_PRIVATE_IRIS_REGION,
- V4L2_CID_PRIVATE_IRIS_SIGNAL_TH,
- V4L2_CID_PRIVATE_IRIS_SRCH_PTY,
- V4L2_CID_PRIVATE_IRIS_SRCH_PI,
- V4L2_CID_PRIVATE_IRIS_SRCH_CNT,
- V4L2_CID_PRIVATE_IRIS_EMPHASIS,
- V4L2_CID_PRIVATE_IRIS_RDS_STD,
- V4L2_CID_PRIVATE_IRIS_SPACING,
- V4L2_CID_PRIVATE_IRIS_RDSON,
- V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC,
- V4L2_CID_PRIVATE_IRIS_LP_MODE,
- V4L2_CID_PRIVATE_IRIS_ANTENNA,
- V4L2_CID_PRIVATE_IRIS_RDSD_BUF,
- V4L2_CID_PRIVATE_IRIS_PSALL, /*0x8000014*/
-
- /*v4l2 Tx controls*/
- V4L2_CID_PRIVATE_IRIS_TX_SETPSREPEATCOUNT,
- V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_PS_NAME,
- V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_RT,
- V4L2_CID_PRIVATE_IRIS_IOVERC,
- V4L2_CID_PRIVATE_IRIS_INTDET,
- V4L2_CID_PRIVATE_IRIS_MPX_DCC,
- V4L2_CID_PRIVATE_IRIS_AF_JUMP,
- V4L2_CID_PRIVATE_IRIS_RSSI_DELTA,
- V4L2_CID_PRIVATE_IRIS_HLSI, /*0x800001d*/
-
- /*Diagnostic commands*/
- V4L2_CID_PRIVATE_IRIS_SOFT_MUTE,
- V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_ADDR,
- V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_LEN,
- V4L2_CID_PRIVATE_IRIS_RIVA_PEEK,
- V4L2_CID_PRIVATE_IRIS_RIVA_POKE,
- V4L2_CID_PRIVATE_IRIS_SSBI_ACCS_ADDR,
- V4L2_CID_PRIVATE_IRIS_SSBI_PEEK,
- V4L2_CID_PRIVATE_IRIS_SSBI_POKE,
- V4L2_CID_PRIVATE_IRIS_TX_TONE,
- V4L2_CID_PRIVATE_IRIS_RDS_GRP_COUNTERS,
- V4L2_CID_PRIVATE_IRIS_SET_NOTCH_FILTER, /* 0x8000028 */
- V4L2_CID_PRIVATE_IRIS_SET_AUDIO_PATH, /* TAVARUA specific command */
- V4L2_CID_PRIVATE_IRIS_DO_CALIBRATION,
- V4L2_CID_PRIVATE_IRIS_SRCH_ALGORITHM, /* TAVARUA specific command */
- V4L2_CID_PRIVATE_IRIS_GET_SINR,
- V4L2_CID_PRIVATE_INTF_LOW_THRESHOLD,
- V4L2_CID_PRIVATE_INTF_HIGH_THRESHOLD,
- V4L2_CID_PRIVATE_SINR_THRESHOLD,
- V4L2_CID_PRIVATE_SINR_SAMPLES,
- V4L2_CID_PRIVATE_SPUR_FREQ,
- V4L2_CID_PRIVATE_SPUR_FREQ_RMSSI,
- V4L2_CID_PRIVATE_SPUR_SELECTION,
- V4L2_CID_PRIVATE_UPDATE_SPUR_TABLE,
- V4L2_CID_PRIVATE_VALID_CHANNEL,
- V4L2_CID_PRIVATE_AF_RMSSI_TH,
- V4L2_CID_PRIVATE_AF_RMSSI_SAMPLES,
- V4L2_CID_PRIVATE_GOOD_CH_RMSSI_TH,
- V4L2_CID_PRIVATE_SRCHALGOTYPE,
- V4L2_CID_PRIVATE_CF0TH12,
- V4L2_CID_PRIVATE_SINRFIRSTSTAGE,
- V4L2_CID_PRIVATE_RMSSIFIRSTSTAGE,
-
-
- /*using private CIDs under userclass*/
- V4L2_CID_PRIVATE_IRIS_READ_DEFAULT = 0x00980928,
- V4L2_CID_PRIVATE_IRIS_WRITE_DEFAULT,
- V4L2_CID_PRIVATE_IRIS_SET_CALIBRATION,
-};
-
-
-enum iris_evt_t {
- IRIS_EVT_RADIO_READY,
- IRIS_EVT_TUNE_SUCC,
- IRIS_EVT_SEEK_COMPLETE,
- IRIS_EVT_SCAN_NEXT,
- IRIS_EVT_NEW_RAW_RDS,
- IRIS_EVT_NEW_RT_RDS,
- IRIS_EVT_NEW_PS_RDS,
- IRIS_EVT_ERROR,
- IRIS_EVT_BELOW_TH,
- IRIS_EVT_ABOVE_TH,
- IRIS_EVT_STEREO,
- IRIS_EVT_MONO,
- IRIS_EVT_RDS_AVAIL,
- IRIS_EVT_RDS_NOT_AVAIL,
- IRIS_EVT_NEW_SRCH_LIST,
- IRIS_EVT_NEW_AF_LIST,
- IRIS_EVT_TXRDSDAT,
- IRIS_EVT_TXRDSDONE,
- IRIS_EVT_RADIO_DISABLED,
- IRIS_EVT_NEW_ODA,
- IRIS_EVT_NEW_RT_PLUS,
- IRIS_EVT_NEW_ERT,
-};
enum emphasis_type {
FM_RX_EMP75 = 0x0,
FM_RX_EMP50 = 0x1
diff --git a/include/trace/events/mmc.h b/include/trace/events/mmc.h
new file mode 100644
index 0000000..37115c4
--- /dev/null
+++ b/include/trace/events/mmc.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM mmc
+
+#if !defined(_TRACE_MMC_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_MMC_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(mmc_clk,
+ TP_PROTO(char *print_info),
+
+ TP_ARGS(print_info),
+
+ TP_STRUCT__entry(
+ __string(print_info, print_info)
+ ),
+
+ TP_fast_assign(
+ __assign_str(print_info, print_info);
+ ),
+
+ TP_printk("%s",
+ __get_str(print_info)
+ )
+);
+
+#endif /* if !defined(_TRACE_MMC_H) || defined(TRACE_HEADER_MULTI_READ) */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c
index edd656c..e6a2e35 100644
--- a/kernel/hrtimer.c
+++ b/kernel/hrtimer.c
@@ -224,10 +224,13 @@
raw_spin_unlock(&base->cpu_base->lock);
raw_spin_lock(&new_base->cpu_base->lock);
- if (cpu != this_cpu && hrtimer_check_target(timer, new_base)) {
- cpu = this_cpu;
+ this_cpu = smp_processor_id();
+
+ if (cpu != this_cpu && (hrtimer_check_target(timer, new_base)
+ || !cpu_online(cpu))) {
raw_spin_unlock(&new_base->cpu_base->lock);
raw_spin_lock(&base->cpu_base->lock);
+ cpu = smp_processor_id();
timer->base = base;
goto again;
}
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index d6dd07a..69b9521 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1128,7 +1128,7 @@
#ifdef CONFIG_CMA
if (migratetype == MIGRATE_MOVABLE && !zone->cma_alloc)
page = __rmqueue_smallest(zone, order, MIGRATE_CMA);
- else
+ if (!page)
#endif
retry_reserve :
page = __rmqueue_smallest(zone, order, migratetype);
diff --git a/mm/readahead.c b/mm/readahead.c
index cbcbb02..728a7a3 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -261,6 +261,8 @@
/*
* Set the initial window size, round to next power of 2 and square
+ * Small size is not dependant on max value - only a one-page read is regarded
+ * as small.
* for small size, x 4 for medium, and x 2 for large
* for 128k (32 page) max ra
* 1-8 page = 32k initial, > 8 page = 128k initial
@@ -269,7 +271,7 @@
{
unsigned long newsize = roundup_pow_of_two(size);
- if (newsize <= max / 32)
+ if (newsize <= 1)
newsize = newsize * 4;
else if (newsize <= max / 4)
newsize = newsize * 2;
diff --git a/sound/soc/codecs/wcd9306.c b/sound/soc/codecs/wcd9306.c
index ca8cfaa..fd3e0dc 100644
--- a/sound/soc/codecs/wcd9306.c
+++ b/sound/soc/codecs/wcd9306.c
@@ -1662,7 +1662,7 @@
int i;
int ret;
int num_anc_slots;
- struct anc_header *anc_head;
+ struct wcd9xxx_anc_header *anc_head;
struct tapan_priv *tapan = snd_soc_codec_get_drvdata(codec);
u32 anc_writes_size = 0;
int anc_size_remaining;
@@ -1683,16 +1683,18 @@
return -ENODEV;
}
- if (fw->size < sizeof(struct anc_header)) {
+ if (fw->size < sizeof(struct wcd9xxx_anc_header)) {
dev_err(codec->dev, "Not enough data\n");
release_firmware(fw);
return -ENOMEM;
}
/* First number is the number of register writes */
- anc_head = (struct anc_header *)(fw->data);
- anc_ptr = (u32 *)((u32)fw->data + sizeof(struct anc_header));
- anc_size_remaining = fw->size - sizeof(struct anc_header);
+ anc_head = (struct wcd9xxx_anc_header *)(fw->data);
+ anc_ptr = (u32 *)((u32)fw->data +
+ sizeof(struct wcd9xxx_anc_header));
+ anc_size_remaining = fw->size -
+ sizeof(struct wcd9xxx_anc_header);
num_anc_slots = anc_head->num_anc_slots;
if (tapan->anc_slot >= num_anc_slots) {
@@ -4226,7 +4228,12 @@
return ret;
}
- /* TODO: wcd9xxx_mbhc_init to enable mbhc */
+ ret = wcd9xxx_mbhc_init(&tapan->mbhc, &tapan->resmgr, codec,
+ WCD9XXX_MBHC_VERSION_TAPAN);
+ if (ret) {
+ pr_err("%s: mbhc init failed %d\n", __func__, ret);
+ return ret;
+ }
tapan->codec = codec;
for (i = 0; i < COMPANDER_MAX; i++) {
diff --git a/sound/soc/codecs/wcd9306.h b/sound/soc/codecs/wcd9306.h
index 61d47b5..fdd62d1 100644
--- a/sound/soc/codecs/wcd9306.h
+++ b/sound/soc/codecs/wcd9306.h
@@ -71,11 +71,6 @@
TAPAN_TX_MAX,
};
-struct anc_header {
- u32 reserved[3];
- u32 num_anc_slots;
-};
-
extern int tapan_mclk_enable(struct snd_soc_codec *codec, int mclk_enable,
bool dapm);
extern int tapan_hs_detect(struct snd_soc_codec *codec,
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index 8f63250..cc1e8eb 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -2855,7 +2855,7 @@
int i;
int ret;
int num_anc_slots;
- struct anc_header *anc_head;
+ struct wcd9xxx_anc_header *anc_head;
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
u32 anc_writes_size = 0;
int anc_size_remaining;
@@ -2878,16 +2878,18 @@
return -ENODEV;
}
- if (fw->size < sizeof(struct anc_header)) {
+ if (fw->size < sizeof(struct wcd9xxx_anc_header)) {
dev_err(codec->dev, "Not enough data\n");
release_firmware(fw);
return -ENOMEM;
}
/* First number is the number of register writes */
- anc_head = (struct anc_header *)(fw->data);
- anc_ptr = (u32 *)((u32)fw->data + sizeof(struct anc_header));
- anc_size_remaining = fw->size - sizeof(struct anc_header);
+ anc_head = (struct wcd9xxx_anc_header *)(fw->data);
+ anc_ptr = (u32 *)((u32)fw->data +
+ sizeof(struct wcd9xxx_anc_header));
+ anc_size_remaining = fw->size -
+ sizeof(struct wcd9xxx_anc_header);
num_anc_slots = anc_head->num_anc_slots;
if (taiko->anc_slot >= num_anc_slots) {
@@ -4308,7 +4310,7 @@
.rate_max = 192000,
.rate_min = 8000,
.channels_min = 1,
- .channels_max = 4,
+ .channels_max = 5,
},
.ops = &taiko_dai_ops,
},
@@ -5831,7 +5833,8 @@
taiko_init_slim_slave_cfg(codec);
wcd9xxx_mbhc_deinit(&taiko->mbhc);
- ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec);
+ ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec,
+ WCD9XXX_MBHC_VERSION_TAIKO);
if (ret)
pr_err("%s: mbhc init failed %d\n", __func__, ret);
else
@@ -5991,7 +5994,8 @@
wcd9xxx_clsh_init(&taiko->clsh_d, &taiko->resmgr);
/* init and start mbhc */
- ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec);
+ ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec,
+ WCD9XXX_MBHC_VERSION_TAIKO);
if (ret) {
pr_err("%s: mbhc init failed %d\n", __func__, ret);
return ret;
diff --git a/sound/soc/codecs/wcd9320.h b/sound/soc/codecs/wcd9320.h
index 36310e5..a4dbd7a 100644
--- a/sound/soc/codecs/wcd9320.h
+++ b/sound/soc/codecs/wcd9320.h
@@ -90,11 +90,6 @@
TAIKO_TX_MAX,
};
-struct anc_header {
- u32 reserved[3];
- u32 num_anc_slots;
-};
-
struct mad_audio_header {
u32 reserved[3];
u32 num_reg_cfg;
diff --git a/sound/soc/codecs/wcd9xxx-common.h b/sound/soc/codecs/wcd9xxx-common.h
index dc00ec6..6bc581c 100644
--- a/sound/soc/codecs/wcd9xxx-common.h
+++ b/sound/soc/codecs/wcd9xxx-common.h
@@ -55,6 +55,10 @@
struct wcd9xxx_resmgr *resmgr;
};
+struct wcd9xxx_anc_header {
+ u32 reserved[3];
+ u32 num_anc_slots;
+};
enum wcd9xxx_buck_volt {
WCD9XXX_CDC_BUCK_UNSUPPORTED = 0,
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
index cbfff1c..652992f 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.c
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -34,6 +34,7 @@
#include <linux/kernel.h>
#include <linux/gpio.h>
#include "wcd9320.h"
+#include "wcd9306.h"
#include "wcd9xxx-mbhc.h"
#include "wcd9xxx-resmgr.h"
@@ -92,6 +93,9 @@
#define WCD9XXX_USLEEP_RANGE_MARGIN_US 1000
+#define WCD9XXX_IRQ_MBHC_JACK_SWITCH_TAIKO 28
+#define WCD9XXX_IRQ_MBHC_JACK_SWITCH_TAPAN 21
+
static bool detect_use_vddio_switch;
struct wcd9xxx_mbhc_detect {
@@ -140,6 +144,64 @@
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, on << 2);
}
+static int wcd9xxx_enable_mux_bias_block(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc *mbhc)
+{
+ switch (mbhc->mbhc_version) {
+ case WCD9XXX_MBHC_VERSION_TAIKO:
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
+ break;
+ case WCD9XXX_MBHC_VERSION_TAPAN:
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x00);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int wcd9xxx_put_cfilt_fast_mode(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc *mbhc)
+{
+ switch (mbhc->mbhc_version) {
+ case WCD9XXX_MBHC_VERSION_TAIKO:
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x70, 0x00);
+ break;
+ case WCD9XXX_MBHC_VERSION_TAPAN:
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x70, 0x70);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int wcd9xxx_codec_specific_cal_setup(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc *mbhc)
+{
+ switch (mbhc->mbhc_version) {
+ case WCD9XXX_MBHC_VERSION_TAIKO:
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x04, 0x04);
+ break;
+ case WCD9XXX_MBHC_VERSION_TAPAN:
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x0C, 0x04);
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0xE0, 0xE0);
+ /* Make sure the calibration is ON */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_val,
+ 0x02, 0x02);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
/* called under codec_resource_lock acquisition */
static void wcd9xxx_pause_hs_polling(struct wcd9xxx_mbhc *mbhc)
{
@@ -161,17 +223,24 @@
{
struct snd_soc_codec *codec = mbhc->codec;
int mbhc_state = mbhc->mbhc_state;
+ int ret;
pr_debug("%s: enter\n", __func__);
if (!mbhc->polling_active) {
pr_debug("Polling is not active, do not start polling\n");
return;
}
- snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x84);
+ ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
+ if (ret) {
+ pr_err("%s: Error returned, ret: %d\n", __func__, ret);
+ return;
+ }
+
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
if (!mbhc->no_mic_headset_override &&
mbhc_state == MBHC_STATE_POTENTIAL) {
- pr_debug("%s recovering MBHC state macine\n", __func__);
+ pr_debug("%s recovering MBHC state machine\n", __func__);
mbhc->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
/* set to max button press threshold */
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL, 0x7F);
@@ -347,33 +416,65 @@
(mbhc->mbhc_data.v_brl >> 8) & 0xFF);
}
-static void wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
+static int wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
bool fast)
{
struct snd_soc_codec *codec = mbhc->codec;
u8 reg_mode_val, cur_mode_val;
- if (fast)
- reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
- else
- reg_mode_val = WCD9XXX_CFILT_SLOW_MODE;
+ switch (mbhc->mbhc_version) {
+ case WCD9XXX_MBHC_VERSION_TAIKO:
+ if (fast)
+ reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
+ else
+ reg_mode_val = WCD9XXX_CFILT_SLOW_MODE;
- cur_mode_val =
- snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40;
+ cur_mode_val =
+ snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40;
- if (cur_mode_val != reg_mode_val) {
- if (mbhc->polling_active)
- wcd9xxx_pause_hs_polling(mbhc);
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40,
- reg_mode_val);
- if (mbhc->polling_active)
- wcd9xxx_start_hs_polling(mbhc);
- pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
- cur_mode_val, reg_mode_val);
- } else {
- pr_debug("%s: CFILT Value is already %x\n",
- __func__, cur_mode_val);
+ if (cur_mode_val != reg_mode_val) {
+ if (mbhc->polling_active)
+ wcd9xxx_pause_hs_polling(mbhc);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x40, reg_mode_val);
+ if (mbhc->polling_active)
+ wcd9xxx_start_hs_polling(mbhc);
+ pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
+ cur_mode_val, reg_mode_val);
+ } else {
+ pr_debug("%s: CFILT Value is already %x\n",
+ __func__, cur_mode_val);
+ }
+ break;
+ case WCD9XXX_MBHC_VERSION_TAPAN:
+ if (fast)
+ reg_mode_val = WCD9XXX_CFILT_EXT_PRCHG_EN;
+ else
+ reg_mode_val = WCD9XXX_CFILT_EXT_PRCHG_DSBL;
+
+ cur_mode_val =
+ snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x70;
+
+ if (cur_mode_val != reg_mode_val) {
+ if (mbhc->polling_active)
+ wcd9xxx_pause_hs_polling(mbhc);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x70, reg_mode_val);
+ if (mbhc->polling_active)
+ wcd9xxx_start_hs_polling(mbhc);
+ pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
+ cur_mode_val, reg_mode_val);
+ } else {
+ pr_debug("%s: CFILT Value is already %x\n",
+ __func__, cur_mode_val);
+ }
+ break;
+ default:
+ return -EINVAL;
}
+ return 0;
}
static void wcd9xxx_jack_report(struct wcd9xxx_mbhc *mbhc,
@@ -838,6 +939,7 @@
struct snd_soc_codec *codec = mbhc->codec;
short bias_value;
u8 cfilt_mode;
+ int ret;
WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
@@ -858,10 +960,14 @@
/* Make sure CFILT is in fast mode, save current mode */
cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x70, 0x00);
-
+ ret = wcd9xxx_put_cfilt_fast_mode(codec, mbhc);
+ if (ret)
+ goto gen_err;
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
- snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x84);
+ ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
+ if (ret)
+ goto gen_err;
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x80);
snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x1F, 0x1C);
@@ -878,11 +984,14 @@
/* don't flip override */
bias_value = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40,
- cfilt_mode);
+ snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
return bias_value;
+
+gen_err:
+ pr_err("%s: Error returned, ret: %d\n", __func__, ret);
+ return ret;
}
static void wcd9xxx_shutdown_hs_removal_detect(struct wcd9xxx_mbhc *mbhc)
@@ -1273,8 +1382,9 @@
0);
}
- snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc, 0x3,
- mbhc->mbhc_cfg->micbias);
+ if (mbhc->resmgr->reg_addr->micb_4_mbhc)
+ snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc,
+ 0x3, mbhc->mbhc_cfg->micbias);
wcd9xxx_enable_irq(mbhc->resmgr->core, WCD9XXX_IRQ_MBHC_INSERTION);
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
@@ -2580,6 +2690,7 @@
{
u8 cfilt_mode;
u16 reg0, reg1;
+ int ret;
struct snd_soc_codec *codec = mbhc->codec;
pr_debug("%s: enter\n", __func__);
@@ -2596,8 +2707,9 @@
* Need to restore defaults once calculation is done.
*/
cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40, 0x00);
-
+ ret = wcd9xxx_put_cfilt_fast_mode(codec, mbhc);
+ if (ret)
+ goto gen_err;
/*
* Micbias, CFILT, LDOH, MBHC MUX mode settings
* to perform ADC calibration
@@ -2607,8 +2719,9 @@
snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60);
snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78);
- snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x04);
-
+ ret = wcd9xxx_codec_specific_cal_setup(codec, mbhc);
+ if (ret)
+ goto gen_err;
/* Pull down micbias to ground */
reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 1, 1);
@@ -2616,7 +2729,10 @@
reg1 = snd_soc_read(codec, WCD9XXX_A_MAD_ANA_CTRL);
snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, 1 << 0);
/* Connect the MUX to micbias */
- snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x82);
+ ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
+ if (ret)
+ goto gen_err;
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
usleep_range(WCD9XXX_MUX_SWITCH_READY_WAIT_US,
WCD9XXX_MUX_SWITCH_READY_WAIT_US +
WCD9XXX_USLEEP_RANGE_MARGIN_US);
@@ -2635,7 +2751,10 @@
/* DCE measurment for MB voltage */
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
- snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x82);
+ ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
+ if (ret)
+ goto gen_err;
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
usleep_range(100, 100);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
@@ -2645,7 +2764,10 @@
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
- snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x82);
+ ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
+ if (ret)
+ goto gen_err;
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
usleep_range(100, 100);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
@@ -2653,16 +2775,20 @@
/* Restore default settings. */
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40,
- cfilt_mode);
-
- snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x84);
+ snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
+ ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
+ if (ret)
+ goto gen_err;
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
usleep_range(100, 100);
wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
wcd9xxx_turn_onoff_rel_detection(codec, true);
pr_debug("%s: leave\n", __func__);
+
+gen_err:
+ pr_err("%s: Error returned, ret: %d\n", __func__, ret);
}
static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc)
@@ -2702,8 +2828,9 @@
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
btn_det->mbhc_nsc << 3);
- snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc, 0x03,
- MBHC_MICBIAS2);
+ if (mbhc->resmgr->reg_addr->micb_4_mbhc)
+ snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc,
+ 0x03, MBHC_MICBIAS2);
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
@@ -2720,6 +2847,7 @@
{
int ret = 0;
void *core = mbhc->resmgr->core;
+ int jack_irq;
if (mbhc->mbhc_cfg->gpio) {
ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL,
@@ -2741,13 +2869,25 @@
/* Enable HPHL_10K_SW */
snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
1 << 1, 1 << 1);
- ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_JACK_SWITCH,
+
+ switch (mbhc->mbhc_version) {
+ case WCD9XXX_MBHC_VERSION_TAIKO:
+ jack_irq = WCD9XXX_IRQ_MBHC_JACK_SWITCH_TAIKO;
+ break;
+ case WCD9XXX_MBHC_VERSION_TAPAN:
+ jack_irq = WCD9XXX_IRQ_MBHC_JACK_SWITCH_TAPAN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = wcd9xxx_request_irq(core, jack_irq,
wcd9xxx_mech_plug_detect_irq,
"Jack Detect",
mbhc);
if (ret)
pr_err("%s: Failed to request insert detect irq %d\n",
- __func__, WCD9XXX_IRQ_MBHC_JACK_SWITCH);
+ __func__, jack_irq);
}
return ret;
@@ -2977,7 +3117,7 @@
int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
struct wcd9xxx_mbhc_config *mbhc_cfg)
{
- int rc = 0;
+ int rc;
struct snd_soc_codec *codec = mbhc->codec;
pr_debug("%s: enter\n", __func__);
@@ -3001,10 +3141,10 @@
wcd9xxx_get_mbhc_micbias_regs(mbhc, &mbhc->mbhc_bias_regs);
/* Put CFILT in fast mode by default */
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
- 0x40, WCD9XXX_CFILT_FAST_MODE);
-
- if (!mbhc->mbhc_cfg->read_fw_bin)
+ rc = wcd9xxx_put_cfilt_fast_mode(codec, mbhc);
+ if (rc)
+ pr_err("%s: Error returned, ret: %d\n", __func__, rc);
+ else if (!mbhc->mbhc_cfg->read_fw_bin)
rc = wcd9xxx_init_and_calibrate(mbhc);
else
schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
@@ -3211,7 +3351,7 @@
* Switch CFILT to slow mode if MBHC CFILT is being
* used.
*/
- wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
+ ret = wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
break;
case WCD9XXX_EVENT_POST_CFILT_1_OFF:
case WCD9XXX_EVENT_POST_CFILT_2_OFF:
@@ -3222,7 +3362,7 @@
* Switch CFILT to fast mode if MBHC CFILT is not
* used anymore.
*/
- wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
+ ret = wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
break;
/* System resume */
case WCD9XXX_EVENT_POST_RESUME:
@@ -3244,7 +3384,7 @@
pr_debug("%s: leave\n", __func__);
- return 0;
+ return ret;
}
/*
@@ -3253,7 +3393,7 @@
* NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
*/
int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
- struct snd_soc_codec *codec)
+ struct snd_soc_codec *codec, int version)
{
int ret;
void *core;
@@ -3276,6 +3416,7 @@
mbhc->codec = codec;
mbhc->resmgr = resmgr;
mbhc->resmgr->mbhc = mbhc;
+ mbhc->mbhc_version = version;
if (mbhc->headset_jack.jack == NULL) {
ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.h b/sound/soc/codecs/wcd9xxx-mbhc.h
index fb1dfdc..300e34e 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.h
+++ b/sound/soc/codecs/wcd9xxx-mbhc.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -16,6 +16,8 @@
#define WCD9XXX_CFILT_FAST_MODE 0x00
#define WCD9XXX_CFILT_SLOW_MODE 0x40
+#define WCD9XXX_CFILT_EXT_PRCHG_EN 0x70
+#define WCD9XXX_CFILT_EXT_PRCHG_DSBL 0x40
struct mbhc_micbias_regs {
u16 cfilt_val;
@@ -51,6 +53,12 @@
s16 v_inval_ins_high;
};
+enum wcd9xxx_mbhc_version {
+ WCD9XXX_MBHC_VERSION_UNKNOWN = 0,
+ WCD9XXX_MBHC_VERSION_TAIKO,
+ WCD9XXX_MBHC_VERSION_TAPAN,
+};
+
enum wcd9xxx_mbhc_plug_type {
PLUG_TYPE_INVALID = -1,
PLUG_TYPE_NONE,
@@ -243,6 +251,8 @@
struct notifier_block nblock;
+ enum wcd9xxx_mbhc_version mbhc_version;
+
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_poke;
struct dentry *debugfs_mbhc;
@@ -307,7 +317,7 @@
int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
struct wcd9xxx_mbhc_config *mbhc_cfg);
int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
- struct snd_soc_codec *codec);
+ struct snd_soc_codec *codec, int version);
void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc);
void *wcd9xxx_mbhc_cal_btn_det_mp(
const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
diff --git a/sound/soc/msm/msm-dai-q6.c b/sound/soc/msm/msm-dai-q6.c
index 7381677..d973c17 100644
--- a/sound/soc/msm/msm-dai-q6.c
+++ b/sound/soc/msm/msm-dai-q6.c
@@ -1805,7 +1805,7 @@
SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
- .channels_max = 4,
+ .channels_max = 5,
.rate_min = 8000,
.rate_max = 192000,
},
diff --git a/sound/soc/msm/msm-pcm-lpa.c b/sound/soc/msm/msm-pcm-lpa.c
index ba054bd..95c5cd7 100644
--- a/sound/soc/msm/msm-pcm-lpa.c
+++ b/sound/soc/msm/msm-pcm-lpa.c
@@ -24,6 +24,7 @@
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/control.h>
+#include <sound/pcm_params.h>
#include <asm/dma.h>
#include <linux/dma-mapping.h>
@@ -470,8 +471,8 @@
return -EPERM;
ret = q6asm_audio_client_buf_alloc_contiguous(dir,
prtd->audio_client,
- runtime->hw.period_bytes_min,
- runtime->hw.periods_max);
+ params_period_bytes(params),
+ params_periods(params));
if (ret < 0) {
pr_err("Audio Start: Buffer Allocation failed \
rc = %d\n", ret);
@@ -488,7 +489,7 @@
dma_buf->private_data = NULL;
dma_buf->area = buf[0].data;
dma_buf->addr = buf[0].phys;
- dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ dma_buf->bytes = params_period_bytes(params) * params_periods(params);
if (!dma_buf->area)
return -ENOMEM;
diff --git a/sound/soc/msm/msm8226.c b/sound/soc/msm/msm8226.c
index b97d9dc..08731f6 100644
--- a/sound/soc/msm/msm8226.c
+++ b/sound/soc/msm/msm8226.c
@@ -56,7 +56,7 @@
.mclk_rate = TAPAN_EXT_CLK_RATE,
.gpio = 0,
.gpio_irq = 0,
- .gpio_level_insert = 1,
+ .gpio_level_insert = 0,
.detect_extn_cable = true,
.insert_detect = true,
.swap_gnd_mic = NULL,
@@ -402,11 +402,8 @@
/* start mbhc */
mbhc_cfg.calibration = def_tapan_mbhc_cal();
- if (mbhc_cfg.calibration) {
- pr_info("%s: WCD9306: Headset detection disabled\n",
- __func__);
- }
-
+ if (mbhc_cfg.calibration)
+ err = tapan_hs_detect(codec, &mbhc_cfg);
else
err = -ENOMEM;
@@ -439,8 +436,8 @@
S(t_ldoh, 100);
S(t_bg_fast_settle, 100);
S(t_shutdown_plug_rem, 255);
- S(mbhc_nsa, 4);
- S(mbhc_navg, 4);
+ S(mbhc_nsa, 2);
+ S(mbhc_navg, 128);
#undef S
#define S(X, Y) ((WCD9XXX_MBHC_CAL_PLUG_DET_PTR(tapan_cal)->X) = (Y))
S(mic_current, TAPAN_PID_MIC_5_UA);
@@ -451,13 +448,13 @@
#undef S
#define S(X, Y) ((WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(tapan_cal)->X) = (Y))
S(v_no_mic, 30);
- S(v_hs_max, 2400);
+ S(v_hs_max, 1650);
#undef S
#define S(X, Y) ((WCD9XXX_MBHC_CAL_BTN_DET_PTR(tapan_cal)->X) = (Y))
S(c[0], 62);
S(c[1], 124);
S(nc, 1);
- S(n_meas, 3);
+ S(n_meas, 5);
S(mbhc_nsc, 11);
S(n_btn_meas, 1);
S(n_btn_con, 2);
@@ -487,13 +484,13 @@
btn_high[7] = 330;
n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_N_READY);
n_ready[0] = 80;
- n_ready[1] = 68;
+ n_ready[1] = 12;
n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_N_CIC);
n_cic[0] = 60;
n_cic[1] = 47;
gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_GAIN);
gain[0] = 11;
- gain[1] = 9;
+ gain[1] = 14;
return tapan_cal;
}
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index 9ff968f..8affc09 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -193,6 +193,7 @@
static int msm_btsco_ch = 1;
static int msm_hdmi_rx_ch = 2;
static int slim0_rx_sample_rate = SAMPLING_RATE_48KHZ;
+static int msm_proxy_rx_ch = 2;
static struct mutex cdc_mclk_mutex;
static struct q_clkdiv *codec_clk;
@@ -584,12 +585,15 @@
static const char *const spk_function[] = {"Off", "On"};
static const char *const slim0_rx_ch_text[] = {"One", "Two"};
-static const char *const slim0_tx_ch_text[] = {"One", "Two", "Three", "Four"};
+static const char *const slim0_tx_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five"};
static char const *hdmi_rx_ch_text[] = {"Two", "Three", "Four", "Five",
"Six", "Seven", "Eight"};
static char const *rx_bit_format_text[] = {"S16_LE", "S24_LE"};
static char const *slim0_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96",
"KHZ_192"};
+static const char *const proxy_rx_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven", "Eight"};
static const char *const btsco_rate_text[] = {"8000", "16000"};
static const struct soc_enum msm_btsco_enum[] = {
@@ -854,6 +858,23 @@
}
return 0;
}
+static int msm_proxy_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_proxy_rx_ch = %d\n", __func__,
+ msm_proxy_rx_ch);
+ ucontrol->value.integer.value[0] = msm_proxy_rx_ch - 1;
+ return 0;
+}
+
+static int msm_proxy_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_proxy_rx_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_proxy_rx_ch = %d\n", __func__,
+ msm_proxy_rx_ch);
+ return 1;
+}
static int msm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
@@ -870,15 +891,31 @@
return 0;
}
-static int msm_proxy_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
+static int msm_proxy_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
{
struct snd_interval *rate = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_RATE);
+ SNDRV_PCM_HW_PARAM_RATE);
- pr_debug("%s()\n", __func__);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s: msm_proxy_rx_ch =%d\n", __func__, msm_proxy_rx_ch);
+
+ if (channels->max < 2)
+ channels->min = channels->max = 2;
+ channels->min = channels->max = msm_proxy_rx_ch;
rate->min = rate->max = 48000;
+ return 0;
+}
+static int msm_proxy_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ rate->min = rate->max = 48000;
return 0;
}
@@ -1146,10 +1183,11 @@
static const struct soc_enum msm_snd_enum[] = {
SOC_ENUM_SINGLE_EXT(2, spk_function),
SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
- SOC_ENUM_SINGLE_EXT(4, slim0_tx_ch_text),
+ SOC_ENUM_SINGLE_EXT(5, slim0_tx_ch_text),
SOC_ENUM_SINGLE_EXT(7, hdmi_rx_ch_text),
SOC_ENUM_SINGLE_EXT(2, rx_bit_format_text),
SOC_ENUM_SINGLE_EXT(3, slim0_rx_sample_rate_text),
+ SOC_ENUM_SINGLE_EXT(8, proxy_rx_ch_text),
};
static const struct snd_kcontrol_new msm_snd_controls[] = {
@@ -1169,6 +1207,8 @@
slim0_rx_sample_rate_get, slim0_rx_sample_rate_put),
SOC_ENUM_EXT("HDMI_RX Bit Format", msm_snd_enum[4],
hdmi_rx_bit_format_get, hdmi_rx_bit_format_put),
+ SOC_ENUM_EXT("PROXY_RX Channels", msm_snd_enum[6],
+ msm_proxy_rx_ch_get, msm_proxy_rx_ch_put),
};
static bool msm8974_swap_gnd_mic(struct snd_soc_codec *codec)
@@ -1809,7 +1849,7 @@
.codec_dai_name = "msm-stub-rx",
.no_pcm = 1,
.be_id = MSM_BACKEND_DAI_AFE_PCM_RX,
- .be_hw_params_fixup = msm_proxy_be_hw_params_fixup,
+ .be_hw_params_fixup = msm_proxy_rx_be_hw_params_fixup,
/* this dainlink has playback support */
.ignore_pmdown_time = 1,
.ignore_suspend = 1,
@@ -1823,7 +1863,7 @@
.codec_dai_name = "msm-stub-tx",
.no_pcm = 1,
.be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
- .be_hw_params_fixup = msm_proxy_be_hw_params_fixup,
+ .be_hw_params_fixup = msm_proxy_tx_be_hw_params_fixup,
.ignore_suspend = 1,
},
/* HDMI Hostless */
diff --git a/sound/soc/msm/qdsp6v2/audio_acdb.c b/sound/soc/msm/qdsp6v2/audio_acdb.c
index 96da2f1..a2e0b87 100644
--- a/sound/soc/msm/qdsp6v2/audio_acdb.c
+++ b/sound/soc/msm/qdsp6v2/audio_acdb.c
@@ -1043,7 +1043,7 @@
goto done;
}
- if (size <= 0) {
+ if ((size <= 0) || (size > sizeof(data))) {
pr_err("%s: Invalid size sent to driver: %d\n",
__func__, size);
result = -EFAULT;
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index 1b789cd..8bb3eaf 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -1864,8 +1864,8 @@
if (ch_cnt) {
dai_data->rx_dai.mi2s_dai_data.port_config.i2s.channel_mode =
- mi2s_pdata->rx_sd_lines;
- dai_data->rx_dai.pdata_mi2s_lines = mi2s_pdata->rx_sd_lines;
+ sd_line;
+ dai_data->rx_dai.pdata_mi2s_lines = sd_line;
dai_driver->playback.channels_min = 1;
dai_driver->playback.channels_max = ch_cnt << 1;
} else {
@@ -1882,8 +1882,8 @@
if (ch_cnt) {
dai_data->tx_dai.mi2s_dai_data.port_config.i2s.channel_mode =
- mi2s_pdata->tx_sd_lines;
- dai_data->tx_dai.pdata_mi2s_lines = mi2s_pdata->tx_sd_lines;
+ sd_line;
+ dai_data->tx_dai.pdata_mi2s_lines = sd_line;
dai_driver->capture.channels_min = 1;
dai_driver->capture.channels_max = ch_cnt << 1;
} else {
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
index 96ddcf6..2a64ae2 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
@@ -32,16 +32,22 @@
#include <linux/memory_alloc.h>
#include "msm-pcm-afe-v2.h"
-#define MIN_PERIOD_SIZE (128 * 2)
-#define MAX_PERIOD_SIZE (128 * 2 * 2 * 6)
-#define MAX_NUM_PERIODS 384
-#define MIN_NUM_PERIODS 32
-static struct snd_pcm_hardware msm_afe_hardware = {
- .info = (SNDRV_PCM_INFO_MMAP |
+#define MIN_PLAYBACK_PERIOD_SIZE (128 * 2)
+#define MAX_PLAYBACK_PERIOD_SIZE (128 * 2 * 2 * 6)
+#define MIN_PLAYBACK_NUM_PERIODS (32)
+#define MAX_PLAYBACK_NUM_PERIODS (384)
+
+#define MIN_CAPTURE_PERIOD_SIZE (128 * 2 * 4)
+#define MAX_CAPTURE_PERIOD_SIZE (128 * 2 * 2 * 6 * 4)
+#define MIN_CAPTURE_NUM_PERIODS (32)
+#define MAX_CAPTURE_NUM_PERIODS (384)
+
+static struct snd_pcm_hardware msm_afe_hardware_playback = {
+ .info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED),
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ .formats = SNDRV_PCM_FMTBIT_S16_LE|
SNDRV_PCM_FMTBIT_S24_LE,
.rates = (SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000 |
@@ -50,13 +56,39 @@
.rate_max = 48000,
.channels_min = 1,
.channels_max = 6,
- .buffer_bytes_max = MAX_PERIOD_SIZE * MIN_NUM_PERIODS,
- .period_bytes_min = MIN_PERIOD_SIZE,
- .period_bytes_max = MAX_PERIOD_SIZE,
- .periods_min = MIN_NUM_PERIODS,
- .periods_max = MAX_NUM_PERIODS,
+ .buffer_bytes_max = MAX_PLAYBACK_PERIOD_SIZE *
+ MIN_PLAYBACK_NUM_PERIODS,
+ .period_bytes_min = MIN_PLAYBACK_PERIOD_SIZE,
+ .period_bytes_max = MAX_PLAYBACK_PERIOD_SIZE,
+ .periods_min = MIN_PLAYBACK_NUM_PERIODS,
+ .periods_max = MAX_PLAYBACK_NUM_PERIODS,
.fifo_size = 0,
};
+
+static struct snd_pcm_hardware msm_afe_hardware_capture = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE|
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .rates = (SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000),
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 6,
+ .buffer_bytes_max = MAX_CAPTURE_PERIOD_SIZE *
+ MIN_CAPTURE_NUM_PERIODS,
+ .period_bytes_min = MIN_CAPTURE_PERIOD_SIZE,
+ .period_bytes_max = MAX_CAPTURE_PERIOD_SIZE,
+ .periods_min = MIN_CAPTURE_NUM_PERIODS,
+ .periods_max = MAX_CAPTURE_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+
static enum hrtimer_restart afe_hrtimer_callback(struct hrtimer *hrt);
static enum hrtimer_restart afe_hrtimer_rec_callback(struct hrtimer *hrt);
@@ -130,6 +162,8 @@
struct snd_pcm_substream *substream = NULL;
struct snd_pcm_runtime *runtime = NULL;
uint16_t event;
+ uint64_t period_bytes;
+ uint64_t bytes_one_sec;
if (prtd == NULL)
return;
@@ -143,12 +177,28 @@
switch (event) {
case AFE_EVENT_RTPORT_START: {
prtd->dsp_cnt = 0;
- prtd->poll_time = ((unsigned long)((
- snd_pcm_lib_period_bytes
- (prtd->substream) *
- 1000 * 1000)/
- (runtime->rate *
- runtime->channels * 2)));
+ /* Calculate poll time.
+ * Split steps to avoid overflow.
+ * Poll time-time corresponding to one period
+ * in bytes.
+ * (Samplerate * channelcount * format) =
+ * bytes in 1 sec.
+ * Poll time =
+ * (period bytes / bytes in one sec) *
+ * 1000000 micro seconds.
+ * Multiplication by 1000000 is done in two
+ * steps to keep the accuracy of poll time.
+ */
+ period_bytes = ((uint64_t)(
+ (snd_pcm_lib_period_bytes(
+ prtd->substream)) *
+ 1000));
+ bytes_one_sec =
+ (runtime->rate * runtime->channels * 2);
+ bytes_one_sec =
+ div_u64(bytes_one_sec, 1000);
+ prtd->poll_time =
+ div_u64(period_bytes, bytes_one_sec);
pr_debug("prtd->poll_time: %d",
prtd->poll_time);
break;
@@ -197,6 +247,8 @@
struct snd_pcm_substream *substream = NULL;
struct snd_pcm_runtime *runtime = NULL;
uint16_t event;
+ uint64_t period_bytes;
+ uint64_t bytes_one_sec;
if (prtd == NULL)
return;
@@ -210,11 +262,22 @@
switch (event) {
case AFE_EVENT_RTPORT_START: {
prtd->dsp_cnt = 0;
- prtd->poll_time = ((unsigned long)((
- snd_pcm_lib_period_bytes(prtd->substream)
- * 1000 * 1000)/(runtime->rate
- * runtime->channels * 2)));
- pr_debug("prtd->poll_time : %d", prtd->poll_time);
+ /* Calculate poll time. Split steps to avoid overflow.
+ * Poll time-time corresponding to one period in bytes.
+ * (Samplerate * channelcount * format)=bytes in 1 sec.
+ * Poll time = (period bytes / bytes in one sec) *
+ * 1000000 micro seconds.
+ * Multiplication by 1000000 is done in two steps to
+ * keep the accuracy of poll time.
+ */
+ period_bytes = ((uint64_t)(
+ (snd_pcm_lib_period_bytes(prtd->substream)) *
+ 1000));
+ bytes_one_sec = (runtime->rate * runtime->channels * 2);
+ bytes_one_sec = div_u64(bytes_one_sec , 1000);
+ prtd->poll_time =
+ div_u64(period_bytes, bytes_one_sec);
+ pr_debug("prtd->poll_time : %d\n", prtd->poll_time);
break;
}
case AFE_EVENT_RTPORT_STOP:
@@ -326,7 +389,11 @@
mutex_lock(&prtd->lock);
- runtime->hw = msm_afe_hardware;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = msm_afe_hardware_playback;
+ else
+ runtime->hw = msm_afe_hardware_capture;
+
prtd->substream = substream;
runtime->private_data = prtd;
prtd->audio_client = q6afe_audio_client_alloc(prtd);
@@ -355,6 +422,18 @@
if (ret < 0)
pr_err("snd_pcm_hw_constraint_integer failed\n");
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ MIN_CAPTURE_NUM_PERIODS * MIN_CAPTURE_PERIOD_SIZE,
+ MAX_CAPTURE_NUM_PERIODS * MAX_CAPTURE_PERIOD_SIZE);
+
+ if (ret < 0) {
+ pr_err("constraint for buffer bytes min max ret = %d\n",
+ ret);
+ }
+ }
+
return 0;
}
@@ -497,10 +576,18 @@
dir = IN;
else
dir = OUT;
+
rc = q6afe_audio_client_buf_alloc_contiguous(dir,
- prtd->audio_client,
- runtime->hw.period_bytes_min,
- runtime->hw.periods_max);
+ prtd->audio_client,
+ (params_buffer_bytes(params) / params_periods(params)),
+ params_periods(params));
+ pr_debug("params_buffer_bytes(params) = %d\n",
+ (params_buffer_bytes(params)));
+ pr_debug("params_periods(params) = %d\n",
+ (params_periods(params)));
+ pr_debug("params_periodsize(params) = %d\n",
+ (params_buffer_bytes(params) / params_periods(params)));
+
if (rc < 0) {
pr_err("Audio Start: Buffer Allocation failed rc = %d\n", rc);
mutex_unlock(&prtd->lock);
@@ -519,14 +606,18 @@
dma_buf->private_data = NULL;
dma_buf->area = buf[0].data;
dma_buf->addr = buf[0].phys;
- dma_buf->bytes = runtime->hw.buffer_bytes_max;
+
+ dma_buf->bytes = params_buffer_bytes(params);
+
if (!dma_buf->area) {
pr_err("%s:MSM AFE physical memory allocation failed\n",
__func__);
mutex_unlock(&prtd->lock);
return -ENOMEM;
}
- memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max);
+
+ memset(dma_buf->area, 0, params_buffer_bytes(params));
+
prtd->dma_addr = (u32) dma_buf->addr;
mutex_unlock(&prtd->lock);
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
index c8de460..a163f6a 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
@@ -24,6 +24,7 @@
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/control.h>
+#include <sound/pcm_params.h>
#include <asm/dma.h>
#include <linux/dma-mapping.h>
@@ -493,8 +494,8 @@
return -EPERM;
ret = q6asm_audio_client_buf_alloc_contiguous(dir,
prtd->audio_client,
- runtime->hw.period_bytes_min,
- runtime->hw.periods_max);
+ params_period_bytes(params),
+ params_periods(params));
if (ret < 0) {
pr_err("Audio Start: Buffer Allocation failed rc = %d\n",
ret);
@@ -511,7 +512,7 @@
dma_buf->private_data = NULL;
dma_buf->area = buf[0].data;
dma_buf->addr = buf[0].phys;
- dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ dma_buf->bytes = params_period_bytes(params) * params_periods(params);
if (!dma_buf->area)
return -ENOMEM;
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
index 4df66d0..25bb72f 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
@@ -472,6 +472,7 @@
voc_set_tty_mode(voc_get_session_id(VOICE_SESSION_NAME), tty_mode);
voc_set_tty_mode(voc_get_session_id(VOICE2_SESSION_NAME), tty_mode);
+ voc_set_tty_mode(voc_get_session_id(VOLTE_SESSION_NAME), tty_mode);
return 0;
}
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index fffd0b3..c2b824f 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -2866,6 +2866,7 @@
if (buf_node->buf_addr_lsw == buf_add) {
list_del(&buf_node->list);
kfree(buf_node);
+ break;
}
}
rc = 0;