Merge "USB: gadget: diag: Ratelimit ep request queuing failure messages"
diff --git a/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt b/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt
index 70fea73..e777094 100644
--- a/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt
+++ b/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt
@@ -6,22 +6,32 @@
- reg: offset and length of the register regions(s) for the device.
- reg-names: a list of strings that map in order to the list of regs.
-- <supply-name>-supply: phandle to the regulator device tree node.
-- <compatible-name>-supply-names: a list of strings that map in order
+- hpd-5v-supply: phandle to the 5V regulator device tree node.
+- core-vdda-supply: phandle to the HDMI vdda regulator device tree node.
+- core-vcc-supply: phandle to the HDMI vcc regulator device tree node.
+- qcom,hdmi-tx-supply-names: a list of strings that map in order
to the list of supplies.
-- <<compatible-name>-supply-type: a type of supply(ies) mentioned above.
+- qcom,hdmi-tx-supply-type: a type of supply(ies) mentioned above.
0 = supply with controlled output
1 = supply without controlled output. i.e. voltage switch
-- <compatible-name>-min-voltage-level: specifies minimum voltage level
+- qcom,hdmi-tx-min-voltage-level: specifies minimum voltage level
of supply(ies) mentioned above.
-- <compatible-name>-max-voltage-level: specifies maximum voltage level
+- qcom,hdmi-tx-max-voltage-level: specifies maximum voltage level
of supply(ies) mentioned above.
-- <compatible-name>-op-mode: specifies optimum operating mode of
+- qcom,hdmi-tx-op-mode: specifies optimum operating mode of
supply(ies) mentioned above.
-- gpios: specifies gpios assigned for the device.
-- <compatible-name>-gpio-names: a list of strings that map in order to
- the list of gpios
+- qcom,hdmi-tx-cec: gpio for Consumer Electronics Control (cec) line.
+- qcom,hdmi-tx-ddc-clk: gpio for Display Data Channel (ddc) clock line.
+- qcom,hdmi-tx-ddc-data: gpio for ddc data line.
+- qcom,hdmi-tx-hpd: gpio required for HDMI hot-plug detect.
+
+Optional properties:
+- qcom,hdmi-tx-mux-sel: gpio required to toggle HDMI output between
+ docking station, type A, and liquid device, type D, ports. Required
+ property for liquid devices.
+- qcom,hdmi-tx-mux-en: gpio required to enable mux for HDMI output
+ on liquid devices. Required property for liquid devices.
Example:
qcom,hdmi_tx@fd922100 {
@@ -41,10 +51,8 @@
qcom,hdmi-tx-max-voltage-level = <0 1800000 1800000>;
qcom,hdmi-tx-op-mode = <0 1800000 0>;
- gpios = <&msmgpio 31 0>,
- <&msmgpio 32 0>,
- <&msmgpio 33 0>,
- <&msmgpio 34 0>;
- qcom,hdmi-tx-gpio-names =
- "cec-pin", "hpd-ddc-clk", "hpd-ddc-data", "hpd-pin";
+ qcom,hdmi-tx-cec = <&msmgpio 31 0>;
+ qcom,hdmi-tx-ddc-clk = <&msmgpio 32 0>;
+ qcom,hdmi-tx-ddc-data = <&msmgpio 33 0>;
+ qcom,hdmi-tx-hpd = <&msmgpio 34 0>;
};
diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt
index 38b2721..9164647 100644
--- a/Documentation/devicetree/bindings/gpu/adreno.txt
+++ b/Documentation/devicetree/bindings/gpu/adreno.txt
@@ -5,7 +5,12 @@
Required properties:
- label: A string used as a descriptive name for the device.
- compatible: Must be "qcom,kgsl-3d0" and "qcom,kgsl-3d"
-- reg: Specifies the base address and address size for this device.
+- reg: Specifies the register base address and size. The second interval
+ specifies the shader memory base address and size.
+- reg-names: Resource names used for the physical address of device registers
+ and shader memory. "kgsl_3d0_reg_memory" gives the physical address
+ and length of device registers while "kgsl_3d0_shader_memory" gives
+ physical address and length of device shader memory.
- interrupts: Interrupt mapping for GPU IRQ.
- interrupt-names: String property to describe the name of the interrupt.
- qcom,id: An integer used as an identification number for the device.
@@ -70,8 +75,9 @@
qcom,kgsl-3d0@fdb00000 {
label = "kgsl-3d0";
compatible = "qcom,kgsl-3d0", "qcom,kgsl-3d";
- reg = <0xfdb00000 0x20000>;
- reg-names = "kgsl_3d0_reg_memory";
+ reg = <0xfdb00000 0x10000
+ 0xfdb20000 0x10000>;
+ reg-names = "kgsl_3d0_reg_memory", "kgsl_3d0_shader_memory";
interrupts = <0 33 0>;
interrupt-names = "kgsl_3d0_irq";
qcom,id = <0>;
diff --git a/Documentation/devicetree/bindings/usb/msm-ehci-hsic.txt b/Documentation/devicetree/bindings/usb/msm-ehci-hsic.txt
new file mode 100644
index 0000000..0e59f69
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/msm-ehci-hsic.txt
@@ -0,0 +1,20 @@
+MSM HSIC EHCI controller
+
+Required properties :
+- compatible : should be "qcom,hsic-host"
+- regs : offset and length of the register set in the memory map
+- interrupts: IRQ lines used by this controller
+- interrupt-names : Required interrupt resource entries are:
+ HSIC EHCI expects "core_irq" and optionally "async_irq".
+- <supply-name>-supply: handle to the regulator device tree node
+ Required "supply-name" is "HSIC_VDDCX" and optionally - "HSIC_GDSC".
+
+Example MSM HSIC EHCI controller device node :
+ hsic@f9a15000 {
+ compatible = "qcom,hsic-host";
+ reg = <0xf9a15000 0x400>;
+ interrupts = <0 136 0>;
+ interrupt-names = "core_irq";
+ HSIC_VDDCX-supply = <&pm8019_l12>;
+ HSIC_GDSC-supply = <&gdsc_usb_hsic>;
+ };
diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
index 7bff0f2..015822f 100644
--- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
@@ -100,13 +100,24 @@
- compatible: should be "qcom,usb-bam-msm"
- reg : pairs of physical base addresses and region sizes
of all the memory mapped BAM devices present
-- reg-names : Register region name(s) referenced in reg above
- SSUSB BAM expects "ssusb" and "hsusb" for HSSUB BAM.
- Specify "qscratch_ram1_reg" to provide QSCRATCH's RAM1
- register to control USB3 private memory for uses as BAM FIFOs.
+- reg-names : Register region name(s), in 1-1 correspondence with the
+ registers in 'reg'. This list should contain at least as many names
+ as the number of unique values given in both 'usb-active-bam' and
+ all the subnodes' 'usb-bam-type' properties.
+
+ If SSUSB_BAM is used, "ssusb" should be present.
+ If HSUSB_BAM is used, "hsusb" should be present.
+ If HSIC_BAM is used, "hsic" should be present.
+
+ If a QSCRATCH RAM1 register is designated for providing USB3
+ private memory to use as a BAM FIFO, specify "qscratch_ram1_reg".
- interrupts: IRQ lines for BAM devices
-- interrupt-names: BAM interrupt name(s) referenced in interrupts above
- SSUSB BAM expects "ssusb" and "hsusb" for HSSUB BAM
+- interrupt-names: BAM interrupt name(s), in 1-1 correspondence with
+ 'interrupts' above.
+
+ If SSUSB_BAM is used, "ssusb" should be present.
+ If HSUSB_BAM is used, "hsusb" should be present.
+ If HSIC_BAM is used, "hsic" should be present.
- qcom,usb-active-bam: active BAM type. Can be one of
0 - SSUSB_BAM
1 - HSUSB_BAM
diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
index 57d776f..d686523 100644
--- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
@@ -10,12 +10,20 @@
"irq" : Interrupt for DWC3 core
"otg_irq" : Interrupt for DWC3 core's OTG Events
- <supply-name>-supply: phandle to the regulator device tree node
- Required "supply-name" examples are "SSUSB_VDDCX", "SSUSB_1p8",
- "HSUSB_VDDCX", "HSUSB_1p8", "HSUSB_3p3" and "vbus_dwc3".
+ Required "supply-name" examples are:
+ "SSUSB_lp8" : 1.8v supply for SSPHY
+ "HSUSB_1p8" : 1.8v supply for HSPHY
+ "HSUSB_3p3" : 3.3v supply for HSPHY
+ "vbus_dwc3" : vbus supply for host mode
+ "ssusb_vdd_dig" : vdd supply for SSPHY digital circuit operation
+ "hsusb_vdd_dig" : vdd supply for HSPHY digital circuit operation
- qcom,dwc-usb3-msm-dbm-eps: Number of endpoints avaliable for
the DBM (Device Bus Manager). The DBM is HW unit which is part of
the MSM USB3.0 core (which also includes the Synopsys DesignWare
USB3.0 controller)
+- qcom,vdd-voltage-level: This property must be a list of three integer
+ values (no, min, max) where each value represents either a voltage in
+ microvolts or a value corresponding to voltage corner
Optional properties :
- Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
@@ -40,13 +48,14 @@
<0xFD4AB000 0x4>;
interrupts = <0 131 0>, <0 179 0>, <0 133 0>;
interrupt-names = "irq", "otg_irq", "hs_phy_irq";
- SSUSB_VDDCX-supply = <&pm8841_s2>;
+ ssusb_vdd_dig-supply = <&pm8841_s2_corner>;
SSUSB_1p8-supply = <&pm8941_l6>;
- HSUSB_VDDCX-supply = <&pm8841_s2>;
+ hsusb_vdd_dig-supply = <&pm8841_s2_corner>;
HSUSB_1p8-supply = <&pm8941_l6>;
HSUSB_3p3-supply = <&pm8941_l24>;
vbus_dwc3-supply = <&pm8941_mvs1>;
qcom,dwc-usb3-msm-dbm-eps = <4>
+ qcom,vdd-voltage-level = <1 5 7>;
qcom,msm_bus,name = "usb3";
qcom,msm_bus,num_cases = <2>;
diff --git a/arch/arm/boot/dts/msm8910-ion.dtsi b/arch/arm/boot/dts/msm8910-ion.dtsi
new file mode 100644
index 0000000..88bb1ab
--- /dev/null
+++ b/arch/arm/boot/dts/msm8910-ion.dtsi
@@ -0,0 +1,62 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/ {
+ qcom,ion {
+ compatible = "qcom,msm-ion";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,ion-heap@30 { /* SYSTEM HEAP */
+ reg = <30>;
+ };
+
+ qcom,ion-heap@8 { /* CP_MM HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <8>;
+ qcom,heap-align = <0x1000>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0x3800000>;
+ };
+
+ qcom,ion-heap@25 { /* IOMMU HEAP */
+ reg = <25>;
+ };
+
+ qcom,ion-heap@27 { /* QSECOM HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <27>;
+ qcom,heap-align = <0x1000>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0x780000>;
+ };
+
+ qcom,ion-heap@28 { /* AUDIO HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <28>;
+ qcom,heap-align = <0x1000>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0x314000>;
+ };
+
+ qcom,ion-heap@29 { /* FIRMWARE HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <29>;
+ qcom,heap-align = <0x20000>;
+ qcom,heap-adjacent = <8>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0xA00000>;
+ };
+
+ };
+};
+
diff --git a/arch/arm/boot/dts/msm8910.dtsi b/arch/arm/boot/dts/msm8910.dtsi
index 1c31e5d..9514e5a 100644
--- a/arch/arm/boot/dts/msm8910.dtsi
+++ b/arch/arm/boot/dts/msm8910.dtsi
@@ -11,6 +11,7 @@
*/
/include/ "skeleton.dtsi"
+/include/ "msm8910-ion.dtsi"
/ {
model = "Qualcomm MSM 8910";
diff --git a/arch/arm/boot/dts/msm8974-gpu.dtsi b/arch/arm/boot/dts/msm8974-gpu.dtsi
index 403a5cc..6623568 100644
--- a/arch/arm/boot/dts/msm8974-gpu.dtsi
+++ b/arch/arm/boot/dts/msm8974-gpu.dtsi
@@ -13,8 +13,9 @@
qcom,kgsl-3d0@fdb00000 {
label = "kgsl-3d0";
compatible = "qcom,kgsl-3d0", "qcom,kgsl-3d";
- reg = <0xfdb00000 0x20000>;
- reg-names = "kgsl_3d0_reg_memory";
+ reg = <0xfdb00000 0x10000
+ 0xfdb20000 0x10000>;
+ reg-names = "kgsl_3d0_reg_memory" , "kgsl_3d0_shader_memory";
interrupts = <0 33 0>;
interrupt-names = "kgsl_3d0_irq";
qcom,id = <0>;
diff --git a/arch/arm/boot/dts/msm8974-liquid.dtsi b/arch/arm/boot/dts/msm8974-liquid.dtsi
index d04462f..0f65dc8 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dtsi
+++ b/arch/arm/boot/dts/msm8974-liquid.dtsi
@@ -77,6 +77,9 @@
qcom,hdmi_tx@fd922100 {
status = "ok";
+
+ qcom,hdmi-tx-mux-sel = <&pm8841_mpps 3 0>;
+ qcom,hdmi-tx-mux-en = <&pm8841_mpps 4 0>;
};
drv2667_vreg: drv2667_vdd_vreg {
diff --git a/arch/arm/boot/dts/msm8974-mdss.dtsi b/arch/arm/boot/dts/msm8974-mdss.dtsi
index a51a38d..b765611 100644
--- a/arch/arm/boot/dts/msm8974-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8974-mdss.dtsi
@@ -48,11 +48,10 @@
qcom,hdmi-tx-max-voltage-level = <0 1800000 1800000>;
qcom,hdmi-tx-op-mode = <0 1800000 0>;
- gpios = <&msmgpio 31 0>,
- <&msmgpio 32 0>,
- <&msmgpio 33 0>,
- <&msmgpio 34 0>;
- qcom,hdmi-tx-gpio-names = "cec-pin", "hpd-ddc-clk", "hpd-ddc-data", "hpd-pin";
+ qcom,hdmi-tx-cec = <&msmgpio 31 0>;
+ qcom,hdmi-tx-ddc-clk = <&msmgpio 32 0>;
+ qcom,hdmi-tx-ddc-data = <&msmgpio 33 0>;
+ qcom,hdmi-tx-hpd = <&msmgpio 34 0>;
};
qcom,mdss_wb_panel {
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index dfca860..19b8828 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -666,13 +666,14 @@
<0xfd4ab000 0x4>;
interrupts = <0 131 0>, <0 179 0>, <0 133 0>;
interrupt-names = "irq", "otg_irq", "hs_phy_irq";
- SSUSB_VDDCX-supply = <&pm8841_s2>;
+ ssusb_vdd_dig-supply = <&pm8841_s2_corner>;
SSUSB_1p8-supply = <&pm8941_l6>;
- HSUSB_VDDCX-supply = <&pm8841_s2>;
+ hsusb_vdd_dig-supply = <&pm8841_s2_corner>;
HSUSB_1p8-supply = <&pm8941_l6>;
HSUSB_3p3-supply = <&pm8941_l24>;
vbus_dwc3-supply = <&pm8941_mvs1>;
qcom,dwc-usb3-msm-dbm-eps = <4>;
+ qcom,vdd-voltage-level = <1 5 7>;
qcom,msm-bus,name = "usb3";
qcom,msm-bus,num-cases = <2>;
diff --git a/arch/arm/boot/dts/msm9625-pm.dtsi b/arch/arm/boot/dts/msm9625-pm.dtsi
index d62f7e7..2839864 100644
--- a/arch/arm/boot/dts/msm9625-pm.dtsi
+++ b/arch/arm/boot/dts/msm9625-pm.dtsi
@@ -115,6 +115,21 @@
qcom,lpm-level@3 {
reg = <0x3>;
+ qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+ qcom,xo = <1>; /* ON */
+ qcom,l2 = <0>; /* GDHS */
+ qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <950000>; /* SVS SOC */
+ qcom,vdd-dig-upper-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-lower-bound = <3>; /* SVS SOC */
+ qcom,latency-us = <4500>;
+ qcom,ss-power = <5000>;
+ qcom,energy-overhead = <60350000>;
+ qcom,time-overhead = <7300>;
+ };
+
+ qcom,lpm-level@4 {
+ reg = <0x4>;
qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <0>; /* OFF */
qcom,l2 = <0>; /* OFF */
@@ -128,8 +143,8 @@
qcom,time-overhead = <13300>;
};
- qcom,lpm-level@4 {
- reg = <0x4>;
+ qcom,lpm-level@5 {
+ reg = <0x5>;
qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <0>; /* OFF */
qcom,l2 = <0>; /* OFF */
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index 09c388f..d1c731e 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -88,6 +88,47 @@
reg = <0xfc42b0c8 0xc8>;
};
+ hsic@f9a15000 {
+ compatible = "qcom,hsic-host";
+ reg = <0xf9a15000 0x400>;
+ interrupts = <0 136 0>;
+ interrupt-names = "core_irq";
+ HSIC_VDDCX-supply = <&pm8019_l12>;
+ HSIC_GDSC-supply = <&gdsc_usb_hsic>;
+ };
+
+ qcom,usbbam@f9a44000 {
+ compatible = "qcom,usb-bam-msm";
+ reg = <0xf9a44000 0x11000>;
+ reg-names = "hsusb";
+ interrupts = <0 135 0>;
+ interrupt-names = "hsusb";
+ qcom,usb-active-bam = <1>;
+ qcom,usb-total-bam-num = <3>;
+ qcom,usb-bam-num-pipes = <16>;
+ qcom,ignore-core-reset-ack;
+
+ qcom,pipe0 {
+ label = "usb-to-ipa";
+ qcom,usb-bam-type = <1>;
+ qcom,usb-bam-mem-type = <2>;
+ qcom,src-bam-physical-address = <0xf9a44000>;
+ qcom,src-bam-pipe-index = <1>;
+ qcom,data-fifo-size = <0x600>;
+ qcom,descriptor-fifo-size = <0x300>;
+ };
+
+ qcom,pipe1 {
+ label = "ipa-to-usb";
+ qcom,usb-bam-type = <1>;
+ qcom,usb-bam-mem-type = <2>;
+ qcom,dst-bam-physical-address = <0xf9a44000>;
+ qcom,dst-bam-pipe-index = <0>;
+ qcom,data-fifo-size = <0x600>;
+ qcom,descriptor-fifo-size = <0x100>;
+ };
+ };
+
qcom,nand@f9ac0000 {
compatible = "qcom,msm-nand";
reg = <0xf9ac0000 0x1000>,
@@ -245,6 +286,42 @@
interrupts = <0 29 1>;
};
+ qcom,ipa@fd4c0000 {
+ compatible = "qcom,ipa";
+ reg = <0xfd4c0000 0x26000>,
+ <0xfd4c4000 0x14818>;
+ reg-names = "ipa-base", "bam-base";
+ interrupts = <0 252 0>,
+ <0 253 0>;
+ interrupt-names = "ipa-irq", "bam-irq";
+
+ qcom,pipe1 {
+ label = "a2-to-ipa";
+ qcom,src-bam-physical-address = <0xfc834000>;
+ qcom,ipa-bam-mem-type = <0>;
+ qcom,src-bam-pipe-index = <1>;
+ qcom,dst-bam-physical-address = <0xfd4c0000>;
+ qcom,dst-bam-pipe-index = <6>;
+ qcom,data-fifo-offset = <0x1000>;
+ qcom,data-fifo-size = <0xd00>;
+ qcom,descriptor-fifo-offset = <0x1d00>;
+ qcom,descriptor-fifo-size = <0x300>;
+ };
+
+ qcom,pipe2 {
+ label = "ipa-to-a2";
+ qcom,src-bam-physical-address = <0xfd4c0000>;
+ qcom,ipa-bam-mem-type = <0>;
+ qcom,src-bam-pipe-index = <7>;
+ qcom,dst-bam-physical-address = <0xfc834000>;
+ qcom,dst-bam-pipe-index = <0>;
+ qcom,data-fifo-offset = <0x00>;
+ qcom,data-fifo-size = <0xd00>;
+ qcom,descriptor-fifo-offset = <0xd00>;
+ qcom,descriptor-fifo-size = <0x300>;
+ };
+ };
+
qcom,acpuclk@f9010000 {
compatible = "qcom,acpuclk-9625";
reg = <0xf9010008 0x10>,
diff --git a/arch/arm/configs/msm8910_defconfig b/arch/arm/configs/msm8910_defconfig
index e2e05b2..2dd4b30 100644
--- a/arch/arm/configs/msm8910_defconfig
+++ b/arch/arm/configs/msm8910_defconfig
@@ -46,7 +46,7 @@
CONFIG_MSM_WATCHDOG_V2=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
-# CONFIG_SMP_ON_UP is not set
+CONFIG_SMP=y
CONFIG_ARM_ARCH_TIMER=y
CONFIG_HOTPLUG_CPU=y
CONFIG_PREEMPT=y
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index a721f30..f468fe0 100644
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -271,6 +271,7 @@
CONFIG_GPIO_QPNP_PIN=y
CONFIG_GPIO_QPNP_PIN_DEBUG=y
CONFIG_POWER_SUPPLY=y
+CONFIG_SMB350_CHARGER=y
CONFIG_BATTERY_BQ28400=y
CONFIG_QPNP_CHARGER=y
CONFIG_BATTERY_BCL=y
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index 97fef39..d47870e 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -21,6 +21,7 @@
CONFIG_BLK_DEV_INITRD=y
CONFIG_RD_BZIP2=y
CONFIG_RD_LZMA=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_PANIC_TIMEOUT=5
CONFIG_KALLSYMS_ALL=y
CONFIG_EMBEDDED=y
@@ -168,6 +169,7 @@
CONFIG_RTC_CLASS=y
# CONFIG_RTC_DRV_MSM is not set
CONFIG_RTC_DRV_QPNP=y
+CONFIG_IPA=y
CONFIG_SPS=y
CONFIG_USB_BAM=y
CONFIG_SPS_SUPPORT_BAMDMA=y
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index a0868c7..f3a6fee 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -67,6 +67,7 @@
select MSM_PM2 if PM
select HOLES_IN_ZONE if SPARSEMEM
select MSM_MODEM_RESTART
+ select ARM_HAS_SG_CHAIN
config ARCH_QSD8X50
bool "QSD8X50"
@@ -279,6 +280,7 @@
select MEMORY_HOLE_CARVEOUT
select MSM_RPM_STATS_LOG
select QMI_ENCDEC
+ select DONT_MAP_HOLE_AFTER_MEMBANK0
config ARCH_MPQ8092
bool "MPQ8092"
@@ -369,7 +371,6 @@
bool "MSM8910"
select ARM_GIC
select GIC_SECURE
- select SMP
select ARCH_MSM_CORTEXMP
select CPU_V7
select MSM_SCM if SMP
@@ -385,7 +386,6 @@
bool "MSM8226"
select ARM_GIC
select GIC_SECURE
- select SMP
select ARCH_MSM_CORTEXMP
select CPU_V7
select MSM_SCM if SMP
@@ -394,6 +394,8 @@
select MULTI_IRQ_HANDLER
select GPIO_MSM_V3
select MSM_GPIOMUX
+ select MSM_NATIVE_RESTART
+ select MSM_RESTART_V2
endmenu
choice
diff --git a/arch/arm/mach-msm/acpuclock-8064.c b/arch/arm/mach-msm/acpuclock-8064.c
index 88237af..5c1d3e1 100644
--- a/arch/arm/mach-msm/acpuclock-8064.c
+++ b/arch/arm/mach-msm/acpuclock-8064.c
@@ -213,6 +213,32 @@
{ 0, { 0 } }
};
+static struct acpu_level tbl_faster[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 850000 },
+ { 0, { 432000, HFPLL, 2, 0x20 }, L2(6), 875000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 875000 },
+ { 0, { 540000, HFPLL, 2, 0x28 }, L2(6), 900000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(6), 900000 },
+ { 0, { 648000, HFPLL, 1, 0x18 }, L2(6), 925000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(6), 925000 },
+ { 0, { 756000, HFPLL, 1, 0x1C }, L2(6), 962500 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(6), 962500 },
+ { 0, { 864000, HFPLL, 1, 0x20 }, L2(6), 975000 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(6), 975000 },
+ { 0, { 972000, HFPLL, 1, 0x24 }, L2(6), 1000000 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(6), 1000000 },
+ { 0, { 1080000, HFPLL, 1, 0x28 }, L2(15), 1050000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1050000 },
+ { 0, { 1188000, HFPLL, 1, 0x2C }, L2(15), 1075000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1075000 },
+ { 0, { 1296000, HFPLL, 1, 0x30 }, L2(15), 1100000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1100000 },
+ { 0, { 1404000, HFPLL, 1, 0x34 }, L2(15), 1112500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1112500 },
+ { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 1125000 },
+ { 0, { 0 } }
+};
+
static struct acpu_level tbl_PVS0_1700MHz[] __initdata = {
{ 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 950000 },
{ 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 975000 },
@@ -369,7 +395,7 @@
[0][PVS_SLOW] = {tbl_slow, sizeof(tbl_slow), 0 },
[0][PVS_NOMINAL] = {tbl_nom, sizeof(tbl_nom), 25000 },
[0][PVS_FAST] = {tbl_fast, sizeof(tbl_fast), 25000 },
- [0][PVS_FASTER] = {tbl_fast, sizeof(tbl_fast), 25000 },
+ [0][PVS_FASTER] = {tbl_faster, sizeof(tbl_faster), 25000 },
[1][0] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz), 0 },
[1][1] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz), 0 },
diff --git a/arch/arm/mach-msm/board-8064-gpu.c b/arch/arm/mach-msm/board-8064-gpu.c
index 5ebb010..38ac83e 100644
--- a/arch/arm/mach-msm/board-8064-gpu.c
+++ b/arch/arm/mach-msm/board-8064-gpu.c
@@ -182,6 +182,12 @@
{
.name = KGSL_3D0_REG_MEMORY,
.start = 0x04300000, /* GFX3D address */
+ .end = 0x0430ffff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = KGSL_3D0_SHADER_MEMORY,
+ .start = 0x04310000, /* Shader Mem Address */
.end = 0x0431ffff,
.flags = IORESOURCE_MEM,
},
diff --git a/arch/arm/mach-msm/board-8226.c b/arch/arm/mach-msm/board-8226.c
index e5263c7..3e90489 100644
--- a/arch/arm/mach-msm/board-8226.c
+++ b/arch/arm/mach-msm/board-8226.c
@@ -33,6 +33,7 @@
#include <mach/board.h>
#include <mach/gpiomux.h>
#include <mach/msm_iomap.h>
+#include <mach/restart.h>
#ifdef CONFIG_ION_MSM
#include <mach/ion.h>
#endif
@@ -63,6 +64,12 @@
CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
CLK_DUMMY("iface_clk", HSUSB_IFACE_CLK, "f9a55000.usb", OFF),
CLK_DUMMY("core_clk", HSUSB_CORE_CLK, "f9a55000.usb", OFF),
+ CLK_DUMMY("iface_clk", NULL, "msm_sdcc.1", OFF),
+ CLK_DUMMY("core_clk", NULL, "msm_sdcc.1", OFF),
+ CLK_DUMMY("bus_clk", NULL, "msm_sdcc.1", OFF),
+ CLK_DUMMY("iface_clk", NULL, "msm_sdcc.2", OFF),
+ CLK_DUMMY("core_clk", NULL, "msm_sdcc.2", OFF),
+ CLK_DUMMY("bus_clk", NULL, "msm_sdcc.2", OFF),
};
static struct clock_init_data msm_dummy_clock_init_data __initdata = {
@@ -70,6 +77,14 @@
.size = ARRAY_SIZE(msm_clocks_dummy),
};
+static struct of_dev_auxdata msm8226_auxdata_lookup[] __initdata = {
+ OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF9824000, \
+ "msm_sdcc.1", NULL),
+ OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \
+ "msm_sdcc.2", NULL),
+ {}
+};
+
static struct reserve_info msm8226_reserve_info __initdata = {
.memtype_reserve_table = msm8226_reserve_table,
.paddr_to_memtype = msm8226_paddr_to_memtype,
@@ -89,13 +104,16 @@
void __init msm8226_init(void)
{
+ struct of_dev_auxdata *adata = msm8226_auxdata_lookup;
+
msm8226_init_gpiomux();
+
msm_clock_init(&msm_dummy_clock_init_data);
if (socinfo_init() < 0)
pr_err("%s: socinfo_init() failed\n", __func__);
- of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+ of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
}
static const char *msm8226_dt_match[] __initconst = {
@@ -111,5 +129,6 @@
.timer = &msm_dt_timer,
.dt_compat = msm8226_dt_match,
.reserve = msm8226_reserve,
- .init_very_early = msm8226_early_memory
+ .init_very_early = msm8226_early_memory,
+ .restart = msm_restart,
MACHINE_END
diff --git a/arch/arm/mach-msm/board-8910.c b/arch/arm/mach-msm/board-8910.c
index b031dac..42fe1ea 100644
--- a/arch/arm/mach-msm/board-8910.c
+++ b/arch/arm/mach-msm/board-8910.c
@@ -35,12 +35,29 @@
#ifdef CONFIG_ION_MSM
#include <mach/ion.h>
#endif
+#include <mach/msm_memtypes.h>
#include <mach/socinfo.h>
#include <mach/board.h>
#include <mach/clk-provider.h>
#include "board-dt.h"
#include "clock.h"
+static struct memtype_reserve msm8910_reserve_table[] __initdata = {
+ [MEMTYPE_SMI] = {
+ },
+ [MEMTYPE_EBI0] = {
+ .flags = MEMTYPE_FLAGS_1M_ALIGN,
+ },
+ [MEMTYPE_EBI1] = {
+ .flags = MEMTYPE_FLAGS_1M_ALIGN,
+ },
+};
+
+static int msm8910_paddr_to_memtype(unsigned int paddr)
+{
+ return MEMTYPE_EBI1;
+}
+
static struct clk_lookup msm_clocks_dummy[] = {
CLK_DUMMY("core_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
@@ -67,6 +84,22 @@
{}
};
+static struct reserve_info msm8910_reserve_info __initdata = {
+ .memtype_reserve_table = msm8910_reserve_table,
+ .paddr_to_memtype = msm8910_paddr_to_memtype,
+};
+
+static void __init msm8910_early_memory(void)
+{
+ reserve_info = &msm8910_reserve_info;
+ of_scan_flat_dt(dt_scan_for_memory_reserve, msm8910_reserve_table);
+}
+
+static void __init msm8910_reserve(void)
+{
+ msm_reserve();
+}
+
void __init msm8910_init(void)
{
struct of_dev_auxdata *adata = msm8910_auxdata_lookup;
@@ -93,4 +126,6 @@
.timer = &msm_dt_timer,
.dt_compat = msm8910_dt_match,
.restart = msm_restart,
+ .reserve = msm8910_reserve,
+ .init_very_early = msm8910_early_memory
MACHINE_END
diff --git a/arch/arm/mach-msm/board-8930-display.c b/arch/arm/mach-msm/board-8930-display.c
index 7e477b1..4506ea7 100644
--- a/arch/arm/mach-msm/board-8930-display.c
+++ b/arch/arm/mach-msm/board-8930-display.c
@@ -132,6 +132,15 @@
};
static bool dsi_power_on;
+static struct mipi_dsi_panel_platform_data novatek_pdata;
+static void pm8917_gpio_set_backlight(int bl_level)
+{
+ int gpio24 = PM8917_GPIO_PM_TO_SYS(24);
+ if (bl_level > 0)
+ gpio_set_value_cansleep(gpio24, 1);
+ else
+ gpio_set_value_cansleep(gpio24, 0);
+}
/*
* TODO: When physical 8930/PM8038 hardware becomes
@@ -214,9 +223,13 @@
rc);
return -ENODEV;
}
+ gpio_set_value_cansleep(gpio24, 0);
+ novatek_pdata.gpio_set_backlight =
+ pm8917_gpio_set_backlight;
}
dsi_power_on = true;
}
+
if (on) {
rc = regulator_set_optimum_mode(reg_l8, 100000);
if (rc < 0) {
@@ -256,8 +269,6 @@
gpio_set_value(DISP_RST_GPIO, 1);
gpio_set_value(DISP_3D_2D_MODE, 1);
usleep(20);
- if (socinfo_get_pmic_model() == PMIC_MODEL_PM8917)
- gpio_set_value_cansleep(gpio24, 1);
} else {
gpio_set_value(DISP_RST_GPIO, 0);
@@ -294,8 +305,6 @@
}
gpio_set_value(DISP_3D_2D_MODE, 0);
usleep(20);
- if (socinfo_get_pmic_model() == PMIC_MODEL_PM8917)
- gpio_set_value_cansleep(gpio24, 0);
}
return 0;
}
@@ -441,7 +450,7 @@
#ifdef CONFIG_MSM_BUS_SCALING
.mdp_bus_scale_table = &mdp_bus_scale_pdata,
#endif
- .mdp_rev = MDP_REV_42,
+ .mdp_rev = MDP_REV_43,
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
.mem_hid = BIT(ION_CP_MM_HEAP_ID),
#else
diff --git a/arch/arm/mach-msm/board-8930-gpu.c b/arch/arm/mach-msm/board-8930-gpu.c
index 578c665..3eb7d8a 100644
--- a/arch/arm/mach-msm/board-8930-gpu.c
+++ b/arch/arm/mach-msm/board-8930-gpu.c
@@ -88,6 +88,12 @@
{
.name = KGSL_3D0_REG_MEMORY,
.start = 0x04300000, /* GFX3D address */
+ .end = 0x0430ffff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = KGSL_3D0_SHADER_MEMORY,
+ .start = 0x04310000,
.end = 0x0431ffff,
.flags = IORESOURCE_MEM,
},
diff --git a/arch/arm/mach-msm/board-8960-display.c b/arch/arm/mach-msm/board-8960-display.c
index 3052902..ecf5ec6 100644
--- a/arch/arm/mach-msm/board-8960-display.c
+++ b/arch/arm/mach-msm/board-8960-display.c
@@ -988,6 +988,12 @@
void __init msm8960_init_fb(void)
{
+ uint32_t soc_platform_version = socinfo_get_version();
+
+
+ if (SOCINFO_VERSION_MAJOR(soc_platform_version) >= 3)
+ mdp_pdata.mdp_rev = MDP_REV_43;
+
if (cpu_is_msm8960ab())
mdp_pdata.mdp_rev = MDP_REV_44;
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 9efc60a..c9ebb24 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -2890,8 +2890,18 @@
kgsl_3d0_pdata->chipid = ADRENO_CHIPID(3, 2, 1, 0);
/* 8960PRO nominal clock rate is 320Mhz */
kgsl_3d0_pdata->pwrlevel[1].gpu_freq = 320000000;
+
+ /*
+ * If this an A320 GPU device (MSM8960AB), then
+ * switch the resource table to 8960AB, to reflect the
+ * separate register and shader memory mapping used in A320.
+ */
+
+ msm_kgsl_3d0.num_resources = kgsl_num_resources_8960ab;
+ msm_kgsl_3d0.resource = kgsl_3d0_resources_8960ab;
} else {
kgsl_3d0_pdata->iommu_count = 1;
+
if (SOCINFO_VERSION_MAJOR(soc_platform_version) == 1) {
kgsl_3d0_pdata->pwrlevel[0].gpu_freq = 320000000;
kgsl_3d0_pdata->pwrlevel[1].gpu_freq = 266667000;
diff --git a/arch/arm/mach-msm/board-8974.c b/arch/arm/mach-msm/board-8974.c
index b092a53..c47b688 100644
--- a/arch/arm/mach-msm/board-8974.c
+++ b/arch/arm/mach-msm/board-8974.c
@@ -71,10 +71,17 @@
.paddr_to_memtype = msm8974_paddr_to_memtype,
};
-static void __init msm8974_early_memory(void)
+void __init msm_8974_reserve(void)
{
reserve_info = &msm8974_reserve_info;
of_scan_flat_dt(dt_scan_for_memory_reserve, msm8974_reserve_table);
+ msm_reserve();
+}
+
+static void __init msm8974_early_memory(void)
+{
+ reserve_info = &msm8974_reserve_info;
+ of_scan_flat_dt(dt_scan_for_memory_hole, msm8974_reserve_table);
}
#define BIMC_BASE 0xfc380000
@@ -389,7 +396,7 @@
.handle_irq = gic_handle_irq,
.timer = &msm_dt_timer,
.dt_compat = msm8974_dt_match,
- .reserve = msm_reserve,
+ .reserve = msm_8974_reserve,
.init_very_early = msm8974_init_very_early,
.restart = msm_restart,
MACHINE_END
diff --git a/arch/arm/mach-msm/board-9625-gpiomux.c b/arch/arm/mach-msm/board-9625-gpiomux.c
index 9102875..686bb41 100644
--- a/arch/arm/mach-msm/board-9625-gpiomux.c
+++ b/arch/arm/mach-msm/board-9625-gpiomux.c
@@ -259,6 +259,23 @@
},
};
+static struct gpiomux_setting sdc2_card_det_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ .dir = GPIOMUX_IN,
+};
+
+struct msm_gpiomux_config sdc2_card_det_config[] __initdata = {
+ {
+ .gpio = 66,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc2_card_det_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc2_card_det_cfg,
+ },
+ },
+};
+
void __init msm9625_init_gpiomux(void)
{
int rc;
@@ -277,4 +294,6 @@
ARRAY_SIZE(mdm9625_mi2s_configs));
msm_gpiomux_install(mdm9625_cdc_reset_config,
ARRAY_SIZE(mdm9625_cdc_reset_config));
+ msm_gpiomux_install(sdc2_card_det_config,
+ ARRAY_SIZE(sdc2_card_det_config));
}
diff --git a/arch/arm/mach-msm/board-9625.c b/arch/arm/mach-msm/board-9625.c
index 8e8d3e7..42f3f41 100644
--- a/arch/arm/mach-msm/board-9625.c
+++ b/arch/arm/mach-msm/board-9625.c
@@ -109,6 +109,8 @@
"msm_sdcc.3", NULL),
OF_DEV_AUXDATA("qcom,msm-tsens", 0xFC4A8000, \
"msm-tsens", NULL),
+ OF_DEV_AUXDATA("qcom,usb-bam-msm", 0xF9A44000, \
+ "usb_bam", NULL),
{}
};
diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c
index 023ce86..a25290f 100644
--- a/arch/arm/mach-msm/board-qrd7627a.c
+++ b/arch/arm/mach-msm/board-qrd7627a.c
@@ -46,6 +46,7 @@
#include <mach/usbdiag.h>
#include <mach/msm_memtypes.h>
#include <mach/msm_serial_hs.h>
+#include <mach/msm_serial_pdata.h>
#include <mach/pmic.h>
#include <mach/socinfo.h>
#include <mach/vreg.h>
@@ -82,6 +83,10 @@
.id = -1,
};
+static struct msm_serial_platform_data msm_8625_uart1_pdata = {
+ .userid = 10,
+};
+
static struct msm_gpio qup_i2c_gpios_io[] = {
{ GPIO_CFG(60, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
"qup_scl" },
@@ -994,6 +999,7 @@
if (machine_is_msm8625_evb() || machine_is_msm8625_qrd7()
|| machine_is_msm8625_evt()
|| machine_is_qrd_skud_prime()) {
+ msm8625_device_uart1.dev.platform_data = &msm_8625_uart1_pdata;
platform_add_devices(msm8625_evb_devices,
ARRAY_SIZE(msm8625_evb_devices));
platform_add_devices(qrd3_devices,
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index 14edbcf..f559629 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -2735,6 +2735,7 @@
static struct msm_dcvs_platform_data apq8064_dcvs_data = {
.sync_rules = apq8064_dcvs_sync_rules,
.num_sync_rules = ARRAY_SIZE(apq8064_dcvs_sync_rules),
+ .gpu_max_nom_khz = 320000,
};
struct platform_device apq8064_dcvs_device = {
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index a839fcf..c59461a 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -3246,7 +3246,30 @@
};
#endif
-static struct resource kgsl_3d0_resources[] = {
+struct resource kgsl_3d0_resources_8960ab[] = {
+ {
+ .name = KGSL_3D0_REG_MEMORY,
+ .start = 0x04300000, /* GFX3D address */
+ .end = 0x0430ffff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = KGSL_3D0_SHADER_MEMORY,
+ .start = 0x04310000, /* Shader Mem Address (8960AB) */
+ .end = 0x0431ffff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = KGSL_3D0_IRQ,
+ .start = GFX3D_IRQ,
+ .end = GFX3D_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+int kgsl_num_resources_8960ab = ARRAY_SIZE(kgsl_3d0_resources_8960ab);
+
+static struct resource kgsl_3d0_resources_8960[] = {
{
.name = KGSL_3D0_REG_MEMORY,
.start = 0x04300000, /* GFX3D address */
@@ -3330,8 +3353,8 @@
struct platform_device msm_kgsl_3d0 = {
.name = "kgsl-3d0",
.id = 0,
- .num_resources = ARRAY_SIZE(kgsl_3d0_resources),
- .resource = kgsl_3d0_resources,
+ .num_resources = ARRAY_SIZE(kgsl_3d0_resources_8960),
+ .resource = kgsl_3d0_resources_8960,
.dev = {
.platform_data = &kgsl_3d0_pdata,
},
diff --git a/arch/arm/mach-msm/devices-msm7x27a.c b/arch/arm/mach-msm/devices-msm7x27a.c
index 5296048..b4ef76d2 100644
--- a/arch/arm/mach-msm/devices-msm7x27a.c
+++ b/arch/arm/mach-msm/devices-msm7x27a.c
@@ -1917,8 +1917,6 @@
else if (msm8625_cpu_id() == MSM8625)
msm_cpr_pdata.max_freq = 1008000;
- msm_cpr_clk_enable();
-
platform_device_register(&msm8625_vp_device);
platform_device_register(&msm8625_device_cpr);
}
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index bd5a20f..bd9ea49 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -327,6 +327,9 @@
extern struct platform_device msm_kgsl_2d0;
extern struct platform_device msm_kgsl_2d1;
+extern struct resource kgsl_3d0_resources_8960ab[];
+extern int kgsl_num_resources_8960ab;
+
extern struct platform_device msm_mipi_dsi1_device;
extern struct platform_device mipi_dsi_device;
extern struct platform_device msm_lcdc_device;
diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h
index 9c5e52c..cbe2040 100644
--- a/arch/arm/mach-msm/include/mach/board.h
+++ b/arch/arm/mach-msm/include/mach/board.h
@@ -465,6 +465,7 @@
char dlane_swap;
void (*dsi_pwm_cfg)(void);
char enable_wled_bl_ctrl;
+ void (*gpio_set_backlight)(int bl_level);
};
struct lvds_panel_platform_data {
diff --git a/arch/arm/mach-msm/include/mach/ipa.h b/arch/arm/mach-msm/include/mach/ipa.h
index 0f689ac..dae6d3b 100644
--- a/arch/arm/mach-msm/include/mach/ipa.h
+++ b/arch/arm/mach-msm/include/mach/ipa.h
@@ -346,6 +346,8 @@
*/
typedef int (*ipa_pull_fn)(void *buf, uint16_t sz);
+#ifdef CONFIG_IPA
+
/*
* Connect / Disconnect
*/
@@ -455,4 +457,276 @@
int ipa_teardown_sys_pipe(u32 clnt_hdl);
+#else
+
+/*
+ * Connect / Disconnect
+ */
+static inline int ipa_connect(const struct ipa_connect_params *in,
+ struct ipa_sps_params *sps, u32 *clnt_hdl)
+{
+ return -EPERM;
+}
+
+static inline int ipa_disconnect(u32 clnt_hdl)
+{
+ return -EPERM;
+}
+
+
+/*
+ * Configuration
+ */
+static inline int ipa_cfg_ep(u32 clnt_hdl,
+ const struct ipa_ep_cfg *ipa_ep_cfg)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_cfg_ep_nat(u32 clnt_hdl,
+ const struct ipa_ep_cfg_nat *ipa_ep_cfg)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_cfg_ep_hdr(u32 clnt_hdl,
+ const struct ipa_ep_cfg_hdr *ipa_ep_cfg)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_cfg_ep_mode(u32 clnt_hdl,
+ const struct ipa_ep_cfg_mode *ipa_ep_cfg)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_cfg_ep_aggr(u32 clnt_hdl,
+ const struct ipa_ep_cfg_aggr *ipa_ep_cfg)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_cfg_ep_route(u32 clnt_hdl,
+ const struct ipa_ep_cfg_route *ipa_ep_cfg)
+{
+ return -EPERM;
+}
+
+
+/*
+ * Header removal / addition
+ */
+static inline int ipa_add_hdr(struct ipa_ioc_add_hdr *hdrs)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_del_hdr(struct ipa_ioc_del_hdr *hdls)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_commit_hdr(void)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_reset_hdr(void)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_get_hdr(struct ipa_ioc_get_hdr *lookup)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_put_hdr(u32 hdr_hdl)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_copy_hdr(struct ipa_ioc_copy_hdr *copy)
+{
+ return -EPERM;
+}
+
+
+/*
+ * Routing
+ */
+static inline int ipa_add_rt_rule(struct ipa_ioc_add_rt_rule *rules)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_del_rt_rule(struct ipa_ioc_del_rt_rule *hdls)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_commit_rt(enum ipa_ip_type ip)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_reset_rt(enum ipa_ip_type ip)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_get_rt_tbl(struct ipa_ioc_get_rt_tbl *lookup)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_put_rt_tbl(u32 rt_tbl_hdl)
+{
+ return -EPERM;
+}
+
+
+/*
+ * Filtering
+ */
+static inline int ipa_add_flt_rule(struct ipa_ioc_add_flt_rule *rules)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_del_flt_rule(struct ipa_ioc_del_flt_rule *hdls)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_commit_flt(enum ipa_ip_type ip)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_reset_flt(enum ipa_ip_type ip)
+{
+ return -EPERM;
+}
+
+
+/*
+ * NAT
+ */
+static inline int allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_nat_init_cmd(struct ipa_ioc_v4_nat_init *init)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_nat_del_cmd(struct ipa_ioc_v4_nat_del *del)
+{
+ return -EPERM;
+}
+
+
+/*
+ * Aggregation
+ */
+static inline int ipa_set_aggr_mode(enum ipa_aggr_mode mode)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_set_qcncm_ndp_sig(char sig[3])
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_set_single_ndp_per_mbim(bool enable)
+{
+ return -EPERM;
+}
+
+
+/*
+ * rmnet bridge
+ */
+static inline int rmnet_bridge_init(void)
+{
+ return -EPERM;
+}
+
+
+static inline int rmnet_bridge_disconnect(void)
+{
+ return -EPERM;
+}
+
+
+static inline int rmnet_bridge_connect(u32 producer_hdl,
+ u32 consumer_hdl,
+ int wwan_logical_channel_id)
+{
+ return -EPERM;
+}
+
+
+/*
+ * Data path
+ */
+static inline int ipa_tx_dp(enum ipa_client_type dst, struct sk_buff *skb,
+ struct ipa_tx_meta *metadata)
+{
+ return -EPERM;
+}
+
+
+/*
+ * System pipes
+ */
+static inline int ipa_setup_sys_pipe(struct ipa_sys_connect_params *sys_in,
+ u32 *clnt_hdl)
+{
+ return -EPERM;
+}
+
+
+static inline int ipa_teardown_sys_pipe(u32 clnt_hdl)
+{
+ return -EPERM;
+}
+
+
+#endif /* CONFIG_IPA*/
+
#endif /* _IPA_H_ */
diff --git a/arch/arm/mach-msm/include/mach/irqs-9625.h b/arch/arm/mach-msm/include/mach/irqs-9625.h
index abafc23..b1f65d1 100644
--- a/arch/arm/mach-msm/include/mach/irqs-9625.h
+++ b/arch/arm/mach-msm/include/mach/irqs-9625.h
@@ -21,6 +21,10 @@
* 32+: SPI (shared peripheral interrupts)
*/
+#define GIC_PPI_START 16
+
+#define INT_ARMQC_PERFMON (GIC_PPI_START + 7)
+
#define GIC_SPI_START 32
#define APCC_QGICL2PERFMONIRPTREQ (GIC_SPI_START + 1)
diff --git a/arch/arm/mach-msm/include/mach/kgsl.h b/arch/arm/mach-msm/include/mach/kgsl.h
index a51cc46..f07a9e8 100644
--- a/arch/arm/mach-msm/include/mach/kgsl.h
+++ b/arch/arm/mach-msm/include/mach/kgsl.h
@@ -27,6 +27,7 @@
(val*1000*1000U)
#define KGSL_3D0_REG_MEMORY "kgsl_3d0_reg_memory"
+#define KGSL_3D0_SHADER_MEMORY "kgsl_3d0_shader_memory"
#define KGSL_3D0_IRQ "kgsl_3d0_irq"
#define KGSL_2D0_REG_MEMORY "kgsl_2d0_reg_memory"
#define KGSL_2D0_IRQ "kgsl_2d0_irq"
diff --git a/arch/arm/mach-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h
index acfbe4a..d089924 100644
--- a/arch/arm/mach-msm/include/mach/memory.h
+++ b/arch/arm/mach-msm/include/mach/memory.h
@@ -98,26 +98,25 @@
#define finish_arch_switch(prev) do { store_ttbr0(); } while (0)
#endif
+#define MAX_HOLE_ADDRESS (PHYS_OFFSET + 0x10000000)
+extern unsigned long memory_hole_offset;
+extern unsigned long memory_hole_start;
+extern unsigned long memory_hole_end;
#ifdef CONFIG_DONT_MAP_HOLE_AFTER_MEMBANK0
-extern unsigned long membank0_size;
-extern unsigned long membank1_start;
-void find_membank0_hole(void);
+void find_memory_hole(void);
-#define MEMBANK0_PHYS_OFFSET PHYS_OFFSET
-#define MEMBANK0_PAGE_OFFSET PAGE_OFFSET
-
-#define MEMBANK1_PHYS_OFFSET (membank1_start)
-#define MEMBANK1_PAGE_OFFSET (MEMBANK0_PAGE_OFFSET + (membank0_size))
+#define MEM_HOLE_END_PHYS_OFFSET (memory_hole_end)
+#define MEM_HOLE_PAGE_OFFSET (PAGE_OFFSET + memory_hole_offset)
#define __phys_to_virt(phys) \
- ((MEMBANK1_PHYS_OFFSET && ((phys) >= MEMBANK1_PHYS_OFFSET)) ? \
- (phys) - MEMBANK1_PHYS_OFFSET + MEMBANK1_PAGE_OFFSET : \
- (phys) - MEMBANK0_PHYS_OFFSET + MEMBANK0_PAGE_OFFSET)
+ ((MEM_HOLE_END_PHYS_OFFSET && ((phys) >= MEM_HOLE_END_PHYS_OFFSET)) ? \
+ (phys) - MEM_HOLE_END_PHYS_OFFSET + MEM_HOLE_PAGE_OFFSET : \
+ (phys) - PHYS_OFFSET + PAGE_OFFSET)
#define __virt_to_phys(virt) \
- ((MEMBANK1_PHYS_OFFSET && ((virt) >= MEMBANK1_PAGE_OFFSET)) ? \
- (virt) - MEMBANK1_PAGE_OFFSET + MEMBANK1_PHYS_OFFSET : \
- (virt) - MEMBANK0_PAGE_OFFSET + MEMBANK0_PHYS_OFFSET)
+ ((MEM_HOLE_END_PHYS_OFFSET && ((virt) >= MEM_HOLE_PAGE_OFFSET)) ? \
+ (virt) - MEM_HOLE_PAGE_OFFSET + MEM_HOLE_END_PHYS_OFFSET : \
+ (virt) - PAGE_OFFSET + PHYS_OFFSET)
#endif
/*
diff --git a/arch/arm/mach-msm/include/mach/msm_dcvs.h b/arch/arm/mach-msm/include/mach/msm_dcvs.h
index c29b57a..2ad7d22 100644
--- a/arch/arm/mach-msm/include/mach/msm_dcvs.h
+++ b/arch/arm/mach-msm/include/mach/msm_dcvs.h
@@ -44,6 +44,7 @@
struct msm_dcvs_platform_data {
struct msm_dcvs_sync_rule *sync_rules;
unsigned num_sync_rules;
+ unsigned long gpu_max_nom_khz;
};
struct msm_gov_platform_data {
@@ -154,4 +155,23 @@
* Update the frequency known to dcvs when the limits are changed.
*/
extern void msm_dcvs_update_limits(int dcvs_core_id);
+
+/**
+ * msm_dcvs_apply_gpu_floor
+ * @cpu_freq: CPU frequency to compare to GPU sync rules
+ *
+ * Apply a GPU floor frequency if the corresponding CPU frequency,
+ * or the number of CPUs online, requires it.
+ */
+extern void msm_dcvs_apply_gpu_floor(unsigned long cpu_freq);
+
+/**
+ * msm_dcvs_update_algo_params
+ * @return:
+ * 0 on success, < 0 on error
+ *
+ * Updates the DCVS algorithm with parameters depending on the
+ * number of CPUs online.
+ */
+extern int msm_dcvs_update_algo_params(void);
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8226.h b/arch/arm/mach-msm/include/mach/msm_iomap-8226.h
index c03b513..ab43a8a 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-8226.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-8226.h
@@ -37,9 +37,12 @@
#define MSM8226_TLMM_PHYS 0xFD510000
#define MSM8226_TLMM_SIZE SZ_16K
-#define MSM8226_IMEM_PHYS 0xFC42B000
+#define MSM8226_IMEM_PHYS 0xFE805000
#define MSM8226_IMEM_SIZE SZ_4K
+#define MSM8226_MPM2_PSHOLD_PHYS 0xFC4AB000
+#define MSM8226_MPM2_PSHOLD_SIZE SZ_4K
+
#ifdef CONFIG_DEBUG_MSM8226_UART
#define MSM_DEBUG_UART_BASE IOMEM(0xFA71E000)
#define MSM_DEBUG_UART_PHYS 0xF991E000
diff --git a/arch/arm/mach-msm/include/mach/msm_memtypes.h b/arch/arm/mach-msm/include/mach/msm_memtypes.h
index 5ca5861..80e454a 100644
--- a/arch/arm/mach-msm/include/mach/msm_memtypes.h
+++ b/arch/arm/mach-msm/include/mach/msm_memtypes.h
@@ -68,6 +68,8 @@
int __init dt_scan_for_memory_reserve(unsigned long node, const char *uname,
int depth, void *data);
-
+int __init dt_scan_for_memory_hole(unsigned long node, const char *uname,
+ int depth, void *data);
+void adjust_meminfo(unsigned long start, unsigned long size);
unsigned long __init reserve_memory_for_fmem(unsigned long, unsigned long);
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_serial_pdata.h b/arch/arm/mach-msm/include/mach/msm_serial_pdata.h
index 4153cb2..40bdc9d 100644
--- a/arch/arm/mach-msm/include/mach/msm_serial_pdata.h
+++ b/arch/arm/mach-msm/include/mach/msm_serial_pdata.h
@@ -10,8 +10,8 @@
* GNU General Public License for more details.
*/
-#ifndef __ASM_ARCH_MSM_SERIAL_HS_H
-#define __ASM_ARCH_MSM_SERIAL_HS_H
+#ifndef __ASM_ARCH_MSM_SERIAL_H
+#define __ASM_ARCH_MSM_SERIAL_H
#include <linux/serial_core.h>
@@ -22,6 +22,7 @@
/* bool: inject char into rx tty on wakeup */
unsigned char inject_rx_on_wakeup;
char rx_to_inject;
+ int userid;
};
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_spi.h b/arch/arm/mach-msm/include/mach/msm_spi.h
index 11d3014..ab5271f 100644
--- a/arch/arm/mach-msm/include/mach/msm_spi.h
+++ b/arch/arm/mach-msm/include/mach/msm_spi.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2008-2009, 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -20,6 +20,10 @@
void (*gpio_release)(void);
int (*dma_config)(void);
const char *rsl_id;
- uint32_t pm_lat;
- uint32_t infinite_mode;
+ u32 pm_lat;
+ u32 infinite_mode;
+ bool ver_reg_exists;
+ bool use_bam;
+ u32 bam_consumer_pipe_index;
+ u32 bam_producer_pipe_index;
};
diff --git a/arch/arm/mach-msm/include/mach/ocmem.h b/arch/arm/mach-msm/include/mach/ocmem.h
index cb8aae0..6124cd6 100644
--- a/arch/arm/mach-msm/include/mach/ocmem.h
+++ b/arch/arm/mach-msm/include/mach/ocmem.h
@@ -22,7 +22,7 @@
/* Maximum number of slots in DM */
#define OCMEM_MAX_CHUNKS 32
-#define MIN_CHUNK_SIZE 128
+#define MIN_CHUNK_SIZE SZ_4K
struct ocmem_notifier;
diff --git a/arch/arm/mach-msm/include/mach/usb_bam.h b/arch/arm/mach-msm/include/mach/usb_bam.h
index 47313a7..5e1ef6f 100644
--- a/arch/arm/mach-msm/include/mach/usb_bam.h
+++ b/arch/arm/mach-msm/include/mach/usb_bam.h
@@ -13,6 +13,7 @@
#ifndef _USB_BAM_H_
#define _USB_BAM_H_
#include "sps.h"
+#include <mach/ipa.h>
/**
* SPS Pipes direction.
@@ -27,6 +28,22 @@
PEER_PERIPHERAL_TO_USB,
};
+struct usb_bam_connect_ipa_params {
+ u8 idx;
+ u32 *src_pipe;
+ u32 *dst_pipe;
+ enum usb_bam_pipe_dir dir;
+ /* client handle assigned by IPA to client */
+ u32 prod_clnt_hdl;
+ u32 cons_clnt_hdl;
+ /* params assigned by the CD */
+ enum ipa_client_type client;
+ struct ipa_ep_cfg ipa_ep_cfg;
+ void *priv;
+ void (*notify)(void *priv, enum ipa_dp_evt_type evt,
+ unsigned long data);
+};
+
#ifdef CONFIG_USB_BAM
/**
* Connect USB-to-Periperal SPS connection.
@@ -47,6 +64,31 @@
int usb_bam_connect(u8 idx, u32 *src_pipe_idx, u32 *dst_pipe_idx);
/**
+ * Connect USB-to-IPA SPS connection.
+ *
+ * This function returns the allocated pipes number adn clnt handles.
+ *
+ * @ipa_params - in/out parameters
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params);
+
+/**
+ * Disconnect USB-to-IPA SPS connection.
+ *
+ * @idx - Connection index.
+ *
+ * @ipa_params - in/out parameters
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int usb_bam_disconnect_ipa(u8 idx,
+ struct usb_bam_connect_ipa_params *ipa_params);
+
+/**
* Register a wakeup callback from peer BAM.
*
* @idx - Connection index.
@@ -96,6 +138,18 @@
return -ENODEV;
}
+static inline int usb_bam_connect_ipa(
+ struct usb_bam_connect_ipa_params *ipa_params)
+{
+ return -ENODEV;
+}
+
+static inline int usb_bam_disconnect_ipa(u8 idx,
+ struct usb_bam_connect_ipa_params *ipa_params)
+{
+ return -ENODEV;
+}
+
static inline int usb_bam_register_wake_cb(u8 idx,
int (*callback)(void *), void* param)
{
diff --git a/arch/arm/mach-msm/include/mach/usb_gadget_xport.h b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
index be11989..41dac62 100644
--- a/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
+++ b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
@@ -21,12 +21,13 @@
USB_GADGET_XPORT_SMD,
USB_GADGET_XPORT_BAM,
USB_GADGET_XPORT_BAM2BAM,
+ USB_GADGET_XPORT_BAM2BAM_IPA,
USB_GADGET_XPORT_HSIC,
USB_GADGET_XPORT_HSUART,
USB_GADGET_XPORT_NONE,
};
-#define XPORT_STR_LEN 10
+#define XPORT_STR_LEN 12
static char *xport_to_str(enum transport_type t)
{
@@ -41,6 +42,8 @@
return "BAM";
case USB_GADGET_XPORT_BAM2BAM:
return "BAM2BAM";
+ case USB_GADGET_XPORT_BAM2BAM_IPA:
+ return "BAM2BAM_IPA";
case USB_GADGET_XPORT_HSIC:
return "HSIC";
case USB_GADGET_XPORT_HSUART:
@@ -64,6 +67,8 @@
return USB_GADGET_XPORT_BAM;
if (!strncasecmp("BAM2BAM", name, XPORT_STR_LEN))
return USB_GADGET_XPORT_BAM2BAM;
+ if (!strncasecmp("BAM2BAM_IPA", name, XPORT_STR_LEN))
+ return USB_GADGET_XPORT_BAM2BAM_IPA;
if (!strncasecmp("HSIC", name, XPORT_STR_LEN))
return USB_GADGET_XPORT_HSIC;
if (!strncasecmp("HSUART", name, XPORT_STR_LEN))
diff --git a/arch/arm/mach-msm/include/mach/usbdiag.h b/arch/arm/mach-msm/include/mach/usbdiag.h
index d1e3605..d9320c3 100644
--- a/arch/arm/mach-msm/include/mach/usbdiag.h
+++ b/arch/arm/mach-msm/include/mach/usbdiag.h
@@ -1,6 +1,6 @@
/* include/asm-arm/arch-msm/usbdiag.h
*
- * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2010, 2012, The Linux Foundation. All rights reserved.
*
* All source code in this file is licensed under the following license except
* where indicated.
@@ -21,8 +21,11 @@
#ifndef _DRIVERS_USB_DIAG_H_
#define _DRIVERS_USB_DIAG_H_
+#include <linux/err.h>
+
#define DIAG_LEGACY "diag"
#define DIAG_MDM "diag_mdm"
+#define DIAG_QSC "diag_qsc"
#define USB_DIAG_CONNECT 0
#define USB_DIAG_DISCONNECT 1
@@ -45,6 +48,7 @@
void *priv_usb;
};
+#ifdef CONFIG_USB_G_ANDROID
struct usb_diag_ch *usb_diag_open(const char *name, void *priv,
void (*notify)(void *, unsigned, struct diag_request *));
void usb_diag_close(struct usb_diag_ch *ch);
@@ -52,7 +56,32 @@
void usb_diag_free_req(struct usb_diag_ch *ch);
int usb_diag_read(struct usb_diag_ch *ch, struct diag_request *d_req);
int usb_diag_write(struct usb_diag_ch *ch, struct diag_request *d_req);
-
-int diag_read_from_cb(unsigned char * , int);
-
+#else
+static inline struct usb_diag_ch *usb_diag_open(const char *name, void *priv,
+ void (*notify)(void *, unsigned, struct diag_request *))
+{
+ return ERR_PTR(-ENODEV);
+}
+static inline void usb_diag_close(struct usb_diag_ch *ch)
+{
+}
+static inline
+int usb_diag_alloc_req(struct usb_diag_ch *ch, int n_write, int n_read)
+{
+ return -ENODEV;
+}
+static inline void usb_diag_free_req(struct usb_diag_ch *ch)
+{
+}
+static inline
+int usb_diag_read(struct usb_diag_ch *ch, struct diag_request *d_req)
+{
+ return -ENODEV;
+}
+static inline
+int usb_diag_write(struct usb_diag_ch *ch, struct diag_request *d_req)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_USB_G_ANDROID */
#endif /* _DRIVERS_USB_DIAG_H_ */
diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c
index 52bb8ef..cd70ae9 100644
--- a/arch/arm/mach-msm/io.c
+++ b/arch/arm/mach-msm/io.c
@@ -519,6 +519,7 @@
MSM_CHIP_DEVICE(APCS_GCC, MSM8226),
MSM_CHIP_DEVICE(TLMM, MSM8226),
MSM_CHIP_DEVICE(IMEM, MSM8226),
+ MSM_CHIP_DEVICE(MPM2_PSHOLD, MSM8226),
{
.virtual = (unsigned long) MSM_SHARED_RAM_BASE,
.length = MSM_SHARED_RAM_SIZE,
diff --git a/arch/arm/mach-msm/memory.c b/arch/arm/mach-msm/memory.c
index 3fe65b8..9cc2a9d 100644
--- a/arch/arm/mach-msm/memory.c
+++ b/arch/arm/mach-msm/memory.c
@@ -27,6 +27,7 @@
#include <asm/setup.h>
#include <asm/mach-types.h>
#include <mach/msm_memtypes.h>
+#include <mach/memory.h>
#include <linux/hardirq.h>
#if defined(CONFIG_MSM_NPA_REMOTE)
#include "npa_remote.h"
@@ -365,7 +366,7 @@
return ret;
}
-static int check_for_compat(unsigned long node)
+static int __init check_for_compat(unsigned long node)
{
char **start = __compat_exports_start;
@@ -454,6 +455,79 @@
return 0;
}
+/* This function scans the device tree to populate the memory hole table */
+int __init dt_scan_for_memory_hole(unsigned long node, const char *uname,
+ int depth, void *data)
+{
+ unsigned int *memory_remove_prop;
+ unsigned long memory_remove_prop_length;
+ unsigned long hole_start;
+ unsigned long hole_size;
+
+ memory_remove_prop = of_get_flat_dt_prop(node,
+ "qcom,memblock-remove",
+ &memory_remove_prop_length);
+
+ if (memory_remove_prop) {
+ if (!check_for_compat(node))
+ goto out;
+ } else {
+ goto out;
+ }
+
+ if (memory_remove_prop) {
+ if (memory_remove_prop_length != (2*sizeof(unsigned int))) {
+ WARN(1, "Memory remove malformed\n");
+ goto out;
+ }
+
+ hole_start = be32_to_cpu(memory_remove_prop[0]);
+ hole_size = be32_to_cpu(memory_remove_prop[1]);
+
+ if (hole_start + hole_size <= MAX_HOLE_ADDRESS) {
+ if (memory_hole_start == 0 && memory_hole_end == 0) {
+ memory_hole_start = hole_start;
+ memory_hole_end = hole_start + hole_size;
+ } else if ((memory_hole_end - memory_hole_start)
+ <= hole_size) {
+ memory_hole_start = hole_start;
+ memory_hole_end = hole_start + hole_size;
+ }
+ }
+ adjust_meminfo(hole_start, hole_size);
+ }
+
+out:
+ return 0;
+}
+
+/*
+ * Split the memory bank to reflect the hole, if present,
+ * using the start and end of the memory hole.
+ */
+void adjust_meminfo(unsigned long start, unsigned long size)
+{
+ int i, j;
+
+ for (i = 0, j = 0; i < meminfo.nr_banks; i++) {
+ struct membank *bank = &meminfo.bank[j];
+ *bank = meminfo.bank[i];
+
+ if (((start + size) <= (bank->start + bank->size)) &&
+ (start >= bank->start)) {
+ memmove(bank + 1, bank,
+ (meminfo.nr_banks - i) * sizeof(*bank));
+ meminfo.nr_banks++;
+ i++;
+ bank[1].size -= (start + size);
+ bank[1].start = (start + size);
+ bank[1].highmem = 0;
+ j++;
+ bank->size = start - bank->start;
+ }
+ j++;
+ }
+}
unsigned long get_ddr_size(void)
{
unsigned int i;
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c
index cfd84eb..5c20a4e 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c
@@ -806,7 +806,7 @@
.tier = tier2,
.num_tiers = ARRAY_SIZE(tier2),
.hw_sel = MSM_BUS_NOC,
- .perm_mode = NOC_QOS_MODES_ALL_PERM,
+ .perm_mode = NOC_QOS_PERM_MODE_BYPASS,
.mode = NOC_QOS_MODE_BYPASS,
.ws = 10000,
.qport = qports_oxili,
@@ -819,7 +819,7 @@
.tier = tier2,
.num_tiers = ARRAY_SIZE(tier2),
.hw_sel = MSM_BUS_NOC,
- .perm_mode = NOC_QOS_MODES_ALL_PERM,
+ .perm_mode = NOC_QOS_PERM_MODE_BYPASS,
.mode = NOC_QOS_MODE_BYPASS,
.qport = qports_gemini,
.ws = 10000,
@@ -832,7 +832,7 @@
.tier = tier2,
.num_tiers = ARRAY_SIZE(tier2),
.hw_sel = MSM_BUS_NOC,
- .perm_mode = NOC_QOS_MODES_ALL_PERM,
+ .perm_mode = NOC_QOS_PERM_MODE_BYPASS,
.mode = NOC_QOS_MODE_BYPASS,
.qport = qports_mdp,
.ws = 10000,
@@ -845,7 +845,7 @@
.tier = tier2,
.num_tiers = ARRAY_SIZE(tier2),
.hw_sel = MSM_BUS_NOC,
- .perm_mode = NOC_QOS_MODES_ALL_PERM,
+ .perm_mode = NOC_QOS_PERM_MODE_BYPASS,
.mode = NOC_QOS_MODE_BYPASS,
.ws = 10000,
.qport = qports_venus_p0,
@@ -858,7 +858,7 @@
.tier = tier2,
.num_tiers = ARRAY_SIZE(tier2),
.hw_sel = MSM_BUS_NOC,
- .perm_mode = NOC_QOS_MODES_ALL_PERM,
+ .perm_mode = NOC_QOS_PERM_MODE_BYPASS,
.mode = NOC_QOS_MODE_BYPASS,
.ws = 10000,
.qport = qports_venus_p1,
@@ -871,7 +871,7 @@
.tier = tier2,
.num_tiers = ARRAY_SIZE(tier2),
.hw_sel = MSM_BUS_NOC,
- .perm_mode = NOC_QOS_MODES_ALL_PERM,
+ .perm_mode = NOC_QOS_PERM_MODE_BYPASS,
.mode = NOC_QOS_MODE_BYPASS,
.ws = 10000,
.qport = qports_vfe,
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_noc.c b/arch/arm/mach-msm/msm_bus/msm_bus_noc.c
index fb2e5da..9e89256 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_noc.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_noc.c
@@ -362,16 +362,18 @@
}
for (i = 0; i < info->node_info->num_mports; i++) {
- if (info->node_info->mode != NOC_QOS_MODE_BYPASS)
+ if (info->node_info->mode != NOC_QOS_MODE_BYPASS) {
noc_set_qos_priority(ninfo, info->node_info->qport[i],
prio);
- if (info->node_info->mode != NOC_QOS_MODE_FIXED) {
- struct msm_bus_noc_qos_bw qbw;
- qbw.ws = info->node_info->ws;
- qbw.bw = 0;
- msm_bus_noc_set_qos_bw(ninfo, info->node_info->qport[i],
- info->node_info->perm_mode, &qbw);
+ if (info->node_info->mode != NOC_QOS_MODE_FIXED) {
+ struct msm_bus_noc_qos_bw qbw;
+ qbw.ws = info->node_info->ws;
+ qbw.bw = 0;
+ msm_bus_noc_set_qos_bw(ninfo, info->node_info->
+ qport[i], info->node_info->perm_mode,
+ &qbw);
+ }
}
noc_set_qos_mode(ninfo, info->node_info->qport[i], info->
diff --git a/arch/arm/mach-msm/msm_cpr.c b/arch/arm/mach-msm/msm_cpr.c
index b68a8db..c7a8b98 100644
--- a/arch/arm/mach-msm/msm_cpr.c
+++ b/arch/arm/mach-msm/msm_cpr.c
@@ -906,6 +906,14 @@
return -ENOMEM;
}
+ /* enable clk for cpr */
+ if (!pdata->clk_enable) {
+ pr_err("CPR: Invalid clk_enable hook\n");
+ return -EFAULT;
+ }
+
+ pdata->clk_enable();
+
/* Initialize platform_data */
cpr->config = pdata;
diff --git a/arch/arm/mach-msm/msm_dcvs.c b/arch/arm/mach-msm/msm_dcvs.c
index 2736870..41afd24 100644
--- a/arch/arm/mach-msm/msm_dcvs.c
+++ b/arch/arm/mach-msm/msm_dcvs.c
@@ -150,6 +150,8 @@
static unsigned num_cpu_freqs;
static struct msm_dcvs_platform_data *dcvs_pdata;
+static DEFINE_MUTEX(gpu_floor_mutex);
+
static void force_stop_slack_timer(struct dcvs_core *core)
{
unsigned long flags;
@@ -256,33 +258,49 @@
spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
}
-static void apply_gpu_floor(int cpu_freq)
+void msm_dcvs_apply_gpu_floor(unsigned long cpu_freq)
{
- int i;
- int gpu_floor_freq = 0;
+ static unsigned long curr_cpu0_freq;
+ unsigned long gpu_floor_freq = 0;
struct dcvs_core *gpu;
+ int i;
if (!dcvs_pdata)
return;
+ mutex_lock(&gpu_floor_mutex);
+
+ if (cpu_freq)
+ curr_cpu0_freq = cpu_freq;
+
for (i = 0; i < dcvs_pdata->num_sync_rules; i++)
- if (cpu_freq > dcvs_pdata->sync_rules[i].cpu_khz) {
+ if (curr_cpu0_freq > dcvs_pdata->sync_rules[i].cpu_khz) {
gpu_floor_freq =
dcvs_pdata->sync_rules[i].gpu_floor_khz;
break;
}
- if (!gpu_floor_freq)
+ if (num_online_cpus() > 1)
+ gpu_floor_freq = max(gpu_floor_freq,
+ dcvs_pdata->gpu_max_nom_khz);
+
+ if (!gpu_floor_freq) {
+ mutex_unlock(&gpu_floor_mutex);
return;
+ }
for (i = GPU_OFFSET; i < CORES_MAX; i++) {
gpu = &core_list[i];
if (gpu->dcvs_core_id == -1)
continue;
- if (gpu->set_floor_frequency)
+
+ if (gpu->pending_freq != STOP_FREQ_CHANGE &&
+ gpu->set_floor_frequency)
gpu->set_floor_frequency(gpu->type_core_num,
gpu_floor_freq);
}
+
+ mutex_unlock(&gpu_floor_mutex);
}
static int __msm_dcvs_change_freq(struct dcvs_core *core)
@@ -295,21 +313,16 @@
uint32_t ret1 = 0;
spin_lock_irqsave(&core->pending_freq_lock, flags);
+ if (core->pending_freq == STOP_FREQ_CHANGE)
+ goto out;
repeat:
BUG_ON(!core->pending_freq);
- if (core->pending_freq == STOP_FREQ_CHANGE)
- BUG();
requested_freq = core->pending_freq;
time_start = core->time_start;
core->time_start = ns_to_ktime(0);
- if (requested_freq < 0) {
- requested_freq = -1 * requested_freq;
- core->pending_freq = STOP_FREQ_CHANGE;
- } else {
- core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
- }
+ core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
if (requested_freq == core->actual_freq)
goto out;
@@ -318,7 +331,7 @@
if (core->type == MSM_DCVS_CORE_TYPE_CPU &&
core->type_core_num == 0)
- apply_gpu_floor(requested_freq);
+ msm_dcvs_apply_gpu_floor(requested_freq);
/**
* Call the frequency sink driver to change the frequency
@@ -458,10 +471,7 @@
}
if (new_freq == STOP_FREQ_CHANGE) {
- if (core->pending_freq == NO_OUTSTANDING_FREQ_CHANGE)
- core->pending_freq = STOP_FREQ_CHANGE;
- else if (core->pending_freq > 0)
- core->pending_freq = -1 * core->pending_freq;
+ core->pending_freq = STOP_FREQ_CHANGE;
return;
}
@@ -537,6 +547,36 @@
return HRTIMER_NORESTART;
}
+int msm_dcvs_update_algo_params(void)
+{
+ static struct msm_dcvs_algo_param curr_params;
+ static DEFINE_MUTEX(param_update_mutex);
+ struct msm_dcvs_algo_param *new_params;
+ int cpu, ret = 0;
+
+ mutex_lock(¶m_update_mutex);
+ new_params = &core_list[CPU_OFFSET + num_online_cpus() - 1].algo_param;
+
+ if (memcmp(&curr_params, new_params,
+ sizeof(struct msm_dcvs_algo_param))) {
+ for_each_possible_cpu(cpu) {
+ ret = msm_dcvs_scm_set_algo_params(CPU_OFFSET + cpu,
+ new_params);
+ if (ret) {
+ pr_err("scm set algo params failed on cpu %d, ret %d\n",
+ cpu, ret);
+ mutex_unlock(¶m_update_mutex);
+ return ret;
+ }
+ }
+ memcpy(&curr_params, new_params,
+ sizeof(struct msm_dcvs_algo_param));
+ }
+
+ mutex_unlock(¶m_update_mutex);
+ return ret;
+}
+
/* Helper functions and macros for sysfs nodes for a core */
#define CORE_FROM_ATTRIBS(attr, name) \
container_of(container_of(attr, struct core_attribs, name), \
@@ -591,12 +631,9 @@
} else { \
uint32_t old_val = core->algo_param._name; \
core->algo_param._name = val; \
- ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id, \
- &core->algo_param); \
+ ret = msm_dcvs_update_algo_params(); \
if (ret) { \
core->algo_param._name = old_val; \
- __err("Error(%d) in setting %d for algo param %s\n",\
- ret, val, __stringify(_name)); \
} \
} \
return count; \
@@ -936,7 +973,6 @@
core->get_frequency = get_frequency;
core->idle_enable = idle_enable;
core->set_floor_frequency = set_floor_frequency;
- core->pending_freq = STOP_FREQ_CHANGE;
core->info = info;
if (type == MSM_DCVS_CORE_TYPE_CPU)
@@ -1102,10 +1138,18 @@
0, core->actual_freq, &freq, &ret1);
core->idle_enable(core->type_core_num,
MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
+
+ if (core->type == MSM_DCVS_CORE_TYPE_GPU)
+ mutex_lock(&gpu_floor_mutex);
+
spin_lock_irqsave(&core->pending_freq_lock, flags);
/* flush out all the pending freq changes */
request_freq_change(core, STOP_FREQ_CHANGE);
spin_unlock_irqrestore(&core->pending_freq_lock, flags);
+
+ if (core->type == MSM_DCVS_CORE_TYPE_GPU)
+ mutex_unlock(&gpu_floor_mutex);
+
force_stop_slack_timer(core);
return 0;
@@ -1230,8 +1274,10 @@
goto done;
}
- for (i = 0; i < CORES_MAX; i++)
+ for (i = 0; i < CORES_MAX; i++) {
core_list[i].dcvs_core_id = -1;
+ core_list[i].pending_freq = STOP_FREQ_CHANGE;
+ }
done:
return ret;
}
diff --git a/arch/arm/mach-msm/msm_mpdecision.c b/arch/arm/mach-msm/msm_mpdecision.c
index 407be6a..94b546a 100644
--- a/arch/arm/mach-msm/msm_mpdecision.c
+++ b/arch/arm/mach-msm/msm_mpdecision.c
@@ -391,6 +391,8 @@
break;
}
msm_mpd.hpupdate = HPUPDATE_WAITING;
+ msm_dcvs_apply_gpu_floor(0);
+ msm_dcvs_update_algo_params();
}
return 0;
diff --git a/arch/arm/mach-msm/ocmem.c b/arch/arm/mach-msm/ocmem.c
index 7829d8d..34fd8d2 100644
--- a/arch/arm/mach-msm/ocmem.c
+++ b/arch/arm/mach-msm/ocmem.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -395,7 +395,8 @@
pr_debug("ocmem: Disabled br clock\n");
}
-static struct ocmem_plat_data *parse_dt_config(struct platform_device *pdev)
+static struct ocmem_plat_data * __devinit parse_dt_config
+ (struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
diff --git a/arch/arm/mach-msm/ocmem_api.c b/arch/arm/mach-msm/ocmem_api.c
index 689e015..8b56775 100644
--- a/arch/arm/mach-msm/ocmem_api.c
+++ b/arch/arm/mach-msm/ocmem_api.c
@@ -304,6 +304,7 @@
for (i = 0; i < list->num_chunks; i++) {
if (!chunks[i].ddr_paddr ||
+ !IS_ALIGNED(chunks[i].ddr_paddr, MIN_CHUNK_SIZE) ||
chunks[i].size < MIN_CHUNK_SIZE ||
!IS_ALIGNED(chunks[i].size, MIN_CHUNK_SIZE)) {
pr_err("Invalid ocmem chunk at index %d (p: %lx, size %lx)\n",
diff --git a/arch/arm/mach-msm/pil-q6v5-mss.c b/arch/arm/mach-msm/pil-q6v5-mss.c
index 5bed8b4..07cbe19 100644
--- a/arch/arm/mach-msm/pil-q6v5-mss.c
+++ b/arch/arm/mach-msm/pil-q6v5-mss.c
@@ -423,7 +423,7 @@
struct mba_data *drv = subsys_to_drv(subsys);
if (!drv->is_loadable)
- return -ENODEV;
+ return 0;
/* MBA doesn't support shutdown */
pil_shutdown(&drv->q6->desc);
return 0;
@@ -435,7 +435,7 @@
int ret;
if (!drv->is_loadable)
- return -ENODEV;
+ return 0;
/*
* At this time, the modem is shutdown. Therefore this function cannot
* run concurrently with either the watchdog bite error handler or the
@@ -527,7 +527,7 @@
struct mba_data *drv = subsys_to_drv(desc);
if (!drv->is_loadable)
- return -ENODEV;
+ return 0;
ret = pil_boot(&drv->q6->desc);
if (ret)
diff --git a/arch/arm/mach-msm/platsmp-8625.c b/arch/arm/mach-msm/platsmp-8625.c
index 3b31b9f..0e75cae 100644
--- a/arch/arm/mach-msm/platsmp-8625.c
+++ b/arch/arm/mach-msm/platsmp-8625.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -121,10 +121,10 @@
write_pen_release(-1);
/* clear the IPC pending SPI */
- if (power_collapsed) {
+ if (per_cpu(power_collapsed, cpu)) {
raise_clear_spi(cpu, false);
clear_pending_spi(cpu_data[cpu].ipc_irq);
- power_collapsed = 0;
+ per_cpu(power_collapsed, cpu) = 0;
}
/*
@@ -216,7 +216,7 @@
* GDFS which needs to be brought out by raising an SPI.
*/
- if (power_collapsed) {
+ if (per_cpu(power_collapsed, cpu)) {
gic_configure_and_raise(cpu_data[cpu].ipc_irq, cpu);
raise_clear_spi(cpu, true);
} else {
diff --git a/arch/arm/mach-msm/pm.h b/arch/arm/mach-msm/pm.h
index faefe34..bd61feb 100644
--- a/arch/arm/mach-msm/pm.h
+++ b/arch/arm/mach-msm/pm.h
@@ -1,7 +1,7 @@
/* arch/arm/mach-msm/pm.h
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
* Author: San Mehat <san@android.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -27,7 +27,7 @@
#define msm_secondary_startup NULL
#endif
-extern int power_collapsed;
+DECLARE_PER_CPU(int, power_collapsed);
struct msm_pm_irq_calls {
unsigned int (*irq_pending)(void);
diff --git a/arch/arm/mach-msm/pm2.c b/arch/arm/mach-msm/pm2.c
index ae2a4bc..96c1218 100644
--- a/arch/arm/mach-msm/pm2.c
+++ b/arch/arm/mach-msm/pm2.c
@@ -3,7 +3,7 @@
* MSM Power Management Routines
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2008-2012 Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2012 The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -75,8 +75,9 @@
MSM_PM_DEBUG_HOTPLUG = BIT(7),
};
+DEFINE_PER_CPU(int, power_collapsed);
+
static int msm_pm_debug_mask;
-int power_collapsed;
module_param_named(
debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
);
@@ -565,7 +566,7 @@
__raw_writel(0, APPS_PWRDOWN);
mb();
- if (power_collapsed) {
+ if (per_cpu(power_collapsed, 1)) {
/*
* enable the SCU while coming out of power
* collapse.
@@ -983,6 +984,7 @@
* path by reading the MPA5_GDFS_CNT_VAL register.
*/
if (cpu_is_msm8625()) {
+ int cpu;
/*
* on system reset, default value of MPA5_GDFS_CNT_VAL
* is = 0x0, later modem reprogram this value to
@@ -997,7 +999,11 @@
/* 8x25Q */
if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 3) {
if (val != 0x000F0002) {
- power_collapsed = 1;
+ for_each_possible_cpu(cpu) {
+ if (!cpu)
+ continue;
+ per_cpu(power_collapsed, cpu) = 1;
+ }
/*
* override DBGNOPOWERDN and program the GDFS
* count val
@@ -1008,7 +1014,11 @@
modem_early_exit = 1;
} else {
if (val != 0x00030002) {
- power_collapsed = 1;
+ for_each_possible_cpu(cpu) {
+ if (!cpu)
+ continue;
+ per_cpu(power_collapsed, cpu) = 1;
+ }
/*
* override DBGNOPOWERDN and program the GDFS
* count val
diff --git a/arch/arm/mach-msm/pmu.c b/arch/arm/mach-msm/pmu.c
index c426ff9..f0b83f9 100644
--- a/arch/arm/mach-msm/pmu.c
+++ b/arch/arm/mach-msm/pmu.c
@@ -16,8 +16,20 @@
#include <mach/irqs.h>
#include <mach/socinfo.h>
+/*
+ * If a GIC is present, then all IRQ's < 32 are PPI's and can only be
+ * requested and free'd using the percpu IRQ API.
+ * If a VIC is present, then only the traditional request, free API works.
+ *
+ * All MPCore's have GIC's. The Cortex A5 however may or may not be MPcore, but
+ * it still has a GIC. Except, the 7x27a, which is an A5 and yet has a VIC.
+ * So if the chip is A5 but does not have a GIC, default to the traditional
+ * IRQ {request, free}_irq API.
+ */
+
#if defined(CONFIG_ARCH_MSM_KRAITMP) || defined(CONFIG_ARCH_MSM_SCORPIONMP) \
- || defined(CONFIG_ARCH_MSM8625)
+ || defined(CONFIG_ARCH_MSM8625) || \
+ (defined(CONFIG_ARCH_MSM_CORTEX_A5) && !defined(CONFIG_MSM_VIC))
static DEFINE_PER_CPU(u32, pmu_irq_cookie);
static void enable_irq_callback(void *info)
@@ -141,8 +153,10 @@
* handlers to call the percpu API.
* Defaults to unicore API {request,free}_irq().
* See arch/arm/kernel/perf_event.c
+ * See Comment above on the A5 and MSM_VIC.
*/
-#if defined(CONFIG_ARCH_MSM_KRAITMP) || defined(CONFIG_ARCH_MSM_SCORPIONMP)
+#if defined(CONFIG_ARCH_MSM_KRAITMP) || defined(CONFIG_ARCH_MSM_SCORPIONMP) \
+ || (defined(CONFIG_ARCH_MSM_CORTEX_A5) && !defined(CONFIG_MSM_VIC))
cpu_pmu_device.dev.platform_data = &multicore_data;
#endif
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_acdb.c b/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
index a24b9ec..cad845f 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
@@ -730,12 +730,19 @@
static int deregister_memory(void)
{
+ int i;
+
if (atomic64_read(&acdb_data.mem_len)) {
mutex_lock(&acdb_data.acdb_mutex);
+ atomic64_set(&acdb_data.mem_len, 0);
atomic_set(&acdb_data.vocstrm_total_cal_size, 0);
atomic_set(&acdb_data.vocproc_total_cal_size, 0);
atomic_set(&acdb_data.vocvol_total_cal_size, 0);
- atomic64_set(&acdb_data.mem_len, 0);
+
+ for (i = 0; i < MAX_VOCPROC_TYPES; i++) {
+ kfree(acdb_data.col_data[i]);
+ acdb_data.col_data[i] = NULL;
+ }
ion_unmap_kernel(acdb_data.ion_client, acdb_data.ion_handle);
ion_free(acdb_data.ion_client, acdb_data.ion_handle);
ion_client_destroy(acdb_data.ion_client);
@@ -747,12 +754,19 @@
static int register_memory(void)
{
int result;
+ int i;
unsigned long paddr;
void *kvptr;
unsigned long kvaddr;
unsigned long mem_len;
mutex_lock(&acdb_data.acdb_mutex);
+ for (i = 0; i < MAX_VOCPROC_TYPES; i++) {
+ acdb_data.col_data[i] = kmalloc(MAX_COL_SIZE, GFP_KERNEL);
+ atomic_set(&acdb_data.vocproc_col_cal[i].cal_kvaddr,
+ (uint32_t)acdb_data.col_data[i]);
+ }
+
acdb_data.ion_client =
msm_ion_client_create(UINT_MAX, "audio_acdb_client");
if (IS_ERR_OR_NULL(acdb_data.ion_client)) {
@@ -1029,7 +1043,6 @@
static int acdb_release(struct inode *inode, struct file *f)
{
- int i;
s32 result = 0;
atomic_dec(&usage_count);
@@ -1038,11 +1051,6 @@
pr_debug("%s: ref count %d!\n", __func__,
atomic_read(&usage_count));
- for (i = 0; i < MAX_VOCPROC_TYPES; i++) {
- kfree(acdb_data.col_data[i]);
- acdb_data.col_data[i] = NULL;
- }
-
if (atomic_read(&usage_count) >= 1)
result = -EBUSY;
else
@@ -1067,16 +1075,10 @@
static int __init acdb_init(void)
{
- int i;
memset(&acdb_data, 0, sizeof(acdb_data));
mutex_init(&acdb_data.acdb_mutex);
atomic_set(&usage_count, 0);
- for (i = 0; i < MAX_VOCPROC_TYPES; i++) {
- acdb_data.col_data[i] = kmalloc(MAX_COL_SIZE, GFP_KERNEL);
- atomic_set(&acdb_data.vocproc_col_cal[i].cal_kvaddr,
- (uint32_t)acdb_data.col_data[i]);
- }
return misc_register(&acdb_misc);
}
diff --git a/arch/arm/mach-msm/rpm-smd.c b/arch/arm/mach-msm/rpm-smd.c
index b6fb88c..a59b338 100644
--- a/arch/arm/mach-msm/rpm-smd.c
+++ b/arch/arm/mach-msm/rpm-smd.c
@@ -228,9 +228,6 @@
memcpy(handle->kvp[i].value, data, size);
handle->kvp[i].valid = true;
- if (handle->msg_hdr.set == MSM_RPM_CTX_SLEEP_SET)
- msm_rpm_notify_sleep_chain(&handle->msg_hdr, &handle->kvp[i]);
-
return 0;
}
@@ -736,6 +733,11 @@
memcpy(tmpbuff, cdata->kvp[i].value, cdata->kvp[i].nbytes);
tmpbuff += cdata->kvp[i].nbytes;
+
+ if (cdata->msg_hdr.set == MSM_RPM_CTX_SLEEP_SET)
+ msm_rpm_notify_sleep_chain(&cdata->msg_hdr,
+ &cdata->kvp[i]);
+
}
if (msm_rpm_debug_mask
diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c
index 1bea82a..ff68d81 100644
--- a/arch/arm/mach-msm/smd_rpcrouter.c
+++ b/arch/arm/mach-msm/smd_rpcrouter.c
@@ -545,12 +545,13 @@
D("%s: registering device %x\n",
__func__, board_info->dev->prog);
list_del(&board_info->list);
+ spin_unlock_irqrestore(&rpc_board_dev_list_lock, flags);
rc = platform_device_register(&board_info->dev->pdev);
if (rc)
pr_err("%s: board dev register failed %d\n",
__func__, rc);
kfree(board_info);
- break;
+ return;
}
}
spin_unlock_irqrestore(&rpc_board_dev_list_lock, flags);
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index e2cd0120..0ebc2b9 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -375,29 +375,61 @@
return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
}
+unsigned long memory_hole_offset;
+EXPORT_SYMBOL(memory_hole_offset);
+unsigned long memory_hole_start;
+EXPORT_SYMBOL(memory_hole_start);
+unsigned long memory_hole_end;
+EXPORT_SYMBOL(memory_hole_end);
+
#ifdef CONFIG_DONT_MAP_HOLE_AFTER_MEMBANK0
-unsigned long membank0_size;
-EXPORT_SYMBOL(membank0_size);
-unsigned long membank1_start;
-EXPORT_SYMBOL(membank1_start);
-
-void __init find_membank0_hole(void)
+void find_memory_hole(void)
{
- sort(&meminfo.bank, meminfo.nr_banks,
- sizeof(meminfo.bank[0]), meminfo_cmp, NULL);
+ int i;
+ unsigned long hole_start;
+ unsigned long hole_size;
- membank0_size = meminfo.bank[0].size;
- membank1_start = meminfo.bank[1].start;
+ /*
+ * Find the start and end of the hole, using meminfo
+ * if it hasnt been found already.
+ */
+ if (memory_hole_start == 0 && memory_hole_end == 0) {
+ for (i = 0; i < (meminfo.nr_banks - 1); i++) {
+ if ((meminfo.bank[i].start + meminfo.bank[i].size) !=
+ meminfo.bank[i+1].start) {
+ if (meminfo.bank[i].start + meminfo.bank[i].size
+ <= MAX_HOLE_ADDRESS) {
+
+ hole_start = meminfo.bank[i].start +
+ meminfo.bank[i].size;
+ hole_size = meminfo.bank[i+1].start -
+ hole_start;
+
+ if (memory_hole_start == 0 &&
+ memory_hole_end == 0) {
+ memory_hole_start = hole_start;
+ memory_hole_end = hole_start +
+ hole_size;
+ } else if ((memory_hole_end -
+ memory_hole_start) <= hole_size) {
+ memory_hole_start = hole_start;
+ memory_hole_end = hole_start +
+ hole_size;
+ }
+ }
+ }
+ }
+ }
+ memory_hole_offset = memory_hole_start - PHYS_OFFSET;
}
+
#endif
void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc)
{
int i;
-#ifndef CONFIG_DONT_MAP_HOLE_AFTER_MEMBANK0
sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[0]), meminfo_cmp, NULL);
-#endif
for (i = 0; i < mi->nr_banks; i++)
memblock_add(mi->bank[i].start, mi->bank[i].size);
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index 1cb6cba..8575f78 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -961,7 +961,7 @@
int i, j, highmem = 0;
#ifdef CONFIG_DONT_MAP_HOLE_AFTER_MEMBANK0
- find_membank0_hole();
+ find_memory_hole();
#endif
#if (defined CONFIG_HIGHMEM) && (defined CONFIG_FIX_MOVABLE_ZONE)
diff --git a/block/Kconfig.iosched b/block/Kconfig.iosched
index 5fd98ea..5751d28 100644
--- a/block/Kconfig.iosched
+++ b/block/Kconfig.iosched
@@ -15,7 +15,7 @@
config IOSCHED_TEST
tristate "Test I/O scheduler"
depends on DEBUG_FS
- default m
+ default y
---help---
The test I/O scheduler is a duplicate of the noop scheduler with
addition of test utlity.
diff --git a/drivers/char/diag/Kconfig b/drivers/char/diag/Kconfig
index 8f8707f..91fcdfc 100644
--- a/drivers/char/diag/Kconfig
+++ b/drivers/char/diag/Kconfig
@@ -30,9 +30,9 @@
SDIO Transport Layer for DIAG Router
endmenu
-menu "HSIC support for DIAG"
+menu "HSIC/SMUX support for DIAG"
-config DIAG_BRIDGE_CODE
+config DIAGFWD_BRIDGE_CODE
depends on USB_QCOM_DIAG_BRIDGE
default y
bool "Enable QSC/9K DIAG traffic over SMUX/HSIC"
diff --git a/drivers/char/diag/Makefile b/drivers/char/diag/Makefile
index 6ecc970..c9204ea 100644
--- a/drivers/char/diag/Makefile
+++ b/drivers/char/diag/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_DIAG_CHAR) := diagchar.o
obj-$(CONFIG_DIAG_SDIO_PIPE) += diagfwd_sdio.o
-obj-$(CONFIG_DIAG_BRIDGE_CODE) += diagfwd_hsic.o
-obj-$(CONFIG_DIAG_BRIDGE_CODE) += diagfwd_smux.o
+obj-$(CONFIG_DIAGFWD_BRIDGE_CODE) += diagfwd_bridge.o
+obj-$(CONFIG_DIAGFWD_BRIDGE_CODE) += diagfwd_hsic.o
+obj-$(CONFIG_DIAGFWD_BRIDGE_CODE) += diagfwd_smux.o
diagchar-objs := diagchar_core.o diagchar_hdlc.o diagfwd.o diagmem.o diagfwd_cntl.o diag_dci.o diag_masks.o diag_debugfs.o
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 5cd5ce9..e78a2aa 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -134,33 +134,37 @@
pr_alert("diag: No matching PID for DCI data\n");
/* Using PID of client process, find client buffer */
for (i = 0; i < MAX_DCI_CLIENTS; i++) {
- if (curr_client_pid == driver->dci_client_tbl[i].client->tgid) {
- /* copy pkt rsp in client buf */
- entry = &(driver->dci_client_tbl[i]);
- if (DCI_CHK_CAPACITY(entry, 8+write_len)) {
- pr_alert("diag: create capacity for pkt rsp\n");
- entry->total_capacity += 8+write_len;
- temp_buf = krealloc(entry->dci_data,
- entry->total_capacity, GFP_KERNEL);
- if (!temp_buf) {
- pr_err("diag: DCI realloc failed\n");
- break;
- } else {
- entry->dci_data = temp_buf;
+ if (driver->dci_client_tbl[i].client != NULL) {
+ if (curr_client_pid ==
+ driver->dci_client_tbl[i].client->tgid) {
+ /* copy pkt rsp in client buf */
+ entry = &(driver->dci_client_tbl[i]);
+ if (DCI_CHK_CAPACITY(entry, 8+write_len)) {
+ pr_alert("diag: create capacity for pkt rsp\n");
+ entry->total_capacity += 8+write_len;
+ temp_buf = krealloc(entry->dci_data,
+ entry->total_capacity, GFP_KERNEL);
+ if (!temp_buf) {
+ pr_err("diag: DCI realloc failed\n");
+ break;
+ } else {
+ entry->dci_data = temp_buf;
+ }
}
- }
- *(int *)(entry->dci_data+entry->data_len) =
+ *(int *)(entry->dci_data+entry->data_len) =
DCI_PKT_RSP_TYPE;
- entry->data_len += 4;
- *(int *)(entry->dci_data+entry->data_len) = write_len;
- entry->data_len += 4;
- memcpy(entry->dci_data+entry->data_len,
- buf+4+cmd_code_len, write_len);
- entry->data_len += write_len;
- /* delete immediate response entry */
- if (driver->buf_in_dci[8+cmd_code_len] != 0x80)
- driver->req_tracking_tbl[index].pid = 0;
- break;
+ entry->data_len += 4;
+ *(int *)(entry->dci_data+entry->data_len)
+ = write_len;
+ entry->data_len += 4;
+ memcpy(entry->dci_data+entry->data_len,
+ buf+4+cmd_code_len, write_len);
+ entry->data_len += write_len;
+ /* delete immediate response entry */
+ if (driver->buf_in_dci[8+cmd_code_len] != 0x80)
+ driver->req_tracking_tbl[index].pid = 0;
+ break;
+ }
}
}
}
@@ -408,6 +412,7 @@
int count, set_mask, num_codes, byte_index, bit_index, event_id;
uint8_t equip_id, *log_mask_ptr, *head_log_mask_ptr, byte_mask;
uint8_t *event_mask_ptr;
+ int offset = 0;
/* This is Pkt request/response transaction */
if (*(int *)temp > 0) {
@@ -463,10 +468,12 @@
} else if (*(int *)temp == DCI_LOG_TYPE) {
/* find client id and table */
for (i = 0; i < MAX_DCI_CLIENTS; i++) {
- if (driver->dci_client_tbl[i].client->tgid ==
- current->tgid) {
- found = 1;
- break;
+ if (driver->dci_client_tbl[i].client != NULL) {
+ if (driver->dci_client_tbl[i].client->tgid ==
+ current->tgid) {
+ found = 1;
+ break;
+ }
}
}
if (!found) {
@@ -495,6 +502,7 @@
*/
log_mask_ptr = head_log_mask_ptr;
found = 0;
+ offset = 0;
while (log_mask_ptr) {
if (*log_mask_ptr == equip_id) {
found = 1;
@@ -505,6 +513,7 @@
pr_debug("diag: did not find equip id = %x at %p\n",
equip_id, log_mask_ptr);
log_mask_ptr += 514;
+ offset += 514;
}
}
if (!found) {
@@ -517,21 +526,25 @@
*log_mask_ptr |= byte_mask;
else
*log_mask_ptr &= ~byte_mask;
+ /* add to cumulative mask */
+ update_dci_cumulative_log_mask(
+ offset, byte_index,
+ byte_mask);
temp += 2;
count++;
ret = DIAG_DCI_NO_ERROR;
}
- /* add to cumulative mask */
- update_dci_cumulative_log_mask(i);
/* send updated mask to peripherals */
diag_send_dci_log_mask(driver->ch_cntl);
} else if (*(int *)temp == DCI_EVENT_TYPE) {
/* find client id and table */
for (i = 0; i < MAX_DCI_CLIENTS; i++) {
- if (driver->dci_client_tbl[i].client->tgid ==
- current->tgid) {
- found = 1;
- break;
+ if (driver->dci_client_tbl[i].client != NULL) {
+ if (driver->dci_client_tbl[i].client->tgid ==
+ current->tgid) {
+ found = 1;
+ break;
+ }
}
}
if (!found) {
@@ -561,12 +574,12 @@
*(event_mask_ptr + byte_index) |= byte_mask;
else
*(event_mask_ptr + byte_index) &= ~byte_mask;
+ /* add to cumulative mask */
+ update_dci_cumulative_event_mask(byte_index, byte_mask);
temp += sizeof(int);
count++;
ret = DIAG_DCI_NO_ERROR;
}
- /* add to cumulative mask */
- update_dci_cumulative_event_mask(i);
/* send updated mask to peripherals */
diag_send_dci_event_mask(driver->ch_cntl);
} else {
@@ -575,16 +588,29 @@
return ret;
}
-void update_dci_cumulative_event_mask(int client_index)
+void update_dci_cumulative_event_mask(int offset, uint8_t byte_mask)
{
int i;
- uint8_t *update_ptr = dci_cumulative_event_mask;
uint8_t *event_mask_ptr;
+ uint8_t *update_ptr = dci_cumulative_event_mask;
+ bool is_set = false;
mutex_lock(&dci_event_mask_mutex);
- event_mask_ptr = driver->dci_client_tbl[client_index].dci_event_mask;
- for (i = 0; i < DCI_EVENT_MASK_SIZE; i++)
- *(update_ptr+i) |= *(event_mask_ptr+i);
+ update_ptr += offset;
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ event_mask_ptr =
+ driver->dci_client_tbl[i].dci_event_mask;
+ event_mask_ptr += offset;
+ if ((*event_mask_ptr & byte_mask) == byte_mask) {
+ is_set = true;
+ /* break even if one client has the event mask set */
+ break;
+ }
+ }
+ if (is_set == false)
+ *update_ptr &= ~byte_mask;
+ else
+ *update_ptr |= byte_mask;
mutex_unlock(&dci_event_mask_mutex);
}
@@ -624,27 +650,39 @@
mutex_unlock(&driver->diag_cntl_mutex);
}
-void update_dci_cumulative_log_mask(int client_index)
+void update_dci_cumulative_log_mask(int offset, int byte_index,
+ uint8_t byte_mask)
{
- int i, j;
+ int i;
uint8_t *update_ptr = dci_cumulative_log_mask;
- uint8_t *log_mask_ptr =
- driver->dci_client_tbl[client_index].dci_log_mask;
+ uint8_t *log_mask_ptr;
+ bool is_set = false;
mutex_lock(&dci_log_mask_mutex);
- *update_ptr = 0; /* add first equip id */
- /* skip the first equip id */
- update_ptr++; log_mask_ptr++;
- for (i = 0; i < 16; i++) {
- for (j = 0; j < 513; j++) {
- *update_ptr |= *log_mask_ptr;
- update_ptr++;
- log_mask_ptr++;
+ *update_ptr = 0;
+ /* set the equipment IDs */
+ for (i = 0; i < 16; i++)
+ *(update_ptr + (i*514)) = i;
+
+ update_ptr += offset;
+ /* update the dirty bit */
+ *(update_ptr+1) = 1;
+ update_ptr = update_ptr + byte_index;
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ log_mask_ptr =
+ (driver->dci_client_tbl[i].dci_log_mask);
+ log_mask_ptr = log_mask_ptr + offset + byte_index;
+ if ((*log_mask_ptr & byte_mask) == byte_mask) {
+ is_set = true;
+ /* break even if one client has the log mask set */
+ break;
}
- *update_ptr = i+1;
- update_ptr++;
- log_mask_ptr++;
}
+
+ if (is_set == false)
+ *update_ptr &= ~byte_mask;
+ else
+ *update_ptr |= byte_mask;
mutex_unlock(&dci_log_mask_mutex);
}
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index afcabcc..435c750 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -85,11 +85,12 @@
void extract_dci_pkt_rsp(unsigned char *buf);
/* DCI Log streaming functions */
void create_dci_log_mask_tbl(unsigned char *tbl_buf);
-void update_dci_cumulative_log_mask(int client_index);
+void update_dci_cumulative_log_mask(int offset, int byte_index,
+ uint8_t byte_mask);
void diag_send_dci_log_mask(smd_channel_t *ch);
void extract_dci_log(unsigned char *buf);
/* DCI event streaming functions */
-void update_dci_cumulative_event_mask(int client_index);
+void update_dci_cumulative_event_mask(int offset, uint8_t byte_mask);
void diag_send_dci_event_mask(smd_channel_t *ch);
void extract_dci_events(unsigned char *buf);
void create_dci_event_mask_tbl(unsigned char *tbl_buf);
diff --git a/drivers/char/diag/diag_debugfs.c b/drivers/char/diag/diag_debugfs.c
index ed0f08e..7863f74 100644
--- a/drivers/char/diag/diag_debugfs.c
+++ b/drivers/char/diag/diag_debugfs.c
@@ -16,6 +16,7 @@
#include <linux/debugfs.h>
#include "diagchar.h"
#include "diagfwd.h"
+#include "diagfwd_bridge.h"
#define DEBUG_BUF_SIZE 4096
static struct dentry *diag_dbgfs_dent;
@@ -195,8 +196,8 @@
return ret;
}
-#ifdef CONFIG_DIAG_BRIDGE_CODE
-static ssize_t diag_dbgfs_read_hsic(struct file *file, char __user *ubuf,
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+static ssize_t diag_dbgfs_read_bridge(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
char *buf;
@@ -220,13 +221,17 @@
"count_hsic_write_pool: %d\n"
"diag_hsic_pool: %x\n"
"diag_hsic_write_pool: %x\n"
- "write_len_mdm: %d\n"
+ "HSIC write_len: %d\n"
"num_hsic_buf_tbl_entries: %d\n"
- "usb_mdm_connected: %d\n"
- "diag_read_mdm_work: %d\n"
+ "HSIC usb_connected: %d\n"
+ "HSIC diag_read_work: %d\n"
"diag_read_hsic_work: %d\n"
"diag_disconnect_work: %d\n"
- "diag_usb_read_complete_work: %d\n",
+ "diag_usb_read_complete_work: %d\n"
+ "smux ch: %d"
+ "smux enabled %d"
+ "smux in busy %d"
+ "smux connected %d",
driver->hsic_ch,
driver->hsic_inited,
driver->hsic_device_enabled,
@@ -238,13 +243,17 @@
driver->count_hsic_write_pool,
(unsigned int)driver->diag_hsic_pool,
(unsigned int)driver->diag_hsic_write_pool,
- driver->write_len_mdm,
+ diag_bridge[HSIC].write_len,
driver->num_hsic_buf_tbl_entries,
- driver->usb_mdm_connected,
- work_pending(&(driver->diag_read_mdm_work)),
+ diag_bridge[HSIC].usb_connected,
+ work_pending(&(diag_bridge[HSIC].diag_read_work)),
work_pending(&(driver->diag_read_hsic_work)),
work_pending(&(driver->diag_disconnect_work)),
- work_pending(&(driver->diag_usb_read_complete_work)));
+ work_pending(&(diag_bridge[HSIC].usb_read_complete_work)),
+ driver->lcid,
+ driver->diag_smux_enabled,
+ driver->in_busy_smux,
+ driver->smux_connected);
ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);
@@ -252,8 +261,8 @@
return ret;
}
-const struct file_operations diag_dbgfs_hsic_ops = {
- .read = diag_dbgfs_read_hsic,
+const struct file_operations diag_dbgfs_bridge_ops = {
+ .read = diag_dbgfs_read_bridge,
};
#endif
@@ -284,9 +293,9 @@
debugfs_create_file("work_pending", 0444, diag_dbgfs_dent, 0,
&diag_dbgfs_workpending_ops);
-#ifdef CONFIG_DIAG_BRIDGE_CODE
- debugfs_create_file("hsic", 0444, diag_dbgfs_dent, 0,
- &diag_dbgfs_hsic_ops);
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+ debugfs_create_file("bridge", 0444, diag_dbgfs_dent, 0,
+ &diag_dbgfs_bridge_ops);
#endif
diag_dbgfs_table_index = 0;
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index de3cf52..d1ec5f2 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -273,7 +273,9 @@
struct diag_request *usb_read_mdm_ptr;
struct diag_request *write_ptr_mdm;
#endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+ /* common for all bridges */
+ struct work_struct diag_disconnect_work;
/* SGLTE variables */
int lcid;
unsigned char *buf_in_smux;
@@ -290,18 +292,6 @@
int in_busy_hsic_read_on_device;
int in_busy_hsic_write;
struct work_struct diag_read_hsic_work;
- struct mutex bridge_mutex;
- /* USB MDM channel variables */
- int usb_mdm_connected;
- int read_len_mdm;
- int write_len_mdm;
- unsigned char *usb_buf_mdm_out;
- struct usb_diag_ch *mdm_ch;
- struct workqueue_struct *diag_bridge_wq;
- struct work_struct diag_read_mdm_work;
- struct work_struct diag_disconnect_work;
- struct work_struct diag_usb_read_complete_work;
- struct diag_request *usb_read_mdm_ptr;
int count_hsic_pool;
int count_hsic_write_pool;
unsigned int poolsize_hsic;
@@ -316,5 +306,6 @@
#endif
};
+extern struct diag_bridge_dev *diag_bridge;
extern struct diagchar_dev *driver;
#endif
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 7b17ce4..645d916 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -32,13 +32,14 @@
#ifdef CONFIG_DIAG_SDIO_PIPE
#include "diagfwd_sdio.h"
#endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
#include "diagfwd_hsic.h"
#include "diagfwd_smux.h"
#endif
#include <linux/timer.h>
#include "diag_debugfs.h"
#include "diag_masks.h"
+#include "diagfwd_bridge.h"
MODULE_DESCRIPTION("Diag Char Driver");
MODULE_LICENSE("GPL v2");
@@ -127,7 +128,7 @@
mutex_unlock(&driver->diagchar_mutex);
}
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
void diag_clear_hsic_tbl(void)
{
int i;
@@ -278,7 +279,7 @@
if (driver->logging_process_id == current->tgid) {
driver->logging_mode = USB_MODE;
diagfwd_connect();
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
diag_clear_hsic_tbl();
diagfwd_cancel_hsic();
diagfwd_connect_bridge(0);
@@ -708,7 +709,7 @@
#ifdef CONFIG_DIAG_SDIO_PIPE
driver->in_busy_sdio = 1;
#endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
diagfwd_disconnect_bridge(0);
diag_clear_hsic_tbl();
#endif
@@ -737,7 +738,7 @@
queue_work(driver->diag_sdio_wq,
&(driver->diag_read_sdio_work));
#endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
diagfwd_connect_bridge(0);
#endif
}
@@ -745,13 +746,13 @@
else if (temp == USB_MODE && driver->logging_mode
== NO_LOGGING_MODE) {
diagfwd_disconnect();
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
diagfwd_disconnect_bridge(0);
#endif
} else if (temp == NO_LOGGING_MODE && driver->logging_mode
== USB_MODE) {
diagfwd_connect();
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
diagfwd_connect_bridge(0);
#endif
} else if (temp == USB_MODE && driver->logging_mode
@@ -781,14 +782,14 @@
queue_work(driver->diag_sdio_wq,
&(driver->diag_read_sdio_work));
#endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
diagfwd_cancel_hsic();
diagfwd_connect_bridge(0);
#endif
} else if (temp == MEMORY_DEVICE_MODE &&
driver->logging_mode == USB_MODE) {
diagfwd_connect();
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
diag_clear_hsic_tbl();
diagfwd_cancel_hsic();
diagfwd_connect_bridge(0);
@@ -814,7 +815,7 @@
struct diag_dci_client_tbl *entry;
int index = -1, i = 0, ret = 0;
int num_data = 0, data_type;
-#if defined(CONFIG_DIAG_SDIO_PIPE) || defined(CONFIG_DIAG_BRIDGE_CODE)
+#if defined(CONFIG_DIAG_SDIO_PIPE) || defined(CONFIG_DIAGFWD_BRIDGE_CODE)
int mdm_token = MDM_TOKEN;
#endif
@@ -833,7 +834,7 @@
if ((driver->data_ready[index] & USER_SPACE_DATA_TYPE) && (driver->
logging_mode == MEMORY_DEVICE_MODE)) {
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
unsigned long spin_lock_flags;
struct diag_write_device hsic_buf_tbl[NUM_HSIC_BUF_TBL_ENTRIES];
#endif
@@ -969,7 +970,7 @@
driver->in_busy_sdio = 0;
}
#endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
spin_lock_irqsave(&driver->hsic_spinlock, spin_lock_flags);
for (i = 0; i < driver->poolsize_hsic_write; i++) {
hsic_buf_tbl[i].buf = driver->hsic_buf_tbl[i].buf;
@@ -1120,14 +1121,17 @@
COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
/* check the current client and copy its data */
for (i = 0; i < MAX_DCI_CLIENTS; i++) {
- entry = &(driver->dci_client_tbl[i]);
- if (entry && (current->tgid == entry->client->tgid)) {
- COPY_USER_SPACE_OR_EXIT(buf+4,
- entry->data_len, 4);
- COPY_USER_SPACE_OR_EXIT(buf+8,
- *(entry->dci_data), entry->data_len);
- entry->data_len = 0;
- break;
+ if (driver->dci_client_tbl[i].client != NULL) {
+ entry = &(driver->dci_client_tbl[i]);
+ if (entry && (current->tgid ==
+ entry->client->tgid)) {
+ COPY_USER_SPACE_OR_EXIT(buf+4,
+ entry->data_len, 4);
+ COPY_USER_SPACE_OR_EXIT(buf+8,
+ *(entry->dci_data), entry->data_len);
+ entry->data_len = 0;
+ break;
+ }
}
}
driver->data_ready[index] ^= DCI_DATA_TYPE;
@@ -1199,7 +1203,8 @@
/* Check masks for On-Device logging */
if (driver->mask_check) {
- if (!mask_request_validate(driver->user_space_data)) {
+ if (!mask_request_validate(driver->user_space_data +
+ token_offset)) {
pr_alert("diag: mask request Invalid\n");
return -EFAULT;
}
@@ -1224,7 +1229,7 @@
}
}
#endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
/* send masks to 9k too */
if (driver->hsic_ch && (payload_size > 0) && remote_data) {
/* wait sending mask updates if HSIC ch not ready */
@@ -1530,6 +1535,13 @@
return 0;
}
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+static void diag_disconnect_work_fn(struct work_struct *w)
+{
+ diagfwd_disconnect_bridge(1);
+}
+#endif
+
#ifdef CONFIG_DIAG_SDIO_PIPE
void diag_sdio_fn(int type)
{
@@ -1544,16 +1556,14 @@
inline void diag_sdio_fn(int type) {}
#endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
-void diag_bridge_fn(int type)
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+void diagfwd_bridge_fn(int type)
{
- if (type == INIT)
- diagfwd_bridge_init();
- else if (type == EXIT)
+ if (type == EXIT)
diagfwd_bridge_exit();
}
#else
-inline void diag_bridge_fn(int type) {}
+inline void diagfwd_bridge_fn(int type) { }
#endif
static int __init diagchar_init(void)
@@ -1563,6 +1573,12 @@
pr_debug("diagfwd initializing ..\n");
driver = kzalloc(sizeof(struct diagchar_dev) + 5, GFP_KERNEL);
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+ diag_bridge = kzalloc(MAX_BRIDGES * sizeof(struct diag_bridge_dev),
+ GFP_KERNEL);
+ if (!diag_bridge)
+ pr_warning("diag: could not allocate memory for bridge\n");
+#endif
if (driver) {
driver->used = 0;
@@ -1607,10 +1623,16 @@
diag_debugfs_init();
diag_masks_init();
diagfwd_init();
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+ diagfwd_bridge_init(HSIC);
+ diagfwd_bridge_init(SMUX);
+ INIT_WORK(&(driver->diag_disconnect_work),
+ diag_disconnect_work_fn);
+#endif
diagfwd_cntl_init();
driver->dci_state = diag_dci_init();
diag_sdio_fn(INIT);
- diag_bridge_fn(INIT);
+
pr_debug("diagchar initializing ..\n");
driver->num = 1;
driver->name = ((void *)driver) + sizeof(struct diagchar_dev);
@@ -1645,7 +1667,7 @@
diagfwd_cntl_exit();
diag_masks_exit();
diag_sdio_fn(EXIT);
- diag_bridge_fn(EXIT);
+ diagfwd_bridge_fn(EXIT);
return -1;
}
@@ -1659,7 +1681,7 @@
diagfwd_cntl_exit();
diag_masks_exit();
diag_sdio_fn(EXIT);
- diag_bridge_fn(EXIT);
+ diagfwd_bridge_fn(EXIT);
diag_debugfs_cleanup();
diagchar_cleanup();
printk(KERN_INFO "done diagchar exit\n");
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 978b63b..cee4c96 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -40,6 +40,7 @@
#endif
#include "diag_dci.h"
#include "diag_masks.h"
+#include "diagfwd_bridge.h"
#define MODE_CMD 41
#define RESET_ID 2
@@ -327,7 +328,7 @@
}
}
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
else if (proc_num == HSIC_DATA) {
unsigned long flags;
int foundIndex = -1;
@@ -337,7 +338,7 @@
if (driver->hsic_buf_tbl[i].length == 0) {
driver->hsic_buf_tbl[i].buf = buf;
driver->hsic_buf_tbl[i].length =
- driver->write_len_mdm;
+ diag_bridge[HSIC].write_len;
driver->num_hsic_buf_tbl_entries++;
foundIndex = i;
break;
@@ -349,7 +350,7 @@
else
pr_debug("diag: ENQUEUE HSIC buf ptr and length is %x , %d\n",
(unsigned int)buf,
- driver->write_len_mdm);
+ diag_bridge[HSIC].write_len);
}
#endif
for (i = 0; i < driver->num_clients; i++)
@@ -386,10 +387,10 @@
&(driver->diag_read_sdio_work));
}
#endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
else if (proc_num == HSIC_DATA) {
if (driver->hsic_ch)
- queue_work(driver->diag_bridge_wq,
+ queue_work(diag_bridge[HSIC].wq,
&(driver->diag_read_hsic_work));
}
#endif
@@ -436,7 +437,7 @@
"while USB write\n");
}
#endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
else if (proc_num == HSIC_DATA) {
if (driver->hsic_device_enabled) {
struct diag_request *write_ptr_mdm;
@@ -447,9 +448,10 @@
if (write_ptr_mdm) {
write_ptr_mdm->buf = buf;
write_ptr_mdm->length =
- driver->write_len_mdm;
- err = usb_diag_write(driver->mdm_ch,
- write_ptr_mdm);
+ diag_bridge[HSIC].write_len;
+ write_ptr_mdm->context = (void *)HSIC;
+ err = usb_diag_write(
+ diag_bridge[HSIC].ch, write_ptr_mdm);
/* Return to the pool immediately */
if (err) {
diagmem_free(driver,
@@ -463,14 +465,16 @@
err = -1;
}
} else {
- pr_err("diag: Incorrect hsic data "
+ pr_err("diag: Incorrect HSIC data "
"while USB write\n");
err = -1;
}
} else if (proc_num == SMUX_DATA) {
write_ptr->buf = buf;
+ write_ptr->context = (void *)SMUX;
pr_debug("diag: writing SMUX data\n");
- err = usb_diag_write(driver->mdm_ch, write_ptr);
+ err = usb_diag_write(diag_bridge[SMUX].ch,
+ write_ptr);
}
#endif
APPEND_DEBUG('d');
diff --git a/drivers/char/diag/diagfwd_bridge.c b/drivers/char/diag/diagfwd_bridge.c
new file mode 100644
index 0000000..75fdeb4
--- /dev/null
+++ b/drivers/char/diag/diagfwd_bridge.c
@@ -0,0 +1,355 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/diagchar.h>
+#include <linux/kmemleak.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+#include <linux/ratelimit.h>
+#include <linux/platform_device.h>
+#include <linux/smux.h>
+#ifdef CONFIG_DIAG_OVER_USB
+#include <mach/usbdiag.h>
+#endif
+#include "diagchar.h"
+#include "diagmem.h"
+#include "diagfwd_cntl.h"
+#include "diagfwd_smux.h"
+#include "diagfwd_hsic.h"
+#include "diag_masks.h"
+#include "diagfwd_bridge.h"
+
+struct diag_bridge_dev *diag_bridge;
+
+/* diagfwd_connect_bridge is called when the USB mdm channel is connected */
+int diagfwd_connect_bridge(int process_cable)
+{
+ int i;
+
+ pr_debug("diag: in %s\n", __func__);
+
+ for (i = 0; i < MAX_BRIDGES; i++)
+ if (diag_bridge[i].enabled)
+ connect_bridge(process_cable, i);
+ return 0;
+}
+
+void connect_bridge(int process_cable, int index)
+{
+ int err;
+
+ mutex_lock(&diag_bridge[index].bridge_mutex);
+ /* If the usb cable is being connected */
+ if (process_cable) {
+ err = usb_diag_alloc_req(diag_bridge[index].ch, N_MDM_WRITE,
+ N_MDM_READ);
+ if (err)
+ pr_err("diag: unable to alloc USB req on mdm ch err:%d\n",
+ err);
+
+ diag_bridge[index].usb_connected = 1;
+ }
+
+ if (index == SMUX && driver->diag_smux_enabled) {
+ driver->in_busy_smux = 0;
+ diagfwd_connect_smux();
+ } else if (index == HSIC && driver->hsic_device_enabled) {
+ driver->in_busy_hsic_read_on_device = 0;
+ driver->in_busy_hsic_write = 0;
+ /* If the HSIC (diag_bridge) platform device is not open */
+ if (!driver->hsic_device_opened) {
+ err = diag_bridge_open(&hsic_diag_bridge_ops);
+ if (err) {
+ pr_err("diag: HSIC channel open error: %d\n",
+ err);
+ } else {
+ pr_debug("diag: opened HSIC channel\n");
+ driver->hsic_device_opened = 1;
+ }
+ } else {
+ pr_debug("diag: HSIC channel already open\n");
+ }
+ /*
+ * Turn on communication over usb mdm and HSIC, if the HSIC
+ * device driver is enabled and opened
+ */
+ if (driver->hsic_device_opened) {
+ driver->hsic_ch = 1;
+ /* Poll USB mdm channel to check for data */
+ if (driver->logging_mode == USB_MODE)
+ queue_work(diag_bridge[HSIC].wq,
+ &diag_bridge[HSIC].diag_read_work);
+ /* Poll HSIC channel to check for data */
+ queue_work(diag_bridge[HSIC].wq,
+ &driver->diag_read_hsic_work);
+ }
+ }
+ mutex_unlock(&diag_bridge[index].bridge_mutex);
+}
+
+/*
+ * diagfwd_disconnect_bridge is called when the USB mdm channel
+ * is disconnected. So disconnect should happen for all bridges
+ */
+int diagfwd_disconnect_bridge(int process_cable)
+{
+ int i;
+ pr_debug("diag: In %s, process_cable: %d\n", __func__, process_cable);
+
+ for (i = 0; i < MAX_BRIDGES; i++) {
+ if (diag_bridge[i].enabled) {
+ mutex_lock(&diag_bridge[i].bridge_mutex);
+ /* If the usb cable is being disconnected */
+ if (process_cable) {
+ diag_bridge[i].usb_connected = 0;
+ usb_diag_free_req(diag_bridge[i].ch);
+ }
+
+ if (i == HSIC && driver->hsic_device_enabled &&
+ driver->logging_mode != MEMORY_DEVICE_MODE) {
+ driver->in_busy_hsic_read_on_device = 1;
+ driver->in_busy_hsic_write = 1;
+ /* Turn off communication over usb and HSIC */
+ diag_hsic_close();
+ } else if (i == SMUX && driver->diag_smux_enabled &&
+ driver->logging_mode == USB_MODE) {
+ driver->in_busy_smux = 1;
+ driver->lcid = LCID_INVALID;
+ driver->smux_connected = 0;
+ /* Turn off communication over usb and smux */
+ msm_smux_close(LCID_VALID);
+ }
+ mutex_unlock(&diag_bridge[i].bridge_mutex);
+ }
+ }
+ return 0;
+}
+
+/* Called after the asychronous usb_diag_read() on mdm channel is complete */
+int diagfwd_read_complete_bridge(struct diag_request *diag_read_ptr)
+{
+ int index = (int)(diag_read_ptr->context);
+
+ /* The read of the usb on the mdm (not HSIC/SMUX) has completed */
+ diag_bridge[index].read_len = diag_read_ptr->actual;
+
+ if (index == SMUX) {
+ if (driver->diag_smux_enabled) {
+ diagfwd_read_complete_smux();
+ return 0;
+ } else {
+ pr_warning("diag: incorrect callback for smux\n");
+ }
+ }
+
+ /* If SMUX not enabled, check for HSIC */
+ driver->in_busy_hsic_read_on_device = 0;
+ if (!driver->hsic_ch) {
+ pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
+ return 0;
+ }
+
+ /*
+ * The read of the usb driver on the mdm channel has completed.
+ * If there is no write on the HSIC in progress, check if the
+ * read has data to pass on to the HSIC. If so, pass the usb
+ * mdm data on to the HSIC.
+ */
+ if (!driver->in_busy_hsic_write && diag_bridge[HSIC].usb_buf_out &&
+ (diag_bridge[HSIC].read_len > 0)) {
+
+ /*
+ * Initiate the HSIC write. The HSIC write is
+ * asynchronous. When complete the write
+ * complete callback function will be called
+ */
+ int err;
+ driver->in_busy_hsic_write = 1;
+ err = diag_bridge_write(diag_bridge[HSIC].usb_buf_out,
+ diag_bridge[HSIC].read_len);
+ if (err) {
+ pr_err_ratelimited("diag: mdm data on HSIC write err: %d\n",
+ err);
+ /*
+ * If the error is recoverable, then clear
+ * the write flag, so we will resubmit a
+ * write on the next frame. Otherwise, don't
+ * resubmit a write on the next frame.
+ */
+ if ((-ENODEV) != err)
+ driver->in_busy_hsic_write = 0;
+ }
+ }
+
+ /*
+ * If there is no write of the usb mdm data on the
+ * HSIC channel
+ */
+ if (!driver->in_busy_hsic_write)
+ queue_work(diag_bridge[HSIC].wq,
+ &diag_bridge[HSIC].diag_read_work);
+
+ return 0;
+}
+
+static void diagfwd_bridge_notifier(void *priv, unsigned event,
+ struct diag_request *d_req)
+{
+ int index;
+
+ switch (event) {
+ case USB_DIAG_CONNECT:
+ diagfwd_connect_bridge(1);
+ break;
+ case USB_DIAG_DISCONNECT:
+ queue_work(driver->diag_wq,
+ &driver->diag_disconnect_work);
+ break;
+ case USB_DIAG_READ_DONE:
+ index = (int)(d_req->context);
+ queue_work(diag_bridge[index].wq,
+ &diag_bridge[index].usb_read_complete_work);
+ break;
+ case USB_DIAG_WRITE_DONE:
+ index = (int)(d_req->context);
+ if (index == HSIC && driver->hsic_device_enabled)
+ diagfwd_write_complete_hsic(d_req);
+ else if (index == SMUX && driver->diag_smux_enabled)
+ diagfwd_write_complete_smux();
+ break;
+ default:
+ pr_err("diag: in %s: Unknown event from USB diag:%u\n",
+ __func__, event);
+ break;
+ }
+}
+
+void diagfwd_bridge_init(int index)
+{
+ int ret;
+ unsigned char name[20];
+
+ if (index == HSIC)
+ strlcpy(name, "hsic", sizeof(name));
+ else
+ strlcpy(name, "smux", sizeof(name));
+
+ strlcpy(diag_bridge[index].name, name, sizeof(diag_bridge[index].name));
+ strlcat(name, "_diag_wq", sizeof(diag_bridge[index].name));
+ diag_bridge[index].enabled = 1;
+ diag_bridge[index].wq = create_singlethread_workqueue(name);
+ diag_bridge[index].read_len = 0;
+ diag_bridge[index].write_len = 0;
+ if (diag_bridge[index].usb_buf_out == NULL)
+ diag_bridge[index].usb_buf_out =
+ kzalloc(USB_MAX_OUT_BUF, GFP_KERNEL);
+ if (diag_bridge[index].usb_buf_out == NULL)
+ goto err;
+ if (diag_bridge[index].usb_read_ptr == NULL)
+ diag_bridge[index].usb_read_ptr =
+ kzalloc(sizeof(struct diag_request), GFP_KERNEL);
+ if (diag_bridge[index].usb_read_ptr == NULL)
+ goto err;
+ if (diag_bridge[index].usb_read_ptr->context == NULL)
+ diag_bridge[index].usb_read_ptr->context =
+ kzalloc(sizeof(int), GFP_KERNEL);
+ if (diag_bridge[index].usb_read_ptr->context == NULL)
+ goto err;
+ mutex_init(&diag_bridge[index].bridge_mutex);
+
+ if (index == HSIC) {
+ INIT_WORK(&(diag_bridge[index].usb_read_complete_work),
+ diag_usb_read_complete_hsic_fn);
+#ifdef CONFIG_DIAG_OVER_USB
+ INIT_WORK(&(diag_bridge[index].diag_read_work),
+ diag_read_usb_hsic_work_fn);
+ diag_bridge[index].ch = usb_diag_open(DIAG_MDM, (void *)index,
+ diagfwd_bridge_notifier);
+ if (IS_ERR(diag_bridge[index].ch)) {
+ pr_err("diag: Unable to open USB diag MDM channel\n");
+ goto err;
+ }
+#endif
+ /* register HSIC device */
+ ret = platform_driver_register(&msm_hsic_ch_driver);
+ if (ret)
+ pr_err("diag: could not register HSIC device, ret: %d\n",
+ ret);
+ } else if (index == SMUX) {
+ INIT_WORK(&(diag_bridge[index].usb_read_complete_work),
+ diag_usb_read_complete_smux_fn);
+#ifdef CONFIG_DIAG_OVER_USB
+ INIT_WORK(&(diag_bridge[index].diag_read_work),
+ diag_read_usb_smux_work_fn);
+ diag_bridge[index].ch = usb_diag_open(DIAG_QSC, (void *)index,
+ diagfwd_bridge_notifier);
+ if (IS_ERR(diag_bridge[index].ch)) {
+ pr_err("diag: Unable to open USB diag QSC channel\n");
+ goto err;
+ }
+#endif
+ ret = platform_driver_register(&msm_diagfwd_smux_driver);
+ if (ret)
+ pr_err("diag: could not register SMUX device, ret: %d\n",
+ ret);
+ }
+ return;
+err:
+ pr_err("diag: Could not initialize for bridge forwarding\n");
+ kfree(diag_bridge[index].usb_buf_out);
+ kfree(driver->hsic_buf_tbl);
+ kfree(driver->write_ptr_mdm);
+ kfree(diag_bridge[index].usb_read_ptr);
+ if (diag_bridge[index].wq)
+ destroy_workqueue(diag_bridge[index].wq);
+ return;
+}
+
+void diagfwd_bridge_exit(void)
+{
+ int i;
+ pr_debug("diag: in %s\n", __func__);
+
+ if (driver->hsic_device_enabled) {
+ diag_hsic_close();
+ driver->hsic_device_enabled = 0;
+ diag_bridge[HSIC].enabled = 0;
+ }
+ driver->hsic_inited = 0;
+ diagmem_exit(driver, POOL_TYPE_ALL);
+ if (driver->diag_smux_enabled) {
+ driver->lcid = LCID_INVALID;
+ kfree(driver->buf_in_smux);
+ driver->diag_smux_enabled = 0;
+ diag_bridge[SMUX].enabled = 0;
+ }
+ platform_driver_unregister(&msm_hsic_ch_driver);
+ platform_driver_unregister(&msm_diagfwd_smux_driver);
+ /* destroy USB MDM specific variables */
+ for (i = 0; i < MAX_BRIDGES; i++) {
+ if (diag_bridge[i].enabled) {
+#ifdef CONFIG_DIAG_OVER_USB
+ if (diag_bridge[i].usb_connected)
+ usb_diag_free_req(diag_bridge[i].ch);
+ usb_diag_close(diag_bridge[i].ch);
+#endif
+ kfree(diag_bridge[i].usb_buf_out);
+ kfree(diag_bridge[i].usb_read_ptr);
+ destroy_workqueue(diag_bridge[i].wq);
+ diag_bridge[i].enabled = 0;
+ }
+ }
+ kfree(driver->hsic_buf_tbl);
+ kfree(driver->write_ptr_mdm);
+}
diff --git a/drivers/char/diag/diagfwd_bridge.h b/drivers/char/diag/diagfwd_bridge.h
new file mode 100644
index 0000000..06e6a96
--- /dev/null
+++ b/drivers/char/diag/diagfwd_bridge.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef DIAGFWD_BRIDGE_H
+#define DIAGFWD_BRIDGE_H
+
+#include "diagfwd.h"
+
+#define MAX_BRIDGES 5
+#define HSIC 0
+#define SMUX 1
+
+int diagfwd_connect_bridge(int);
+void connect_bridge(int, int);
+int diagfwd_disconnect_bridge(int);
+void diagfwd_bridge_init(int index);
+void diagfwd_bridge_exit(void);
+int diagfwd_read_complete_bridge(struct diag_request *diag_read_ptr);
+
+/* Diag-Bridge structure, n bridges can be used at same time
+ * for instance SMUX, HSIC working at same time
+ */
+struct diag_bridge_dev {
+ char name[20];
+ int enabled;
+ struct mutex bridge_mutex;
+ int usb_connected;
+ int read_len;
+ int write_len;
+ unsigned char *usb_buf_out;
+ struct usb_diag_ch *ch;
+ struct workqueue_struct *wq;
+ struct work_struct diag_read_work;
+ struct diag_request *usb_read_ptr;
+ struct work_struct usb_read_complete_work;
+};
+
+#endif
diff --git a/drivers/char/diag/diagfwd_hsic.c b/drivers/char/diag/diagfwd_hsic.c
index 7aef01f..3d5eea5 100644
--- a/drivers/char/diag/diagfwd_hsic.c
+++ b/drivers/char/diag/diagfwd_hsic.c
@@ -31,6 +31,7 @@
#include "diagfwd.h"
#include "diagfwd_hsic.h"
#include "diagfwd_smux.h"
+#include "diagfwd_bridge.h"
#define READ_HSIC_BUF_SIZE 2048
@@ -72,7 +73,7 @@
write_ptrs_available--;
/*
- * No sense queuing a read if the hsic bridge was
+ * No sense queuing a read if the HSIC bridge was
* closed in another thread
*/
if (!driver->hsic_ch)
@@ -82,7 +83,7 @@
POOL_TYPE_HSIC);
if (buf_in_hsic) {
/*
- * Initiate the read from the hsic. The hsic read is
+ * Initiate the read from the HSIC. The HSIC read is
* asynchronous. Once the read is complete the read
* callback function will be called.
*/
@@ -116,7 +117,7 @@
if ((driver->count_hsic_pool < driver->poolsize_hsic) &&
(num_reads_submitted == 0) && (err != -ENODEV) &&
(driver->hsic_ch != 0))
- queue_work(driver->diag_bridge_wq,
+ queue_work(diag_bridge[HSIC].wq,
&driver->diag_read_hsic_work);
}
@@ -127,7 +128,7 @@
if (!driver->hsic_ch) {
/*
- * The hsic channel is closed. Return the buffer to
+ * The HSIC channel is closed. Return the buffer to
* the pool. Do not send it on.
*/
diagmem_free(driver, buf, POOL_TYPE_HSIC);
@@ -149,7 +150,7 @@
* Send data in buf to be written on the
* appropriate device, e.g. USB MDM channel
*/
- driver->write_len_mdm = actual_size;
+ diag_bridge[HSIC].write_len = actual_size;
err = diag_device_write((void *)buf, HSIC_DATA, NULL);
/* If an error, return buffer to the pool */
if (err) {
@@ -170,13 +171,13 @@
}
/*
- * If for some reason there was no hsic data to write to the
+ * If for some reason there was no HSIC data to write to the
* mdm channel, set up another read
*/
if (err &&
((driver->logging_mode == MEMORY_DEVICE_MODE) ||
- (driver->usb_mdm_connected && !driver->hsic_suspend))) {
- queue_work(driver->diag_bridge_wq,
+ (diag_bridge[HSIC].usb_connected && !driver->hsic_suspend))) {
+ queue_work(diag_bridge[HSIC].wq,
&driver->diag_read_hsic_work);
}
}
@@ -195,8 +196,10 @@
if (actual_size < 0)
pr_err("DIAG in %s: actual_size: %d\n", __func__, actual_size);
- if (driver->usb_mdm_connected && (driver->logging_mode == USB_MODE))
- queue_work(driver->diag_bridge_wq, &driver->diag_read_mdm_work);
+ if (diag_bridge[HSIC].usb_connected &&
+ (driver->logging_mode == USB_MODE))
+ queue_work(diag_bridge[HSIC].wq,
+ &diag_bridge[HSIC].diag_read_work);
}
static int diag_hsic_suspend(void *ctxt)
@@ -223,12 +226,12 @@
if ((driver->count_hsic_pool < driver->poolsize_hsic) &&
((driver->logging_mode == MEMORY_DEVICE_MODE) ||
- (driver->usb_mdm_connected)))
- queue_work(driver->diag_bridge_wq,
+ (diag_bridge[HSIC].usb_connected)))
+ queue_work(diag_bridge[HSIC].wq,
&driver->diag_read_hsic_work);
}
-static struct diag_bridge_ops hsic_diag_bridge_ops = {
+struct diag_bridge_ops hsic_diag_bridge_ops = {
.ctxt = NULL,
.read_complete_cb = diag_hsic_read_complete_callback,
.write_complete_cb = diag_hsic_write_complete_callback,
@@ -236,7 +239,7 @@
.resume = diag_hsic_resume,
};
-static void diag_hsic_close(void)
+void diag_hsic_close(void)
{
if (driver->hsic_device_enabled) {
driver->hsic_ch = 0;
@@ -257,7 +260,7 @@
{
int err;
- mutex_lock(&driver->bridge_mutex);
+ mutex_lock(&diag_bridge[HSIC].bridge_mutex);
if (driver->hsic_device_enabled) {
if (driver->hsic_device_opened) {
driver->hsic_ch = 0;
@@ -274,112 +277,7 @@
}
}
}
-
- mutex_unlock(&driver->bridge_mutex);
- return 0;
-}
-
-/* diagfwd_connect_bridge is called when the USB mdm channel is connected */
-int diagfwd_connect_bridge(int process_cable)
-{
- int err;
-
- pr_debug("diag: in %s\n", __func__);
-
- mutex_lock(&driver->bridge_mutex);
- /* If the usb cable is being connected */
- if (process_cable) {
- err = usb_diag_alloc_req(driver->mdm_ch, N_MDM_WRITE,
- N_MDM_READ);
- if (err)
- pr_err("diag: unable to alloc USB req on mdm"
- " ch err:%d\n", err);
-
- driver->usb_mdm_connected = 1;
- }
-
- if (driver->hsic_device_enabled) {
- driver->in_busy_hsic_read_on_device = 0;
- driver->in_busy_hsic_write = 0;
- } else if (driver->diag_smux_enabled) {
- driver->in_busy_smux = 0;
- diagfwd_connect_smux();
- mutex_unlock(&driver->bridge_mutex);
- return 0;
- }
-
- /* If the hsic (diag_bridge) platform device is not open */
- if (driver->hsic_device_enabled) {
- if (!driver->hsic_device_opened) {
- err = diag_bridge_open(&hsic_diag_bridge_ops);
- if (err) {
- pr_err("diag: HSIC channel open error: %d\n",
- err);
- } else {
- pr_debug("diag: opened HSIC channel\n");
- driver->hsic_device_opened = 1;
- }
- } else {
- pr_debug("diag: HSIC channel already open\n");
- }
-
- /*
- * Turn on communication over usb mdm and hsic, if the hsic
- * device driver is enabled and opened
- */
- if (driver->hsic_device_opened) {
- driver->hsic_ch = 1;
-
- /* Poll USB mdm channel to check for data */
- if (driver->logging_mode == USB_MODE)
- queue_work(driver->diag_bridge_wq,
- &driver->diag_read_mdm_work);
-
- /* Poll HSIC channel to check for data */
- queue_work(driver->diag_bridge_wq,
- &driver->diag_read_hsic_work);
- }
- } else {
- /* The hsic device driver has not yet been enabled */
- pr_info("diag: HSIC channel not yet enabled\n");
- }
-
- mutex_unlock(&driver->bridge_mutex);
- return 0;
-}
-
-/*
- * diagfwd_disconnect_bridge is called when the USB mdm channel
- * is disconnected
- */
-int diagfwd_disconnect_bridge(int process_cable)
-{
- pr_debug("diag: In %s, process_cable: %d\n", __func__, process_cable);
-
- mutex_lock(&driver->bridge_mutex);
-
- /* If the usb cable is being disconnected */
- if (process_cable) {
- driver->usb_mdm_connected = 0;
- usb_diag_free_req(driver->mdm_ch);
- }
-
- if (driver->hsic_device_enabled &&
- driver->logging_mode != MEMORY_DEVICE_MODE) {
- driver->in_busy_hsic_read_on_device = 1;
- driver->in_busy_hsic_write = 1;
- /* Turn off communication over usb mdm and hsic */
- diag_hsic_close();
- } else if (driver->diag_smux_enabled &&
- driver->logging_mode == USB_MODE) {
- driver->in_busy_smux = 1;
- driver->lcid = LCID_INVALID;
- driver->smux_connected = 0;
- /* Turn off communication over usb mdm and smux */
- msm_smux_close(LCID_VALID);
- }
-
- mutex_unlock(&driver->bridge_mutex);
+ mutex_unlock(&diag_bridge[HSIC].bridge_mutex);
return 0;
}
@@ -403,225 +301,128 @@
return 0;
}
- /* Read data from the hsic */
- queue_work(driver->diag_bridge_wq, &driver->diag_read_hsic_work);
+ /* Read data from the HSIC */
+ queue_work(diag_bridge[HSIC].wq, &driver->diag_read_hsic_work);
return 0;
}
-/* Called after the asychronous usb_diag_read() on mdm channel is complete */
-static int diagfwd_read_complete_bridge(struct diag_request *diag_read_ptr)
+void diag_usb_read_complete_hsic_fn(struct work_struct *w)
{
- /* The read of the usb driver on the mdm (not hsic) has completed */
- driver->in_busy_hsic_read_on_device = 0;
- driver->read_len_mdm = diag_read_ptr->actual;
+ diagfwd_read_complete_bridge(diag_bridge[HSIC].usb_read_ptr);
+}
- if (driver->diag_smux_enabled) {
- diagfwd_read_complete_smux();
- return 0;
- }
- /* If SMUX not enabled, check for HSIC */
+
+void diag_read_usb_hsic_work_fn(struct work_struct *work)
+{
if (!driver->hsic_ch) {
- pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
- return 0;
- }
-
- /*
- * The read of the usb driver on the mdm channel has completed.
- * If there is no write on the hsic in progress, check if the
- * read has data to pass on to the hsic. If so, pass the usb
- * mdm data on to the hsic.
- */
- if (!driver->in_busy_hsic_write && driver->usb_buf_mdm_out &&
- (driver->read_len_mdm > 0)) {
-
- /*
- * Initiate the hsic write. The hsic write is
- * asynchronous. When complete the write
- * complete callback function will be called
- */
- int err;
- driver->in_busy_hsic_write = 1;
- err = diag_bridge_write(driver->usb_buf_mdm_out,
- driver->read_len_mdm);
- if (err) {
- pr_err_ratelimited("diag: mdm data on hsic write err: %d\n",
- err);
- /*
- * If the error is recoverable, then clear
- * the write flag, so we will resubmit a
- * write on the next frame. Otherwise, don't
- * resubmit a write on the next frame.
- */
- if ((-ENODEV) != err)
- driver->in_busy_hsic_write = 0;
- }
- }
-
- /*
- * If there is no write of the usb mdm data on the
- * hsic channel
- */
- if (!driver->in_busy_hsic_write && (driver->logging_mode == USB_MODE))
- queue_work(driver->diag_bridge_wq, &driver->diag_read_mdm_work);
-
- return 0;
-}
-
-static void diagfwd_bridge_notifier(void *priv, unsigned event,
- struct diag_request *d_req)
-{
- switch (event) {
- case USB_DIAG_CONNECT:
- diagfwd_connect_bridge(1);
- break;
- case USB_DIAG_DISCONNECT:
- queue_work(driver->diag_bridge_wq,
- &driver->diag_disconnect_work);
- break;
- case USB_DIAG_READ_DONE:
- queue_work(driver->diag_bridge_wq,
- &driver->diag_usb_read_complete_work);
- break;
- case USB_DIAG_WRITE_DONE:
- if (driver->hsic_device_enabled)
- diagfwd_write_complete_hsic(d_req);
- else if (driver->diag_smux_enabled)
- diagfwd_write_complete_smux();
- break;
- default:
- pr_err("diag: in %s: Unknown event from USB diag:%u\n",
- __func__, event);
- break;
- }
-}
-
-static void diag_usb_read_complete_fn(struct work_struct *w)
-{
- diagfwd_read_complete_bridge(driver->usb_read_mdm_ptr);
-}
-
-static void diag_disconnect_work_fn(struct work_struct *w)
-{
- diagfwd_disconnect_bridge(1);
-}
-
-static void diag_read_mdm_work_fn(struct work_struct *work)
-{
- int ret;
- if (driver->diag_smux_enabled) {
- if (driver->lcid && driver->usb_buf_mdm_out &&
- (driver->read_len_mdm > 0) &&
- driver->smux_connected) {
- ret = msm_smux_write(driver->lcid, NULL,
- driver->usb_buf_mdm_out, driver->read_len_mdm);
- if (ret)
- pr_err("diag: writing to SMUX ch, r = %d,"
- "lcid = %d\n", ret, driver->lcid);
- }
- driver->usb_read_mdm_ptr->buf = driver->usb_buf_mdm_out;
- driver->usb_read_mdm_ptr->length = USB_MAX_OUT_BUF;
- usb_diag_read(driver->mdm_ch, driver->usb_read_mdm_ptr);
+ pr_err("diag: in %s: driver->hsic_ch == 0\n", __func__);
return;
}
-
- /* if SMUX not enabled, check for HSIC */
- if (!driver->hsic_ch) {
- pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
- return;
- }
-
/*
* If there is no data being read from the usb mdm channel
* and there is no mdm channel data currently being written
- * to the hsic
+ * to the HSIC
*/
if (!driver->in_busy_hsic_read_on_device &&
- !driver->in_busy_hsic_write) {
+ !driver->in_busy_hsic_write) {
APPEND_DEBUG('x');
-
/* Setup the next read from usb mdm channel */
driver->in_busy_hsic_read_on_device = 1;
- driver->usb_read_mdm_ptr->buf = driver->usb_buf_mdm_out;
- driver->usb_read_mdm_ptr->length = USB_MAX_OUT_BUF;
- usb_diag_read(driver->mdm_ch, driver->usb_read_mdm_ptr);
+ diag_bridge[HSIC].usb_read_ptr->buf =
+ diag_bridge[HSIC].usb_buf_out;
+ diag_bridge[HSIC].usb_read_ptr->length = USB_MAX_OUT_BUF;
+ diag_bridge[HSIC].usb_read_ptr->context = (void *)HSIC;
+ usb_diag_read(diag_bridge[HSIC].ch,
+ diag_bridge[HSIC].usb_read_ptr);
APPEND_DEBUG('y');
}
-
- /*
- * If for some reason there was no mdm channel read initiated,
+ /* If for some reason there was no mdm channel read initiated,
* queue up the reading of data from the mdm channel
*/
+
if (!driver->in_busy_hsic_read_on_device &&
(driver->logging_mode == USB_MODE))
- queue_work(driver->diag_bridge_wq, &driver->diag_read_mdm_work);
+ queue_work(diag_bridge[HSIC].wq,
+ &(diag_bridge[HSIC].diag_read_work));
}
static int diag_hsic_probe(struct platform_device *pdev)
{
int err = 0;
+
pr_debug("diag: in %s\n", __func__);
+ mutex_lock(&diag_bridge[HSIC].bridge_mutex);
if (!driver->hsic_inited) {
+ spin_lock_init(&driver->hsic_spinlock);
+ driver->num_hsic_buf_tbl_entries = 0;
+ if (driver->hsic_buf_tbl == NULL)
+ driver->hsic_buf_tbl = kzalloc(NUM_HSIC_BUF_TBL_ENTRIES
+ * sizeof(struct diag_write_device), GFP_KERNEL);
+ if (driver->hsic_buf_tbl == NULL) {
+ mutex_unlock(&diag_bridge[HSIC].bridge_mutex);
+ return -ENOMEM;
+ }
+ driver->count_hsic_pool = 0;
+ driver->count_hsic_write_pool = 0;
+ driver->itemsize_hsic = READ_HSIC_BUF_SIZE;
+ driver->poolsize_hsic = N_MDM_WRITE;
+ driver->itemsize_hsic_write = sizeof(struct diag_request);
+ driver->poolsize_hsic_write = N_MDM_WRITE;
diagmem_hsic_init(driver);
INIT_WORK(&(driver->diag_read_hsic_work),
- diag_read_hsic_work_fn);
+ diag_read_hsic_work_fn);
driver->hsic_inited = 1;
}
-
- mutex_lock(&driver->bridge_mutex);
-
/*
* The probe function was called after the usb was connected
* on the legacy channel OR ODL is turned on. Communication over usb
- * mdm and hsic needs to be turned on.
+ * mdm and HSIC needs to be turned on.
*/
- if (driver->usb_mdm_connected || (driver->logging_mode ==
- MEMORY_DEVICE_MODE)) {
+ if (diag_bridge[HSIC].usb_connected || (driver->logging_mode ==
+ MEMORY_DEVICE_MODE)) {
if (driver->hsic_device_opened) {
/* should not happen. close it before re-opening */
pr_warn("diag: HSIC channel already opened in probe\n");
diag_bridge_close();
}
-
err = diag_bridge_open(&hsic_diag_bridge_ops);
if (err) {
pr_err("diag: could not open HSIC, err: %d\n", err);
driver->hsic_device_opened = 0;
- mutex_unlock(&driver->bridge_mutex);
+ mutex_unlock(&diag_bridge[HSIC].bridge_mutex);
return err;
}
pr_info("diag: opened HSIC channel\n");
driver->hsic_device_opened = 1;
driver->hsic_ch = 1;
-
driver->in_busy_hsic_read_on_device = 0;
driver->in_busy_hsic_write = 0;
- if (driver->usb_mdm_connected) {
+ if (diag_bridge[HSIC].usb_connected) {
/* Poll USB mdm channel to check for data */
- queue_work(driver->diag_bridge_wq,
- &driver->diag_read_mdm_work);
+ queue_work(diag_bridge[HSIC].wq,
+ &diag_bridge[HSIC].diag_read_work);
}
-
/* Poll HSIC channel to check for data */
- queue_work(driver->diag_bridge_wq,
- &driver->diag_read_hsic_work);
+ queue_work(diag_bridge[HSIC].wq,
+ &driver->diag_read_hsic_work);
}
-
- /* The hsic (diag_bridge) platform device driver is enabled */
+ /* The HSIC (diag_bridge) platform device driver is enabled */
driver->hsic_device_enabled = 1;
- mutex_unlock(&driver->bridge_mutex);
+ mutex_unlock(&diag_bridge[HSIC].bridge_mutex);
return err;
}
static int diag_hsic_remove(struct platform_device *pdev)
{
pr_debug("diag: %s called\n", __func__);
- mutex_lock(&driver->bridge_mutex);
+ mutex_lock(&diag_bridge[HSIC].bridge_mutex);
diag_hsic_close();
driver->hsic_device_enabled = 0;
- mutex_unlock(&driver->bridge_mutex);
+ mutex_unlock(&diag_bridge[HSIC].bridge_mutex);
+
return 0;
}
@@ -642,7 +443,7 @@
.runtime_resume = diagfwd_hsic_runtime_resume,
};
-static struct platform_driver msm_hsic_ch_driver = {
+struct platform_driver msm_hsic_ch_driver = {
.probe = diag_hsic_probe,
.remove = diag_hsic_remove,
.driver = {
@@ -651,112 +452,3 @@
.pm = &diagfwd_hsic_dev_pm_ops,
},
};
-
-void diagfwd_bridge_init(void)
-{
- int ret;
-
- pr_debug("diag: in %s\n", __func__);
- driver->diag_bridge_wq = create_singlethread_workqueue(
- "diag_bridge_wq");
- driver->read_len_mdm = 0;
- driver->write_len_mdm = 0;
- driver->num_hsic_buf_tbl_entries = 0;
- spin_lock_init(&driver->hsic_spinlock);
- if (driver->usb_buf_mdm_out == NULL)
- driver->usb_buf_mdm_out = kzalloc(USB_MAX_OUT_BUF,
- GFP_KERNEL);
- if (driver->usb_buf_mdm_out == NULL)
- goto err;
- /* Only used by smux move to smux probe function */
- if (driver->write_ptr_mdm == NULL)
- driver->write_ptr_mdm = kzalloc(
- sizeof(struct diag_request), GFP_KERNEL);
- if (driver->write_ptr_mdm == NULL)
- goto err;
- if (driver->usb_read_mdm_ptr == NULL)
- driver->usb_read_mdm_ptr = kzalloc(
- sizeof(struct diag_request), GFP_KERNEL);
- if (driver->usb_read_mdm_ptr == NULL)
- goto err;
-
- if (driver->hsic_buf_tbl == NULL)
- driver->hsic_buf_tbl = kzalloc(NUM_HSIC_BUF_TBL_ENTRIES *
- sizeof(struct diag_write_device), GFP_KERNEL);
- if (driver->hsic_buf_tbl == NULL)
- goto err;
-
- driver->count_hsic_pool = 0;
- driver->count_hsic_write_pool = 0;
-
- driver->itemsize_hsic = READ_HSIC_BUF_SIZE;
- driver->poolsize_hsic = N_MDM_WRITE;
- driver->itemsize_hsic_write = sizeof(struct diag_request);
- driver->poolsize_hsic_write = N_MDM_WRITE;
-
- mutex_init(&driver->bridge_mutex);
-#ifdef CONFIG_DIAG_OVER_USB
- INIT_WORK(&(driver->diag_read_mdm_work), diag_read_mdm_work_fn);
-#endif
- INIT_WORK(&(driver->diag_disconnect_work), diag_disconnect_work_fn);
- INIT_WORK(&(driver->diag_usb_read_complete_work),
- diag_usb_read_complete_fn);
-#ifdef CONFIG_DIAG_OVER_USB
- driver->mdm_ch = usb_diag_open(DIAG_MDM, driver,
- diagfwd_bridge_notifier);
- if (IS_ERR(driver->mdm_ch)) {
- pr_err("diag: Unable to open USB diag MDM channel\n");
- goto err;
- }
-#endif
- /* register HSIC device */
- ret = platform_driver_register(&msm_hsic_ch_driver);
- if (ret)
- pr_err("diag: could not register HSIC device, ret: %d\n", ret);
- /* register SMUX device */
- ret = platform_driver_register(&msm_diagfwd_smux_driver);
- if (ret)
- pr_err("diag: could not register SMUX device, ret: %d\n", ret);
-
- return;
-err:
- pr_err("diag: Could not initialize for bridge forwarding\n");
- kfree(driver->usb_buf_mdm_out);
- kfree(driver->hsic_buf_tbl);
- kfree(driver->write_ptr_mdm);
- kfree(driver->usb_read_mdm_ptr);
- if (driver->diag_bridge_wq)
- destroy_workqueue(driver->diag_bridge_wq);
-
- return;
-}
-
-void diagfwd_bridge_exit(void)
-{
- pr_debug("diag: in %s\n", __func__);
-
- if (driver->hsic_device_enabled) {
- diag_hsic_close();
- driver->hsic_device_enabled = 0;
- }
- driver->hsic_inited = 0;
- diagmem_exit(driver, POOL_TYPE_ALL);
- if (driver->diag_smux_enabled) {
- driver->lcid = LCID_INVALID;
- kfree(driver->buf_in_smux);
- driver->diag_smux_enabled = 0;
- }
- platform_driver_unregister(&msm_hsic_ch_driver);
- platform_driver_unregister(&msm_diagfwd_smux_driver);
- /* destroy USB MDM specific variables */
-#ifdef CONFIG_DIAG_OVER_USB
- if (driver->usb_mdm_connected)
- usb_diag_free_req(driver->mdm_ch);
- usb_diag_close(driver->mdm_ch);
-#endif
- kfree(driver->usb_buf_mdm_out);
- kfree(driver->hsic_buf_tbl);
- kfree(driver->write_ptr_mdm);
- kfree(driver->usb_read_mdm_ptr);
- destroy_workqueue(driver->diag_bridge_wq);
-}
diff --git a/drivers/char/diag/diagfwd_hsic.h b/drivers/char/diag/diagfwd_hsic.h
index 19ed3c7..2190fff 100644
--- a/drivers/char/diag/diagfwd_hsic.h
+++ b/drivers/char/diag/diagfwd_hsic.h
@@ -17,14 +17,14 @@
#define N_MDM_WRITE 8
#define N_MDM_READ 1
-
#define NUM_HSIC_BUF_TBL_ENTRIES N_MDM_WRITE
-int diagfwd_connect_bridge(int);
-int diagfwd_disconnect_bridge(int);
int diagfwd_write_complete_hsic(struct diag_request *);
int diagfwd_cancel_hsic(void);
-void diagfwd_bridge_init(void);
-void diagfwd_bridge_exit(void);
+void diag_read_usb_hsic_work_fn(struct work_struct *work);
+void diag_usb_read_complete_hsic_fn(struct work_struct *w);
+extern struct diag_bridge_ops hsic_diag_bridge_ops;
+extern struct platform_driver msm_hsic_ch_driver;
+void diag_hsic_close(void);
#endif
diff --git a/drivers/char/diag/diagfwd_smux.c b/drivers/char/diag/diagfwd_smux.c
index ae90686..0a97baf 100644
--- a/drivers/char/diag/diagfwd_smux.c
+++ b/drivers/char/diag/diagfwd_smux.c
@@ -18,6 +18,8 @@
#include "diagchar.h"
#include "diagfwd.h"
#include "diagfwd_smux.h"
+#include "diagfwd_hsic.h"
+#include "diagfwd_bridge.h"
void diag_smux_event(void *priv, int event_type, const void *metadata)
{
@@ -30,8 +32,8 @@
driver->smux_connected = 1;
driver->in_busy_smux = 0;
/* read data from USB MDM channel & Initiate first write */
- queue_work(driver->diag_bridge_wq,
- &(driver->diag_read_mdm_work));
+ queue_work(diag_bridge[SMUX].wq,
+ &diag_bridge[SMUX].diag_read_work);
break;
case SMUX_DISCONNECTED:
driver->smux_connected = 0;
@@ -67,7 +69,7 @@
int diagfwd_read_complete_smux(void)
{
- queue_work(driver->diag_bridge_wq, &(driver->diag_read_mdm_work));
+ queue_work(diag_bridge[SMUX].wq, &diag_bridge[SMUX].diag_read_work);
return 0;
}
@@ -85,6 +87,36 @@
return 0;
}
+void diag_usb_read_complete_smux_fn(struct work_struct *w)
+{
+ diagfwd_read_complete_bridge(diag_bridge[SMUX].usb_read_ptr);
+}
+
+void diag_read_usb_smux_work_fn(struct work_struct *work)
+{
+ int ret;
+
+ if (driver->diag_smux_enabled) {
+ if (driver->lcid && diag_bridge[SMUX].usb_buf_out &&
+ (diag_bridge[SMUX].read_len > 0) &&
+ driver->smux_connected) {
+ ret = msm_smux_write(driver->lcid, NULL,
+ diag_bridge[SMUX].usb_buf_out,
+ diag_bridge[SMUX].read_len);
+ if (ret)
+ pr_err("diag: writing to SMUX ch, r = %d, lcid = %d\n",
+ ret, driver->lcid);
+ }
+ diag_bridge[SMUX].usb_read_ptr->buf =
+ diag_bridge[SMUX].usb_buf_out;
+ diag_bridge[SMUX].usb_read_ptr->length = USB_MAX_OUT_BUF;
+ diag_bridge[SMUX].usb_read_ptr->context = (void *)SMUX;
+ usb_diag_read(diag_bridge[SMUX].ch,
+ diag_bridge[SMUX].usb_read_ptr);
+ return;
+ }
+}
+
static int diagfwd_smux_runtime_suspend(struct device *dev)
{
dev_dbg(dev, "pm_runtime: suspending...\n");
@@ -120,7 +152,7 @@
}
}
/* Poll USB channel to check for data*/
- queue_work(driver->diag_bridge_wq, &(driver->diag_read_mdm_work));
+ queue_work(diag_bridge[SMUX].wq, &(diag_bridge[SMUX].diag_read_work));
return ret;
}
@@ -142,6 +174,11 @@
* if (ret)
* pr_err("diag: error setting SMUX ch option, r = %d\n", ret);
*/
+ if (driver->write_ptr_mdm == NULL)
+ driver->write_ptr_mdm = kzalloc(sizeof(struct diag_request),
+ GFP_KERNEL);
+ if (driver->write_ptr_mdm == NULL)
+ goto err;
ret = diagfwd_connect_smux();
return ret;
diff --git a/drivers/char/diag/diagfwd_smux.h b/drivers/char/diag/diagfwd_smux.h
index e78b7ed..b45fd5d 100644
--- a/drivers/char/diag/diagfwd_smux.h
+++ b/drivers/char/diag/diagfwd_smux.h
@@ -20,6 +20,8 @@
int diagfwd_read_complete_smux(void);
int diagfwd_write_complete_smux(void);
int diagfwd_connect_smux(void);
+void diag_usb_read_complete_smux_fn(struct work_struct *w);
+void diag_read_usb_smux_work_fn(struct work_struct *work);
extern struct platform_driver msm_diagfwd_smux_driver;
#endif
diff --git a/drivers/char/diag/diagmem.c b/drivers/char/diag/diagmem.c
index 1a522d5..ab1aa75 100644
--- a/drivers/char/diag/diagmem.c
+++ b/drivers/char/diag/diagmem.c
@@ -51,7 +51,7 @@
driver->diag_write_struct_pool, GFP_ATOMIC);
}
}
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
} else if (pool_type == POOL_TYPE_HSIC) {
if (driver->diag_hsic_pool) {
if (driver->count_hsic_pool < driver->poolsize_hsic) {
@@ -105,7 +105,7 @@
} else if (driver->ref_count == 0 && pool_type == POOL_TYPE_ALL)
printk(KERN_ALERT "Unable to destroy STRUCT mempool");
}
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
if (driver->diag_hsic_pool && (driver->hsic_inited == 0)) {
if (driver->count_hsic_pool == 0) {
mempool_destroy(driver->diag_hdlc_pool);
@@ -156,7 +156,7 @@
pr_err("diag: Attempt to free up DIAG driver "
"USB structure mempool which is already free %d ",
driver->count_write_struct_pool);
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
} else if (pool_type == POOL_TYPE_HSIC) {
if (driver->diag_hsic_pool != NULL &&
driver->count_hsic_pool > 0) {
@@ -210,7 +210,7 @@
printk(KERN_INFO "Cannot allocate diag USB struct mempool\n");
}
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
void diagmem_hsic_init(struct diagchar_dev *driver)
{
if (driver->count_hsic_pool == 0)
diff --git a/drivers/char/diag/diagmem.h b/drivers/char/diag/diagmem.h
index 8665c75..36def72f 100644
--- a/drivers/char/diag/diagmem.h
+++ b/drivers/char/diag/diagmem.h
@@ -18,7 +18,7 @@
void diagmem_free(struct diagchar_dev *driver, void *buf, int pool_type);
void diagmem_init(struct diagchar_dev *driver);
void diagmem_exit(struct diagchar_dev *driver, int pool_type);
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
void diagmem_hsic_init(struct diagchar_dev *driver);
#endif
#endif
diff --git a/drivers/char/msm_rotator.c b/drivers/char/msm_rotator.c
index e1e3ff5..b3843fa 100644
--- a/drivers/char/msm_rotator.c
+++ b/drivers/char/msm_rotator.c
@@ -422,6 +422,7 @@
case MDP_Y_CRCB_H2V1:
case MDP_Y_CBCR_H2V1:
case MDP_Y_CRCB_H1V2:
+ case MDP_Y_CBCR_H1V2:
p->num_planes = 2;
p->plane_size[0] = w * h;
p->plane_size[1] = w * h;
@@ -470,8 +471,24 @@
unsigned int out_chroma_paddr)
{
int bpp;
-
- if (info->src.format != info->dst.format)
+ uint32_t dst_format;
+ switch (info->src.format) {
+ case MDP_Y_CRCB_H2V1:
+ if (info->rotations & MDP_ROT_90)
+ dst_format = MDP_Y_CRCB_H1V2;
+ else
+ dst_format = info->src.format;
+ break;
+ case MDP_Y_CBCR_H2V1:
+ if (info->rotations & MDP_ROT_90)
+ dst_format = MDP_Y_CBCR_H1V2;
+ else
+ dst_format = info->src.format;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (info->dst.format != dst_format)
return -EINVAL;
bpp = get_bpp(info->src.format);
@@ -1281,10 +1298,18 @@
is_rgb = 1;
info.dst.format = info.src.format;
break;
+ case MDP_Y_CBCR_H2V1:
+ if (info.rotations & MDP_ROT_90) {
+ info.dst.format = MDP_Y_CBCR_H1V2;
+ break;
+ }
+ case MDP_Y_CRCB_H2V1:
+ if (info.rotations & MDP_ROT_90) {
+ info.dst.format = MDP_Y_CRCB_H1V2;
+ break;
+ }
case MDP_Y_CBCR_H2V2:
case MDP_Y_CRCB_H2V2:
- case MDP_Y_CBCR_H2V1:
- case MDP_Y_CRCB_H2V1:
case MDP_YCBCR_H1V1:
case MDP_YCRCB_H1V1:
info.dst.format = info.src.format;
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index 4361263..d605a61 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -46,7 +46,7 @@
uint32_t cnt;
};
static struct bam_registration_info bam_registry;
-
+static bool ce_bam_registered;
/*
* CE HW device structure.
* Each engine has an instance of the structure.
@@ -250,7 +250,7 @@
pce = cmdlistinfo->go_proc;
if (i == authk_size_in_word) {
- pce->addr = (uint32_t)(CRYPTO_GOPROC_OEM_KEY_REG +
+ pce->addr = (uint32_t)(CRYPTO_GOPROC_QC_KEY_REG +
pce_dev->phy_iobase);
} else {
pce->addr = (uint32_t)(CRYPTO_GOPROC_REG +
@@ -434,7 +434,7 @@
pce = cmdlistinfo->go_proc;
if (i == enck_size_in_word) {
use_hw_key = true;
- pce->addr = (uint32_t)(CRYPTO_GOPROC_OEM_KEY_REG +
+ pce->addr = (uint32_t)(CRYPTO_GOPROC_QC_KEY_REG +
pce_dev->phy_iobase);
} else {
pce->addr = (uint32_t)(CRYPTO_GOPROC_REG +
@@ -1157,15 +1157,21 @@
pr_debug("bam virtual base=0x%x\n", (u32)bam.virt_addr);
mutex_lock(&bam_register_cnt);
+ if (ce_bam_registered == false) {
+ bam_registry.handle = 0;
+ bam_registry.cnt = 0;
+ }
if ((bam_registry.handle == 0) && (bam_registry.cnt == 0)) {
/* Register CE Peripheral BAM device to SPS driver */
rc = sps_register_bam_device(&bam, &bam_registry.handle);
if (rc) {
+ mutex_unlock(&bam_register_cnt);
pr_err("sps_register_bam_device() failed! err=%d", rc);
return -EIO;
}
bam_registry.cnt++;
register_bam = true;
+ ce_bam_registered = true;
} else {
bam_registry.cnt++;
}
@@ -1191,9 +1197,14 @@
sps_connect_consumer_err:
qce_sps_exit_ep_conn(pce_dev, &pce_dev->ce_sps.producer);
sps_connect_producer_err:
- if (register_bam)
+ if (register_bam) {
+ mutex_lock(&bam_register_cnt);
sps_deregister_bam_device(pce_dev->ce_sps.bam_handle);
-
+ ce_bam_registered = false;
+ bam_registry.handle = 0;
+ bam_registry.cnt = 0;
+ mutex_unlock(&bam_register_cnt);
+ }
return rc;
}
@@ -2787,21 +2798,5 @@
}
EXPORT_SYMBOL(qce_hw_support);
-static int __init qce_init(void)
-{
- bam_registry.handle = 0;
- bam_registry.cnt = 0;
- return 0;
-}
-
-static void __exit qce_exit(void)
-{
- bam_registry.handle = 0;
- bam_registry.cnt = 0;
-}
-
-module_init(qce_init);
-module_exit(qce_exit);
-
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Crypto Engine driver");
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 0109d26..060e89a 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -98,6 +98,7 @@
.irq_name = KGSL_3D0_IRQ,
},
.iomemname = KGSL_3D0_REG_MEMORY,
+ .shadermemname = KGSL_3D0_SHADER_MEMORY,
.ftbl = &adreno_functable,
#ifdef CONFIG_HAS_EARLYSUSPEND
.display_off = {
@@ -392,13 +393,6 @@
*cmds++ = cp_type3_packet(CP_INVALIDATE_STATE, 1);
*cmds++ = 0x7fff;
sizedwords += 2;
- /*
- * add an interrupt at the end of commands so that the smmu
- * disable clock off function will get called
- */
- *cmds++ = cp_type3_packet(CP_INTERRUPT, 1);
- *cmds++ = CP_INT_CNTL__RB_INT_MASK;
- sizedwords += 2;
/* This returns the per context timestamp but we need to
* use the global timestamp for iommu clock disablement */
adreno_ringbuffer_issuecmds(device, adreno_ctx,
@@ -1776,12 +1770,6 @@
return status;
}
-static inline void adreno_poke(struct kgsl_device *device)
-{
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- adreno_regwrite(device, REG_CP_RB_WPTR, adreno_dev->ringbuffer.wptr);
-}
-
static int adreno_ringbuffer_drain(struct kgsl_device *device,
unsigned int *regs)
{
@@ -1802,12 +1790,8 @@
wait = jiffies + msecs_to_jiffies(100);
- adreno_poke(device);
-
do {
if (time_after(jiffies, wait)) {
- adreno_poke(device);
-
/* Check to see if the core is hung */
if (adreno_hang_detect(device, regs))
return -ETIMEDOUT;
@@ -2021,12 +2005,23 @@
return memdesc ? kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr) : NULL;
}
-void adreno_regread(struct kgsl_device *device, unsigned int offsetwords,
- unsigned int *value)
+/**
+ * adreno_read - General read function to read adreno device memory
+ * @device - Pointer to the GPU device struct (for adreno device)
+ * @base - Base address (kernel virtual) where the device memory is mapped
+ * @offsetwords - Offset in words from the base address, of the memory that
+ * is to be read
+ * @value - Value read from the device memory
+ * @mem_len - Length of the device memory mapped to the kernel
+ */
+static void adreno_read(struct kgsl_device *device, void *base,
+ unsigned int offsetwords, unsigned int *value,
+ unsigned int mem_len)
{
+
unsigned int *reg;
- BUG_ON(offsetwords*sizeof(uint32_t) >= device->reg_len);
- reg = (unsigned int *)(device->reg_virt + (offsetwords << 2));
+ BUG_ON(offsetwords*sizeof(uint32_t) >= mem_len);
+ reg = (unsigned int *)(base + (offsetwords << 2));
if (!in_interrupt())
kgsl_pre_hwaccess(device);
@@ -2037,6 +2032,31 @@
rmb();
}
+/**
+ * adreno_regread - Used to read adreno device registers
+ * @offsetwords - Word (4 Bytes) offset to the register to be read
+ * @value - Value read from device register
+ */
+void adreno_regread(struct kgsl_device *device, unsigned int offsetwords,
+ unsigned int *value)
+{
+ adreno_read(device, device->reg_virt, offsetwords, value,
+ device->reg_len);
+}
+
+/**
+ * adreno_shadermem_regread - Used to read GPU (adreno) shader memory
+ * @device - GPU device whose shader memory is to be read
+ * @offsetwords - Offset in words, of the shader memory address to be read
+ * @value - Pointer to where the read shader mem value is to be stored
+ */
+void adreno_shadermem_regread(struct kgsl_device *device,
+ unsigned int offsetwords, unsigned int *value)
+{
+ adreno_read(device, device->shader_mem_virt, offsetwords, value,
+ device->shader_mem_len);
+}
+
void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords,
unsigned int value)
{
@@ -2070,6 +2090,67 @@
return context_id;
}
+static void adreno_next_event(struct kgsl_device *device,
+ struct kgsl_event *event)
+{
+ int status;
+ unsigned int ref_ts, enableflag;
+ unsigned int context_id = _get_context_id(event->context);
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+ status = kgsl_check_timestamp(device, event->context, event->timestamp);
+ if (!status) {
+ kgsl_sharedmem_readl(&device->memstore, &enableflag,
+ KGSL_MEMSTORE_OFFSET(context_id, ts_cmp_enable));
+ /*
+ * Barrier is needed here to make sure the read from memstore
+ * has posted
+ */
+
+ mb();
+
+ if (enableflag) {
+ kgsl_sharedmem_readl(&device->memstore, &ref_ts,
+ KGSL_MEMSTORE_OFFSET(context_id,
+ ref_wait_ts));
+
+ /* Make sure the memstore read has posted */
+ mb();
+ if (timestamp_cmp(ref_ts, event->timestamp) >= 0) {
+ kgsl_sharedmem_writel(&device->memstore,
+ KGSL_MEMSTORE_OFFSET(context_id,
+ ref_wait_ts), event->timestamp);
+ /* Make sure the memstore write is posted */
+ wmb();
+ }
+ } else {
+ unsigned int cmds[2];
+ kgsl_sharedmem_writel(&device->memstore,
+ KGSL_MEMSTORE_OFFSET(context_id,
+ ref_wait_ts), event->timestamp);
+ enableflag = 1;
+ kgsl_sharedmem_writel(&device->memstore,
+ KGSL_MEMSTORE_OFFSET(context_id,
+ ts_cmp_enable), enableflag);
+
+ /* Make sure the memstore write gets posted */
+ wmb();
+
+ /*
+ * submit a dummy packet so that even if all
+ * commands upto timestamp get executed we will still
+ * get an interrupt
+ */
+ cmds[0] = cp_type3_packet(CP_NOP, 1);
+ cmds[1] = 0;
+
+ if (adreno_dev->drawctxt_active)
+ adreno_ringbuffer_issuecmds_intr(device,
+ event->context, &cmds[0], 2);
+ }
+ }
+}
+
static int kgsl_check_interrupt_timestamp(struct kgsl_device *device,
struct kgsl_context *context, unsigned int timestamp)
{
@@ -2167,8 +2248,24 @@
if (!adreno_dev->fast_hang_detect)
return 0;
- if (is_adreno_rbbm_status_idle(device))
+ if (is_adreno_rbbm_status_idle(device)) {
+
+ /*
+ * On A2XX if the RPTR != WPTR and the device is idle, then
+ * the last write to WPTR probably failed to latch so write it
+ * again
+ */
+
+ if (adreno_is_a2xx(adreno_dev)) {
+ unsigned int rptr;
+ adreno_regread(device, REG_CP_RB_RPTR, &rptr);
+ if (rptr != adreno_dev->ringbuffer.wptr)
+ adreno_regwrite(device, REG_CP_RB_WPTR,
+ adreno_dev->ringbuffer.wptr);
+ }
+
return 0;
+ }
for (i = 0; i < hang_detect_regs_count; i++) {
@@ -2186,178 +2283,225 @@
return hang_detected;
}
-
-/* MUST be called with the device mutex held */
-static int adreno_waittimestamp(struct kgsl_device *device,
- struct kgsl_context *context,
- unsigned int timestamp,
- unsigned int msecs)
+/**
+ * adreno_handle_hang - Process a hang detected in adreno_waittimestamp
+ * @device - pointer to a KGSL device structure
+ * @context - pointer to the active KGSL context
+ * @timestamp - the timestamp that the process was waiting for
+ *
+ * Process a possible GPU hang and try to recover from it cleanly
+ */
+static int adreno_handle_hang(struct kgsl_device *device,
+ struct kgsl_context *context, unsigned int timestamp)
{
- long status = 0;
- uint io = 1;
- static uint io_cnt;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- struct kgsl_pwrctrl *pwr = &device->pwrctrl;
- struct adreno_context *adreno_ctx = context ? context->devctxt : NULL;
- int retries = 0;
- unsigned int ts_issued;
unsigned int context_id = _get_context_id(context);
- unsigned int time_elapsed = 0;
- unsigned int prev_reg_val[hang_detect_regs_count];
- unsigned int wait;
- unsigned int retry_ts_cmp = 0;
- unsigned int retry_ts_cmp_msecs = KGSL_SYNCOBJ_SERVER_TIMEOUT;
+ unsigned int ts_issued;
- memset(prev_reg_val, 0, sizeof(prev_reg_val));
+ /* Do one last check to see if we somehow made it through */
+ if (kgsl_check_timestamp(device, context, timestamp))
+ return 0;
ts_issued = adreno_dev->ringbuffer.timestamp[context_id];
- /* Don't wait forever, set a max value for now */
- if (msecs == KGSL_TIMEOUT_DEFAULT)
- msecs = adreno_dev->wait_timeout;
-
- /*
- * With user generated ts, if this check fails perform this check
- * again after 'retry_ts_cmp_msecs' milliseconds.
- */
- if (timestamp_cmp(timestamp, ts_issued) > 0) {
- if (adreno_ctx == NULL ||
- !(adreno_ctx->flags & CTXT_FLAGS_USER_GENERATED_TS)) {
- if (context && !context->wait_on_invalid_ts) {
- KGSL_DRV_ERR(device,
- "Cannot wait for invalid ts <%d:0x%x>, "
- "last issued ts <%d:0x%x>\n",
- context_id, timestamp, context_id, ts_issued);
- /*
- * Prevent the above message from spamming the
- * kernel logs and causing a watchdog
- */
- context->wait_on_invalid_ts = true;
- }
- status = -EINVAL;
- goto done;
- } else
- retry_ts_cmp = 1;
- } else if (context && context->wait_on_invalid_ts) {
- /* Once we wait for a valid ts reset the invalid wait flag */
- context->wait_on_invalid_ts = false;
- }
-
- /*
- * Make the first timeout interval 100 msecs and then try to kick the
- * wptr again. This helps to ensure the wptr is updated properly. If
- * the requested timeout is less than 100 msecs, then wait 20msecs which
- * is the minimum amount of time we can safely wait at 100HZ
- */
-
- if (msecs == 0 || msecs >= 100)
- wait = 100;
- else
- wait = 20;
-
- do {
- /*
- * If the context ID is invalid, we are in a race with
- * the context being destroyed by userspace so bail.
- */
- if (context_id == KGSL_CONTEXT_INVALID) {
- KGSL_DRV_WARN(device, "context was detached");
- status = -EINVAL;
- goto done;
- }
- if (kgsl_check_timestamp(device, context, timestamp)) {
- /* if the timestamp happens while we're not
- * waiting, there's a chance that an interrupt
- * will not be generated and thus the timestamp
- * work needs to be queued.
- */
- queue_work(device->work_queue, &device->ts_expired_ws);
- status = 0;
- goto done;
- }
- adreno_poke(device);
- io_cnt = (io_cnt + 1) % 100;
- if (io_cnt <
- pwr->pwrlevels[pwr->active_pwrlevel].io_fraction)
- io = 0;
-
- if ((retries > 0) &&
- (adreno_hang_detect(device, prev_reg_val)))
- goto hang_dump;
-
- mutex_unlock(&device->mutex);
- /* We need to make sure that the process is
- * placed in wait-q before its condition is called
- */
- status = kgsl_wait_event_interruptible_timeout(
- device->wait_queue,
- kgsl_check_interrupt_timestamp(device,
- context, timestamp),
- msecs_to_jiffies(wait), io);
-
- mutex_lock(&device->mutex);
-
- if (status > 0) {
- /*completed before the wait finished */
- status = 0;
- goto done;
- } else if (status < 0) {
- /*an error occurred*/
- goto done;
- }
- /*this wait timed out*/
-
- time_elapsed += wait;
- wait = KGSL_TIMEOUT_PART;
-
- if (!retry_ts_cmp)
- retries++;
- else if (time_elapsed >= retry_ts_cmp_msecs) {
- ts_issued =
- adreno_dev->ringbuffer.timestamp[context_id];
- if (timestamp_cmp(timestamp, ts_issued) > 0) {
- if (context && !context->wait_on_invalid_ts) {
- KGSL_DRV_ERR(device,
- "Cannot wait for user-generated ts <%d:0x%x>, "
- "not submitted within server timeout period. "
- "last issued ts <%d:0x%x>\n",
- context_id, timestamp, context_id,
- ts_issued);
- context->wait_on_invalid_ts = true;
- }
- status = -EINVAL;
- goto done;
- } else if (context && context->wait_on_invalid_ts) {
- context->wait_on_invalid_ts = false;
- }
- retry_ts_cmp = 0;
- }
-
- } while (!msecs || time_elapsed < msecs);
-
-hang_dump:
- /*
- * Check if timestamp has retired here because we may have hit
- * recovery which can take some time and cause waiting threads
- * to timeout
- */
- if (kgsl_check_timestamp(device, context, timestamp))
- goto done;
- status = -ETIMEDOUT;
KGSL_DRV_ERR(device,
"Device hang detected while waiting for timestamp: "
"<%d:0x%x>, last submitted timestamp: <%d:0x%x>, "
"wptr: 0x%x\n",
context_id, timestamp, context_id, ts_issued,
adreno_dev->ringbuffer.wptr);
- if (!adreno_dump_and_recover(device)) {
- /* The timestamp that this process wanted
- * to wait on may be invalid or expired now
- * after successful recovery */
- status = 0;
+
+ /* Return 0 after a successful recovery */
+ if (!adreno_dump_and_recover(device))
+ return 0;
+
+ return -ETIMEDOUT;
+}
+
+static int _check_pending_timestamp(struct kgsl_device *device,
+ struct kgsl_context *context, unsigned int timestamp)
+{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ unsigned int context_id = _get_context_id(context);
+ unsigned int ts_issued;
+
+ if (context_id == KGSL_CONTEXT_INVALID)
+ return -EINVAL;
+
+ ts_issued = adreno_dev->ringbuffer.timestamp[context_id];
+
+ if (timestamp_cmp(timestamp, ts_issued) <= 0)
+ return 0;
+
+ if (context && !context->wait_on_invalid_ts) {
+ KGSL_DRV_ERR(device, "Cannot wait for invalid ts <%d:0x%x>, last issued ts <%d:0x%x>\n",
+ context_id, timestamp, context_id, ts_issued);
+
+ /* Only print this message once */
+ context->wait_on_invalid_ts = true;
}
-done:
- return (int)status;
+
+ return -EINVAL;
+}
+
+/**
+ * adreno_waittimestamp - sleep while waiting for the specified timestamp
+ * @device - pointer to a KGSL device structure
+ * @context - pointer to the active kgsl context
+ * @timestamp - GPU timestamp to wait for
+ * @msecs - amount of time to wait (in milliseconds)
+ *
+ * Wait 'msecs' milliseconds for the specified timestamp to expire. Wake up
+ * every KGSL_TIMEOUT_PART milliseconds to check for a device hang and process
+ * one if it happened. Otherwise, spend most of our time in an interruptible
+ * wait for the timestamp interrupt to be processed. This function must be
+ * called with the mutex already held.
+ */
+static int adreno_waittimestamp(struct kgsl_device *device,
+ struct kgsl_context *context,
+ unsigned int timestamp,
+ unsigned int msecs)
+{
+ static unsigned int io_cnt;
+ struct adreno_context *adreno_ctx = context ? context->devctxt : NULL;
+ struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+ unsigned int context_id = _get_context_id(context);
+ unsigned int prev_reg_val[hang_detect_regs_count];
+ unsigned int time_elapsed = 0;
+ unsigned int wait;
+ int ts_compare = 1;
+ int io, ret = -ETIMEDOUT;
+
+ /* Get out early if the context has already been destroyed */
+
+ if (context_id == KGSL_CONTEXT_INVALID) {
+ KGSL_DRV_WARN(device, "context was detached");
+ return -EINVAL;
+ }
+
+ /*
+ * Check to see if the requested timestamp is "newer" then the last
+ * timestamp issued. If it is complain once and return error. Only
+ * print the message once per context so that badly behaving
+ * applications don't spam the logs
+ */
+
+ if (adreno_ctx && !(adreno_ctx->flags & CTXT_FLAGS_USER_GENERATED_TS)) {
+ if (_check_pending_timestamp(device, context, timestamp))
+ return -EINVAL;
+
+ /* Reset the invalid timestamp flag on a valid wait */
+ context->wait_on_invalid_ts = false;
+ }
+
+
+ /* Clear the registers used for hang detection */
+ memset(prev_reg_val, 0, sizeof(prev_reg_val));
+
+ /*
+ * On the first time through the loop only wait 100ms.
+ * this gives enough time for the engine to start moving and oddly
+ * provides better hang detection results than just going the full
+ * KGSL_TIMEOUT_PART right off the bat. The exception to this rule
+ * is if msecs happens to be < 100ms then just use the full timeout
+ */
+
+ wait = 100;
+
+ do {
+ long status;
+
+ if (wait > (msecs - time_elapsed))
+ wait = msecs - time_elapsed;
+
+ /*
+ * if the timestamp happens while we're not
+ * waiting, there's a chance that an interrupt
+ * will not be generated and thus the timestamp
+ * work needs to be queued.
+ */
+
+ if (kgsl_check_timestamp(device, context, timestamp)) {
+ queue_work(device->work_queue, &device->ts_expired_ws);
+ ret = 0;
+ break;
+ }
+
+ /* Check to see if the GPU is hung */
+ if (adreno_hang_detect(device, prev_reg_val)) {
+ ret = adreno_handle_hang(device, context, timestamp);
+ break;
+ }
+
+ /*
+ * For proper power accounting sometimes we need to call
+ * io_wait_interruptible_timeout and sometimes we need to call
+ * plain old wait_interruptible_timeout. We call the regular
+ * timeout N times out of 100, where N is a number specified by
+ * the current power level
+ */
+
+ io_cnt = (io_cnt + 1) % 100;
+ io = (io_cnt < pwr->pwrlevels[pwr->active_pwrlevel].io_fraction)
+ ? 0 : 1;
+
+ mutex_unlock(&device->mutex);
+
+ /* Wait for a timestamp event */
+ status = kgsl_wait_event_interruptible_timeout(
+ device->wait_queue,
+ kgsl_check_interrupt_timestamp(device, context,
+ timestamp), msecs_to_jiffies(wait), io);
+
+ mutex_lock(&device->mutex);
+
+ /*
+ * If status is non zero then either the condition was satisfied
+ * or there was an error. In either event, this is the end of
+ * the line for us
+ */
+
+ if (status != 0) {
+ ret = (status > 0) ? 0 : (int) status;
+ break;
+ }
+
+ time_elapsed += wait;
+
+ /* If user specified timestamps are being used, wait at least
+ * KGSL_SYNCOBJ_SERVER_TIMEOUT msecs for the user driver to
+ * issue a IB for a timestamp before checking to see if the
+ * current timestamp we are waiting for is valid or not
+ */
+
+ if (ts_compare && (adreno_ctx &&
+ (adreno_ctx->flags & CTXT_FLAGS_USER_GENERATED_TS))) {
+ if (time_elapsed > KGSL_SYNCOBJ_SERVER_TIMEOUT) {
+ ret = _check_pending_timestamp(device, context,
+ timestamp);
+ if (ret)
+ break;
+
+ /* Don't do this check again */
+ ts_compare = 0;
+
+ /*
+ * Reset the invalid timestamp flag on a valid
+ * wait
+ */
+ context->wait_on_invalid_ts = false;
+ }
+ }
+
+ /*
+ * all subsequent trips through the loop wait the full
+ * KGSL_TIMEOUT_PART interval
+ */
+ wait = KGSL_TIMEOUT_PART;
+
+ } while (!msecs || time_elapsed < msecs);
+
+ return ret;
}
static unsigned int adreno_readtimestamp(struct kgsl_device *device,
@@ -2516,6 +2660,7 @@
.drawctxt_destroy = adreno_drawctxt_destroy,
.setproperty = adreno_setproperty,
.postmortem_dump = adreno_dump,
+ .next_event = adreno_next_event,
};
static struct platform_driver adreno_platform_driver = {
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index f9d0316..61378fe 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -181,6 +181,10 @@
void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords,
unsigned int value);
+void adreno_shadermem_regread(struct kgsl_device *device,
+ unsigned int offsetwords,
+ unsigned int *value);
+
int adreno_dump(struct kgsl_device *device, int manual);
unsigned int adreno_a3xx_rbbm_clock_ctl_default(struct adreno_device
*adreno_dev);
diff --git a/drivers/gpu/msm/adreno_a3xx_snapshot.c b/drivers/gpu/msm/adreno_a3xx_snapshot.c
index e4f5733..1243dd0 100644
--- a/drivers/gpu/msm/adreno_a3xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a3xx_snapshot.c
@@ -11,6 +11,7 @@
*
*/
+#include <linux/io.h>
#include "kgsl.h"
#include "adreno.h"
#include "kgsl_snapshot.h"
@@ -19,14 +20,27 @@
#define DEBUG_SECTION_SZ(_dwords) (((_dwords) * sizeof(unsigned int)) \
+ sizeof(struct kgsl_snapshot_debug))
+/* Shader memory size in words */
#define SHADER_MEMORY_SIZE 0x4000
+/**
+ * a3xx_snapshot_shader_memory - Helper function to dump the GPU shader
+ * memory to the snapshot buffer.
+ * @device - GPU device whose shader memory is to be dumped
+ * @snapshot - Pointer to binary snapshot data blob being made
+ * @remain - Number of remaining bytes in the snapshot blob
+ * @priv - Unused parameter
+ */
static int a3xx_snapshot_shader_memory(struct kgsl_device *device,
void *snapshot, int remain, void *priv)
{
struct kgsl_snapshot_debug *header = snapshot;
+ unsigned int i;
unsigned int *data = snapshot + sizeof(*header);
- int i;
+ unsigned int shader_read_len = SHADER_MEMORY_SIZE;
+
+ if (SHADER_MEMORY_SIZE > (device->shader_mem_len >> 2))
+ shader_read_len = (device->shader_mem_len >> 2);
if (remain < DEBUG_SECTION_SZ(SHADER_MEMORY_SIZE)) {
SNAPSHOT_ERR_NOMEM(device, "SHADER MEMORY");
@@ -36,8 +50,22 @@
header->type = SNAPSHOT_DEBUG_SHADER_MEMORY;
header->size = SHADER_MEMORY_SIZE;
- for (i = 0; i < SHADER_MEMORY_SIZE; i++)
- adreno_regread(device, 0x4000 + i, &data[i]);
+ /* Map shader memory to kernel, for dumping */
+ if (device->shader_mem_virt == NULL)
+ device->shader_mem_virt = devm_ioremap(device->dev,
+ device->shader_mem_phys,
+ device->shader_mem_len);
+
+ if (device->shader_mem_virt == NULL) {
+ KGSL_DRV_ERR(device,
+ "Unable to map shader memory region\n");
+ return 0;
+ }
+
+ /* Now, dump shader memory to snapshot */
+ for (i = 0; i < shader_read_len; i++)
+ adreno_shadermem_regread(device, i, &data[i]);
+
return DEBUG_SECTION_SZ(SHADER_MEMORY_SIZE);
}
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index 9648f27..27343c5 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -555,6 +555,9 @@
total_sizedwords += 4; /* global timestamp for recovery*/
}
+ if (adreno_is_a20x(adreno_dev))
+ total_sizedwords += 2; /* CACHE_FLUSH */
+
ringcmds = adreno_ringbuffer_allocspace(rb, context, total_sizedwords);
if (!ringcmds) {
/*
@@ -672,6 +675,12 @@
rb->timestamp[KGSL_MEMSTORE_GLOBAL]);
}
+ if (adreno_is_a20x(adreno_dev)) {
+ GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ cp_type3_packet(CP_EVENT_WRITE, 1));
+ GSL_RB_WRITE(ringcmds, rcmd_gpu, CACHE_FLUSH);
+ }
+
if (context) {
/* Conditional execution based on memory values */
GSL_RB_WRITE(ringcmds, rcmd_gpu,
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index c040bf3..e61b040 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -504,6 +504,22 @@
kfree(event);
}
+ /* Send the next pending event for each context to the device */
+ if (device->ftbl->next_event) {
+ unsigned int id = KGSL_MEMSTORE_GLOBAL;
+
+ list_for_each_entry(event, &device->events, list) {
+
+ if (!event->context)
+ continue;
+
+ if (event->context->id != id) {
+ device->ftbl->next_event(device, event);
+ id = event->context->id;
+ }
+ }
+ }
+
mutex_unlock(&device->mutex);
}
EXPORT_SYMBOL(kgsl_timestamp_expired);
@@ -2475,6 +2491,7 @@
kgsl_ion_client = msm_ion_client_create(UINT_MAX, KGSL_NAME);
+ /* Get starting physical address of device registers */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
device->iomemname);
if (res == NULL) {
@@ -2492,6 +2509,33 @@
device->reg_phys = res->start;
device->reg_len = resource_size(res);
+ /*
+ * Check if a shadermemname is defined, and then get shader memory
+ * details including shader memory starting physical address
+ * and shader memory length
+ */
+ if (device->shadermemname != NULL) {
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ device->shadermemname);
+
+ if (res == NULL) {
+ KGSL_DRV_ERR(device,
+ "Shader memory: platform_get_resource_byname failed\n");
+ }
+
+ else {
+ device->shader_mem_phys = res->start;
+ device->shader_mem_len = resource_size(res);
+ }
+
+ if (!devm_request_mem_region(device->dev,
+ device->shader_mem_phys,
+ device->shader_mem_len,
+ device->name)) {
+ KGSL_DRV_ERR(device, "request_mem_region_failed\n");
+ }
+ }
+
if (!devm_request_mem_region(device->dev, device->reg_phys,
device->reg_len, device->name)) {
KGSL_DRV_ERR(device, "request_mem_region failed\n");
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index d962bf1..35ffc1b 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -58,6 +58,7 @@
struct kgsl_device_private;
struct kgsl_context;
struct kgsl_power_stats;
+struct kgsl_event;
struct kgsl_functable {
/* Mandatory functions - these functions must be implemented
@@ -112,6 +113,8 @@
enum kgsl_property_type type, void *value,
unsigned int sizebytes);
int (*postmortem_dump) (struct kgsl_device *device, int manual);
+ void (*next_event)(struct kgsl_device *device,
+ struct kgsl_event *event);
};
/* MH register values */
@@ -140,11 +143,27 @@
unsigned int ver_minor;
uint32_t flags;
enum kgsl_deviceid id;
+
+ /* Starting physical address for GPU registers */
unsigned long reg_phys;
+
+ /* Starting Kernel virtual address for GPU registers */
void *reg_virt;
+
+ /* Total memory size for all GPU registers */
unsigned int reg_len;
+
+ /* Kernel virtual address for GPU shader memory */
+ void *shader_mem_virt;
+
+ /* Starting physical address for GPU shader memory */
+ unsigned long shader_mem_phys;
+
+ /* GPU shader memory size */
+ unsigned int shader_mem_len;
struct kgsl_memdesc memstore;
const char *iomemname;
+ const char *shadermemname;
struct kgsl_mh mh;
struct kgsl_mmu mmu;
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index 6fe119d..bf39587 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -25,6 +25,7 @@
#include "kgsl_mmu.h"
#include "kgsl_device.h"
#include "kgsl_sharedmem.h"
+#include "adreno.h"
#define KGSL_MMU_ALIGN_SHIFT 13
#define KGSL_MMU_ALIGN_MASK (~((1 << KGSL_MMU_ALIGN_SHIFT) - 1))
@@ -536,6 +537,12 @@
uint32_t flags)
{
struct kgsl_device *device = mmu->device;
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+ if (!(flags & (KGSL_MMUFLAGS_TLBFLUSH | KGSL_MMUFLAGS_PTUPDATE))
+ && !adreno_is_a2xx(adreno_dev))
+ return;
+
if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type)
return;
else if (device->ftbl->setstate)
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 7a7a8dc..bb127be 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -885,6 +885,9 @@
kgsl_pwrstate_to_str(device->state));
break;
}
+
+ kgsl_mmu_disable_clk_on_ts(&device->mmu, 0, false);
+
return 0;
}
diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c
index a384103..e319813 100644
--- a/drivers/hwmon/qpnp-adc-common.c
+++ b/drivers/hwmon/qpnp-adc-common.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -299,81 +299,83 @@
{419, 128000}
};
+/* Voltage to temperature */
static const struct qpnp_vadc_map_pt adcmap_100k_104ef_104fb[] = {
- {-40, 1758},
- {-35, 1742},
- {-30, 1719},
- {-25, 1691},
- {-20, 1654},
- {-15, 1608},
- {-10, 1551},
- {-5, 1483},
- {0, 1404},
- {5, 1315},
- {10, 1218},
- {15, 1114},
- {20, 1007},
- {25, 900},
- {30, 795},
- {35, 696},
- {40, 605},
- {45, 522},
- {50, 448},
- {55, 383},
- {60, 327},
- {65, 278},
- {70, 237},
- {75, 202},
- {80, 172},
- {85, 146},
- {90, 125},
- {95, 107},
- {100, 92},
- {105, 79},
- {110, 68},
- {115, 59},
- {120, 51},
- {125, 44}
+ {1758, -40},
+ {1742, -35},
+ {1719, -30},
+ {1691, -25},
+ {1654, -20},
+ {1608, -15},
+ {1551, -10},
+ {1483, -5},
+ {1404, 0},
+ {1315, 5},
+ {1218, 10},
+ {1114, 15},
+ {1007, 20},
+ {900, 25},
+ {795, 30},
+ {696, 35},
+ {605, 40},
+ {522, 45},
+ {448, 50},
+ {383, 55},
+ {327, 60},
+ {278, 65},
+ {237, 70},
+ {202, 75},
+ {172, 80},
+ {146, 85},
+ {125, 90},
+ {107, 95},
+ {92, 100},
+ {79, 105},
+ {68, 110},
+ {59, 115},
+ {51, 120},
+ {44, 125}
};
+/* Voltage to temperature */
static const struct qpnp_vadc_map_pt adcmap_150k_104ef_104fb[] = {
- {-40, 1738},
- {-35, 1714},
- {-30, 1682},
- {-25, 1641},
- {-20, 1589},
- {-15, 1526},
- {-10, 1451},
- {-5, 1363},
- {0, 1266},
- {5, 1159},
- {10, 1048},
- {15, 936},
- {20, 825},
- {25, 720},
- {30, 622},
- {35, 533},
- {40, 454},
- {45, 385},
- {50, 326},
- {55, 275},
- {60, 232},
- {65, 195},
- {70, 165},
- {75, 139},
- {80, 118},
- {85, 100},
- {90, 85},
- {95, 73},
- {100, 62},
- {105, 53},
- {110, 46},
- {115, 40},
- {120, 34},
- {125, 30}
+ {1738, -40},
+ {1714, -35},
+ {1682, -30},
+ {1641, -25},
+ {1589, -20},
+ {1526, -15},
+ {1451, -10},
+ {1363, -5},
+ {1266, 0},
+ {1159, 5},
+ {1048, 10},
+ {936, 15},
+ {825, 20},
+ {720, 25},
+ {622, 30},
+ {533, 35},
+ {454, 40},
+ {385, 45},
+ {326, 50},
+ {275, 55},
+ {232, 60},
+ {195, 65},
+ {165, 70},
+ {139, 75},
+ {118, 80},
+ {100, 85},
+ {85, 90},
+ {73, 95},
+ {62, 100},
+ {53, 105},
+ {46, 110},
+ {40, 115},
+ {34, 120},
+ {30, 125}
};
-static int32_t qpnp_adc_map_linear(const struct qpnp_vadc_map_pt *pts,
+static int32_t qpnp_adc_map_voltage_temp(const struct qpnp_vadc_map_pt *pts,
uint32_t tablesize, int32_t input, int64_t *output)
{
bool descending = 1;
@@ -419,7 +421,7 @@
return 0;
}
-static int32_t qpnp_adc_map_batt_therm(const struct qpnp_vadc_map_pt *pts,
+static int32_t qpnp_adc_map_temp_voltage(const struct qpnp_vadc_map_pt *pts,
uint32_t tablesize, int32_t input, int64_t *output)
{
bool descending = 1;
@@ -552,7 +554,7 @@
xo_thm = qpnp_adc_scale_ratiometric_calib(adc_code,
adc_properties, chan_properties);
xo_thm <<= 4;
- qpnp_adc_map_linear(adcmap_ntcg_104ef_104fb,
+ qpnp_adc_map_voltage_temp(adcmap_ntcg_104ef_104fb,
ARRAY_SIZE(adcmap_ntcg_104ef_104fb),
xo_thm, &adc_chan_result->physical);
@@ -570,7 +572,7 @@
bat_voltage = qpnp_adc_scale_ratiometric_calib(adc_code,
adc_properties, chan_properties);
- return qpnp_adc_map_batt_therm(
+ return qpnp_adc_map_temp_voltage(
adcmap_btm_threshold,
ARRAY_SIZE(adcmap_btm_threshold),
bat_voltage,
@@ -588,7 +590,7 @@
therm_voltage = qpnp_adc_scale_ratiometric_calib(adc_code,
adc_properties, chan_properties);
- qpnp_adc_map_linear(adcmap_150k_104ef_104fb,
+ qpnp_adc_map_voltage_temp(adcmap_150k_104ef_104fb,
ARRAY_SIZE(adcmap_150k_104ef_104fb),
therm_voltage, &adc_chan_result->physical);
@@ -606,7 +608,7 @@
therm_voltage = qpnp_adc_scale_ratiometric_calib(adc_code,
adc_properties, chan_properties);
- qpnp_adc_map_linear(adcmap_100k_104ef_104fb,
+ qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb,
ARRAY_SIZE(adcmap_100k_104ef_104fb),
therm_voltage, &adc_chan_result->physical);
diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb/dvb-core/demux.h
index b7ace53..f802a38 100644
--- a/drivers/media/dvb/dvb-core/demux.h
+++ b/drivers/media/dvb/dvb-core/demux.h
@@ -205,6 +205,8 @@
dmx_ts_data_ready_cb callback);
int (*notify_data_read)(struct dmx_ts_feed *feed,
u32 bytes_num);
+ int (*set_tsp_out_format) (struct dmx_ts_feed *feed,
+ enum dmx_tsp_format_t tsp_format);
};
/*--------------------------------------------------------------------------*/
@@ -369,9 +371,6 @@
int (*set_tsp_format) (struct dmx_demux *demux,
enum dmx_tsp_format_t tsp_format);
- int (*set_tsp_out_format) (struct dmx_demux *demux,
- enum dmx_tsp_format_t tsp_format);
-
int (*set_playback_mode) (struct dmx_demux *demux,
enum dmx_playback_mode_t mode,
dmx_ts_fullness ts_fullness_callback,
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index 71642a5..507c014 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -465,6 +465,90 @@
return NULL;
}
+static int dvr_input_thread_entry(void *arg)
+{
+ struct dmxdev *dmxdev = arg;
+ struct dvb_ringbuffer *src = &dmxdev->dvr_input_buffer;
+ int ret;
+ size_t todo;
+ size_t split;
+
+ while (1) {
+ /* wait for input */
+ ret = wait_event_interruptible(src->queue,
+ (!src->data) ||
+ (dvb_ringbuffer_avail(src)) ||
+ (src->error != 0) ||
+ (dmxdev->dvr_in_exit) ||
+ kthread_should_stop());
+
+ if ((ret < 0) || kthread_should_stop())
+ break;
+
+ spin_lock(&dmxdev->dvr_in_lock);
+
+ if (!src->data || dmxdev->exit || dmxdev->dvr_in_exit) {
+ spin_unlock(&dmxdev->dvr_in_lock);
+ break;
+ }
+
+ if (src->error) {
+ spin_unlock(&dmxdev->dvr_in_lock);
+ wake_up_all(&src->queue);
+ break;
+ }
+
+ dmxdev->dvr_processing_input = 1;
+
+ ret = dvb_ringbuffer_avail(src);
+ todo = ret;
+
+ split = (src->pread + ret > src->size) ?
+ src->size - src->pread :
+ 0;
+
+ /*
+ * In DVR PULL mode, write might block.
+ * Lock on DVR buffer is released before calling to
+ * write, if DVR was released meanwhile, dvr_in_exit is
+ * prompted. Lock is aquired when updating the read pointer
+ * again to preserve read/write pointers consistancy
+ */
+ if (split > 0) {
+ spin_unlock(&dmxdev->dvr_in_lock);
+ dmxdev->demux->write(dmxdev->demux,
+ src->data + src->pread,
+ split);
+
+ if (dmxdev->dvr_in_exit)
+ break;
+
+ spin_lock(&dmxdev->dvr_in_lock);
+
+ todo -= split;
+ DVB_RINGBUFFER_SKIP(src, split);
+ }
+
+ spin_unlock(&dmxdev->dvr_in_lock);
+ dmxdev->demux->write(dmxdev->demux,
+ src->data + src->pread, todo);
+
+ if (dmxdev->dvr_in_exit)
+ break;
+
+ spin_lock(&dmxdev->dvr_in_lock);
+
+ DVB_RINGBUFFER_SKIP(src, todo);
+ dmxdev->dvr_processing_input = 0;
+ spin_unlock(&dmxdev->dvr_in_lock);
+
+ wake_up_all(&src->queue);
+ }
+
+ return 0;
+}
+
+
static int dvb_dvr_open(struct inode *inode, struct file *file)
{
struct dvb_device *dvbdev = file->private_data;
@@ -542,6 +626,17 @@
dmxdev->demux->dvr_input.priv_handle = NULL;
dmxdev->demux->dvr_input.ringbuff = &dmxdev->dvr_input_buffer;
dvbdev->writers--;
+
+ dmxdev->dvr_input_thread =
+ kthread_run(
+ dvr_input_thread_entry,
+ (void *)dmxdev,
+ "dvr_input");
+
+ if (IS_ERR(dmxdev->dvr_input_thread)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ENOMEM;
+ }
}
dvbdev->users++;
@@ -601,11 +696,11 @@
dmxdev->demux->write_cancel(dmxdev->demux);
/*
- * Now flush dvr-in workqueue so that no one
+ * Now stop dvr-input thread so that no one
* would process data from dvr input buffer any more
* before it gets freed.
*/
- flush_workqueue(dmxdev->dvr_input_workqueue);
+ kthread_stop(dmxdev->dvr_input_thread);
dvbdev->writers++;
dmxdev->demux->disconnect_frontend(dmxdev->demux);
@@ -773,12 +868,7 @@
buf += ret;
mutex_unlock(&dmxdev->mutex);
-
wake_up_all(&src->queue);
-
- if (!work_pending(&dmxdev->dvr_input_work))
- queue_work(dmxdev->dvr_input_workqueue,
- &dmxdev->dvr_input_work);
}
return (count - todo) ? (count - todo) : ret;
@@ -827,87 +917,6 @@
return res;
}
-static void dvr_input_work_func(struct work_struct *worker)
-{
- struct dmxdev *dmxdev =
- container_of(worker, struct dmxdev, dvr_input_work);
- struct dvb_ringbuffer *src = &dmxdev->dvr_input_buffer;
- int ret;
- size_t todo;
- size_t split;
-
- while (1) {
- /* wait for input */
- ret = wait_event_interruptible(src->queue,
- (!src->data) ||
- (dvb_ringbuffer_avail(src)) ||
- (src->error != 0) ||
- (dmxdev->dvr_in_exit));
-
- if (ret < 0)
- break;
-
- spin_lock(&dmxdev->dvr_in_lock);
-
- if (!src->data || dmxdev->exit || dmxdev->dvr_in_exit) {
- spin_unlock(&dmxdev->dvr_in_lock);
- break;
- }
-
- if (src->error) {
- spin_unlock(&dmxdev->dvr_in_lock);
- wake_up_all(&src->queue);
- break;
- }
-
- dmxdev->dvr_processing_input = 1;
-
- ret = dvb_ringbuffer_avail(src);
- todo = ret;
-
- split = (src->pread + ret > src->size) ?
- src->size - src->pread :
- 0;
-
- /*
- * In DVR PULL mode, write might block.
- * Lock on DVR buffer is released before calling to
- * write, if DVR was released meanwhile, dvr_in_exit is
- * prompted. Lock is aquired when updating the read pointer
- * again to preserve read/write pointers consistancy
- */
- if (split > 0) {
- spin_unlock(&dmxdev->dvr_in_lock);
- dmxdev->demux->write(dmxdev->demux,
- src->data + src->pread,
- split);
-
- if (dmxdev->dvr_in_exit)
- break;
-
- spin_lock(&dmxdev->dvr_in_lock);
-
- todo -= split;
- DVB_RINGBUFFER_SKIP(src, split);
- }
-
- spin_unlock(&dmxdev->dvr_in_lock);
- dmxdev->demux->write(dmxdev->demux,
- src->data + src->pread, todo);
-
- if (dmxdev->dvr_in_exit)
- break;
-
- spin_lock(&dmxdev->dvr_in_lock);
-
- DVB_RINGBUFFER_SKIP(src, todo);
- dmxdev->dvr_processing_input = 0;
- spin_unlock(&dmxdev->dvr_in_lock);
-
- wake_up_all(&src->queue);
- }
-}
-
static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev,
unsigned int f_flags,
unsigned long size)
@@ -1205,10 +1214,6 @@
wake_up_all(&buffer->queue);
- if (!work_pending(&dmxdev->dvr_input_work))
- queue_work(dmxdev->dvr_input_workqueue,
- &dmxdev->dvr_input_work);
-
return 0;
}
@@ -1331,6 +1336,21 @@
return 0;
}
+static int dvb_dmxdev_set_tsp_out_format(struct dmxdev_filter *dmxdevfilter,
+ enum dmx_tsp_format_t dmx_tsp_format)
+{
+ if (dmxdevfilter->state >= DMXDEV_STATE_GO)
+ return -EBUSY;
+
+ if ((dmx_tsp_format > DMX_TSP_FORMAT_192_HEAD) ||
+ (dmx_tsp_format < DMX_TSP_FORMAT_188))
+ return -EINVAL;
+
+ dmxdevfilter->dmx_tsp_format = dmx_tsp_format;
+
+ return 0;
+}
+
static int dvb_dmxdev_set_pes_buffer_size(struct dmxdev_filter *dmxdevfilter,
unsigned long size)
{
@@ -2255,6 +2275,9 @@
return ret;
}
+ if (tsfeed->set_tsp_out_format)
+ tsfeed->set_tsp_out_format(tsfeed, filter->dmx_tsp_format);
+
/* Support indexing for video PES */
if ((para->pes_type == DMX_PES_VIDEO0) ||
(para->pes_type == DMX_PES_VIDEO1) ||
@@ -2465,6 +2488,8 @@
dmxdevfilter->pes_buffer_size = 32768;
+ dmxdevfilter->dmx_tsp_format = DMX_TSP_FORMAT_188;
+
dvbdev->users++;
mutex_unlock(&dmxdev->mutex);
@@ -2854,19 +2879,15 @@
break;
case DMX_SET_TS_OUT_FORMAT:
- if (!dmxdev->demux->set_tsp_out_format) {
- ret = -EINVAL;
- break;
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
}
- if (dmxdevfilter->state >= DMXDEV_STATE_GO) {
- ret = -EBUSY;
- break;
- }
-
- ret = dmxdev->demux->set_tsp_out_format(
- dmxdev->demux,
+ ret = dvb_dmxdev_set_tsp_out_format(dmxdevfilter,
*(enum dmx_tsp_format_t *)parg);
+
+ mutex_unlock(&dmxdevfilter->mutex);
break;
case DMX_SET_DECODER_BUFFER_SIZE:
@@ -3244,14 +3265,6 @@
if (!dmxdev->filter)
return -ENOMEM;
- dmxdev->dvr_input_workqueue =
- create_singlethread_workqueue("dvr_workqueue");
-
- if (dmxdev->dvr_input_workqueue == NULL) {
- vfree(dmxdev->filter);
- return -ENOMEM;
- }
-
dmxdev->playback_mode = DMX_PB_MODE_PUSH;
mutex_init(&dmxdev->mutex);
@@ -3272,9 +3285,6 @@
dvb_ringbuffer_init(&dmxdev->dvr_buffer, NULL, 8192);
dvb_ringbuffer_init(&dmxdev->dvr_input_buffer, NULL, 8192);
- INIT_WORK(&dmxdev->dvr_input_work,
- dvr_input_work_func);
-
if (dmxdev->demux->debugfs_demux_dir)
debugfs_create_file("filters", S_IRUGO,
dmxdev->demux->debugfs_demux_dir, dmxdev,
@@ -3297,9 +3307,6 @@
dmxdev->dvr_dvbdev->users==1);
}
- flush_workqueue(dmxdev->dvr_input_workqueue);
- destroy_workqueue(dmxdev->dvr_input_workqueue);
-
dvb_unregister_device(dmxdev->dvbdev);
dvb_unregister_device(dmxdev->dvr_dvbdev);
diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h
index e30c2c3..d1c1cc3 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.h
+++ b/drivers/media/dvb/dvb-core/dmxdev.h
@@ -34,7 +34,7 @@
#include <linux/string.h>
#include <linux/mutex.h>
#include <linux/slab.h>
-#include <linux/workqueue.h>
+#include <linux/kthread.h>
#include <linux/dvb/dmx.h>
#include "dvbdev.h"
@@ -122,6 +122,8 @@
/* relevent for decoder PES */
unsigned long pes_buffer_size;
+ /* for recording output */
+ enum dmx_tsp_format_t dmx_tsp_format;
u32 rec_chunk_size;
/* only for sections */
@@ -131,8 +133,6 @@
};
struct dmxdev {
- struct work_struct dvr_input_work;
-
struct dvb_device *dvbdev;
struct dvb_device *dvr_dvbdev;
@@ -165,7 +165,7 @@
struct dvb_ringbuffer dvr_input_buffer;
enum dmx_buffer_mode dvr_input_buffer_mode;
- struct workqueue_struct *dvr_input_workqueue;
+ struct task_struct *dvr_input_thread;
#define DVR_BUFFER_SIZE (10*188*1024)
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index 2c5294f..6d66d45 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -42,6 +42,8 @@
*/
// #define DVB_DEMUX_SECTION_LOSS_LOG
+#define TIMESTAMP_LEN 4
+
static int dvb_demux_tscheck;
module_param(dvb_demux_tscheck, int, 0644);
MODULE_PARM_DESC(dvb_demux_tscheck,
@@ -417,17 +419,16 @@
static inline void dvb_dmx_swfilter_output_packet(
struct dvb_demux_feed *feed,
- const u8 *buf)
+ const u8 *buf,
+ const u8 timestamp[TIMESTAMP_LEN])
{
- u8 time_stamp[4] = {0};
- struct dvb_demux *demux = feed->demux;
-
/*
* if we output 192 packet with timestamp at head of packet,
* output the timestamp now before the 188 TS packet
*/
- if (demux->tsp_out_format == DMX_TSP_FORMAT_192_HEAD)
- feed->cb.ts(time_stamp, 4, NULL, 0, &feed->feed.ts, DMX_OK);
+ if (feed->tsp_out_format == DMX_TSP_FORMAT_192_HEAD)
+ feed->cb.ts(timestamp, TIMESTAMP_LEN, NULL,
+ 0, &feed->feed.ts, DMX_OK);
feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
@@ -435,8 +436,9 @@
* if we output 192 packet with timestamp at tail of packet,
* output the timestamp now after the 188 TS packet
*/
- if (demux->tsp_out_format == DMX_TSP_FORMAT_192_TAIL)
- feed->cb.ts(time_stamp, 4, NULL, 0, &feed->feed.ts, DMX_OK);
+ if (feed->tsp_out_format == DMX_TSP_FORMAT_192_TAIL)
+ feed->cb.ts(timestamp, TIMESTAMP_LEN, NULL,
+ 0, &feed->feed.ts, DMX_OK);
}
static inline void dvb_dmx_configure_decoder_fullness(
@@ -571,7 +573,7 @@
}
static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed,
- const u8 *buf)
+ const u8 *buf, const u8 timestamp[TIMESTAMP_LEN])
{
switch (feed->type) {
case DMX_TYPE_TS:
@@ -581,7 +583,8 @@
if (feed->ts_type & TS_PAYLOAD_ONLY)
dvb_dmx_swfilter_payload(feed, buf);
else
- dvb_dmx_swfilter_output_packet(feed, buf);
+ dvb_dmx_swfilter_output_packet(feed,
+ buf, timestamp);
}
if (feed->ts_type & TS_DECODER)
if (feed->demux->write_to_decoder)
@@ -605,7 +608,8 @@
((f)->feed.ts.is_filtering) && \
(((f)->ts_type & (TS_PACKET | TS_DEMUX)) == TS_PACKET))
-static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
+static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf,
+ const u8 timestamp[TIMESTAMP_LEN])
{
struct dvb_demux_feed *feed;
u16 pid = ts_pid(buf);
@@ -678,10 +682,10 @@
continue;
if (feed->pid == pid)
- dvb_dmx_swfilter_packet_type(feed, buf);
+ dvb_dmx_swfilter_packet_type(feed, buf, timestamp);
else if ((feed->pid == 0x2000) &&
(feed->feed.ts.is_filtering))
- dvb_dmx_swfilter_output_packet(feed, buf);
+ dvb_dmx_swfilter_output_packet(feed, buf, timestamp);
}
}
@@ -735,6 +739,7 @@
size_t count)
{
struct timespec pre_time;
+ u8 timestamp[TIMESTAMP_LEN] = {0};
if (dvb_demux_performancecheck)
pre_time = current_kernel_time();
@@ -746,7 +751,7 @@
while (count--) {
if (buf[0] == 0x47)
- dvb_dmx_swfilter_packet(demux, buf);
+ dvb_dmx_swfilter_packet(demux, buf, timestamp);
buf += 188;
}
@@ -794,6 +799,7 @@
int p = 0, i, j;
const u8 *q;
struct timespec pre_time;
+ u8 timestamp[TIMESTAMP_LEN];
if (dvb_demux_performancecheck)
pre_time = current_kernel_time();
@@ -812,12 +818,23 @@
goto bailout;
}
memcpy(&demux->tsbuf[i], buf, j);
+
+ if (pktsize == 192) {
+ if (leadingbytes)
+ memcpy(timestamp, &buf[p], TIMESTAMP_LEN);
+ else
+ memcpy(timestamp, &buf[188], TIMESTAMP_LEN);
+ } else {
+ memset(timestamp, 0, TIMESTAMP_LEN);
+ }
+
if (pktsize == 192 &&
leadingbytes &&
demux->tsbuf[leadingbytes] == 0x47) /* double check */
- dvb_dmx_swfilter_packet(demux, demux->tsbuf+4);
+ dvb_dmx_swfilter_packet(demux,
+ demux->tsbuf + TIMESTAMP_LEN, timestamp);
else if (demux->tsbuf[0] == 0x47) /* double check */
- dvb_dmx_swfilter_packet(demux, demux->tsbuf);
+ dvb_dmx_swfilter_packet(demux, demux->tsbuf, timestamp);
demux->tsbufp = 0;
p += j;
}
@@ -841,10 +858,18 @@
q = demux->tsbuf;
}
- if (pktsize == 192 && leadingbytes)
- q = &buf[p+leadingbytes];
+ if (pktsize == 192) {
+ if (leadingbytes) {
+ q = &buf[p+leadingbytes];
+ memcpy(timestamp, &buf[p], TIMESTAMP_LEN);
+ } else {
+ memcpy(timestamp, &buf[188], TIMESTAMP_LEN);
+ }
+ } else {
+ memset(timestamp, 0, TIMESTAMP_LEN);
+ }
- dvb_dmx_swfilter_packet(demux, q);
+ dvb_dmx_swfilter_packet(demux, q, timestamp);
p += pktsize;
}
@@ -891,7 +916,7 @@
break;
case DMX_TSP_FORMAT_192_HEAD:
- _dvb_dmx_swfilter(demux, buf, count, 192, 4);
+ _dvb_dmx_swfilter(demux, buf, count, 192, TIMESTAMP_LEN);
break;
case DMX_TSP_FORMAT_204:
@@ -1154,6 +1179,25 @@
return 0;
}
+static int dmx_ts_set_tsp_out_format(
+ struct dmx_ts_feed *ts_feed,
+ enum dmx_tsp_format_t tsp_format)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+ struct dvb_demux *dvbdmx = feed->demux;
+
+ mutex_lock(&dvbdmx->mutex);
+
+ if (feed->state == DMX_STATE_GO) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -EINVAL;
+ }
+
+ feed->tsp_out_format = tsp_format;
+ mutex_unlock(&dvbdmx->mutex);
+ return 0;
+}
+
static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx,
struct dmx_ts_feed **ts_feed,
dmx_ts_cb callback)
@@ -1175,6 +1219,7 @@
feed->pid = 0xffff;
feed->peslen = 0;
feed->buffer = NULL;
+ feed->tsp_out_format = DMX_TSP_FORMAT_188;
memset(&feed->indexing_params, 0,
sizeof(struct dmx_indexing_video_params));
@@ -1193,6 +1238,7 @@
(*ts_feed)->stop_filtering = dmx_ts_feed_stop_filtering;
(*ts_feed)->set = dmx_ts_feed_set;
(*ts_feed)->set_indexing_params = dmx_ts_set_indexing_params;
+ (*ts_feed)->set_tsp_out_format = dmx_ts_set_tsp_out_format;
(*ts_feed)->get_decoder_buff_status = dmx_ts_feed_decoder_buff_status;
(*ts_feed)->data_ready_cb = dmx_ts_feed_data_ready_cb;
(*ts_feed)->notify_data_read = NULL;
@@ -1689,23 +1735,6 @@
return 0;
}
-static int dvbdmx_set_tsp_out_format(
- struct dmx_demux *demux,
- enum dmx_tsp_format_t tsp_format)
-{
- struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
-
- if ((tsp_format > DMX_TSP_FORMAT_192_HEAD) ||
- (tsp_format < DMX_TSP_FORMAT_188))
- return -EINVAL;
-
- mutex_lock(&dvbdemux->mutex);
-
- dvbdemux->tsp_out_format = tsp_format;
- mutex_unlock(&dvbdemux->mutex);
- return 0;
-}
-
int dvb_dmx_init(struct dvb_demux *dvbdemux)
{
int i;
@@ -1776,7 +1805,6 @@
dvbdemux->tsbufp = 0;
dvbdemux->tsp_format = DMX_TSP_FORMAT_188;
- dvbdemux->tsp_out_format = DMX_TSP_FORMAT_188;
if (!dvbdemux->check_crc32)
dvbdemux->check_crc32 = dvb_dmx_crc32;
@@ -1806,7 +1834,6 @@
dmx->get_pes_pids = dvbdmx_get_pes_pids;
dmx->set_tsp_format = dvbdmx_set_tsp_format;
- dmx->set_tsp_out_format = dvbdmx_set_tsp_out_format;
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 706cd0c..4e6dfaf 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.h
+++ b/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -90,6 +90,7 @@
u16 pid;
u8 *buffer;
int buffer_size;
+ enum dmx_tsp_format_t tsp_out_format;
struct timespec timeout;
struct dvb_demux_filter *filter;
@@ -155,7 +156,6 @@
uint32_t speed_pkts_cnt; /* for TS speed check */
enum dmx_tsp_format_t tsp_format;
- enum dmx_tsp_format_t tsp_out_format;
enum dmx_playback_mode_t playback_mode;
int sw_filter_abort;
diff --git a/drivers/media/radio/radio-iris.c b/drivers/media/radio/radio-iris.c
index fde7cb7..363b541 100644
--- a/drivers/media/radio/radio-iris.c
+++ b/drivers/media/radio/radio-iris.c
@@ -2535,7 +2535,6 @@
case SCAN_FOR_WEAK:
radio->srch_st_list.srch_list_dir = dir;
radio->srch_st_list.srch_list_mode = srch;
- radio->srch_st_list.srch_list_max = 0;
retval = hci_fm_search_station_list(
&radio->srch_st_list, radio->fm_hdev);
break;
@@ -3170,6 +3169,7 @@
radio->srch_rds.srch_pi = ctrl->value;
break;
case V4L2_CID_PRIVATE_IRIS_SRCH_CNT:
+ radio->srch_st_list.srch_list_max = ctrl->value;
break;
case V4L2_CID_PRIVATE_IRIS_SPACING:
if (radio->mode == FM_RECV) {
diff --git a/drivers/media/video/msm_wfd/enc-mfc-subdev.c b/drivers/media/video/msm_wfd/enc-mfc-subdev.c
index d839be3..45532a9 100644
--- a/drivers/media/video/msm_wfd/enc-mfc-subdev.c
+++ b/drivers/media/video/msm_wfd/enc-mfc-subdev.c
@@ -23,6 +23,8 @@
#define VID_ENC_MAX_ENCODER_CLIENTS 1
#define MAX_NUM_CTRLS 20
+#define V4L2_FRAME_FLAGS (V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME | \
+ V4L2_BUF_FLAG_BFRAME | V4L2_QCOM_BUF_FLAG_CODECCONFIG)
static long venc_fill_outbuf(struct v4l2_subdev *sd, void *arg);
@@ -179,6 +181,7 @@
vbuf->v4l2_planes[0].bytesused =
frame_data->data_len;
+ vbuf->v4l2_buf.flags &= ~(V4L2_FRAME_FLAGS);
switch (frame_data->frame) {
case VCD_FRAME_I:
case VCD_FRAME_IDR:
diff --git a/drivers/mfd/pm8821-core.c b/drivers/mfd/pm8821-core.c
index 86bd5ec..fe3e67e 100644
--- a/drivers/mfd/pm8821-core.c
+++ b/drivers/mfd/pm8821-core.c
@@ -312,11 +312,11 @@
drvdata = platform_get_drvdata(pdev);
if (drvdata)
pmic = drvdata->pm_chip_data;
- if (pmic)
- mfd_remove_devices(pmic->dev);
- if (pmic->irq_chip) {
- pm8821_irq_exit(pmic->irq_chip);
- pmic->irq_chip = NULL;
+ if (pmic) {
+ if (pmic->dev)
+ mfd_remove_devices(pmic->dev);
+ if (pmic->irq_chip)
+ pm8821_irq_exit(pmic->irq_chip);
}
platform_set_drvdata(pdev, NULL);
kfree(pmic);
diff --git a/drivers/mfd/pm8xxx-misc.c b/drivers/mfd/pm8xxx-misc.c
index 6bb1441..fb57bd0 100644
--- a/drivers/mfd/pm8xxx-misc.c
+++ b/drivers/mfd/pm8xxx-misc.c
@@ -671,8 +671,7 @@
voltage = chg_config->voltage;
resistor = chg_config->resistor;
- if (resistor < PM8XXX_COINCELL_RESISTOR_2100_OHMS ||
- resistor > PM8XXX_COINCELL_RESISTOR_800_OHMS) {
+ if (resistor > PM8XXX_COINCELL_RESISTOR_800_OHMS) {
pr_err("Invalid resistor value provided\n");
return -EINVAL;
}
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 55ffb17..3715417 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -690,6 +690,7 @@
ion_phys_addr_t pa = 0;
uint32_t len;
struct qseecom_command_scm_resp resp;
+ struct qseecom_check_app_ireq req;
struct qseecom_load_app_ireq load_req;
/* Copy the relevant information needed for loading the image */
@@ -703,88 +704,112 @@
ret = qsee_vote_for_clock(CLK_SFPB);
if (ret)
pr_warning("Unable to vote for SFPB clock");
+ req.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
+ memcpy(req.app_name, load_img_req.img_name, MAX_APP_NAME_SIZE);
- pr_warn("App (%s) does not exist, loading apps for first time\n",
+ ret = __qseecom_check_app_exists(req);
+ if (ret < 0)
+ return ret;
+ else
+ app_id = ret;
+
+ if (app_id) {
+ pr_warn("App id %d (%s) already exists\n", app_id,
+ (char *)(req.app_name));
+ spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
+ list_for_each_entry(entry,
+ &qseecom.registered_app_list_head, list){
+ if (entry->app_id == app_id) {
+ entry->ref_cnt++;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(
+ &qseecom.registered_app_list_lock, flags);
+ } else {
+ pr_warn("App (%s) does'nt exist, loading apps for first time\n",
(char *)(load_img_req.img_name));
- /* Get the handle of the shared fd */
- ihandle = ion_import_dma_buf(qseecom.ion_clnt,
+ /* Get the handle of the shared fd */
+ ihandle = ion_import_dma_buf(qseecom.ion_clnt,
load_img_req.ifd_data_fd);
- if (IS_ERR_OR_NULL(ihandle)) {
- pr_err("Ion client could not retrieve the handle\n");
- qsee_disable_clock_vote(CLK_SFPB);
- return -ENOMEM;
- }
+ if (IS_ERR_OR_NULL(ihandle)) {
+ pr_err("Ion client could not retrieve the handle\n");
+ qsee_disable_clock_vote(CLK_SFPB);
+ return -ENOMEM;
+ }
- /* Get the physical address of the ION BUF */
- ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
+ /* Get the physical address of the ION BUF */
+ ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
- /* Populate the structure for sending scm call to load image */
- memcpy(load_req.app_name, load_img_req.img_name, MAX_APP_NAME_SIZE);
- load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
- load_req.mdt_len = load_img_req.mdt_len;
- load_req.img_len = load_img_req.img_len;
- load_req.phy_addr = pa;
+ /* Populate the structure for sending scm call to load image */
+ memcpy(load_req.app_name, load_img_req.img_name,
+ MAX_APP_NAME_SIZE);
+ load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
+ load_req.mdt_len = load_img_req.mdt_len;
+ load_req.img_len = load_img_req.img_len;
+ load_req.phy_addr = pa;
- /* SCM_CALL to load the app and get the app_id back */
- ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
+ /* SCM_CALL to load the app and get the app_id back */
+ ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
sizeof(struct qseecom_load_app_ireq),
&resp, sizeof(resp));
- if (ret) {
- pr_err("scm_call to load app failed\n");
- return -EINVAL;
- }
-
- if (resp.result == QSEOS_RESULT_FAILURE) {
- pr_err("scm_call rsp.result is QSEOS_RESULT_FAILURE\n");
- if (!IS_ERR_OR_NULL(ihandle))
- ion_free(qseecom.ion_clnt, ihandle);
- qsee_disable_clock_vote(CLK_SFPB);
- return -EFAULT;
- }
-
- if (resp.result == QSEOS_RESULT_INCOMPLETE) {
- ret = __qseecom_process_incomplete_cmd(data, &resp);
if (ret) {
- pr_err("process_incomplete_cmd failed err: %d\n",
- ret);
+ pr_err("scm_call to load app failed\n");
+ return -EINVAL;
+ }
+
+ if (resp.result == QSEOS_RESULT_FAILURE) {
+ pr_err("scm_call rsp.result is QSEOS_RESULT_FAILURE\n");
if (!IS_ERR_OR_NULL(ihandle))
ion_free(qseecom.ion_clnt, ihandle);
qsee_disable_clock_vote(CLK_SFPB);
- return ret;
+ return -EFAULT;
}
- }
- if (resp.result != QSEOS_RESULT_SUCCESS) {
- pr_err("scm_call failed resp.result unknown, %d\n",
+ if (resp.result == QSEOS_RESULT_INCOMPLETE) {
+ ret = __qseecom_process_incomplete_cmd(data, &resp);
+ if (ret) {
+ pr_err("process_incomplete_cmd failed err: %d\n",
+ ret);
+ if (!IS_ERR_OR_NULL(ihandle))
+ ion_free(qseecom.ion_clnt, ihandle);
+ qsee_disable_clock_vote(CLK_SFPB);
+ return ret;
+ }
+ }
+
+ if (resp.result != QSEOS_RESULT_SUCCESS) {
+ pr_err("scm_call failed resp.result unknown, %d\n",
resp.result);
+ if (!IS_ERR_OR_NULL(ihandle))
+ ion_free(qseecom.ion_clnt, ihandle);
+ qsee_disable_clock_vote(CLK_SFPB);
+ return -EFAULT;
+ }
+
+ app_id = resp.data;
+
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry) {
+ pr_err("kmalloc failed\n");
+ qsee_disable_clock_vote(CLK_SFPB);
+ return -ENOMEM;
+ }
+ entry->app_id = app_id;
+ entry->ref_cnt = 1;
+
+ /* Deallocate the handle */
if (!IS_ERR_OR_NULL(ihandle))
ion_free(qseecom.ion_clnt, ihandle);
- qsee_disable_clock_vote(CLK_SFPB);
- return -EFAULT;
- }
- app_id = resp.data;
+ spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
+ list_add_tail(&entry->list, &qseecom.registered_app_list_head);
+ spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
+ flags);
- entry = kmalloc(sizeof(*entry), GFP_KERNEL);
- if (!entry) {
- pr_err("kmalloc failed\n");
- qsee_disable_clock_vote(CLK_SFPB);
- return -ENOMEM;
- }
- entry->app_id = app_id;
- entry->ref_cnt = 1;
-
- /* Deallocate the handle */
- if (!IS_ERR_OR_NULL(ihandle))
- ion_free(qseecom.ion_clnt, ihandle);
-
- spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
- list_add_tail(&entry->list, &qseecom.registered_app_list_head);
- spin_unlock_irqrestore(&qseecom.registered_app_list_lock, flags);
-
- pr_warn("App with id %d (%s) now loaded\n", app_id,
+ pr_warn("App with id %d (%s) now loaded\n", app_id,
(char *)(load_img_req.img_name));
-
+ }
data->client.app_id = app_id;
load_img_req.app_id = app_id;
if (copy_to_user(argp, &load_img_req, sizeof(load_img_req))) {
@@ -1541,18 +1566,18 @@
if (ret)
return -EIO;
- *handle = kzalloc(sizeof(struct qseecom_handle), GFP_KERNEL);
- if (!(*handle)) {
- pr_err("failed to allocate memory for kernel client handle\n");
- return -ENOMEM;
- }
-
app_ireq.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
memcpy(app_ireq.app_name, app_name, MAX_APP_NAME_SIZE);
ret = __qseecom_check_app_exists(app_ireq);
if (ret < 0)
return -EINVAL;
+ *handle = kzalloc(sizeof(struct qseecom_handle), GFP_KERNEL);
+ if (!(*handle)) {
+ pr_err("failed to allocate memory for kernel client handle\n");
+ return -ENOMEM;
+ }
+
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
pr_err("kmalloc failed\n");
diff --git a/drivers/misc/tspp.c b/drivers/misc/tspp.c
index 3b678c5..f310524 100644
--- a/drivers/misc/tspp.c
+++ b/drivers/misc/tspp.c
@@ -327,10 +327,15 @@
int data_inverse;
int sync_inverse;
int enable_inverse;
+ u32 tsif_irq;
/* debugfs */
struct dentry *dent_tsif;
struct dentry *debugfs_tsif_regs[ARRAY_SIZE(debugfs_tsif_regs)];
+ u32 stat_rx;
+ u32 stat_overflow;
+ u32 stat_lost_sync;
+ u32 stat_timeout;
};
enum tspp_buf_state {
@@ -480,6 +485,49 @@
dev_info(&device->pdev->dev, "broken pipe %i", status & 0xffff);
writel_relaxed(status, device->base + TSPP_IRQ_CLEAR);
+
+ /*
+ * Before returning IRQ_HANDLED to the generic interrupt handling
+ * framework need to make sure all operations including clearing of
+ * interrupt status registers in the hardware is performed.
+ * Thus a barrier after clearing the interrupt status register
+ * is required to guarantee that the interrupt status register has
+ * really been cleared by the time we return from this handler.
+ */
+ wmb();
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t tsif_isr(int irq, void *dev)
+{
+ struct tspp_tsif_device *tsif_device = dev;
+ u32 sts_ctl = ioread32(tsif_device->base + TSIF_STS_CTL_OFF);
+
+ if (!(sts_ctl & (TSIF_STS_CTL_PACK_AVAIL |
+ TSIF_STS_CTL_OVERFLOW |
+ TSIF_STS_CTL_LOST_SYNC |
+ TSIF_STS_CTL_TIMEOUT)))
+ return IRQ_NONE;
+
+ if (sts_ctl & TSIF_STS_CTL_OVERFLOW)
+ tsif_device->stat_overflow++;
+
+ if (sts_ctl & TSIF_STS_CTL_LOST_SYNC)
+ tsif_device->stat_lost_sync++;
+
+ if (sts_ctl & TSIF_STS_CTL_TIMEOUT)
+ tsif_device->stat_timeout++;
+
+ iowrite32(sts_ctl, tsif_device->base + TSIF_STS_CTL_OFF);
+
+ /*
+ * Before returning IRQ_HANDLED to the generic interrupt handling
+ * framework need to make sure all operations including clearing of
+ * interrupt status registers in the hardware is performed.
+ * Thus a barrier after clearing the interrupt status register
+ * is required to guarantee that the interrupt status register has
+ * really been cleared by the time we return from this handler.
+ */
wmb();
return IRQ_HANDLED;
}
@@ -527,6 +575,11 @@
channel->waiting->filled = iovec.size;
channel->waiting->read_index = 0;
+ if (channel->src == TSPP_SOURCE_TSIF0)
+ device->tsif[0].stat_rx++;
+ else if (channel->src == TSPP_SOURCE_TSIF1)
+ device->tsif[1].stat_rx++;
+
/* update the pointers */
channel->waiting = channel->waiting->next;
}
@@ -2326,6 +2379,31 @@
base + debugfs_tsif_regs[i].offset,
&fops_iomem_x32);
}
+
+ debugfs_create_u32(
+ "stat_rx_chunks",
+ S_IRUGO|S_IWUGO,
+ tsif_device->dent_tsif,
+ &tsif_device->stat_rx);
+
+ debugfs_create_u32(
+ "stat_overflow",
+ S_IRUGO|S_IWUGO,
+ tsif_device->dent_tsif,
+ &tsif_device->stat_overflow);
+
+ debugfs_create_u32(
+ "stat_lost_sync",
+ S_IRUGO|S_IWUGO,
+ tsif_device->dent_tsif,
+ &tsif_device->stat_lost_sync);
+
+ debugfs_create_u32(
+ "stat_timeout",
+ S_IRUGO|S_IWUGO,
+ tsif_device->dent_tsif,
+ &tsif_device->stat_timeout);
+
}
}
@@ -2504,6 +2582,21 @@
goto err_irq;
}
+ /* map TSIF IRQs */
+ device->tsif[0].tsif_irq = TSIF1_IRQ;
+ device->tsif[1].tsif_irq = TSIF2_IRQ;
+
+ for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
+ rc = request_irq(device->tsif[i].tsif_irq,
+ tsif_isr, IRQF_SHARED,
+ dev_name(&pdev->dev), &device->tsif[i]);
+ if (rc) {
+ dev_warn(&pdev->dev, "failed to request TSIF%d IRQ: %d",
+ i, rc);
+ device->tsif[i].tsif_irq = 0;
+ }
+ }
+
/* BAM IRQ */
device->bam_irq = TSIF_BAM_IRQ;
@@ -2635,8 +2728,11 @@
sps_deregister_bam_device(device->bam_handle);
- for (i = 0; i < TSPP_TSIF_INSTANCES; i++)
+ for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
tsif_debugfs_exit(&device->tsif[i]);
+ if (device->tsif[i].tsif_irq)
+ free_irq(device->tsif[i].tsif_irq, &device->tsif[i]);
+ }
wake_lock_destroy(&device->wake_lock);
free_irq(device->tspp_irq, device);
diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig
index 33f0600..a1bea00 100644
--- a/drivers/mmc/card/Kconfig
+++ b/drivers/mmc/card/Kconfig
@@ -80,6 +80,7 @@
config MMC_BLOCK_TEST
tristate "MMC block test"
depends on MMC_BLOCK && IOSCHED_TEST
+ default y
help
MMC block test can be used with test iosched to test the MMC block
device.
diff --git a/drivers/mmc/card/mmc_block_test.c b/drivers/mmc/card/mmc_block_test.c
index 35bb4ac..610a822 100644
--- a/drivers/mmc/card/mmc_block_test.c
+++ b/drivers/mmc/card/mmc_block_test.c
@@ -1702,6 +1702,12 @@
(bkops_stat->suspend == 0) &&
(bkops_stat->hpi == 1))
goto exit;
+ /* this might happen due to timing issues */
+ else if
+ ((bkops_stat->bkops_level[BKOPS_SEVERITY_1_INDEX] == 0) &&
+ (bkops_stat->suspend == 0) &&
+ (bkops_stat->hpi == 0))
+ goto ignore;
else
goto fail;
break;
@@ -1735,6 +1741,9 @@
exit:
return 0;
+ignore:
+ test_iosched_set_ignore_round(true);
+ return 0;
fail:
if (td->fs_wr_reqs_during_test) {
test_pr_info("%s: wr reqs during test, cancel the round",
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index de7e5bc..4f7d4c3 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -46,6 +46,7 @@
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/pm_qos.h>
+#include <linux/iopoll.h>
#include <asm/cacheflush.h>
#include <asm/div64.h>
@@ -348,23 +349,23 @@
* SDCC controller itself can support hard reset.
*/
if (is_sw_hard_reset(host)) {
- ktime_t start;
+ u32 pwr;
writel_relaxed(readl_relaxed(host->base + MMCIPOWER)
| MCI_SW_RST, host->base + MMCIPOWER);
msmsdcc_sync_reg_wr(host);
- start = ktime_get();
- while (readl_relaxed(host->base + MMCIPOWER) & MCI_SW_RST) {
- /*
- * See comment in msmsdcc_soft_reset() on choosing 1ms
- * poll timeout.
- */
- if (ktime_to_us(ktime_sub(ktime_get(), start)) > 1000) {
- pr_err("%s: %s failed\n",
- mmc_hostname(host->mmc), __func__);
- BUG();
- }
+ /*
+ * See comment in msmsdcc_soft_reset() on choosing 1ms
+ * poll timeout.
+ */
+ ret = readl_poll_timeout_noirq(host->base + MMCIPOWER,
+ pwr, !(pwr & MCI_SW_RST), 100, 10);
+
+ if (ret) {
+ pr_err("%s: %s failed (%d)\n",
+ mmc_hostname(host->mmc), __func__, ret);
+ BUG();
}
} else {
ret = clk_reset(host->clk, CLK_RESET_ASSERT);
@@ -470,7 +471,9 @@
readl_relaxed(host->base + MMCISTATUS)
& MCI_TXACTIVE ? "TX" : "RX");
msmsdcc_dump_sdcc_state(host);
- BUG();
+ msmsdcc_reset_and_restore(host);
+ host->pending_dpsm_reset = false;
+ goto out;
}
}
}
diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c
index c43abca..5cace6b 100644
--- a/drivers/mtd/devices/msm_nand.c
+++ b/drivers/mtd/devices/msm_nand.c
@@ -6757,6 +6757,23 @@
return 0;
}
+static const unsigned int bch_sup_cntrl[] = {
+ 0x307, /* MSM7x2xA */
+ 0x4030, /* MDM 9x15 */
+};
+
+static inline bool msm_nand_has_bch_ecc_engine(unsigned int hw_id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bch_sup_cntrl); i++) {
+ if (hw_id == bch_sup_cntrl[i])
+ return true;
+ }
+
+ return false;
+}
+
/**
* msm_nand_scan - [msm_nand Interface] Scan for the msm_nand device
* @param mtd MTD device structure
@@ -6778,6 +6795,7 @@
uint32_t devcfg;
struct nand_flash_dev *flashdev = NULL;
struct nand_manufacturers *flashman = NULL;
+ unsigned int hw_id;
/* Probe the Flash device for ONFI compliance */
if (!flash_onfi_probe(chip)) {
@@ -6845,7 +6863,8 @@
mtd_writesize = mtd->writesize >> 1;
/* Check whether controller and NAND device support 8bit ECC*/
- if ((flash_rd_reg(chip, MSM_NAND_HW_INFO) == 0x307)
+ hw_id = flash_rd_reg(chip, MSM_NAND_HW_INFO);
+ if (msm_nand_has_bch_ecc_engine(hw_id)
&& (supported_flash.ecc_correctability >= 8)) {
pr_info("Found supported NAND device for %dbit ECC\n",
supported_flash.ecc_correctability);
@@ -6853,7 +6872,8 @@
} else {
pr_info("Found a supported NAND device\n");
}
- pr_info("NAND Id : 0x%x\n", supported_flash.flash_id);
+ pr_info("NAND Controller ID : 0x%x\n", hw_id);
+ pr_info("NAND Device ID : 0x%x\n", supported_flash.flash_id);
pr_info("Buswidth : %d Bits\n", (wide_bus) ? 16 : 8);
pr_info("Density : %lld MByte\n", (mtd->size>>20));
pr_info("Pagesize : %d Bytes\n", mtd->writesize);
diff --git a/drivers/mtd/devices/msm_qpic_nand.c b/drivers/mtd/devices/msm_qpic_nand.c
index d709e17..9a6cc80 100644
--- a/drivers/mtd/devices/msm_qpic_nand.c
+++ b/drivers/mtd/devices/msm_qpic_nand.c
@@ -2351,7 +2351,7 @@
{
struct msm_nand_info *info;
struct resource *res;
- int err, n_parts;
+ int err;
struct device_node *pnode;
struct mtd_part_parser_data parser_data;
@@ -2443,10 +2443,10 @@
goto free_bam;
}
parser_data.of_node = pnode;
- n_parts = mtd_device_parse_register(&info->mtd, NULL, &parser_data,
+ err = mtd_device_parse_register(&info->mtd, NULL, &parser_data,
NULL, 0);
- if (n_parts < 0) {
- pr_err("Unable to register MTD partitions %d\n", n_parts);
+ if (err < 0) {
+ pr_err("Unable to register MTD partitions %d\n", err);
goto free_bam;
}
dev_set_drvdata(&pdev->dev, info);
@@ -2455,7 +2455,6 @@
info->nand_phys, info->bam_phys, info->bam_irq);
pr_info("Allocated DMA buffer at virt_addr 0x%p, phys_addr 0x%x\n",
info->nand_chip.dma_virt_addr, info->nand_chip.dma_phys_addr);
- pr_info("Found %d MTD partitions\n", n_parts);
goto out;
free_bam:
msm_nand_bam_free(info);
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index 6e42fdb..71a9860 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -566,8 +566,6 @@
void __iomem *pmu_spare_reg;
u32 reg = 0;
unsigned long flags;
- struct clk *cxo = clk_get(&penv->pdev->dev, "cxo");
- int rc = 0;
if (!enable_wcnss_suspend_notify)
return;
@@ -576,18 +574,12 @@
return;
/* For Riva */
- rc = clk_prepare_enable(cxo);
- if (rc) {
- pr_err("cxo enable failed\n");
- return;
- }
pmu_spare_reg = penv->msm_wcnss_base + RIVA_SPARE_OFFSET;
spin_lock_irqsave(®_spinlock, flags);
reg = readl_relaxed(pmu_spare_reg);
reg |= RIVA_SUSPEND_BIT;
writel_relaxed(reg, pmu_spare_reg);
spin_unlock_irqrestore(®_spinlock, flags);
- clk_disable_unprepare(cxo);
}
EXPORT_SYMBOL(wcnss_suspend_notify);
@@ -596,8 +588,6 @@
void __iomem *pmu_spare_reg;
u32 reg = 0;
unsigned long flags;
- struct clk *cxo = clk_get(&penv->pdev->dev, "cxo");
- int rc = 0;
if (!enable_wcnss_suspend_notify)
return;
@@ -608,17 +598,11 @@
/* For Riva */
pmu_spare_reg = penv->msm_wcnss_base + RIVA_SPARE_OFFSET;
- rc = clk_prepare_enable(cxo);
- if (rc) {
- pr_err("cxo enable failed\n");
- return;
- }
spin_lock_irqsave(®_spinlock, flags);
reg = readl_relaxed(pmu_spare_reg);
reg &= ~RIVA_SUSPEND_BIT;
writel_relaxed(reg, pmu_spare_reg);
spin_unlock_irqrestore(®_spinlock, flags);
- clk_disable_unprepare(cxo);
}
EXPORT_SYMBOL(wcnss_resume_notify);
@@ -858,7 +842,6 @@
else
wcnss_gpios_config(penv->gpios_5wire, false);
fail_gpio_res:
- kfree(penv);
penv = NULL;
return ret;
}
@@ -899,7 +882,7 @@
}
/* create an environment to track the device */
- penv = kzalloc(sizeof(*penv), GFP_KERNEL);
+ penv = devm_kzalloc(&pdev->dev, sizeof(*penv), GFP_KERNEL);
if (!penv) {
dev_err(&pdev->dev, "cannot allocate device memory.\n");
return -ENOMEM;
@@ -908,8 +891,10 @@
/* register sysfs entries */
ret = wcnss_create_sysfs(&pdev->dev);
- if (ret)
+ if (ret) {
+ penv = NULL;
return -ENOENT;
+ }
#ifdef MODULE
@@ -944,6 +929,7 @@
wcnss_wlan_remove(struct platform_device *pdev)
{
wcnss_remove_sysfs(&pdev->dev);
+ penv = NULL;
return 0;
}
@@ -997,7 +983,6 @@
subsystem_put(penv->pil);
- kfree(penv);
penv = NULL;
}
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index bf78b1c..8f97531 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -23,11 +23,12 @@
#include <linux/usb/msm_hsusb.h>
#include <mach/usb_bam.h>
#include <mach/sps.h>
+#include <mach/ipa.h>
#include <linux/workqueue.h>
#include <linux/dma-mapping.h>
#define USB_SUMMING_THRESHOLD 512
-#define CONNECTIONS_NUM 4
+#define CONNECTIONS_NUM 8
static struct sps_bam_props usb_props;
static struct sps_pipe *sps_pipes[CONNECTIONS_NUM][2];
@@ -49,7 +50,8 @@
u32 *src_pipe;
u32 *dst_pipe;
struct usb_bam_wake_event_info peer_event;
- bool enabled;
+ bool src_enabled;
+ bool dst_enabled;
};
static struct usb_bam_connect_info usb_bam_connections[CONNECTIONS_NUM];
@@ -206,7 +208,7 @@
ret = sps_connect(*pipe, connection);
if (ret < 0) {
- pr_err("%s: tx connect error %d\n", __func__, ret);
+ pr_err("%s: sps_connect failed %d\n", __func__, ret);
goto error;
}
return 0;
@@ -218,9 +220,119 @@
return ret;
}
+static int connect_pipe_ipa(
+ struct usb_bam_connect_ipa_params *connection_params)
+{
+ int ret;
+ u8 conn_idx = connection_params->idx;
+ enum usb_bam_pipe_dir pipe_dir = connection_params->dir;
+ struct sps_pipe **pipe = &sps_pipes[conn_idx][pipe_dir];
+ struct sps_connect *connection =
+ &sps_connections[conn_idx][pipe_dir];
+ struct msm_usb_bam_platform_data *pdata =
+ usb_bam_pdev->dev.platform_data;
+ struct usb_bam_pipe_connect *pipe_connection =
+ &msm_usb_bam_connections_info
+ [pdata->usb_active_bam][conn_idx][pipe_dir];
-static int disconnect_pipe(u8 connection_idx, enum usb_bam_pipe_dir pipe_dir,
- u32 *usb_pipe_idx)
+ struct ipa_connect_params ipa_in_params;
+ struct ipa_sps_params sps_out_params;
+ u32 usb_handle, usb_phy_addr;
+ u32 clnt_hdl = 0;
+
+ memset(&ipa_in_params, 0, sizeof(ipa_in_params));
+ memset(&sps_out_params, 0, sizeof(sps_out_params));
+
+ if (pipe_dir == USB_TO_PEER_PERIPHERAL) {
+ usb_phy_addr = pipe_connection->src_phy_addr;
+ ipa_in_params.client_ep_idx = pipe_connection->src_pipe_index;
+ } else {
+ usb_phy_addr = pipe_connection->dst_phy_addr;
+ ipa_in_params.client_ep_idx = pipe_connection->dst_pipe_index;
+ }
+ /* Get HSUSB / HSIC bam handle */
+ ret = sps_phy2h(usb_phy_addr, &usb_handle);
+ if (ret) {
+ pr_err("%s: sps_phy2h failed (HSUSB/HSIC BAM) %d\n",
+ __func__, ret);
+ goto get_config_failed;
+ }
+
+ /* IPA input parameters */
+ ipa_in_params.client_bam_hdl = usb_handle;
+ ipa_in_params.desc_fifo_sz = pipe_connection->desc_fifo_size;
+ ipa_in_params.data_fifo_sz = pipe_connection->data_fifo_size;
+ ipa_in_params.notify = connection_params->notify;
+ ipa_in_params.priv = connection_params->priv;
+ ipa_in_params.client = connection_params->client;
+ if (pipe_connection->mem_type != SYSTEM_MEM)
+ ipa_in_params.pipe_mem_preferred = true;
+
+ memcpy(&ipa_in_params.ipa_ep_cfg, &connection_params->ipa_ep_cfg,
+ sizeof(struct ipa_ep_cfg));
+
+ ret = ipa_connect(&ipa_in_params, &sps_out_params, &clnt_hdl);
+ if (ret) {
+ pr_err("%s: ipa_connect failed\n", __func__);
+ return ret;
+ }
+
+ *pipe = sps_alloc_endpoint();
+ if (*pipe == NULL) {
+ pr_err("%s: sps_alloc_endpoint failed\n", __func__);
+ ret = -ENOMEM;
+ goto disconnect_ipa;
+ }
+
+ ret = sps_get_config(*pipe, connection);
+ if (ret) {
+ pr_err("%s: tx get config failed %d\n", __func__, ret);
+ goto get_config_failed;
+ }
+
+ if (pipe_dir == USB_TO_PEER_PERIPHERAL) {
+ /* USB src IPA dest */
+ connection->mode = SPS_MODE_SRC;
+ connection_params->cons_clnt_hdl = clnt_hdl;
+ connection->source = usb_handle;
+ connection->destination = sps_out_params.ipa_bam_hdl;
+ connection->src_pipe_index = pipe_connection->src_pipe_index;
+ connection->dest_pipe_index = sps_out_params.ipa_ep_idx;
+ *(connection_params->src_pipe) = connection->src_pipe_index;
+ } else {
+ /* IPA src, USB dest */
+ connection->mode = SPS_MODE_DEST;
+ connection_params->prod_clnt_hdl = clnt_hdl;
+ connection->source = sps_out_params.ipa_bam_hdl;
+ connection->destination = usb_handle;
+ connection->src_pipe_index = sps_out_params.ipa_ep_idx;
+ connection->dest_pipe_index = pipe_connection->dst_pipe_index;
+ *(connection_params->dst_pipe) = connection->dest_pipe_index;
+ }
+
+ connection->data = sps_out_params.data;
+ connection->desc = sps_out_params.desc;
+ connection->event_thresh = 16;
+ connection->options = SPS_O_AUTO_ENABLE;
+
+ ret = sps_connect(*pipe, connection);
+ if (ret < 0) {
+ pr_err("%s: sps_connect failed %d\n", __func__, ret);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ sps_disconnect(*pipe);
+get_config_failed:
+ sps_free_endpoint(*pipe);
+disconnect_ipa:
+ ipa_disconnect(clnt_hdl);
+ return ret;
+}
+
+static int disconnect_pipe(u8 connection_idx, enum usb_bam_pipe_dir pipe_dir)
{
struct msm_usb_bam_platform_data *pdata =
usb_bam_pdev->dev.platform_data;
@@ -269,7 +381,7 @@
return -EINVAL;
}
- if (connection->enabled) {
+ if (connection->src_enabled && connection->dst_enabled) {
pr_debug("%s: connection %d was already established\n",
__func__, idx);
return 0;
@@ -281,22 +393,66 @@
if (src_pipe_idx) {
/* open USB -> Peripheral pipe */
ret = connect_pipe(connection->idx, USB_TO_PEER_PERIPHERAL,
- connection->src_pipe);
+ connection->src_pipe);
if (ret) {
pr_err("%s: src pipe connection failure\n", __func__);
return ret;
}
}
+ connection->src_enabled = 1;
+
if (dst_pipe_idx) {
/* open Peripheral -> USB pipe */
ret = connect_pipe(connection->idx, PEER_PERIPHERAL_TO_USB,
- connection->dst_pipe);
+ connection->dst_pipe);
if (ret) {
pr_err("%s: dst pipe connection failure\n", __func__);
return ret;
}
}
- connection->enabled = 1;
+ connection->dst_enabled = 1;
+
+ return 0;
+}
+
+int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
+{
+ u8 idx = ipa_params->idx;
+ struct usb_bam_connect_info *connection = &usb_bam_connections[idx];
+ int ret;
+
+ if (idx >= CONNECTIONS_NUM) {
+ pr_err("%s: Invalid connection index\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if ((connection->src_enabled &&
+ ipa_params->dir == USB_TO_PEER_PERIPHERAL) ||
+ (connection->dst_enabled &&
+ ipa_params->dir == PEER_PERIPHERAL_TO_USB)) {
+ pr_debug("%s: connection %d was already established\n",
+ __func__, idx);
+ return 0;
+ }
+
+ if (ipa_params->dir == USB_TO_PEER_PERIPHERAL)
+ connection->src_pipe = ipa_params->src_pipe;
+ else
+ connection->dst_pipe = ipa_params->dst_pipe;
+
+ connection->idx = idx;
+
+ ret = connect_pipe_ipa(ipa_params);
+ if (ret) {
+ pr_err("%s: dst pipe connection failure\n", __func__);
+ return ret;
+ }
+
+ if (ipa_params->dir == USB_TO_PEER_PERIPHERAL)
+ connection->src_enabled = 1;
+ else
+ connection->dst_enabled = 1;
return 0;
}
@@ -364,39 +520,78 @@
return -EINVAL;
}
- if (!connection->enabled) {
+ if (!connection->src_enabled && !connection->dst_enabled) {
pr_debug("%s: connection %d isn't enabled\n",
__func__, idx);
return 0;
}
- if (connection->src_pipe) {
+ if (connection->src_enabled) {
/* close USB -> Peripheral pipe */
- ret = disconnect_pipe(connection->idx, USB_TO_PEER_PERIPHERAL,
- connection->src_pipe);
+ ret = disconnect_pipe(connection->idx, USB_TO_PEER_PERIPHERAL);
if (ret) {
pr_err("%s: src pipe connection failure\n", __func__);
return ret;
}
-
+ connection->src_enabled = 0;
}
- if (connection->dst_pipe) {
+ if (connection->dst_enabled) {
/* close Peripheral -> USB pipe */
- ret = disconnect_pipe(connection->idx, PEER_PERIPHERAL_TO_USB,
- connection->dst_pipe);
+ ret = disconnect_pipe(connection->idx, PEER_PERIPHERAL_TO_USB);
if (ret) {
pr_err("%s: dst pipe connection failure\n", __func__);
return ret;
}
+ connection->dst_enabled = 0;
}
connection->src_pipe = 0;
connection->dst_pipe = 0;
- connection->enabled = 0;
return 0;
}
+int usb_bam_disconnect_ipa(u8 idx,
+ struct usb_bam_connect_ipa_params *ipa_params)
+{
+ struct usb_bam_connect_info *connection = &usb_bam_connections[idx];
+ int ret;
+ if (!usb_bam_pdev) {
+ pr_err("%s: usb_bam device not found\n", __func__);
+ return -ENODEV;
+ }
+
+ if (idx >= CONNECTIONS_NUM) {
+ pr_err("%s: Invalid connection index\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* Currently just calls ipa_disconnect, no sps pipes
+ disconenction support */
+
+ /* close IPA -> USB pipe */
+ if (connection->dst_pipe) {
+ ret = ipa_disconnect(ipa_params->prod_clnt_hdl);
+ if (ret) {
+ pr_err("%s: dst pipe disconnection failure\n",
+ __func__);
+ return ret;
+ }
+ }
+ /* close USB -> IPA pipe */
+ if (connection->src_pipe) {
+ ret = ipa_disconnect(ipa_params->cons_clnt_hdl);
+ if (ret) {
+ pr_err("%s: src pipe disconnection failure\n",
+ __func__);
+ return ret;
+ }
+ }
+
+ return 0;
+
+}
static int update_connections_info(struct device_node *node, int bam,
int conn_num, int dir, enum usb_pipe_mem_type mem_type)
{
@@ -412,33 +607,28 @@
key = "qcom,src-bam-physical-address";
rc = of_property_read_u32(node, key, &val);
- if (rc)
- goto err;
- pipe_connection->src_phy_addr = val;
+ if (!rc)
+ pipe_connection->src_phy_addr = val;
key = "qcom,src-bam-pipe-index";
rc = of_property_read_u32(node, key, &val);
- if (rc)
- goto err;
- pipe_connection->src_pipe_index = val;
+ if (!rc)
+ pipe_connection->src_pipe_index = val;
key = "qcom,dst-bam-physical-address";
rc = of_property_read_u32(node, key, &val);
- if (rc)
- goto err;
- pipe_connection->dst_phy_addr = val;
+ if (!rc)
+ pipe_connection->dst_phy_addr = val;
key = "qcom,dst-bam-pipe-index";
rc = of_property_read_u32(node, key, &val);
- if (rc)
- goto err;
- pipe_connection->dst_pipe_index = val;
+ if (!rc)
+ pipe_connection->dst_pipe_index = val;
key = "qcom,data-fifo-offset";
rc = of_property_read_u32(node, key, &val);
- if (rc)
- goto err;
- pipe_connection->data_fifo_base_offset = val;
+ if (!rc)
+ pipe_connection->data_fifo_base_offset = val;
key = "qcom,data-fifo-size";
rc = of_property_read_u32(node, key, &val);
@@ -448,9 +638,8 @@
key = "qcom,descriptor-fifo-offset";
rc = of_property_read_u32(node, key, &val);
- if (rc)
- goto err;
- pipe_connection->desc_fifo_base_offset = val;
+ if (!rc)
+ pipe_connection->desc_fifo_base_offset = val;
key = "qcom,descriptor-fifo-size";
rc = of_property_read_u32(node, key, &val);
@@ -539,10 +728,8 @@
rc = of_property_read_u32(node, "qcom,usb-base-address",
&pdata->usb_base_address);
- if (rc) {
- pr_err("Invalid usb base address property\n");
- return NULL;
- }
+ if (rc)
+ pr_debug("%s: Invalid usb base address property\n", __func__);
pdata->ignore_core_reset_ack = of_property_read_bool(node,
"qcom,ignore-core-reset-ack");
@@ -588,22 +775,28 @@
if (rc)
goto err;
+ if (mem_type == USB_PRIVATE_MEM &&
+ !pdata->usb_base_address)
+ goto err;
+
rc = of_property_read_string(node, "label", &str);
if (rc) {
pr_err("Cannot read string\n");
goto err;
}
- if (strstr(str, "usb-to-peri"))
+ if (strnstr(str, "usb-to", 30))
dir = USB_TO_PEER_PERIPHERAL;
- else if (strstr(str, "peri-to-usb"))
+ else if (strnstr(str, "to-usb", 30))
dir = PEER_PERIPHERAL_TO_USB;
else
goto err;
- /* Check if connection type is suported */
+ /* Check if connection type is supported */
if (!strcmp(str, "usb-to-peri-qdss-dwc3") ||
!strcmp(str, "peri-to-usb-qdss-dwc3") ||
+ !strcmp(str, "usb-to-ipa") ||
+ !strcmp(str, "ipa-to-usb") ||
!strcmp(str, "usb-to-peri-qdss-hsusb") ||
!strcmp(str, "peri-to-usb-qdss-hsusb"))
conn_num = 0;
@@ -772,7 +965,8 @@
dev_dbg(&pdev->dev, "usb_bam_probe\n");
for (i = 0; i < CONNECTIONS_NUM; i++) {
- usb_bam_connections[i].enabled = 0;
+ usb_bam_connections[i].src_enabled = 0;
+ usb_bam_connections[i].dst_enabled = 0;
INIT_WORK(&usb_bam_connections[i].peer_event.wake_w,
usb_bam_wake_work);
}
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
index 8dbdfa3..3995cf7 100644
--- a/drivers/power/pm8921-charger.c
+++ b/drivers/power/pm8921-charger.c
@@ -947,6 +947,11 @@
break;
}
+ if (i < 0) {
+ pr_err("can't find %d in usb_ma_table. Use min.\n", temp);
+ i = 0;
+ }
+
*mA = usb_ma_table[i].usb_ma;
return rc;
@@ -1126,31 +1131,6 @@
PM8921_CHG_LED_SRC_CONFIG_MASK, temp);
}
-static void enable_input_voltage_regulation(struct pm8921_chg_chip *chip)
-{
- u8 temp;
- int rc;
-
- rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0x70);
- if (rc) {
- pr_err("Failed to write 0x70 to CTRL_TEST3 rc = %d\n", rc);
- return;
- }
- rc = pm8xxx_readb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, &temp);
- if (rc) {
- pr_err("Failed to read CTRL_TEST3 rc = %d\n", rc);
- return;
- }
- /* unset the input voltage disable bit */
- temp &= 0xFE;
- /* set the write bit */
- temp |= 0x80;
- rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, temp);
- if (rc) {
- pr_err("Failed to write 0x%x to CTRL_TEST3 rc=%d\n", temp, rc);
- return;
- }
-}
static int64_t read_battery_id(struct pm8921_chg_chip *chip)
{
@@ -1534,6 +1514,34 @@
return pm_chg_get_rt_status(chip, BATT_INSERTED_IRQ);
}
+static int get_prop_batt_status(struct pm8921_chg_chip *chip)
+{
+ int batt_state = POWER_SUPPLY_STATUS_DISCHARGING;
+ int fsm_state = pm_chg_get_fsm_state(chip);
+ int i;
+
+ if (chip->ext_psy) {
+ if (chip->ext_charge_done)
+ return POWER_SUPPLY_STATUS_FULL;
+ if (chip->ext_charging)
+ return POWER_SUPPLY_STATUS_CHARGING;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(map); i++)
+ if (map[i].fsm_state == fsm_state)
+ batt_state = map[i].batt_state;
+
+ if (fsm_state == FSM_STATE_ON_CHG_HIGHI_1) {
+ if (!pm_chg_get_rt_status(chip, BATT_INSERTED_IRQ)
+ || !pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ)
+ || pm_chg_get_rt_status(chip, CHGHOT_IRQ)
+ || pm_chg_get_rt_status(chip, VBATDET_LOW_IRQ))
+
+ batt_state = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ }
+ return batt_state;
+}
+
static int get_prop_batt_capacity(struct pm8921_chg_chip *chip)
{
int percent_soc;
@@ -1553,8 +1561,8 @@
pr_warn_ratelimited("low battery charge = %d%%\n",
percent_soc);
- if (chip->recent_reported_soc == (chip->resume_charge_percent + 1)
- && percent_soc == chip->resume_charge_percent) {
+ if (percent_soc <= chip->resume_charge_percent
+ && get_prop_batt_status(chip) == POWER_SUPPLY_STATUS_FULL) {
pr_debug("soc fell below %d. charging enabled.\n",
chip->resume_charge_percent);
if (chip->is_bat_warm)
@@ -1683,34 +1691,6 @@
return POWER_SUPPLY_CHARGE_TYPE_NONE;
}
-static int get_prop_batt_status(struct pm8921_chg_chip *chip)
-{
- int batt_state = POWER_SUPPLY_STATUS_DISCHARGING;
- int fsm_state = pm_chg_get_fsm_state(chip);
- int i;
-
- if (chip->ext_psy) {
- if (chip->ext_charge_done)
- return POWER_SUPPLY_STATUS_FULL;
- if (chip->ext_charging)
- return POWER_SUPPLY_STATUS_CHARGING;
- }
-
- for (i = 0; i < ARRAY_SIZE(map); i++)
- if (map[i].fsm_state == fsm_state)
- batt_state = map[i].batt_state;
-
- if (fsm_state == FSM_STATE_ON_CHG_HIGHI_1) {
- if (!pm_chg_get_rt_status(chip, BATT_INSERTED_IRQ)
- || !pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ)
- || pm_chg_get_rt_status(chip, CHGHOT_IRQ)
- || pm_chg_get_rt_status(chip, VBATDET_LOW_IRQ))
-
- batt_state = POWER_SUPPLY_STATUS_NOT_CHARGING;
- }
- return batt_state;
-}
-
#define MAX_TOLERABLE_BATT_TEMP_DDC 680
static int get_prop_batt_temp(struct pm8921_chg_chip *chip)
{
@@ -1832,7 +1812,7 @@
return;
}
- if (mA >= 0 && mA <= 2) {
+ if (mA <= 2) {
usb_chg_current = 0;
rc = pm_chg_iusbmax_set(the_chip, 0);
if (rc) {
@@ -1850,6 +1830,12 @@
break;
}
+ if (i < 0) {
+ pr_err("can't find %dmA in usb_ma_table. Use min.\n",
+ mA);
+ i = 0;
+ }
+
/* Check if IUSB_FINE_RES is available */
while ((usb_ma_table[i].value & PM8917_IUSB_FINE_RES)
&& !the_chip->iusb_fine_res)
@@ -2265,7 +2251,6 @@
usb_target_ma = 0;
pm8921_chg_disable_irq(chip, CHG_GONE_IRQ);
}
- enable_input_voltage_regulation(chip);
bms_notify_check(chip);
}
@@ -2547,6 +2532,13 @@
while (!the_chip->iusb_fine_res && i > 0
&& (usb_ma_table[i].value & PM8917_IUSB_FINE_RES))
i--;
+
+ if (i < 0) {
+ pr_err("can't find %dmA in usb_ma_table. Use min.\n",
+ *value);
+ i = 0;
+ }
+
*value = usb_ma_table[i].usb_ma;
}
}
@@ -3235,6 +3227,22 @@
pm_chg_vddmax_set(chip, adj_vdd_max_mv);
}
+static void set_appropriate_vbatdet(struct pm8921_chg_chip *chip)
+{
+ if (chip->is_bat_cool)
+ pm_chg_vbatdet_set(the_chip,
+ the_chip->cool_bat_voltage
+ - the_chip->resume_voltage_delta);
+ else if (chip->is_bat_warm)
+ pm_chg_vbatdet_set(the_chip,
+ the_chip->warm_bat_voltage
+ - the_chip->resume_voltage_delta);
+ else
+ pm_chg_vbatdet_set(the_chip,
+ the_chip->max_voltage_mv
+ - the_chip->resume_voltage_delta);
+}
+
enum {
CHG_IN_PROGRESS,
CHG_NOT_IN_PROGRESS,
@@ -3373,8 +3381,7 @@
if (end == CHG_NOT_IN_PROGRESS) {
count = 0;
- wake_unlock(&chip->eoc_wake_lock);
- return;
+ goto eoc_worker_stop;
}
/* If the disable hw clock switching
@@ -3398,21 +3405,6 @@
if (count == CONSECUTIVE_COUNT) {
count = 0;
pr_info("End of Charging\n");
- /* set the vbatdet back, in case it was changed
- * to trigger charging */
- if (chip->is_bat_cool) {
- pm_chg_vbatdet_set(the_chip,
- the_chip->cool_bat_voltage
- - the_chip->resume_voltage_delta);
- } else if (chip->is_bat_warm) {
- pm_chg_vbatdet_set(the_chip,
- the_chip->warm_bat_voltage
- - the_chip->resume_voltage_delta);
- } else {
- pm_chg_vbatdet_set(the_chip,
- the_chip->max_voltage_mv
- - the_chip->resume_voltage_delta);
- }
pm_chg_auto_enable(chip, 0);
@@ -3425,14 +3417,19 @@
chip->bms_notify.is_battery_full = 1;
/* declare end of charging by invoking chgdone interrupt */
chgdone_irq_handler(chip->pmic_chg_irq[CHGDONE_IRQ], chip);
- wake_unlock(&chip->eoc_wake_lock);
} else {
adjust_vdd_max_for_fastchg(chip, vbat_batt_terminal_uv);
pr_debug("EOC count = %d\n", count);
schedule_delayed_work(&chip->eoc_work,
round_jiffies_relative(msecs_to_jiffies
(EOC_CHECK_PERIOD_MS)));
+ return;
}
+
+eoc_worker_stop:
+ wake_unlock(&chip->eoc_wake_lock);
+ /* set the vbatdet back, in case it was changed to trigger charging */
+ set_appropriate_vbatdet(chip);
}
static void btm_configure_work(struct work_struct *work)
@@ -3473,19 +3470,13 @@
if (enter) {
btm_config.low_thr_temp =
the_chip->cool_temp_dc + TEMP_HYSTERISIS_DEGC;
- set_appropriate_battery_current(the_chip);
pm_chg_vddmax_set(the_chip, the_chip->cool_bat_voltage);
- pm_chg_vbatdet_set(the_chip,
- the_chip->cool_bat_voltage
- - the_chip->resume_voltage_delta);
} else {
btm_config.low_thr_temp = the_chip->cool_temp_dc;
- set_appropriate_battery_current(the_chip);
pm_chg_vddmax_set(the_chip, the_chip->max_voltage_mv);
- pm_chg_vbatdet_set(the_chip,
- the_chip->max_voltage_mv
- - the_chip->resume_voltage_delta);
}
+ set_appropriate_battery_current(the_chip);
+ set_appropriate_vbatdet(the_chip);
schedule_work(&btm_config_work);
}
@@ -3498,19 +3489,13 @@
if (enter) {
btm_config.high_thr_temp =
the_chip->warm_temp_dc - TEMP_HYSTERISIS_DEGC;
- set_appropriate_battery_current(the_chip);
pm_chg_vddmax_set(the_chip, the_chip->warm_bat_voltage);
- pm_chg_vbatdet_set(the_chip,
- the_chip->warm_bat_voltage
- - the_chip->resume_voltage_delta);
} else {
btm_config.high_thr_temp = the_chip->warm_temp_dc;
- set_appropriate_battery_current(the_chip);
pm_chg_vddmax_set(the_chip, the_chip->max_voltage_mv);
- pm_chg_vbatdet_set(the_chip,
- the_chip->max_voltage_mv
- - the_chip->resume_voltage_delta);
}
+ set_appropriate_battery_current(the_chip);
+ set_appropriate_vbatdet(the_chip);
schedule_work(&btm_config_work);
}
@@ -4487,6 +4472,9 @@
enable_irq_wake(chip->pmic_chg_irq[LOOP_CHANGE_IRQ]);
}
+ rc = pm8xxx_batt_alarm_enable(PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
+ if (rc < 0)
+ pr_err("Failed to enable lower comparator\n");
return 0;
}
static int __devinit pm8921_charger_probe(struct platform_device *pdev)
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
index c26da60..0497a32 100644
--- a/drivers/spi/spi_qsd.c
+++ b/drivers/spi/spi_qsd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -30,17 +30,18 @@
#include <linux/workqueue.h>
#include <linux/io.h>
#include <linux/debugfs.h>
-#include <mach/msm_spi.h>
-#include <linux/dma-mapping.h>
-#include <linux/sched.h>
-#include <mach/dma.h>
-#include <asm/atomic.h>
-#include <linux/mutex.h>
#include <linux/gpio.h>
#include <linux/remote_spinlock.h>
#include <linux/pm_qos.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
+#include <linux/dma-mapping.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/atomic.h>
+#include <mach/msm_spi.h>
+#include <mach/sps.h>
+#include <mach/dma.h>
#include "spi_qsd.h"
static inline int msm_spi_configure_gsbi(struct msm_spi *dd,
@@ -211,16 +212,19 @@
&dd->output_block_size, block, mult)) {
goto fifo_size_err;
}
- /* DM mode is not available for this block size */
- if (dd->input_block_size == 4 || dd->output_block_size == 4)
- dd->use_dma = 0;
+ if (dd->qup_ver == SPI_QUP_VERSION_NONE) {
+ /* DM mode is not available for this block size */
+ if (dd->input_block_size == 4 || dd->output_block_size == 4)
+ dd->use_dma = 0;
- /* DM mode is currently unsupported for different block sizes */
- if (dd->input_block_size != dd->output_block_size)
- dd->use_dma = 0;
+ /* DM mode is currently unsupported for different block sizes */
+ if (dd->input_block_size != dd->output_block_size)
+ dd->use_dma = 0;
- if (dd->use_dma)
- dd->burst_size = max(dd->input_block_size, DM_BURST_SIZE);
+ if (dd->use_dma)
+ dd->burst_size = max(dd->input_block_size,
+ DM_BURST_SIZE);
+ }
return;
@@ -352,14 +356,19 @@
return 0;
}
-static inline void msm_spi_add_configs(struct msm_spi *dd, u32 *config, int n)
+/**
+ * msm_spi_set_bpw_and_no_io_flags: configure N, and no-input/no-output flags
+ */
+static inline void
+msm_spi_set_bpw_and_no_io_flags(struct msm_spi *dd, u32 *config, int n)
{
*config &= ~(SPI_NO_INPUT|SPI_NO_OUTPUT);
if (n != (*config & SPI_CFG_N))
*config = (*config & ~SPI_CFG_N) | n;
- if ((dd->mode == SPI_DMOV_MODE) && (!dd->read_len)) {
+ if (((dd->mode == SPI_DMOV_MODE) && (!dd->read_len))
+ || (dd->mode == SPI_BAM_MODE)) {
if (dd->read_buf == NULL)
*config |= SPI_NO_INPUT;
if (dd->write_buf == NULL)
@@ -367,23 +376,207 @@
}
}
-static void msm_spi_set_config(struct msm_spi *dd, int bpw)
+/**
+ * msm_spi_calc_spi_config_loopback_and_input_first: Calculate the values that
+ * should be updated into SPI_CONFIG's LOOPBACK and INPUT_FIRST flags
+ * @return calculatd value for SPI_CONFIG
+ */
+static u32
+msm_spi_calc_spi_config_loopback_and_input_first(u32 spi_config, u8 mode)
{
- u32 spi_config;
-
- spi_config = readl_relaxed(dd->base + SPI_CONFIG);
-
- if (dd->cur_msg->spi->mode & SPI_CPHA)
- spi_config &= ~SPI_CFG_INPUT_FIRST;
- else
- spi_config |= SPI_CFG_INPUT_FIRST;
- if (dd->cur_msg->spi->mode & SPI_LOOP)
+ if (mode & SPI_LOOP)
spi_config |= SPI_CFG_LOOPBACK;
else
spi_config &= ~SPI_CFG_LOOPBACK;
- msm_spi_add_configs(dd, &spi_config, bpw-1);
+
+ if (mode & SPI_CPHA)
+ spi_config &= ~SPI_CFG_INPUT_FIRST;
+ else
+ spi_config |= SPI_CFG_INPUT_FIRST;
+
+ return spi_config;
+}
+
+/**
+ * msm_spi_set_spi_config: prepares register SPI_CONFIG to process the
+ * next transfer
+ */
+static void msm_spi_set_spi_config(struct msm_spi *dd, int bpw)
+{
+ u32 spi_config = readl_relaxed(dd->base + SPI_CONFIG);
+ spi_config = msm_spi_calc_spi_config_loopback_and_input_first(
+ spi_config, dd->cur_msg->spi->mode);
+
+ if (dd->qup_ver == SPI_QUP_VERSION_NONE)
+ /* flags removed from SPI_CONFIG in QUP version-2 */
+ msm_spi_set_bpw_and_no_io_flags(dd, &spi_config, bpw-1);
+ else if (dd->mode == SPI_BAM_MODE)
+ spi_config |= SPI_CFG_INPUT_FIRST;
+
writel_relaxed(spi_config, dd->base + SPI_CONFIG);
- msm_spi_set_qup_config(dd, bpw);
+}
+
+/**
+ * msm_spi_set_mx_counts: set SPI_MX_INPUT_COUNT and SPI_MX_INPUT_COUNT
+ * for FIFO-mode. set SPI_MX_INPUT_COUNT and SPI_MX_OUTPUT_COUNT for
+ * BAM and DMOV modes.
+ * @n_words The number of reads/writes of size N.
+ */
+static void msm_spi_set_mx_counts(struct msm_spi *dd, u32 n_words)
+{
+ /*
+ * n_words cannot exceed fifo_size, and only one READ COUNT
+ * interrupt is generated per transaction, so for transactions
+ * larger than fifo size READ COUNT must be disabled.
+ * For those transactions we usually move to Data Mover mode.
+ */
+ if (dd->mode == SPI_FIFO_MODE) {
+ if (n_words <= dd->input_fifo_size) {
+ writel_relaxed(n_words,
+ dd->base + SPI_MX_READ_COUNT);
+ msm_spi_set_write_count(dd, n_words);
+ } else {
+ writel_relaxed(0, dd->base + SPI_MX_READ_COUNT);
+ msm_spi_set_write_count(dd, 0);
+ }
+ if (dd->qup_ver == SPI_QUP_VERSION_BFAM) {
+ /* must be zero for FIFO */
+ writel_relaxed(0, dd->base + SPI_MX_INPUT_COUNT);
+ writel_relaxed(0, dd->base + SPI_MX_OUTPUT_COUNT);
+ }
+ } else {
+ /* must be zero for BAM and DMOV */
+ writel_relaxed(0, dd->base + SPI_MX_READ_COUNT);
+ msm_spi_set_write_count(dd, 0);
+
+ /*
+ * for DMA transfers, both QUP_MX_INPUT_COUNT and
+ * QUP_MX_OUTPUT_COUNT must be zero to all cases but one.
+ * That case is a non-balanced transfer when there is
+ * only a read_buf.
+ */
+ if (dd->qup_ver == SPI_QUP_VERSION_BFAM) {
+ if (dd->write_buf)
+ writel_relaxed(0,
+ dd->base + SPI_MX_INPUT_COUNT);
+ else
+ writel_relaxed(n_words,
+ dd->base + SPI_MX_INPUT_COUNT);
+
+ writel_relaxed(0, dd->base + SPI_MX_OUTPUT_COUNT);
+ }
+ }
+}
+
+/**
+ * msm_spi_bam_begin_transfer: transfer dd->tx_bytes_remaining bytes
+ * using BAM.
+ * @brief BAM can transfer SPI_MAX_TRFR_BTWN_RESETS byte at a single
+ * transfer. Between transfer QUP must change to reset state. A loop is
+ * issuing a single BAM transfer at a time. If another tsranfer is
+ * required, it waits for the trasfer to finish, then moving to reset
+ * state, and back to run state to issue the next transfer.
+ * The function dose not wait for the last transfer to end, or if only
+ * a single transfer is required, the function dose not wait for it to
+ * end.
+ * @timeout max time in jiffies to wait for a transfer to finish.
+ * @return zero on success
+ */
+static int
+msm_spi_bam_begin_transfer(struct msm_spi *dd, u32 timeout, u8 bpw)
+{
+ u32 bytes_to_send, bytes_sent, n_words_xfr, cons_flags, prod_flags;
+ int ret;
+ /*
+ * QUP must move to reset mode every 64K-1 bytes of transfer
+ * (counter is 16 bit)
+ */
+ if (dd->tx_bytes_remaining > SPI_MAX_TRFR_BTWN_RESETS) {
+ /* assert chip select unconditionally */
+ u32 spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL);
+ if (!(spi_ioc & SPI_IO_C_FORCE_CS))
+ writel_relaxed(spi_ioc | SPI_IO_C_FORCE_CS,
+ dd->base + SPI_IO_CONTROL);
+ }
+
+ /* Following flags are required since we are waiting on all transfers */
+ cons_flags = SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_NWD;
+ /*
+ * on a balanced transaction, BAM will set the flags on the producer
+ * pipe based on the flags set on the consumer pipe
+ */
+ prod_flags = (dd->write_buf) ? 0 : cons_flags;
+
+ while (dd->tx_bytes_remaining > 0) {
+ bytes_sent = dd->cur_transfer->len - dd->tx_bytes_remaining;
+ bytes_to_send = min_t(u32, dd->tx_bytes_remaining
+ , SPI_MAX_TRFR_BTWN_RESETS);
+ n_words_xfr = DIV_ROUND_UP(bytes_to_send
+ , dd->bytes_per_word);
+
+ msm_spi_set_mx_counts(dd, n_words_xfr);
+
+ ret = msm_spi_set_state(dd, SPI_OP_STATE_RUN);
+ if (ret < 0) {
+ dev_err(dd->dev,
+ "%s: Failed to set QUP state to run",
+ __func__);
+ goto xfr_err;
+ }
+
+ /* enqueue read buffer in BAM */
+ if (dd->read_buf) {
+ ret = sps_transfer_one(dd->bam.prod.handle,
+ dd->cur_transfer->rx_dma + bytes_sent,
+ bytes_to_send, dd, prod_flags);
+ if (ret < 0) {
+ dev_err(dd->dev,
+ "%s: Failed to queue producer BAM transfer",
+ __func__);
+ goto xfr_err;
+ }
+ }
+
+ /* enqueue write buffer in BAM */
+ if (dd->write_buf) {
+ ret = sps_transfer_one(dd->bam.cons.handle,
+ dd->cur_transfer->tx_dma + bytes_sent,
+ bytes_to_send, dd, cons_flags);
+ if (ret < 0) {
+ dev_err(dd->dev,
+ "%s: Failed to queue consumer BAM transfer",
+ __func__);
+ goto xfr_err;
+ }
+ }
+
+ dd->tx_bytes_remaining -= bytes_to_send;
+
+ /* move to reset state after SPI_MAX_TRFR_BTWN_RESETS */
+ if (dd->tx_bytes_remaining > 0) {
+ if (!wait_for_completion_timeout(
+ &dd->transfer_complete, timeout)) {
+ dev_err(dd->dev,
+ "%s: SPI transaction timeout",
+ __func__);
+ dd->cur_msg->status = -EIO;
+ ret = -EIO;
+ goto xfr_err;
+ }
+ ret = msm_spi_set_state(dd, SPI_OP_STATE_RESET);
+ if (ret < 0) {
+ dev_err(dd->dev,
+ "%s: Failed to set QUP state to reset",
+ __func__);
+ goto xfr_err;
+ }
+ init_completion(&dd->transfer_complete);
+ }
+ }
+ return 0;
+
+xfr_err:
+ return ret;
}
static void msm_spi_setup_dm_transfer(struct msm_spi *dd)
@@ -767,7 +960,15 @@
return IRQ_HANDLED;
}
-static int msm_spi_map_dma_buffers(struct msm_spi *dd)
+/**
+ * msm_spi_dma_map_buffers: prepares buffer for DMA transfer
+ * @return zero on success or negative error code
+ *
+ * calls dma_map_single() on the read/write buffers, effectively invalidating
+ * their cash entries. for For WR-WR and WR-RD transfers, allocates temporary
+ * buffer and copy the data to/from the client buffers
+ */
+static int msm_spi_dma_map_buffers(struct msm_spi *dd)
{
struct device *dev;
struct spi_transfer *first_xfr;
@@ -847,7 +1048,7 @@
return ret;
}
-static void msm_spi_unmap_dma_buffers(struct msm_spi *dd)
+static void msm_spi_dmov_unmap_buffers(struct msm_spi *dd)
{
struct device *dev;
u32 offset;
@@ -914,56 +1115,190 @@
}
}
+static void msm_spi_bam_unmap_buffers(struct msm_spi *dd)
+{
+ struct device *dev;
+
+ /* mapped by client */
+ if (dd->cur_msg->is_dma_mapped)
+ return;
+
+ dev = &dd->cur_msg->spi->dev;
+ if (dd->cur_transfer->rx_buf)
+ dma_unmap_single(dev, dd->cur_transfer->rx_dma,
+ dd->cur_transfer->len,
+ DMA_FROM_DEVICE);
+
+ if (dd->cur_transfer->tx_buf)
+ dma_unmap_single(dev, dd->cur_transfer->tx_dma,
+ dd->cur_transfer->len,
+ DMA_TO_DEVICE);
+}
+
+static inline void msm_spi_dma_unmap_buffers(struct msm_spi *dd)
+{
+ if (dd->mode == SPI_DMOV_MODE)
+ msm_spi_dmov_unmap_buffers(dd);
+ else if (dd->mode == SPI_BAM_MODE)
+ msm_spi_bam_unmap_buffers(dd);
+}
+
/**
- * msm_use_dm - decides whether to use data mover for this
- * transfer
+ * msm_spi_use_dma - decides whether to use Data-Mover or BAM for
+ * the given transfer
* @dd: device
* @tr: transfer
*
- * Start using DM if:
- * 1. Transfer is longer than 3*block size.
- * 2. Buffers should be aligned to cache line.
- * 3. For WR-RD or WR-WR transfers, if condition (1) and (2) above are met.
+ * Start using DMA if:
+ * 1. Is supported by HW
+ * 2. Is not diabled by platfrom data
+ * 3. Transfer size is greater than 3*block size.
+ * 4. Buffers are aligned to cache line.
+ * 5. Bytes-per-word is 8,16 or 32.
*/
-static inline int msm_use_dm(struct msm_spi *dd, struct spi_transfer *tr,
- u8 bpw)
+static inline bool
+msm_spi_use_dma(struct msm_spi *dd, struct spi_transfer *tr, u8 bpw)
{
- u32 cache_line = dma_get_cache_alignment();
-
if (!dd->use_dma)
- return 0;
+ return false;
+
+ /* check constraints from platform data */
+ if ((dd->qup_ver == SPI_QUP_VERSION_BFAM) && !dd->pdata->use_bam)
+ return false;
if (dd->cur_msg_len < 3*dd->input_block_size)
- return 0;
+ return false;
if (dd->multi_xfr && !dd->read_len && !dd->write_len)
- return 0;
+ return false;
- if (tr->tx_buf) {
- if (!IS_ALIGNED((size_t)tr->tx_buf, cache_line))
- return 0;
- }
- if (tr->rx_buf) {
- if (!IS_ALIGNED((size_t)tr->rx_buf, cache_line))
- return 0;
+ if (dd->qup_ver == SPI_QUP_VERSION_NONE) {
+ u32 cache_line = dma_get_cache_alignment();
+
+ if (tr->tx_buf) {
+ if (!IS_ALIGNED((size_t)tr->tx_buf, cache_line))
+ return 0;
+ }
+ if (tr->rx_buf) {
+ if (!IS_ALIGNED((size_t)tr->rx_buf, cache_line))
+ return false;
+ }
+
+ if (tr->cs_change &&
+ ((bpw != 8) || (bpw != 16) || (bpw != 32)))
+ return false;
}
- if (tr->cs_change &&
- ((bpw != 8) || (bpw != 16) || (bpw != 32)))
- return 0;
- return 1;
+ return true;
+}
+
+/**
+ * msm_spi_set_transfer_mode: Chooses optimal transfer mode. Sets dd->mode and
+ * prepares to process a transfer.
+ */
+static void
+msm_spi_set_transfer_mode(struct msm_spi *dd, u8 bpw, u32 read_count)
+{
+ if (msm_spi_use_dma(dd, dd->cur_transfer, bpw)) {
+ if (dd->qup_ver) {
+ dd->mode = SPI_BAM_MODE;
+ } else {
+ dd->mode = SPI_DMOV_MODE;
+ if (dd->write_len && dd->read_len) {
+ dd->tx_bytes_remaining = dd->write_len;
+ dd->rx_bytes_remaining = dd->read_len;
+ }
+ }
+ } else {
+ dd->mode = SPI_FIFO_MODE;
+ if (dd->multi_xfr) {
+ dd->read_len = dd->cur_transfer->len;
+ dd->write_len = dd->cur_transfer->len;
+ }
+ }
+}
+
+/**
+ * msm_spi_set_qup_io_modes: prepares register QUP_IO_MODES to process a
+ * transfer
+ */
+static void msm_spi_set_qup_io_modes(struct msm_spi *dd)
+{
+ u32 spi_iom;
+ spi_iom = readl_relaxed(dd->base + SPI_IO_MODES);
+ /* Set input and output transfer mode: FIFO, DMOV, or BAM */
+ spi_iom &= ~(SPI_IO_M_INPUT_MODE | SPI_IO_M_OUTPUT_MODE);
+ spi_iom = (spi_iom | (dd->mode << OUTPUT_MODE_SHIFT));
+ spi_iom = (spi_iom | (dd->mode << INPUT_MODE_SHIFT));
+ /* Turn on packing for data mover */
+ if ((dd->mode == SPI_DMOV_MODE) || (dd->mode == SPI_BAM_MODE))
+ spi_iom |= SPI_IO_M_PACK_EN | SPI_IO_M_UNPACK_EN;
+ else
+ spi_iom &= ~(SPI_IO_M_PACK_EN | SPI_IO_M_UNPACK_EN);
+
+ /*if (dd->mode == SPI_BAM_MODE) {
+ spi_iom |= SPI_IO_C_NO_TRI_STATE;
+ spi_iom &= ~(SPI_IO_C_CS_SELECT | SPI_IO_C_CS_N_POLARITY);
+ }*/
+ writel_relaxed(spi_iom, dd->base + SPI_IO_MODES);
+}
+
+static u32 msm_spi_calc_spi_ioc_clk_polarity(u32 spi_ioc, u8 mode)
+{
+ if (mode & SPI_CPOL)
+ spi_ioc |= SPI_IO_C_CLK_IDLE_HIGH;
+ else
+ spi_ioc &= ~SPI_IO_C_CLK_IDLE_HIGH;
+ return spi_ioc;
+}
+
+/**
+ * msm_spi_set_spi_io_control: prepares register SPI_IO_CONTROL to process the
+ * next transfer
+ * @return the new set value of SPI_IO_CONTROL
+ */
+static u32 msm_spi_set_spi_io_control(struct msm_spi *dd)
+{
+ u32 spi_ioc, spi_ioc_orig, chip_select;
+
+ spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL);
+ spi_ioc_orig = spi_ioc;
+ spi_ioc = msm_spi_calc_spi_ioc_clk_polarity(spi_ioc
+ , dd->cur_msg->spi->mode);
+ /* Set chip-select */
+ chip_select = dd->cur_msg->spi->chip_select << 2;
+ if ((spi_ioc & SPI_IO_C_CS_SELECT) != chip_select)
+ spi_ioc = (spi_ioc & ~SPI_IO_C_CS_SELECT) | chip_select;
+ if (!dd->cur_transfer->cs_change)
+ spi_ioc |= SPI_IO_C_MX_CS_MODE;
+
+ if (spi_ioc != spi_ioc_orig)
+ writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL);
+
+ return spi_ioc;
+}
+
+/**
+ * msm_spi_set_qup_op_mask: prepares register QUP_OPERATIONAL_MASK to process
+ * the next transfer
+ */
+static void msm_spi_set_qup_op_mask(struct msm_spi *dd)
+{
+ /* mask INPUT and OUTPUT service flags in to prevent IRQs on FIFO status
+ * change in BAM mode */
+ u32 mask = (dd->mode == SPI_BAM_MODE) ?
+ QUP_OP_MASK_OUTPUT_SERVICE_FLAG | QUP_OP_MASK_INPUT_SERVICE_FLAG
+ : 0;
+ writel_relaxed(mask, dd->base + QUP_OPERATIONAL_MASK);
}
static void msm_spi_process_transfer(struct msm_spi *dd)
{
u8 bpw;
- u32 spi_ioc;
- u32 spi_iom;
- u32 spi_ioc_orig;
u32 max_speed;
- u32 chip_select;
u32 read_count;
u32 timeout;
+ u32 spi_ioc;
u32 int_loopback = 0;
dd->tx_bytes_remaining = dd->cur_msg_len;
@@ -987,6 +1322,10 @@
if (!dd->clock_speed || max_speed != dd->clock_speed)
msm_spi_clock_set(dd, max_speed);
+ timeout = 100 * msecs_to_jiffies(
+ DIV_ROUND_UP(dd->cur_msg_len * 8,
+ DIV_ROUND_UP(max_speed, MSEC_PER_SEC)));
+
read_count = DIV_ROUND_UP(dd->cur_msg_len, dd->bytes_per_word);
if (dd->cur_msg->spi->mode & SPI_LOOP)
int_loopback = 1;
@@ -1004,60 +1343,24 @@
__func__);
return;
}
- if (!msm_use_dm(dd, dd->cur_transfer, bpw)) {
- dd->mode = SPI_FIFO_MODE;
- if (dd->multi_xfr) {
- dd->read_len = dd->cur_transfer->len;
- dd->write_len = dd->cur_transfer->len;
- }
- /* read_count cannot exceed fifo_size, and only one READ COUNT
- interrupt is generated per transaction, so for transactions
- larger than fifo size READ COUNT must be disabled.
- For those transactions we usually move to Data Mover mode.
- */
- if (read_count <= dd->input_fifo_size) {
- writel_relaxed(read_count,
- dd->base + SPI_MX_READ_COUNT);
- msm_spi_set_write_count(dd, read_count);
- } else {
- writel_relaxed(0, dd->base + SPI_MX_READ_COUNT);
- msm_spi_set_write_count(dd, 0);
- }
- } else {
- dd->mode = SPI_DMOV_MODE;
- if (dd->write_len && dd->read_len) {
- dd->tx_bytes_remaining = dd->write_len;
- dd->rx_bytes_remaining = dd->read_len;
- }
- }
- /* Write mode - fifo or data mover*/
- spi_iom = readl_relaxed(dd->base + SPI_IO_MODES);
- spi_iom &= ~(SPI_IO_M_INPUT_MODE | SPI_IO_M_OUTPUT_MODE);
- spi_iom = (spi_iom | (dd->mode << OUTPUT_MODE_SHIFT));
- spi_iom = (spi_iom | (dd->mode << INPUT_MODE_SHIFT));
- /* Turn on packing for data mover */
- if (dd->mode == SPI_DMOV_MODE)
- spi_iom |= SPI_IO_M_PACK_EN | SPI_IO_M_UNPACK_EN;
- else
- spi_iom &= ~(SPI_IO_M_PACK_EN | SPI_IO_M_UNPACK_EN);
- writel_relaxed(spi_iom, dd->base + SPI_IO_MODES);
+ if (msm_spi_set_state(dd, SPI_OP_STATE_RESET))
+ dev_err(dd->dev,
+ "%s: Error setting QUP to reset-state",
+ __func__);
- msm_spi_set_config(dd, bpw);
-
- spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL);
- spi_ioc_orig = spi_ioc;
- if (dd->cur_msg->spi->mode & SPI_CPOL)
- spi_ioc |= SPI_IO_C_CLK_IDLE_HIGH;
- else
- spi_ioc &= ~SPI_IO_C_CLK_IDLE_HIGH;
- chip_select = dd->cur_msg->spi->chip_select << 2;
- if ((spi_ioc & SPI_IO_C_CS_SELECT) != chip_select)
- spi_ioc = (spi_ioc & ~SPI_IO_C_CS_SELECT) | chip_select;
- if (!dd->cur_transfer->cs_change)
- spi_ioc |= SPI_IO_C_MX_CS_MODE;
- if (spi_ioc != spi_ioc_orig)
- writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL);
+ msm_spi_set_transfer_mode(dd, bpw, read_count);
+ msm_spi_set_mx_counts(dd, read_count);
+ if ((dd->mode == SPI_BAM_MODE) || (dd->mode == SPI_DMOV_MODE))
+ if (msm_spi_dma_map_buffers(dd) < 0) {
+ pr_err("Mapping DMA buffers\n");
+ return;
+ }
+ msm_spi_set_qup_io_modes(dd);
+ msm_spi_set_spi_config(dd, bpw);
+ msm_spi_set_qup_config(dd, bpw);
+ spi_ioc = msm_spi_set_spi_io_control(dd);
+ msm_spi_set_qup_op_mask(dd);
if (dd->mode == SPI_DMOV_MODE) {
msm_spi_setup_dm_transfer(dd);
@@ -1071,27 +1374,35 @@
if (msm_spi_prepare_for_write(dd))
goto transfer_end;
msm_spi_start_write(dd, read_count);
+ } else if (dd->mode == SPI_BAM_MODE) {
+ if ((msm_spi_bam_begin_transfer(dd, timeout, bpw)) < 0)
+ dev_err(dd->dev, "%s: BAM transfer setup failed\n",
+ __func__);
}
- /* Only enter the RUN state after the first word is written into
- the output FIFO. Otherwise, the output FIFO EMPTY interrupt
- might fire before the first word is written resulting in a
- possible race condition.
+ /*
+ * On BAM mode, current state here is run.
+ * Only enter the RUN state after the first word is written into
+ * the output FIFO. Otherwise, the output FIFO EMPTY interrupt
+ * might fire before the first word is written resulting in a
+ * possible race condition.
*/
- if (msm_spi_set_state(dd, SPI_OP_STATE_RUN))
- goto transfer_end;
-
- timeout = 100 * msecs_to_jiffies(
- DIV_ROUND_UP(dd->cur_msg_len * 8,
- DIV_ROUND_UP(max_speed, MSEC_PER_SEC)));
+ if (dd->mode != SPI_BAM_MODE)
+ if (msm_spi_set_state(dd, SPI_OP_STATE_RUN)) {
+ dev_warn(dd->dev,
+ "%s: Failed to set QUP to run-state. Mode:%d",
+ __func__, dd->mode);
+ goto transfer_end;
+ }
/* Assume success, this might change later upon transaction result */
dd->cur_msg->status = 0;
do {
if (!wait_for_completion_timeout(&dd->transfer_complete,
timeout)) {
- dev_err(dd->dev, "%s: SPI transaction "
- "timeout\n", __func__);
+ dev_err(dd->dev,
+ "%s: SPI transaction timeout\n",
+ __func__);
dd->cur_msg->status = -EIO;
if (dd->mode == SPI_DMOV_MODE) {
msm_dmov_flush(dd->tx_dma_chan, 1);
@@ -1102,8 +1413,7 @@
} while (msm_spi_dm_send_next(dd));
transfer_end:
- if (dd->mode == SPI_DMOV_MODE)
- msm_spi_unmap_dma_buffers(dd);
+ msm_spi_dma_unmap_buffers(dd);
dd->mode = SPI_MODE_NONE;
msm_spi_set_state(dd, SPI_OP_STATE_RESET);
@@ -1266,10 +1576,10 @@
* WR-WR or WR-RD transfers
*/
if ((!dd->cur_msg->is_dma_mapped) &&
- (msm_use_dm(dd, dd->cur_transfer,
+ (msm_spi_use_dma(dd, dd->cur_transfer,
dd->cur_transfer->bits_per_word))) {
/* Mapping of DMA buffers */
- int ret = msm_spi_map_dma_buffers(dd);
+ int ret = msm_spi_dma_map_buffers(dd);
if (ret < 0) {
dd->cur_msg->status = ret;
goto error;
@@ -1474,22 +1784,13 @@
spi_ioc |= mask;
else
spi_ioc &= ~mask;
- if (spi->mode & SPI_CPOL)
- spi_ioc |= SPI_IO_C_CLK_IDLE_HIGH;
- else
- spi_ioc &= ~SPI_IO_C_CLK_IDLE_HIGH;
+ spi_ioc = msm_spi_calc_spi_ioc_clk_polarity(spi_ioc, spi->mode);
writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL);
spi_config = readl_relaxed(dd->base + SPI_CONFIG);
- if (spi->mode & SPI_LOOP)
- spi_config |= SPI_CFG_LOOPBACK;
- else
- spi_config &= ~SPI_CFG_LOOPBACK;
- if (spi->mode & SPI_CPHA)
- spi_config &= ~SPI_CFG_INPUT_FIRST;
- else
- spi_config |= SPI_CFG_INPUT_FIRST;
+ spi_config = msm_spi_calc_spi_config_loopback_and_input_first(
+ spi_config, spi->mode);
writel_relaxed(spi_config, dd->base + SPI_CONFIG);
/* Ensure previous write completed before disabling the clocks */
@@ -1730,7 +2031,7 @@
roundup(dd->burst_size, cache_line))*2;
}
-static void msm_spi_teardown_dma(struct msm_spi *dd)
+static void msm_spi_dmov_teardown(struct msm_spi *dd)
{
int limit = 0;
@@ -1749,7 +2050,171 @@
dd->tx_padding = dd->rx_padding = NULL;
}
-static __init int msm_spi_init_dma(struct msm_spi *dd)
+static void msm_spi_bam_pipe_teardown(struct msm_spi *dd,
+ enum msm_spi_pipe_direction pipe_dir)
+{
+ struct msm_spi_bam_pipe *pipe = (pipe_dir == SPI_BAM_CONSUMER_PIPE) ?
+ (&dd->bam.prod) : (&dd->bam.cons);
+ if (!pipe->teardown_required)
+ return;
+
+ sps_disconnect(pipe->handle);
+ dma_free_coherent(dd->dev, pipe->config.desc.size,
+ pipe->config.desc.base, pipe->config.desc.phys_base);
+ sps_free_endpoint(pipe->handle);
+ pipe->handle = 0;
+ pipe->teardown_required = false;
+}
+
+static int msm_spi_bam_pipe_init(struct msm_spi *dd,
+ enum msm_spi_pipe_direction pipe_dir)
+{
+ int rc = 0;
+ struct sps_pipe *pipe_handle;
+ struct sps_register_event event = {0};
+ struct msm_spi_bam_pipe *pipe = (pipe_dir == SPI_BAM_CONSUMER_PIPE) ?
+ (&dd->bam.prod) : (&dd->bam.cons);
+ struct sps_connect *pipe_conf = &pipe->config;
+
+ pipe->handle = 0;
+ pipe_handle = sps_alloc_endpoint();
+ if (!pipe_handle) {
+ dev_err(dd->dev, "%s: Failed to allocate BAM endpoint\n"
+ , __func__);
+ return -ENOMEM;
+ }
+
+ memset(pipe_conf, 0, sizeof(*pipe_conf));
+ rc = sps_get_config(pipe_handle, pipe_conf);
+ if (rc) {
+ dev_err(dd->dev, "%s: Failed to get BAM pipe config\n"
+ , __func__);
+ goto config_err;
+ }
+
+ if (pipe_dir == SPI_BAM_CONSUMER_PIPE) {
+ pipe_conf->source = dd->bam.handle;
+ pipe_conf->destination = SPS_DEV_HANDLE_MEM;
+ pipe_conf->mode = SPS_MODE_SRC;
+ pipe_conf->src_pipe_index =
+ dd->pdata->bam_producer_pipe_index;
+ pipe_conf->dest_pipe_index = 0;
+ } else {
+ pipe_conf->source = SPS_DEV_HANDLE_MEM;
+ pipe_conf->destination = dd->bam.handle;
+ pipe_conf->mode = SPS_MODE_DEST;
+ pipe_conf->src_pipe_index = 0;
+ pipe_conf->dest_pipe_index =
+ dd->pdata->bam_consumer_pipe_index;
+ }
+ pipe_conf->options = SPS_O_EOT | SPS_O_AUTO_ENABLE;
+ pipe_conf->desc.size = SPI_BAM_MAX_DESC_NUM * sizeof(struct sps_iovec);
+ pipe_conf->desc.base = dma_alloc_coherent(dd->dev,
+ pipe_conf->desc.size,
+ &pipe_conf->desc.phys_base,
+ GFP_KERNEL);
+ if (!pipe_conf->desc.base) {
+ dev_err(dd->dev, "%s: Failed allocate BAM pipe memory"
+ , __func__);
+ rc = -ENOMEM;
+ goto config_err;
+ }
+
+ memset(pipe_conf->desc.base, 0x00, pipe_conf->desc.size);
+
+ rc = sps_connect(pipe_handle, pipe_conf);
+ if (rc) {
+ dev_err(dd->dev, "%s: Failed to connect BAM pipe", __func__);
+ goto connect_err;
+ }
+
+ event.mode = SPS_TRIGGER_WAIT;
+ event.options = SPS_O_EOT;
+ event.xfer_done = &dd->transfer_complete;
+ event.user = (void *)dd;
+ rc = sps_register_event(pipe_handle, &event);
+ if (rc) {
+ dev_err(dd->dev, "%s: Failed to register BAM EOT event",
+ __func__);
+ goto register_err;
+ }
+
+ pipe->handle = pipe_handle;
+ pipe->teardown_required = true;
+ return 0;
+
+register_err:
+ sps_disconnect(pipe_handle);
+connect_err:
+ dma_free_coherent(dd->dev, pipe_conf->desc.size,
+ pipe_conf->desc.base, pipe_conf->desc.phys_base);
+config_err:
+ sps_free_endpoint(pipe_handle);
+
+ return rc;
+}
+
+static void msm_spi_bam_teardown(struct msm_spi *dd)
+{
+ msm_spi_bam_pipe_teardown(dd, SPI_BAM_PRODUCER_PIPE);
+ msm_spi_bam_pipe_teardown(dd, SPI_BAM_CONSUMER_PIPE);
+
+ if (dd->bam.deregister_required) {
+ sps_deregister_bam_device(dd->bam.handle);
+ dd->bam.deregister_required = false;
+ }
+}
+
+static int msm_spi_bam_init(struct msm_spi *dd)
+{
+ struct sps_bam_props bam_props = {0};
+ u32 bam_handle;
+ int rc = 0;
+
+ rc = sps_phy2h(dd->bam.phys_addr, &bam_handle);
+ if (rc || !bam_handle) {
+ bam_props.phys_addr = dd->bam.phys_addr;
+ bam_props.virt_addr = dd->bam.base;
+ bam_props.irq = dd->bam.irq;
+ bam_props.manage = SPS_BAM_MGR_LOCAL;
+ bam_props.summing_threshold = 0x10;
+
+ rc = sps_register_bam_device(&bam_props, &bam_handle);
+ if (rc) {
+ dev_err(dd->dev,
+ "%s: Failed to register BAM device",
+ __func__);
+ return rc;
+ }
+ dd->bam.deregister_required = true;
+ }
+
+ dd->bam.handle = bam_handle;
+
+ rc = msm_spi_bam_pipe_init(dd, SPI_BAM_PRODUCER_PIPE);
+ if (rc) {
+ dev_err(dd->dev,
+ "%s: Failed to init producer BAM-pipe",
+ __func__);
+ goto bam_init_error;
+ }
+
+ rc = msm_spi_bam_pipe_init(dd, SPI_BAM_CONSUMER_PIPE);
+ if (rc) {
+ dev_err(dd->dev,
+ "%s: Failed to init consumer BAM-pipe",
+ __func__);
+ goto bam_init_error;
+ }
+
+ return 0;
+
+bam_init_error:
+ msm_spi_bam_teardown(dd);
+ return rc;
+}
+
+static __init int msm_spi_dmov_init(struct msm_spi *dd)
{
dmov_box *box;
u32 cache_line = dma_get_cache_alignment();
@@ -1811,10 +2276,15 @@
return 0;
}
-struct msm_spi_platform_data *msm_spi_dt_to_pdata(struct platform_device *pdev)
+/**
+ * msm_spi_dt_to_pdata: copy device-tree data to platfrom data struct
+ */
+struct msm_spi_platform_data *
+__init msm_spi_dt_to_pdata(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct msm_spi_platform_data *pdata;
+ int rc;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
@@ -1827,9 +2297,76 @@
of_property_read_u32(node, "infinite_mode",
&pdata->infinite_mode);
+ pdata->ver_reg_exists = of_property_read_bool(node
+ , "qcom,ver-reg-exists");
+
+ pdata->use_bam = of_property_read_bool(node, "qcom,use-bam");
+
+ if (pdata->use_bam) {
+ rc = of_property_read_u32(node, "qcom,bam-consumer-pipe-index",
+ &pdata->bam_consumer_pipe_index);
+ if (rc) {
+ dev_warn(&pdev->dev,
+ "missing qcom,bam-consumer-pipe-index entry in device-tree\n");
+ pdata->use_bam = false;
+ }
+
+ rc = of_property_read_u32(node, "qcom,bam-producer-pipe-index",
+ &pdata->bam_producer_pipe_index);
+ if (rc) {
+ dev_warn(&pdev->dev,
+ "missing qcom,bam-producer-pipe-index entry in device-tree\n");
+ pdata->use_bam = false;
+ }
+ }
return pdata;
}
+static int __init msm_spi_get_qup_hw_ver(struct device *dev, struct msm_spi *dd)
+{
+ u32 data = readl_relaxed(dd->base + QUP_HARDWARE_VER);
+ return (data >= QUP_HARDWARE_VER_2_1_1) ? SPI_QUP_VERSION_BFAM
+ : SPI_QUP_VERSION_NONE;
+}
+
+static int __init msm_spi_bam_get_resources(struct msm_spi *dd,
+ struct platform_device *pdev, struct spi_master *master)
+{
+ struct resource *resource;
+ size_t bam_mem_size;
+
+ resource = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "spi_bam_physical");
+ if (!resource) {
+ dev_warn(&pdev->dev,
+ "%s: Missing spi_bam_physical entry in DT",
+ __func__);
+ return -ENXIO;
+ }
+
+ dd->bam.phys_addr = resource->start;
+ bam_mem_size = resource_size(resource);
+ dd->bam.base = devm_ioremap(&pdev->dev, dd->bam.phys_addr,
+ bam_mem_size);
+ if (!dd->bam.base) {
+ dev_warn(&pdev->dev,
+ "%s: Failed to ioremap(spi_bam_physical)",
+ __func__);
+ return -ENXIO;
+ }
+
+ dd->bam.irq = platform_get_irq_byname(pdev, "spi_bam_irq");
+ if (dd->bam.irq < 0) {
+ dev_warn(&pdev->dev, "%s: Missing spi_bam_irq entry in DT",
+ __func__);
+ return -EINVAL;
+ }
+
+ dd->dma_init = msm_spi_bam_init;
+ dd->dma_teardown = msm_spi_bam_teardown;
+ return 0;
+}
+
static int __init msm_spi_probe(struct platform_device *pdev)
{
struct spi_master *master;
@@ -1926,21 +2463,39 @@
goto skip_dma_resources;
}
}
- resource = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (resource) {
- dd->rx_dma_chan = resource->start;
- dd->tx_dma_chan = resource->end;
- resource = platform_get_resource(pdev, IORESOURCE_DMA,
- 1);
- if (!resource) {
- rc = -ENXIO;
- goto err_probe_res;
- }
+ if (dd->qup_ver == SPI_QUP_VERSION_NONE) {
+ resource = platform_get_resource(pdev,
+ IORESOURCE_DMA, 0);
+ if (resource) {
+ dd->rx_dma_chan = resource->start;
+ dd->tx_dma_chan = resource->end;
+ resource = platform_get_resource(pdev,
+ IORESOURCE_DMA, 1);
+ if (!resource) {
+ rc = -ENXIO;
+ goto err_probe_res;
+ }
- dd->rx_dma_crci = resource->start;
- dd->tx_dma_crci = resource->end;
+ dd->rx_dma_crci = resource->start;
+ dd->tx_dma_crci = resource->end;
+ dd->use_dma = 1;
+ master->dma_alignment =
+ dma_get_cache_alignment();
+ dd->dma_init = msm_spi_dmov_init ;
+ dd->dma_teardown = msm_spi_dmov_teardown;
+ }
+ } else {
+ if (!dd->pdata->use_bam)
+ goto skip_dma_resources;
+
+ rc = msm_spi_bam_get_resources(dd, pdev, master);
+ if (rc) {
+ dev_warn(dd->dev,
+ "%s: Faild to get BAM resources",
+ __func__);
+ goto skip_dma_resources;
+ }
dd->use_dma = 1;
- master->dma_alignment = dma_get_cache_alignment();
}
}
@@ -1968,6 +2523,15 @@
goto err_probe_reqmem;
}
+ if (pdata && pdata->ver_reg_exists) {
+ enum msm_spi_qup_version ver =
+ msm_spi_get_qup_hw_ver(&pdev->dev, dd);
+ if (dd->qup_ver != ver)
+ dev_warn(&pdev->dev,
+ "%s: HW version different then initially assumed by probe",
+ __func__);
+ }
+
if (pdata && pdata->rsl_id) {
struct remote_mutex_id rmid;
rmid.r_spinlock_id = pdata->rsl_id;
@@ -1984,7 +2548,7 @@
dd->use_rlock = 1;
dd->pm_lat = pdata->pm_lat;
pm_qos_add_request(&qos_req_list, PM_QOS_CPU_DMA_LATENCY,
- PM_QOS_DEFAULT_VALUE);
+ PM_QOS_DEFAULT_VALUE);
}
mutex_lock(&dd->core_lock);
@@ -2026,13 +2590,16 @@
}
pclk_enabled = 1;
- rc = msm_spi_configure_gsbi(dd, pdev);
- if (rc)
- goto err_probe_gsbi;
+ /* GSBI dose not exists on B-family MSM-chips */
+ if (dd->qup_ver != SPI_QUP_VERSION_BFAM) {
+ rc = msm_spi_configure_gsbi(dd, pdev);
+ if (rc)
+ goto err_probe_gsbi;
+ }
msm_spi_calculate_fifo_size(dd);
if (dd->use_dma) {
- rc = msm_spi_init_dma(dd);
+ rc = dd->dma_init(dd);
if (rc)
goto err_probe_dma;
}
@@ -2091,7 +2658,7 @@
err_probe_reg_master:
err_probe_irq:
err_probe_state:
- msm_spi_teardown_dma(dd);
+ dd->dma_teardown(dd);
err_probe_dma:
err_probe_gsbi:
if (pclk_enabled)
@@ -2174,8 +2741,7 @@
spi_debugfs_exit(dd);
sysfs_remove_group(&pdev->dev.kobj, &dev_attr_grp);
- msm_spi_teardown_dma(dd);
-
+ dd->dma_teardown(dd);
clk_put(dd->clk);
clk_put(dd->pclk);
destroy_workqueue(dd->workqueue);
diff --git a/drivers/spi/spi_qsd.h b/drivers/spi/spi_qsd.h
index a0dee34..62f1830 100644
--- a/drivers/spi/spi_qsd.h
+++ b/drivers/spi/spi_qsd.h
@@ -41,8 +41,13 @@
#define GSBI_CTRL_REG 0x0
#define GSBI_SPI_CONFIG 0x30
+/* B-family only registers */
#define QUP_HARDWARE_VER 0x0030
+#define QUP_HARDWARE_VER_2_1_1 0X20010001
#define QUP_OPERATIONAL_MASK 0x0028
+#define QUP_OP_MASK_OUTPUT_SERVICE_FLAG 0x100
+#define QUP_OP_MASK_INPUT_SERVICE_FLAG 0x200
+
#define QUP_ERROR_FLAGS 0x0308
#define SPI_CONFIG QSD_REG(0x0000) QUP_REG(0x0300)
@@ -73,6 +78,7 @@
#define SPI_NO_OUTPUT 0x00000040
#define SPI_CFG_LOOPBACK 0x00000100
#define SPI_CFG_N 0x0000001F
+#define SPI_EN_EXT_OUT_FLAG 0x00010000
/* SPI_IO_CONTROL fields */
#define SPI_IO_C_FORCE_CS 0x00000800
@@ -148,8 +154,18 @@
/* Data Mover commands should be aligned to 64 bit(8 bytes) */
#define DM_BYTE_ALIGN 8
-#define SPI_QUP_VERSION_NONE 0x0
-#define SPI_QUP_VERSION_BFAM 0x2
+enum msm_spi_qup_version {
+ SPI_QUP_VERSION_NONE = 0x0,
+ SPI_QUP_VERSION_BFAM = 0x2,
+};
+
+enum msm_spi_pipe_direction {
+ SPI_BAM_CONSUMER_PIPE = 0x0,
+ SPI_BAM_PRODUCER_PIPE = 0x1,
+};
+
+#define SPI_BAM_MAX_DESC_NUM 32
+#define SPI_MAX_TRFR_BTWN_RESETS ((64 * 1024) - 16) /* 64KB - 16byte */
static char const * const spi_rsrcs[] = {
"spi_clk",
@@ -231,6 +247,22 @@
};
#endif
+struct msm_spi_bam_pipe {
+ struct sps_pipe *handle;
+ struct sps_connect config;
+ bool teardown_required;
+};
+
+struct msm_spi_bam {
+ void __iomem *base;
+ u32 phys_addr;
+ u32 handle;
+ u32 irq;
+ struct msm_spi_bam_pipe prod;
+ struct msm_spi_bam_pipe cons;
+ bool deregister_required;
+};
+
struct msm_spi {
u8 *read_buf;
const u8 *write_buf;
@@ -244,8 +276,8 @@
struct spi_message *cur_msg;
struct spi_transfer *cur_transfer;
struct completion transfer_complete;
- struct clk *clk;
- struct clk *pclk;
+ struct clk *clk; /* core clock */
+ struct clk *pclk; /* interface clock */
unsigned long mem_phys_addr;
size_t mem_size;
int input_fifo_size;
@@ -273,6 +305,9 @@
int tx_dma_crci;
int rx_dma_chan;
int rx_dma_crci;
+ int (*dma_init) (struct msm_spi *dd);
+ void (*dma_teardown) (struct msm_spi *dd);
+ struct msm_spi_bam bam;
/* Data Mover Commands */
struct spi_dmov_cmd *tx_dmov_cmd;
struct spi_dmov_cmd *rx_dmov_cmd;
@@ -321,7 +356,7 @@
int spi_gpios[ARRAY_SIZE(spi_rsrcs)];
/* SPI CS GPIOs for each slave */
struct spi_cs_gpio cs_gpios[ARRAY_SIZE(spi_cs_rsrcs)];
- int qup_ver;
+ enum msm_spi_qup_version qup_ver;
int max_trfr_len;
};
@@ -333,7 +368,7 @@
enum msm_spi_state state);
static void msm_spi_write_word_to_fifo(struct msm_spi *dd);
static inline void msm_spi_write_rmn_to_fifo(struct msm_spi *dd);
-static inline irqreturn_t msm_spi_qup_irq(int irq, void *dev_id);
+static irqreturn_t msm_spi_qup_irq(int irq, void *dev_id);
#if defined(CONFIG_SPI_QSD) || defined(CONFIG_SPI_QSD_MODULE)
static inline void msm_spi_disable_irqs(struct msm_spi *dd)
@@ -385,7 +420,7 @@
static inline void msm_spi_ack_clk_err(struct msm_spi *dd) {}
static inline void msm_spi_set_qup_config(struct msm_spi *dd, int bpw) {}
-static inline int msm_spi_prepare_for_write(struct msm_spi *dd) { return 0; }
+static inline int msm_spi_prepare_for_write(struct msm_spi *dd) { return 0; }
static inline void msm_spi_start_write(struct msm_spi *dd, u32 read_count)
{
msm_spi_write_word_to_fifo(dd);
@@ -441,16 +476,18 @@
writel_relaxed(QUP_ERR_MASK, dd->base + QUP_ERROR_FLAGS);
}
-static inline void msm_spi_add_configs(struct msm_spi *dd, u32 *config, int n);
+static inline void
+msm_spi_set_bpw_and_no_io_flags(struct msm_spi *dd, u32 *config, int n);
-/* QUP has no_input, no_output, and N bits at QUP_CONFIG */
+/**
+ * msm_spi_set_qup_config: set QUP_CONFIG to no_input, no_output, and N bits
+ */
static inline void msm_spi_set_qup_config(struct msm_spi *dd, int bpw)
{
u32 qup_config = readl_relaxed(dd->base + QUP_CONFIG);
- msm_spi_add_configs(dd, &qup_config, bpw-1);
- writel_relaxed(qup_config | QUP_CONFIG_SPI_MODE,
- dd->base + QUP_CONFIG);
+ msm_spi_set_bpw_and_no_io_flags(dd, &qup_config, bpw-1);
+ writel_relaxed(qup_config | QUP_CONFIG_SPI_MODE, dd->base + QUP_CONFIG);
}
static inline int msm_spi_prepare_for_write(struct msm_spi *dd)
@@ -482,12 +519,22 @@
static inline void msm_spi_enable_error_flags(struct msm_spi *dd)
{
- writel_relaxed(0x00000078, dd->base + SPI_ERROR_FLAGS_EN);
+ if (dd->qup_ver == SPI_QUP_VERSION_BFAM)
+ writel_relaxed(
+ SPI_ERR_CLK_UNDER_RUN_ERR | SPI_ERR_CLK_OVER_RUN_ERR,
+ dd->base + SPI_ERROR_FLAGS_EN);
+ else
+ writel_relaxed(0x00000078, dd->base + SPI_ERROR_FLAGS_EN);
}
static inline void msm_spi_clear_error_flags(struct msm_spi *dd)
{
- writel_relaxed(0x0000007C, dd->base + SPI_ERROR_FLAGS);
+ if (dd->qup_ver == SPI_QUP_VERSION_BFAM)
+ writel_relaxed(
+ SPI_ERR_CLK_UNDER_RUN_ERR | SPI_ERR_CLK_OVER_RUN_ERR,
+ dd->base + SPI_ERROR_FLAGS);
+ else
+ writel_relaxed(0x0000007C, dd->base + SPI_ERROR_FLAGS);
}
#endif
diff --git a/drivers/thermal/pm8xxx-tm.c b/drivers/thermal/pm8xxx-tm.c
index ec04369..4568933 100644
--- a/drivers/thermal/pm8xxx-tm.c
+++ b/drivers/thermal/pm8xxx-tm.c
@@ -33,29 +33,32 @@
#include <linux/msm_adc.h>
/* Register TEMP_ALARM_CTRL bits */
-#define TEMP_ALARM_CTRL_ST3_SD 0x80
-#define TEMP_ALARM_CTRL_ST2_SD 0x40
-#define TEMP_ALARM_CTRL_STATUS_MASK 0x30
-#define TEMP_ALARM_CTRL_STATUS_SHIFT 4
-#define TEMP_ALARM_CTRL_THRESH_MASK 0x0C
-#define TEMP_ALARM_CTRL_THRESH_SHIFT 2
-#define TEMP_ALARM_CTRL_OVRD_ST3 0x02
-#define TEMP_ALARM_CTRL_OVRD_ST2 0x01
-#define TEMP_ALARM_CTRL_OVRD_MASK 0x03
+#define TEMP_ALARM_CTRL_ST3_SD 0x80
+#define TEMP_ALARM_CTRL_ST2_SD 0x40
+#define TEMP_ALARM_CTRL_STATUS_MASK 0x30
+#define TEMP_ALARM_CTRL_STATUS_SHIFT 4
+#define TEMP_ALARM_CTRL_THRESH_MASK 0x0C
+#define TEMP_ALARM_CTRL_THRESH_SHIFT 2
+#define TEMP_ALARM_CTRL_OVRD_ST3 0x02
+#define TEMP_ALARM_CTRL_OVRD_ST2 0x01
+#define TEMP_ALARM_CTRL_OVRD_MASK 0x03
-#define TEMP_STAGE_STEP 20000 /* Stage step: 20.000 C */
-#define TEMP_STAGE_HYSTERESIS 2000
+#define TEMP_STAGE_STEP 20000 /* Stage step: 20.000 C */
+#define TEMP_STAGE_HYSTERESIS 2000
-#define TEMP_THRESH_MIN 105000 /* Threshold Min: 105 C */
-#define TEMP_THRESH_STEP 5000 /* Threshold step: 5 C */
+#define TEMP_THRESH_MIN 105000 /* Threshold Min: 105 C */
+#define TEMP_THRESH_STEP 5000 /* Threshold step: 5 C */
/* Register TEMP_ALARM_PWM bits */
-#define TEMP_ALARM_PWM_EN_MASK 0xC0
-#define TEMP_ALARM_PWM_EN_SHIFT 6
-#define TEMP_ALARM_PWM_PER_PRE_MASK 0x38
-#define TEMP_ALARM_PWM_PER_PRE_SHIFT 3
-#define TEMP_ALARM_PWM_PER_DIV_MASK 0x07
-#define TEMP_ALARM_PWM_PER_DIV_SHIFT 0
+#define TEMP_ALARM_PWM_EN_MASK 0xC0
+#define TEMP_ALARM_PWM_EN_NEVER 0x00
+#define TEMP_ALARM_PWM_EN_SLEEP_B 0x40
+#define TEMP_ALARM_PWM_EN_PWM 0x80
+#define TEMP_ALARM_PWM_EN_ALWAYS 0xC0
+#define TEMP_ALARM_PWM_PER_PRE_MASK 0x38
+#define TEMP_ALARM_PWM_PER_PRE_SHIFT 3
+#define TEMP_ALARM_PWM_PER_DIV_MASK 0x07
+#define TEMP_ALARM_PWM_PER_DIV_SHIFT 0
/* Trips: from critical to less critical */
#define TRIP_STAGE3 0
@@ -516,16 +519,15 @@
return rc;
/*
- * Set the PMIC alarm module PWM to have a frequency of 8 Hz. This
- * helps cut down on the number of unnecessary interrupts fired when
- * changing between thermal stages. Also, Enable the over temperature
- * PWM whenever the PMIC is enabled.
+ * Set the PMIC temperature alarm module to be always on. This ensures
+ * that die temperature monitoring is active even if CXO is disabled
+ * (i.e. when sleep_b is low). This is necessary since CXO can be
+ * disabled while the system is still heavily loaded. Also, using
+ * the alway-on instead of PWM-enabled configurations ensures that the
+ * die temperature can be measured by the PMIC ADC without reconfiguring
+ * the temperature alarm module first.
*/
- reg = (1 << TEMP_ALARM_PWM_EN_SHIFT)
- | (3 << TEMP_ALARM_PWM_PER_PRE_SHIFT)
- | (3 << TEMP_ALARM_PWM_PER_DIV_SHIFT);
-
- rc = pm8xxx_tm_write_pwm(chip, reg);
+ rc = pm8xxx_tm_write_pwm(chip, TEMP_ALARM_PWM_EN_ALWAYS);
return rc;
}
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index e6f5bf5..5e7ab9f 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -848,8 +848,7 @@
},
};
-#define UART_NR ARRAY_SIZE(msm_uart_ports)
-
+#define UART_NR 256
static inline struct uart_port * get_port_from_line(unsigned int line)
{
return &msm_uart_ports[line].uart;
@@ -1002,9 +1001,7 @@
struct resource *resource;
struct uart_port *port;
int irq;
-#ifdef CONFIG_SERIAL_MSM_RX_WAKEUP
struct msm_serial_platform_data *pdata = pdev->dev.platform_data;
-#endif
if (unlikely(pdev->id < 0 || pdev->id >= UART_NR))
return -ENXIO;
@@ -1057,6 +1054,8 @@
#endif
pm_runtime_enable(port->dev);
+ if (pdata != NULL && pdata->userid && pdata->userid <= UART_NR)
+ port->line = pdata->userid;
return uart_add_one_port(&msm_uart_driver, port);
}
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 4073fc8..7430e5a 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -36,6 +36,7 @@
#include <linux/power_supply.h>
#include <mach/rpm-regulator.h>
+#include <mach/rpm-regulator-smd.h>
#include <mach/msm_xo.h>
#include <mach/msm_bus.h>
@@ -141,8 +142,6 @@
struct regulator *hsusb_vddcx;
struct regulator *ssusb_1p8;
struct regulator *ssusb_vddcx;
- enum usb_vdd_type ss_vdd_type;
- enum usb_vdd_type hs_vdd_type;
struct dwc3_ext_xceiv ext_xceiv;
bool resume_pending;
atomic_t pm_suspended;
@@ -162,6 +161,9 @@
unsigned int online;
unsigned int host_mode;
unsigned int current_max;
+ unsigned int vdd_no_vol_level;
+ unsigned int vdd_low_vol_level;
+ unsigned int vdd_high_vol_level;
bool vbus_active;
};
@@ -177,23 +179,6 @@
#define USB_SSPHY_1P8_VOL_MAX 1800000 /* uV */
#define USB_SSPHY_1P8_HPM_LOAD 23000 /* uA */
-#define USB_PHY_VDD_DIG_VOL_NONE 0 /* uV */
-#define USB_PHY_VDD_DIG_VOL_MIN 1045000 /* uV */
-#define USB_PHY_VDD_DIG_VOL_MAX 1320000 /* uV */
-
-static const int vdd_val[VDD_TYPE_MAX][VDD_VAL_MAX] = {
- { /* VDD_CX CORNER Voting */
- [VDD_NONE] = RPM_VREG_CORNER_NONE,
- [VDD_MIN] = RPM_VREG_CORNER_NOMINAL,
- [VDD_MAX] = RPM_VREG_CORNER_HIGH,
- },
- { /* VDD_CX Voltage Voting */
- [VDD_NONE] = USB_PHY_VDD_DIG_VOL_NONE,
- [VDD_MIN] = USB_PHY_VDD_DIG_VOL_MIN,
- [VDD_MAX] = USB_PHY_VDD_DIG_VOL_MAX,
- },
-};
-
static struct dwc3_msm *context;
static u64 dwc3_msm_dma_mask = DMA_BIT_MASK(64);
@@ -858,12 +843,11 @@
/* HSPHY */
static int dwc3_hsusb_config_vddcx(int high)
{
- int min_vol, ret;
+ int min_vol, max_vol, ret;
struct dwc3_msm *dwc = context;
- enum usb_vdd_type vdd_type = context->hs_vdd_type;
- int max_vol = vdd_val[vdd_type][VDD_MAX];
- min_vol = vdd_val[vdd_type][high ? VDD_MIN : VDD_NONE];
+ max_vol = dwc->vdd_high_vol_level;
+ min_vol = high ? dwc->vdd_low_vol_level : dwc->vdd_no_vol_level;
ret = regulator_set_voltage(dwc->hsusb_vddcx, min_vol, max_vol);
if (ret) {
dev_err(dwc->dev, "unable to set voltage for HSUSB_VDDCX\n");
@@ -983,12 +967,11 @@
/* SSPHY */
static int dwc3_ssusb_config_vddcx(int high)
{
- int min_vol, ret;
+ int min_vol, max_vol, ret;
struct dwc3_msm *dwc = context;
- enum usb_vdd_type vdd_type = context->ss_vdd_type;
- int max_vol = vdd_val[vdd_type][VDD_MAX];
- min_vol = vdd_val[vdd_type][high ? VDD_MIN : VDD_NONE];
+ max_vol = dwc->vdd_high_vol_level;
+ min_vol = high ? dwc->vdd_low_vol_level : dwc->vdd_no_vol_level;
ret = regulator_set_voltage(dwc->ssusb_vddcx, min_vol, max_vol);
if (ret) {
dev_err(dwc->dev, "unable to set voltage for SSUSB_VDDCX\n");
@@ -1250,6 +1233,14 @@
return 0;
}
+ if (cancel_delayed_work_sync(&mdwc->chg_work))
+ dev_dbg(mdwc->dev, "%s: chg_work was pending\n", __func__);
+ if (mdwc->chg_state != USB_CHG_STATE_DETECTED) {
+ /* charger detection wasn't complete; re-init flags */
+ mdwc->chg_state = USB_CHG_STATE_UNDEFINED;
+ mdwc->charger.chg_type = DWC3_INVALID_CHARGER;
+ }
+
/* Sequence to put hardware in low power state:
* 1. Set OTGDISABLE to disable OTG block in HSPHY (saves power)
* 2. Clear charger detection control fields
@@ -1615,6 +1606,8 @@
struct resource *res;
void __iomem *tcsr;
int ret = 0;
+ int len = 0;
+ u32 tmp[3];
msm = devm_kzalloc(&pdev->dev, sizeof(*msm), GFP_KERNEL);
if (!msm) {
@@ -1689,19 +1682,26 @@
}
clk_prepare_enable(msm->ref_clk);
+
+ of_get_property(node, "qcom,vdd-voltage-level", &len);
+ if (len == sizeof(tmp)) {
+ of_property_read_u32_array(node, "qcom,vdd-voltage-level",
+ tmp, len/sizeof(*tmp));
+ msm->vdd_no_vol_level = tmp[0];
+ msm->vdd_low_vol_level = tmp[1];
+ msm->vdd_high_vol_level = tmp[2];
+ } else {
+ dev_err(&pdev->dev, "no qcom,vdd-voltage-level property\n");
+ ret = -EINVAL;
+ goto disable_ref_clk;
+ }
+
/* SS PHY */
- msm->ss_vdd_type = VDDCX_CORNER;
msm->ssusb_vddcx = devm_regulator_get(&pdev->dev, "ssusb_vdd_dig");
if (IS_ERR(msm->ssusb_vddcx)) {
- msm->ssusb_vddcx = devm_regulator_get(&pdev->dev,
- "SSUSB_VDDCX");
- if (IS_ERR(msm->ssusb_vddcx)) {
- dev_err(&pdev->dev, "unable to get ssusb vddcx\n");
- ret = PTR_ERR(msm->ssusb_vddcx);
- goto disable_ref_clk;
- }
- msm->ss_vdd_type = VDDCX;
- dev_dbg(&pdev->dev, "ss_vdd_type: VDDCX\n");
+ dev_err(&pdev->dev, "unable to get ssusb vddcx\n");
+ ret = PTR_ERR(msm->ssusb_vddcx);
+ goto disable_ref_clk;
}
ret = dwc3_ssusb_config_vddcx(1);
@@ -1729,18 +1729,11 @@
}
/* HS PHY */
- msm->hs_vdd_type = VDDCX_CORNER;
msm->hsusb_vddcx = devm_regulator_get(&pdev->dev, "hsusb_vdd_dig");
if (IS_ERR(msm->hsusb_vddcx)) {
- msm->hsusb_vddcx = devm_regulator_get(&pdev->dev,
- "HSUSB_VDDCX");
- if (IS_ERR(msm->hsusb_vddcx)) {
- dev_err(&pdev->dev, "unable to get hsusb vddcx\n");
- ret = PTR_ERR(msm->ssusb_vddcx);
- goto disable_ss_ldo;
- }
- msm->hs_vdd_type = VDDCX;
- dev_dbg(&pdev->dev, "hs_vdd_type: VDDCX\n");
+ dev_err(&pdev->dev, "unable to get hsusb vddcx\n");
+ ret = PTR_ERR(msm->hsusb_vddcx);
+ goto disable_ss_ldo;
}
ret = dwc3_hsusb_config_vddcx(1);
diff --git a/drivers/usb/dwc3/dwc3_otg.c b/drivers/usb/dwc3/dwc3_otg.c
index 7b672c4..fab443c 100644
--- a/drivers/usb/dwc3/dwc3_otg.c
+++ b/drivers/usb/dwc3/dwc3_otg.c
@@ -640,14 +640,9 @@
}
}
} else {
- if (charger) {
- if (charger->chg_type == DWC3_INVALID_CHARGER)
- charger->start_detection(dotg->charger,
- false);
- else
- charger->chg_type =
- DWC3_INVALID_CHARGER;
- }
+ if (charger)
+ charger->start_detection(dotg->charger, false);
+
dwc3_otg_set_power(phy, 0);
dev_dbg(phy->dev, "No device, trying to suspend\n");
pm_runtime_put_sync(phy->dev);
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index 4357e0d..79aac27 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -18,6 +18,7 @@
#include <linux/spinlock.h>
#include <mach/usb_gadget_xport.h>
+#include <mach/usb_bam.h>
#include "u_rmnet.h"
#include "gadget_chips.h"
@@ -49,6 +50,9 @@
struct list_head cpkt_resp_q;
atomic_t notify_count;
unsigned long cpkts_len;
+
+ /* IPA / RmNet Bridge support*/
+ struct usb_bam_connect_ipa_params ipa_params;
};
#define NR_RMNET_PORTS 3
@@ -149,7 +153,7 @@
/* Super speed support */
static struct usb_endpoint_descriptor rmnet_ss_notify_desc = {
- .bLength = sizeof rmnet_ss_notify_desc,
+ .bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
@@ -168,7 +172,7 @@
};
static struct usb_endpoint_descriptor rmnet_ss_in_desc = {
- .bLength = sizeof rmnet_ss_in_desc,
+ .bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
@@ -185,7 +189,7 @@
};
static struct usb_endpoint_descriptor rmnet_ss_out_desc = {
- .bLength = sizeof rmnet_ss_out_desc,
+ .bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
@@ -430,7 +434,17 @@
case USB_GADGET_XPORT_BAM:
case USB_GADGET_XPORT_BAM2BAM:
ret = gbam_connect(&dev->port, port_num,
- dxport, port_num);
+ dxport, port_num, NULL);
+ if (ret) {
+ pr_err("%s: gbam_connect failed: err:%d\n",
+ __func__, ret);
+ gsmd_ctrl_disconnect(&dev->port, port_num);
+ return ret;
+ }
+ break;
+ case USB_GADGET_XPORT_BAM2BAM_IPA:
+ ret = gbam_connect(&dev->port, port_num,
+ dxport, port_num, &(dev->ipa_params));
if (ret) {
pr_err("%s: gbam_connect failed: err:%d\n",
__func__, ret);
@@ -500,7 +514,11 @@
switch (dxport) {
case USB_GADGET_XPORT_BAM:
case USB_GADGET_XPORT_BAM2BAM:
- gbam_disconnect(&dev->port, port_num, dxport);
+ gbam_disconnect(&dev->port, port_num, dxport, NULL);
+ break;
+ case USB_GADGET_XPORT_BAM2BAM_IPA:
+ gbam_disconnect(&dev->port, port_num, dxport,
+ &(dev->ipa_params));
break;
case USB_GADGET_XPORT_HSIC:
ghsic_data_disconnect(&dev->port, port_num);
@@ -551,6 +569,7 @@
case USB_GADGET_XPORT_BAM:
break;
case USB_GADGET_XPORT_BAM2BAM:
+ case USB_GADGET_XPORT_BAM2BAM_IPA:
gbam_suspend(&dev->port, port_num, dxport);
break;
case USB_GADGET_XPORT_HSIC:
@@ -580,6 +599,7 @@
case USB_GADGET_XPORT_BAM:
break;
case USB_GADGET_XPORT_BAM2BAM:
+ case USB_GADGET_XPORT_BAM2BAM_IPA:
gbam_resume(&dev->port, port_num, dxport);
break;
case USB_GADGET_XPORT_HSIC:
@@ -1235,6 +1255,7 @@
no_data_bam_ports++;
break;
case USB_GADGET_XPORT_BAM2BAM:
+ case USB_GADGET_XPORT_BAM2BAM_IPA:
rmnet_port->data_xport_num = no_data_bam2bam_ports;
no_data_bam2bam_ports++;
break;
diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c
index 43347b3..74dba07 100644
--- a/drivers/usb/gadget/f_serial.c
+++ b/drivers/usb/gadget/f_serial.c
@@ -246,7 +246,7 @@
#ifdef CONFIG_MODEM_SUPPORT
static struct usb_endpoint_descriptor gser_ss_notify_desc = {
- .bLength = sizeof gser_ss_notify_desc,
+ .bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
diff --git a/drivers/usb/gadget/u_bam.c b/drivers/usb/gadget/u_bam.c
index f092329..7f3713f 100644
--- a/drivers/usb/gadget/u_bam.c
+++ b/drivers/usb/gadget/u_bam.c
@@ -101,6 +101,8 @@
u32 src_pipe_idx;
u32 dst_pipe_idx;
u8 connection_idx;
+ enum transport_type trans;
+ struct usb_bam_connect_ipa_params *ipa_params;
/* stats */
unsigned int pending_with_bam;
@@ -640,6 +642,21 @@
clear_bit(BAM_CH_OPENED, &d->flags);
}
+static void gbam2bam_disconnect_work(struct work_struct *w)
+{
+ struct gbam_port *port = container_of(w, struct gbam_port, connect_w);
+ struct bam_ch_info *d = &port->data_ch;
+ int ret;
+
+ if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+ ret = usb_bam_disconnect_ipa(d->connection_idx, d->ipa_params);
+ if (ret)
+ pr_err("%s: usb_bam_disconnect_ipa failed: err:%d\n",
+ __func__, ret);
+ rmnet_bridge_disconnect();
+ }
+}
+
static void gbam_connect_work(struct work_struct *w)
{
struct gbam_port *port = container_of(w, struct gbam_port, connect_w);
@@ -680,12 +697,38 @@
u32 sps_params;
int ret;
- ret = usb_bam_connect(d->connection_idx, &d->src_pipe_idx,
- &d->dst_pipe_idx);
- if (ret) {
- pr_err("%s: usb_bam_connect failed: err:%d\n",
- __func__, ret);
- return;
+ if (d->trans == USB_GADGET_XPORT_BAM2BAM) {
+ ret = usb_bam_connect(d->connection_idx, &d->src_pipe_idx,
+ &d->dst_pipe_idx);
+ if (ret) {
+ pr_err("%s: usb_bam_connect failed: err:%d\n",
+ __func__, ret);
+ return;
+ }
+ } else if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+ d->ipa_params->client = IPA_CLIENT_USB_CONS;
+ d->ipa_params->dir = PEER_PERIPHERAL_TO_USB;
+ ret = usb_bam_connect_ipa(d->ipa_params);
+ if (ret) {
+ pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
+ __func__, ret);
+ return;
+ }
+
+ d->ipa_params->client = IPA_CLIENT_USB_PROD;
+ d->ipa_params->dir = USB_TO_PEER_PERIPHERAL;
+ /* Currently only DMA mode is supported */
+ d->ipa_params->ipa_ep_cfg.mode.mode = IPA_DMA;
+ d->ipa_params->ipa_ep_cfg.mode.dst =
+ IPA_CLIENT_A2_TETHERED_CONS;
+ ret = usb_bam_connect_ipa(d->ipa_params);
+ if (ret) {
+ pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
+ __func__, ret);
+ return;
+ }
+ rmnet_bridge_connect(d->ipa_params->prod_clnt_hdl,
+ d->ipa_params->cons_clnt_hdl, 0);
}
d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_KERNEL);
@@ -873,6 +916,7 @@
spin_lock_init(&port->port_lock_dl);
INIT_WORK(&port->connect_w, gbam2bam_connect_work);
+ INIT_WORK(&port->disconnect_w, gbam2bam_disconnect_work);
/* data ch */
d = &port->data_ch;
@@ -993,7 +1037,8 @@
static void gam_debugfs_init(void) { }
#endif
-void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans)
+void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans,
+ struct usb_bam_connect_ipa_params *ipa_params)
{
struct gbam_port *port;
unsigned long flags;
@@ -1008,7 +1053,8 @@
return;
}
- if (trans == USB_GADGET_XPORT_BAM2BAM &&
+ if ((trans == USB_GADGET_XPORT_BAM2BAM ||
+ trans == USB_GADGET_XPORT_BAM2BAM_IPA) &&
port_num >= n_bam2bam_ports) {
pr_err("%s: invalid bam2bam portno#%d\n",
__func__, port_num);
@@ -1044,12 +1090,14 @@
gr->in->driver_data = NULL;
gr->out->driver_data = NULL;
- if (trans == USB_GADGET_XPORT_BAM)
+ if (trans == USB_GADGET_XPORT_BAM ||
+ trans == USB_GADGET_XPORT_BAM2BAM_IPA)
queue_work(gbam_wq, &port->disconnect_w);
}
int gbam_connect(struct grmnet *gr, u8 port_num,
- enum transport_type trans, u8 connection_idx)
+ enum transport_type trans, u8 connection_idx,
+ struct usb_bam_connect_ipa_params *ipa_params)
{
struct gbam_port *port;
struct bam_ch_info *d;
@@ -1063,7 +1111,9 @@
return -ENODEV;
}
- if (trans == USB_GADGET_XPORT_BAM2BAM && port_num >= n_bam2bam_ports) {
+ if ((trans == USB_GADGET_XPORT_BAM2BAM ||
+ trans == USB_GADGET_XPORT_BAM2BAM_IPA)
+ && port_num >= n_bam2bam_ports) {
pr_err("%s: invalid portno#%d\n", __func__, port_num);
return -ENODEV;
}
@@ -1115,8 +1165,15 @@
if (trans == USB_GADGET_XPORT_BAM2BAM) {
port->gr = gr;
d->connection_idx = connection_idx;
+ } else if (trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+ d->ipa_params = ipa_params;
+ port->gr = gr;
+ d->ipa_params->src_pipe = &(d->src_pipe_idx);
+ d->ipa_params->dst_pipe = &(d->dst_pipe_idx);
+ d->ipa_params->idx = connection_idx;
}
+ d->trans = trans;
queue_work(gbam_wq, &port->connect_w);
return 0;
@@ -1195,7 +1252,8 @@
struct gbam_port *port;
struct bam_ch_info *d;
- if (trans != USB_GADGET_XPORT_BAM2BAM)
+ if (trans != USB_GADGET_XPORT_BAM2BAM &&
+ trans != USB_GADGET_XPORT_BAM2BAM_IPA)
return;
port = bam2bam_ports[port_num];
@@ -1211,7 +1269,8 @@
struct gbam_port *port;
struct bam_ch_info *d;
- if (trans != USB_GADGET_XPORT_BAM2BAM)
+ if (trans != USB_GADGET_XPORT_BAM2BAM &&
+ trans != USB_GADGET_XPORT_BAM2BAM_IPA)
return;
port = bam2bam_ports[port_num];
diff --git a/drivers/usb/gadget/u_rmnet.h b/drivers/usb/gadget/u_rmnet.h
index 0f7c4fb..a3d42fa 100644
--- a/drivers/usb/gadget/u_rmnet.h
+++ b/drivers/usb/gadget/u_rmnet.h
@@ -48,8 +48,10 @@
int gbam_setup(unsigned int no_bam_port, unsigned int no_bam2bam_port);
int gbam_connect(struct grmnet *gr, u8 port_num,
- enum transport_type trans, u8 connection_idx);
-void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans);
+ enum transport_type trans, u8 connection_idx,
+ struct usb_bam_connect_ipa_params *ipa_params);
+void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans,
+ struct usb_bam_connect_ipa_params *ipa_params);
void gbam_suspend(struct grmnet *gr, u8 port_num, enum transport_type trans);
void gbam_resume(struct grmnet *gr, u8 port_num, enum transport_type trans);
int gsmd_ctrl_connect(struct grmnet *gr, int port_num);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index fff9465..1a75bd7 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -337,7 +337,7 @@
/* caller has locked the root hub, and should reset/reinit on error */
-static int ehci_bus_resume (struct usb_hcd *hcd)
+static int __maybe_unused ehci_bus_resume(struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 temp;
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
index 2d69a98..8c22f8e 100644
--- a/drivers/usb/host/ehci-msm-hsic.c
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -76,6 +76,7 @@
struct clk *phy_clk;
struct clk *cal_clk;
struct regulator *hsic_vddcx;
+ struct regulator *hsic_gdsc;
bool async_int;
atomic_t in_lpm;
struct wake_lock wlock;
@@ -103,6 +104,8 @@
struct msm_hsic_hcd *__mehci;
static bool debug_bus_voting_enabled = true;
+static u64 ehci_msm_hsic_dma_mask = DMA_BIT_MASK(32);
+
static unsigned int enable_payload_log = 1;
module_param(enable_payload_log, uint, S_IRUGO | S_IWUSR);
@@ -393,6 +396,35 @@
}
+/* Global Distributed Switch Controller (GDSC) init */
+static int msm_hsic_init_gdsc(struct msm_hsic_hcd *mehci, int init)
+{
+ int ret = 0;
+
+ if (IS_ERR(mehci->hsic_gdsc))
+ return 0;
+
+ if (!mehci->hsic_gdsc) {
+ mehci->hsic_gdsc = devm_regulator_get(mehci->dev,
+ "HSIC_GDSC");
+ if (IS_ERR(mehci->hsic_gdsc))
+ return 0;
+ }
+
+ if (init) {
+ ret = regulator_enable(mehci->hsic_gdsc);
+ if (ret) {
+ dev_err(mehci->dev, "unable to enable hsic gdsc\n");
+ return ret;
+ }
+ } else {
+ regulator_disable(mehci->hsic_gdsc);
+ }
+
+ return 0;
+
+}
+
static int ulpi_read(struct msm_hsic_hcd *mehci, u32 reg)
{
struct usb_hcd *hcd = hsic_to_hcd(mehci);
@@ -563,18 +595,22 @@
#define HSIC_PAD_CALIBRATION 0xA8
#define HSIC_GPIO_PAD_VAL 0x0A0AAA10
#define LINK_RESET_TIMEOUT_USEC (250 * 1000)
-static int msm_hsic_reset(struct msm_hsic_hcd *mehci)
+
+static void msm_hsic_phy_reset(struct msm_hsic_hcd *mehci)
{
struct usb_hcd *hcd = hsic_to_hcd(mehci);
- int ret;
- struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data;
msm_hsic_clk_reset(mehci);
/* select ulpi phy */
writel_relaxed(0x80000000, USB_PORTSC);
-
mb();
+}
+
+static int msm_hsic_start(struct msm_hsic_hcd *mehci)
+{
+ struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data;
+ int ret;
/* HSIC init sequence when HSIC signals (Strobe/Data) are
routed via GPIOs */
@@ -635,6 +671,15 @@
#define PHY_RESUME_TIMEOUT_USEC (100 * 1000)
#ifdef CONFIG_PM_SLEEP
+static int msm_hsic_reset(struct msm_hsic_hcd *mehci)
+{
+ /* reset HSIC phy */
+ msm_hsic_phy_reset(mehci);
+
+ /* HSIC init procedure (caliberation) */
+ return msm_hsic_start(mehci);
+}
+
static int msm_hsic_suspend(struct msm_hsic_hcd *mehci)
{
struct usb_hcd *hcd = hsic_to_hcd(mehci);
@@ -1534,6 +1579,11 @@
dev_dbg(&pdev->dev, "ehci_msm-hsic probe\n");
+ if (!pdev->dev.dma_mask)
+ pdev->dev.dma_mask = &ehci_msm_hsic_dma_mask;
+ if (!pdev->dev.coherent_dma_mask)
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
/* After parent device's probe is executed, it will be put in suspend
* mode. When child device's probe is called, driver core is not
* resuming parent device due to which parent will be in suspend even
@@ -1588,6 +1638,13 @@
if (pdata)
mehci->ehci.log2_irq_thresh = pdata->log2_irq_thresh;
+ ret = msm_hsic_init_gdsc(mehci, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to initialize GDSC\n");
+ ret = -ENODEV;
+ goto put_hcd;
+ }
+
res = platform_get_resource_byname(pdev,
IORESOURCE_IRQ,
"peripheral_status_irq");
@@ -1616,11 +1673,8 @@
init_completion(&mehci->rt_completion);
init_completion(&mehci->gpt0_completion);
- ret = msm_hsic_reset(mehci);
- if (ret) {
- dev_err(&pdev->dev, "unable to initialize PHY\n");
- goto deinit_vddcx;
- }
+
+ msm_hsic_phy_reset(mehci);
ehci_wq = create_singlethread_workqueue("ehci_wq");
if (!ehci_wq) {
@@ -1634,7 +1688,13 @@
ret = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
if (ret) {
dev_err(&pdev->dev, "unable to register HCD\n");
- goto unconfig_gpio;
+ goto destroy_wq;
+ }
+
+ ret = msm_hsic_start(mehci);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to initialize PHY\n");
+ goto destroy_wq;
}
device_init_wakeup(&pdev->dev, 1);
@@ -1710,11 +1770,11 @@
return 0;
-unconfig_gpio:
+destroy_wq:
destroy_workqueue(ehci_wq);
- msm_hsic_config_gpios(mehci, 0);
deinit_vddcx:
msm_hsic_init_vddcx(mehci, 0);
+ msm_hsic_init_gdsc(mehci, 0);
deinit_clocks:
msm_hsic_init_clocks(mehci, 0);
unmap:
@@ -1763,6 +1823,7 @@
usb_remove_hcd(hcd);
msm_hsic_config_gpios(mehci, 0);
msm_hsic_init_vddcx(mehci, 0);
+ msm_hsic_init_gdsc(mehci, 0);
msm_hsic_init_clocks(mehci, 0);
wake_lock_destroy(&mehci->wlock);
@@ -1881,7 +1942,11 @@
msm_hsic_runtime_idle)
};
#endif
-
+static const struct of_device_id hsic_host_dt_match[] = {
+ { .compatible = "qcom,hsic-host",
+ },
+ {}
+};
static struct platform_driver ehci_msm_hsic_driver = {
.probe = ehci_hsic_msm_probe,
.remove = __devexit_p(ehci_hsic_msm_remove),
@@ -1890,5 +1955,6 @@
#ifdef CONFIG_PM
.pm = &msm_hsic_dev_pm_ops,
#endif
+ .of_match_table = hsic_host_dt_match,
},
};
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 3d9422f..38a3c15 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1277,7 +1277,7 @@
static void handle_port_status(struct xhci_hcd *xhci,
union xhci_trb *event)
{
- struct usb_hcd *hcd;
+ struct usb_hcd *hcd = NULL;
u32 port_id;
u32 temp, temp1;
int max_ports;
@@ -1331,6 +1331,8 @@
*/
/* Find the right roothub. */
hcd = xhci_to_hcd(xhci);
+ if (!hcd)
+ goto cleanup;
if ((major_revision == 0x03) != (hcd->speed == HCD_USB3))
hcd = xhci->shared_hcd;
bus_state = &xhci->bus_state[hcd_index(hcd)];
diff --git a/drivers/video/msm/external_common.c b/drivers/video/msm/external_common.c
index c6ffaf2..0411baa 100644
--- a/drivers/video/msm/external_common.c
+++ b/drivers/video/msm/external_common.c
@@ -1407,30 +1407,37 @@
struct hdmi_disp_mode_list_type *disp_mode_list,
uint32 video_format)
{
- const struct hdmi_disp_mode_timing_type *timing =
- hdmi_common_get_supported_mode(video_format);
- boolean supported = timing != NULL;
+ const struct hdmi_disp_mode_timing_type *timing;
+ boolean supported = false;
+ boolean mhl_supported = true;
if (video_format >= HDMI_VFRMT_MAX)
return;
+ timing = hdmi_common_get_supported_mode(video_format);
+ supported = timing != NULL;
DEV_DBG("EDID: format: %d [%s], %s\n",
video_format, video_format_2string(video_format),
supported ? "Supported" : "Not-Supported");
- if (supported) {
- if (mhl_is_enabled()) {
- const struct hdmi_disp_mode_timing_type *mhl_timing =
- hdmi_mhl_get_supported_mode(video_format);
- boolean mhl_supported = mhl_timing != NULL;
- DEV_DBG("EDID: format: %d [%s], %s by MHL\n",
+
+ if (mhl_is_enabled()) {
+ const struct hdmi_disp_mode_timing_type *mhl_timing =
+ hdmi_mhl_get_supported_mode(video_format);
+ mhl_supported = mhl_timing != NULL;
+ DEV_DBG("EDID: format: %d [%s], %s by MHL\n",
video_format, video_format_2string(video_format),
- mhl_supported ? "Supported" : "Not-Supported");
- if (mhl_supported)
- disp_mode_list->disp_mode_list[
+ mhl_supported ? "Supported" : "Not-Supported");
+ }
+
+ if (supported && mhl_supported) {
+ disp_mode_list->disp_mode_list[
disp_mode_list->num_of_elements++] = video_format;
- } else
- disp_mode_list->disp_mode_list[
- disp_mode_list->num_of_elements++] = video_format;
+ if (video_format == external_common_state->video_resolution) {
+ DEV_DBG("%s: Default resolution %d [%s] supported\n",
+ __func__, video_format,
+ video_format_2string(video_format));
+ external_common_state->default_res_supported = true;
+ }
}
}
@@ -1866,6 +1873,7 @@
memset(&external_common_state->disp_mode_list, 0,
sizeof(external_common_state->disp_mode_list));
memset(edid_buf, 0, sizeof(edid_buf));
+ external_common_state->default_res_supported = false;
status = hdmi_common_read_edid_block(0, edid_buf);
if (status || !check_edid_header(edid_buf)) {
diff --git a/drivers/video/msm/external_common.h b/drivers/video/msm/external_common.h
index 43a8794..70a99ee 100644
--- a/drivers/video/msm/external_common.h
+++ b/drivers/video/msm/external_common.h
@@ -210,8 +210,10 @@
boolean hpd_state;
struct kobject *uevent_kobj;
uint32 video_resolution;
+ boolean default_res_supported;
struct device *dev;
struct switch_dev sdev;
+ struct switch_dev audio_sdev;
#ifdef CONFIG_FB_MSM_HDMI_3D
boolean format_3d;
void (*switch_3d)(boolean on);
diff --git a/drivers/video/msm/hdmi_msm.c b/drivers/video/msm/hdmi_msm.c
index 7a92645..516c92c 100644
--- a/drivers/video/msm/hdmi_msm.c
+++ b/drivers/video/msm/hdmi_msm.c
@@ -57,6 +57,22 @@
#define HDCP_DDC_CTRL_1 0x0124
#define HDMI_DDC_CTRL 0x020C
+#define HPD_DISCONNECT_POLARITY 0
+#define HPD_CONNECT_POLARITY 1
+
+#define SWITCH_SET_HDMI_AUDIO(d, force) \
+ do {\
+ if (!hdmi_msm_is_dvi_mode() &&\
+ ((force) ||\
+ (external_common_state->audio_sdev.state != (d)))) {\
+ switch_set_state(&external_common_state->audio_sdev,\
+ (d));\
+ DEV_INFO("%s: hdmi_audio state switched to %d\n",\
+ __func__,\
+ external_common_state->audio_sdev.state);\
+ } \
+ } while (0)
+
struct workqueue_struct *hdmi_work_queue;
struct hdmi_msm_state_type *hdmi_msm_state;
@@ -74,6 +90,7 @@
static int hdmi_msm_audio_off(void);
static int hdmi_msm_read_edid(void);
static void hdmi_msm_hpd_off(void);
+static boolean hdmi_msm_is_dvi_mode(void);
#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
@@ -768,8 +785,8 @@
/* Build EDID table */
hdmi_msm_read_edid();
switch_set_state(&external_common_state->sdev, 1);
- DEV_INFO("Hdmi state switched to %d: %s\n",
- external_common_state->sdev.state, __func__);
+ DEV_INFO("%s: hdmi state switched to %d\n", __func__,
+ external_common_state->sdev.state);
DEV_INFO("HDMI HPD: CONNECTED: send ONLINE\n");
kobject_uevent(external_common_state->uevent_kobj, KOBJ_ONLINE);
@@ -783,8 +800,8 @@
}
} else {
switch_set_state(&external_common_state->sdev, 0);
- DEV_INFO("hdmi: Hdmi state switch to %d: %s\n",
- external_common_state->sdev.state, __func__);
+ DEV_INFO("%s: hdmi state switch to %d\n", __func__,
+ external_common_state->sdev.state);
DEV_INFO("hdmi: HDMI HPD: sense DISCONNECTED: send OFFLINE\n");
kobject_uevent(external_common_state->uevent_kobj,
KOBJ_OFFLINE);
@@ -793,66 +810,13 @@
static void hdmi_msm_hpd_state_work(struct work_struct *work)
{
- boolean hpd_state;
-
if (!hdmi_msm_state || !hdmi_msm_state->hpd_initialized ||
!MSM_HDMI_BASE) {
DEV_ERR("hdmi: %s: ignored, probe failed\n", __func__);
return;
}
- mutex_lock(&hdmi_msm_state_mutex);
- DEV_DBG("%s: Handling HPD event in the workqueue\n", __func__);
-
- if (!hdmi_msm_state->hpd_cable_chg_detected) {
- /* The work item got called from outside the ISR */
- mutex_unlock(&hdmi_msm_state_mutex);
- if (external_common_state->hpd_state) {
- if (!external_common_state->
- disp_mode_list.num_of_elements)
- hdmi_msm_read_edid();
- }
- } else {
- hdmi_msm_state->hpd_cable_chg_detected = FALSE;
- mutex_unlock(&hdmi_msm_state_mutex);
- mutex_lock(&external_common_state_hpd_mutex);
- /*
- * Handle the connect event only if the cable is
- * still connected. This check is needed for the case
- * where we get a connect event followed by a disconnect
- * event in quick succession. In this case, there is no need
- * to process the connect event.
- */
- if ((external_common_state->hpd_state) &&
- !((HDMI_INP(0x0250) & 0x2) >> 1)) {
- external_common_state->hpd_state = 0;
- hdmi_msm_state->hpd_state_in_isr = 0;
- mutex_unlock(&external_common_state_hpd_mutex);
- DEV_DBG("%s: Ignoring HPD connect event\n", __func__);
- return;
- }
- mutex_unlock(&external_common_state_hpd_mutex);
- hdmi_msm_send_event(external_common_state->hpd_state);
- }
-
- /*
- * Wait for a short time before checking for
- * any changes in the connection status
- */
- udelay(100);
-
- mutex_lock(&external_common_state_hpd_mutex);
- /* HPD_INT_STATUS[0x0250] */
- hpd_state = (HDMI_INP(0x0250) & 0x2) >> 1;
-
- if (external_common_state->hpd_state != hpd_state) {
- external_common_state->hpd_state = hpd_state;
- hdmi_msm_state->hpd_state_in_isr = hpd_state;
- mutex_unlock(&external_common_state_hpd_mutex);
- hdmi_msm_send_event(hpd_state);
- } else {
- mutex_unlock(&external_common_state_hpd_mutex);
- }
+ hdmi_msm_send_event(external_common_state->hpd_state);
}
#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
@@ -965,9 +929,7 @@
DEV_INFO("HDCP: AUTH_FAIL_INT received, LINK0_STATUS=0x%08x\n",
link_status);
if (hdmi_msm_state->full_auth_done) {
- switch_set_state(&external_common_state->sdev, 0);
- DEV_INFO("Hdmi state switched to %d: %s\n",
- external_common_state->sdev.state, __func__);
+ SWITCH_SET_HDMI_AUDIO(0, 0);
envp[0] = "HDCP_STATE=FAIL";
envp[1] = NULL;
@@ -1051,55 +1013,18 @@
/* HDMI_HPD_INT_CTRL[0x0254] */
hpd_int_ctrl = HDMI_INP_ND(0x0254);
if ((hpd_int_ctrl & (1 << 2)) && (hpd_int_status & (1 << 0))) {
- boolean cable_detected = (hpd_int_status & 2) >> 1;
- DEV_DBG("%s: HPD IRQ, Ctrl=%04x, State=%04x\n", __func__,
- hpd_int_ctrl, hpd_int_status);
+ /*
+ * Got HPD interrupt. Ack the interrupt and disable any
+ * further HPD interrupts until we process this interrupt.
+ */
+ HDMI_OUTP(0x0254, ((hpd_int_ctrl | (BIT(0))) & ~BIT(2)));
- /* Ack the interrupt */
- HDMI_OUTP(0x0254, (hpd_int_ctrl | (1 << 0)));
-
- mutex_lock(&external_common_state_hpd_mutex);
- if (hdmi_msm_state->hpd_state_in_isr == cable_detected) {
- DEV_INFO("%s: HPD has the same state. Ignoring\n",
- __func__);
- mutex_unlock(&external_common_state_hpd_mutex);
- } else {
- if (!mod_timer(&hdmi_msm_state->hpd_state_timer,
- jiffies + HZ/2)) {
- hdmi_msm_state->hpd_state_in_isr =
- cable_detected;
- hdmi_msm_state->hpd_cable_chg_detected = TRUE;
- DEV_DBG("%s: Scheduled work to handle HPD %s\n",
- __func__,
- cable_detected ? "connect"
- : "disconnect");
- }
-
- mutex_unlock(&external_common_state_hpd_mutex);
- /*
- * HDCP Compliance 1A-01:
- * The Quantum Data Box 882 triggers two consecutive
- * HPD events very close to each other as a part of this
- * test which can trigger two parallel HDCP auth threads
- * if HDCP authentication is going on and we get ISR
- * then stop the authentication , rather than
- * reauthenticating it again
- */
- if (hdmi_msm_state->hdcp_activating &&
- !(hdmi_msm_state->full_auth_done)) {
- DEV_DBG("%s getting hpd while authenticating\n",
- __func__);
- mutex_lock(&hdcp_auth_state_mutex);
- hdmi_msm_state->hpd_during_auth = TRUE;
- mutex_unlock(&hdcp_auth_state_mutex);
- }
- }
-
- /* Set up HPD_CTRL to sense HPD event */
- HDMI_OUTP(0x0254, 4 | (cable_detected ? 0 : 2));
- DEV_DBG("%s: Setting HPD_CTRL=%d\n", __func__,
- HDMI_INP(0x0254));
-
+ external_common_state->hpd_state =
+ (HDMI_INP(0x0250) & BIT(1)) >> 1;
+ DEV_DBG("%s: Queuing work to handle HPD %s event\n", __func__,
+ external_common_state->hpd_state ? "connect" :
+ "disconnect");
+ queue_work(hdmi_work_queue, &hdmi_msm_state->hpd_state_work);
return IRQ_HANDLED;
}
@@ -2468,8 +2393,8 @@
/* 0x0110 HDCP_CTRL
[8] ENCRYPTION_ENABLE
[0] ENABLE */
- /* encryption_enable | enable */
- HDMI_OUTP(0x0110, (1 << 8) | (1 << 0));
+ /* Enable HDCP. Encryption should be enabled after reading R0 */
+ HDMI_OUTP(0x0110, BIT(0));
/*
* Check to see if a HDCP DDC Failure is indicated in
@@ -2664,6 +2589,9 @@
goto error;
}
+ /* Enable HDCP Encryption */
+ HDMI_OUTP(0x0110, BIT(0) | BIT(8));
+
DEV_INFO("HDCP: authentication part I, successful\n");
is_part1_done = FALSE;
return 0;
@@ -3023,17 +2951,15 @@
mutex_unlock(&hdmi_msm_state_mutex);
mutex_lock(&hdcp_auth_state_mutex);
- /*
- * Initialize this to zero here to make
- * sure HPD has not happened yet
- */
- hdmi_msm_state->hpd_during_auth = FALSE;
/* This flag prevents other threads from re-authenticating
* after we've just authenticated (i.e., finished part3)
* We probably need to protect this in a mutex lock */
hdmi_msm_state->full_auth_done = FALSE;
mutex_unlock(&hdcp_auth_state_mutex);
+ /* Disable HDCP before we start part1 */
+ HDMI_OUTP(0x0110, 0x0);
+
/* PART I Authentication*/
ret = hdcp_authentication_part1();
if (ret)
@@ -3082,17 +3008,13 @@
envp[1] = NULL;
kobject_uevent_env(external_common_state->uevent_kobj,
KOBJ_CHANGE, envp);
+
+ SWITCH_SET_HDMI_AUDIO(1, 0);
}
- switch_set_state(&external_common_state->sdev, 1);
- DEV_INFO("Hdmi state switched to %d: %s\n",
- external_common_state->sdev.state, __func__);
return;
error:
- mutex_lock(&hdmi_msm_state_mutex);
- hdmi_msm_state->hdcp_activating = FALSE;
- mutex_unlock(&hdmi_msm_state_mutex);
if (hdmi_msm_state->hpd_during_auth) {
DEV_WARN("Calling Deauthentication: HPD occured during "
"authentication from [%s]\n", __func__);
@@ -3106,9 +3028,9 @@
queue_work(hdmi_work_queue,
&hdmi_msm_state->hdcp_reauth_work);
}
- switch_set_state(&external_common_state->sdev, 0);
- DEV_INFO("Hdmi state switched to %d: %s\n",
- external_common_state->sdev.state, __func__);
+ mutex_lock(&hdmi_msm_state_mutex);
+ hdmi_msm_state->hdcp_activating = FALSE;
+ mutex_unlock(&hdmi_msm_state_mutex);
}
static void hdmi_msm_video_setup(int video_format)
@@ -3663,28 +3585,24 @@
static int hdmi_msm_audio_off(void)
{
- uint32 audio_pkt_ctrl, audio_cfg;
- /* Number of wait iterations */
- int i = 10;
- audio_pkt_ctrl = HDMI_INP_ND(0x0020);
- audio_cfg = HDMI_INP_ND(0x01D0);
+ uint32 audio_cfg;
+ int i, timeout_val = 50;
- /* Checking BIT[0] of AUDIO PACKET CONTROL and */
- /* AUDIO CONFIGURATION register */
- while (((audio_pkt_ctrl & 0x00000001) || (audio_cfg & 0x00000001))
- && (i--)) {
- audio_pkt_ctrl = HDMI_INP_ND(0x0020);
- audio_cfg = HDMI_INP_ND(0x01D0);
- DEV_DBG("%d times :: HDMI AUDIO PACKET is %08x and "
- "AUDIO CFG is %08x", i, audio_pkt_ctrl, audio_cfg);
- msleep(100);
- if (!i) {
- DEV_ERR("%s:failed to set BIT[0] AUDIO PACKET"
- "CONTROL or AUDIO CONFIGURATION REGISTER\n",
- __func__);
- return -ETIMEDOUT;
+ for (i = 0; (i < timeout_val) &&
+ ((audio_cfg = HDMI_INP_ND(0x01D0)) & BIT(0)); i++) {
+ DEV_DBG("%s: %d times: AUDIO CFG is %08xi\n", __func__,
+ i+1, audio_cfg);
+ if (!((i+1) % 10)) {
+ DEV_ERR("%s: audio still on after %d sec. try again\n",
+ __func__, (i+1)/10);
+ SWITCH_SET_HDMI_AUDIO(0, 1);
}
+ msleep(100);
}
+
+ if (i == timeout_val)
+ DEV_ERR("%s: Error: cannot turn off audio engine\n", __func__);
+
hdmi_msm_audio_info_setup(FALSE, 0, 0, 0, FALSE);
hdmi_msm_audio_acr_setup(FALSE, 0, 0, 0);
DEV_INFO("HDMI Audio: Disabled\n");
@@ -4197,8 +4115,17 @@
hdmi_msm_set_mode(TRUE);
hdmi_msm_video_setup(external_common_state->video_resolution);
- if (!hdmi_msm_is_dvi_mode())
+ if (!hdmi_msm_is_dvi_mode()) {
hdmi_msm_audio_setup();
+
+ /*
+ * Send the audio switch device notification if HDCP is
+ * not enabled. Otherwise, the notification would be
+ * sent after HDCP authentication is successful.
+ */
+ if (!hdmi_msm_state->hdcp_enable)
+ SWITCH_SET_HDMI_AUDIO(1, 0);
+ }
hdmi_msm_avi_info_frame();
#ifdef CONFIG_FB_MSM_HDMI_3D
hdmi_msm_vendor_infoframe_packetsetup();
@@ -4223,36 +4150,6 @@
DEV_INFO("HDMI Core: Initialized\n");
}
-static void hdmi_msm_hpd_state_timer(unsigned long data)
-{
- if (!work_busy(&hdmi_msm_state->hpd_state_work)) {
- /*
- * There is no event currently queued.
- * Only queue the work if this event has not already
- * been processed.
- */
- if (external_common_state->hpd_state !=
- hdmi_msm_state->hpd_state_in_isr) {
- /*
- * There is no need to use any synchronization
- * construct for safeguarding these state vairables
- * here since the only other place these are modified
- * is in the HPD work thread, which is known to be not
- * pending/running.
- */
- external_common_state->hpd_state =
- hdmi_msm_state->hpd_state_in_isr;
- DEV_DBG("%s: Queuing work to handle HPD %s event\n",
- __func__,
- external_common_state->hpd_state ?
- "connect" : "disconnect");
- queue_work(hdmi_work_queue,
- &hdmi_msm_state->hpd_state_work);
- return;
- }
- }
-}
-
static void hdmi_msm_hdcp_timer(unsigned long data)
{
if (!hdmi_msm_state->hdcp_enable) {
@@ -4270,6 +4167,27 @@
}
#endif
+static void hdmi_msm_hpd_polarity_setup(bool polarity, bool trigger)
+{
+ u32 cable_sense;
+ if (polarity)
+ HDMI_OUTP(0x0254, BIT(2) | BIT(1));
+ else
+ HDMI_OUTP(0x0254, BIT(2));
+
+ cable_sense = (HDMI_INP(0x0250) & BIT(1)) >> 1;
+ DEV_DBG("%s: listen=%s, sense=%s\n", __func__,
+ polarity ? "connect" : "disconnect",
+ cable_sense ? "connect" : "disconnect");
+ if (trigger && (cable_sense == polarity)) {
+ u32 reg_val = HDMI_INP(0x0258);
+
+ /* Toggle HPD circuit to trigger HPD sense */
+ HDMI_OUTP(0x0258, reg_val & ~BIT(28));
+ HDMI_OUTP(0x0258, reg_val | BIT(28));
+ }
+}
+
static void hdmi_msm_hpd_off(void)
{
int rc = 0;
@@ -4280,7 +4198,6 @@
}
DEV_DBG("%s: (timer, 5V, IRQ off)\n", __func__);
- del_timer(&hdmi_msm_state->hpd_state_timer);
disable_irq(hdmi_msm_state->irq);
/* Disable HPD interrupt */
@@ -4349,27 +4266,22 @@
/* Set up HPD state variables */
mutex_lock(&external_common_state_hpd_mutex);
external_common_state->hpd_state = 0;
- hdmi_msm_state->hpd_state_in_isr = 0;
mutex_unlock(&external_common_state_hpd_mutex);
mutex_lock(&hdmi_msm_state_mutex);
- hdmi_msm_state->hpd_cable_chg_detected = TRUE;
mutex_unlock(&hdmi_msm_state_mutex);
- /* Set up HPD_CTRL to sense HPD event */
- HDMI_OUTP(0x0254, 0x6);
- DEV_DBG("%s: Setting HPD_CTRL=%d\n", __func__,
- HDMI_INP(0x0254));
+ enable_irq(hdmi_msm_state->irq);
hdmi_msm_state->hpd_initialized = TRUE;
- enable_irq(hdmi_msm_state->irq);
-
/* set timeout to 4.1ms (max) for hardware debounce */
hpd_ctrl = HDMI_INP(0x0258) | 0x1FFF;
- /* Toggle HPD circuit to trigger HPD sense */
- HDMI_OUTP(0x0258, ~(1 << 28) & hpd_ctrl);
- HDMI_OUTP(0x0258, (1 << 28) | hpd_ctrl);
+ /* Turn on HPD HW circuit */
+ HDMI_OUTP(0x0258, hpd_ctrl | BIT(28));
+
+ /* Set up HPD_CTRL to sense HPD event */
+ hdmi_msm_hpd_polarity_setup(HPD_CONNECT_POLARITY, true);
}
DEV_DBG("%s: (IRQ, 5V on)\n", __func__);
@@ -4413,35 +4325,54 @@
if (!hdmi_msm_state || !hdmi_msm_state->hdmi_app_clk || !MSM_HDMI_BASE)
return -ENODEV;
+ if (!hdmi_msm_state->hpd_initialized ||
+ !external_common_state->hpd_state) {
+ DEV_DBG("%s: HPD not initialized/cable not conn. Returning\n",
+ __func__);
+ return 0;
+ }
+
DEV_INFO("power: ON (%dx%d %d)\n", mfd->var_xres, mfd->var_yres,
mfd->var_pixclock);
+ /* Only start transmission with supported resolution */
changed = hdmi_common_get_video_format_from_drv_data(mfd);
- hdmi_msm_audio_info_setup(TRUE, 0, 0, 0, FALSE);
+ if (changed || external_common_state->default_res_supported) {
+ hdmi_msm_audio_info_setup(TRUE, 0, 0, 0, FALSE);
+ mutex_lock(&external_common_state_hpd_mutex);
+ hdmi_msm_state->panel_power_on = TRUE;
+ if (external_common_state->hpd_state &&
+ hdmi_msm_is_power_on()) {
+ DEV_DBG("%s: Turning HDMI on\n", __func__);
+ mutex_unlock(&external_common_state_hpd_mutex);
+ hdmi_msm_turn_on();
- mutex_lock(&external_common_state_hpd_mutex);
- hdmi_msm_state->panel_power_on = TRUE;
- if (external_common_state->hpd_state && hdmi_msm_is_power_on()) {
- DEV_DBG("%s: Turning HDMI on\n", __func__);
- mutex_unlock(&external_common_state_hpd_mutex);
- hdmi_msm_turn_on();
-
- if (hdmi_msm_state->hdcp_enable) {
- /* Kick off HDCP Authentication */
- mutex_lock(&hdcp_auth_state_mutex);
- hdmi_msm_state->reauth = FALSE;
- hdmi_msm_state->full_auth_done = FALSE;
- mutex_unlock(&hdcp_auth_state_mutex);
- mod_timer(&hdmi_msm_state->hdcp_timer, jiffies + HZ/2);
+ if (hdmi_msm_state->hdcp_enable) {
+ /* Kick off HDCP Authentication */
+ mutex_lock(&hdcp_auth_state_mutex);
+ hdmi_msm_state->reauth = FALSE;
+ hdmi_msm_state->full_auth_done = FALSE;
+ mutex_unlock(&hdcp_auth_state_mutex);
+ mod_timer(&hdmi_msm_state->hdcp_timer,
+ jiffies + HZ/2);
+ }
+ } else {
+ mutex_unlock(&external_common_state_hpd_mutex);
}
- } else
- mutex_unlock(&external_common_state_hpd_mutex);
- hdmi_msm_dump_regs("HDMI-ON: ");
+ hdmi_msm_dump_regs("HDMI-ON: ");
+ DEV_INFO("power=%s DVI= %s\n",
+ hdmi_msm_is_power_on() ? "ON" : "OFF" ,
+ hdmi_msm_is_dvi_mode() ? "ON" : "OFF");
+ } else {
+ DEV_ERR("%s: Video fmt %d not supp. Returning\n",
+ __func__,
+ external_common_state->video_resolution);
+ }
- DEV_INFO("power=%s DVI= %s\n",
- hdmi_msm_is_power_on() ? "ON" : "OFF" ,
- hdmi_msm_is_dvi_mode() ? "ON" : "OFF");
+ /* Enable HPD interrupt and listen to disconnect interrupts */
+ hdmi_msm_hpd_polarity_setup(HPD_DISCONNECT_POLARITY,
+ external_common_state->hpd_state);
return 0;
}
@@ -4450,11 +4381,6 @@
char *envp[2];
/* Simulating a HPD event based on MHL event */
- hdmi_msm_state->hpd_cable_chg_detected = FALSE;
- /* QDSP OFF preceding the HPD event notification */
- switch_set_state(&external_common_state->sdev, 0);
- DEV_INFO("Hdmi state switched to %d: %s\n",
- external_common_state->sdev.state, __func__);
if (on) {
hdmi_msm_read_edid();
hdmi_msm_state->reauth = FALSE ;
@@ -4472,8 +4398,8 @@
kobject_uevent_env(external_common_state->uevent_kobj,
KOBJ_CHANGE, envp);
switch_set_state(&external_common_state->sdev, 1);
- DEV_INFO("Hdmi state switched to %d: %s\n",
- external_common_state->sdev.state, __func__);
+ DEV_INFO("%s: hdmi state switched to %d\n",
+ __func__, external_common_state->sdev.state);
} else {
hdmi_msm_hdcp_enable();
}
@@ -4482,8 +4408,8 @@
kobject_uevent(external_common_state->uevent_kobj,
KOBJ_OFFLINE);
switch_set_state(&external_common_state->sdev, 0);
- DEV_INFO("Hdmi state switched to %d: %s\n",
- external_common_state->sdev.state, __func__);
+ DEV_INFO("%s: hdmi state switched to %d\n", __func__,
+ external_common_state->sdev.state);
}
}
EXPORT_SYMBOL(mhl_connect_api);
@@ -4499,21 +4425,48 @@
if (!hdmi_msm_state->hdmi_app_clk)
return -ENODEV;
- mutex_lock(&hdmi_msm_state_mutex);
- if (hdmi_msm_state->hdcp_activating) {
- hdmi_msm_state->panel_power_on = FALSE;
- mutex_unlock(&hdmi_msm_state_mutex);
- DEV_INFO("HDCP: activating, returning\n");
+ if (!hdmi_msm_state->panel_power_on) {
+ DEV_DBG("%s: panel not on. returning\n", __func__);
return 0;
}
- mutex_unlock(&hdmi_msm_state_mutex);
- DEV_INFO("power: OFF (audio off, Reset Core)\n");
- hdmi_msm_audio_off();
- hdcp_deauthenticate();
+ if (hdmi_msm_state->hdcp_enable) {
+ if (hdmi_msm_state->hdcp_activating) {
+ /*
+ * Let the HDCP work know that we got an HPD
+ * disconnect so that it can stop the
+ * reauthentication loop.
+ */
+ mutex_lock(&hdcp_auth_state_mutex);
+ hdmi_msm_state->hpd_during_auth = TRUE;
+ mutex_unlock(&hdcp_auth_state_mutex);
+ }
+
+ /*
+ * Cancel any pending reauth attempts.
+ * If one is ongoing, wait for it to finish
+ */
+ cancel_work_sync(&hdmi_msm_state->hdcp_reauth_work);
+ cancel_work_sync(&hdmi_msm_state->hdcp_work);
+ del_timer_sync(&hdmi_msm_state->hdcp_timer);
+
+ hdcp_deauthenticate();
+ }
+
+ SWITCH_SET_HDMI_AUDIO(0, 0);
+
+ if (!hdmi_msm_is_dvi_mode())
+ hdmi_msm_audio_off();
+
hdmi_msm_powerdown_phy();
hdmi_msm_state->panel_power_on = FALSE;
+ DEV_INFO("power: OFF (audio off)\n");
+
+ /* Enable HPD interrupt and listen to connect interrupts */
+ hdmi_msm_hpd_polarity_setup(HPD_CONNECT_POLARITY,
+ !external_common_state->hpd_state);
+
return 0;
}
@@ -4649,13 +4602,6 @@
}
disable_irq(hdmi_msm_state->irq);
- init_timer(&hdmi_msm_state->hpd_state_timer);
- hdmi_msm_state->hpd_state_timer.function =
- hdmi_msm_hpd_state_timer;
- hdmi_msm_state->hpd_state_timer.data = (uint32)NULL;
-
- hdmi_msm_state->hpd_state_timer.expires = 0xffffffffL;
-
#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
init_timer(&hdmi_msm_state->cec_read_timer);
hdmi_msm_state->cec_read_timer.function =
@@ -4689,8 +4635,19 @@
external_common_state->sdev.name = "hdmi_as_primary";
else
external_common_state->sdev.name = "hdmi";
- if (switch_dev_register(&external_common_state->sdev) < 0)
+ if (switch_dev_register(&external_common_state->sdev) < 0) {
DEV_ERR("Hdmi switch registration failed\n");
+ rc = -ENODEV;
+ goto error;
+ }
+
+ external_common_state->audio_sdev.name = "hdmi_audio";
+ if (switch_dev_register(&external_common_state->audio_sdev) < 0) {
+ DEV_ERR("Hdmi audio switch registration failed\n");
+ switch_dev_unregister(&external_common_state->sdev);
+ rc = -ENODEV;
+ goto error;
+ }
/* Set the default video resolution for MHL-enabled display */
if (hdmi_msm_state->is_mhl_enabled) {
@@ -4733,6 +4690,7 @@
/* Unregister hdmi node from switch driver */
switch_dev_unregister(&external_common_state->sdev);
+ switch_dev_unregister(&external_common_state->audio_sdev);
hdmi_msm_hpd_off();
free_irq(hdmi_msm_state->irq, NULL);
@@ -4772,9 +4730,14 @@
if (on) {
rc = hdmi_msm_hpd_on();
} else {
+ external_common_state->hpd_state = 0;
hdmi_msm_hpd_off();
+ SWITCH_SET_HDMI_AUDIO(0, 0);
+
/* Set HDMI switch node to 0 on HPD feature disable */
switch_set_state(&external_common_state->sdev, 0);
+ DEV_INFO("%s: hdmi state switched to %d\n", __func__,
+ external_common_state->sdev.state);
}
return rc;
diff --git a/drivers/video/msm/hdmi_msm.h b/drivers/video/msm/hdmi_msm.h
index 20bd492..17cefdd 100644
--- a/drivers/video/msm/hdmi_msm.h
+++ b/drivers/video/msm/hdmi_msm.h
@@ -53,15 +53,12 @@
struct hdmi_msm_state_type {
boolean panel_power_on;
boolean hpd_initialized;
- boolean hpd_state_in_isr;
#ifdef CONFIG_SUSPEND
boolean pm_suspended;
#endif
- boolean hpd_cable_chg_detected;
boolean full_auth_done;
boolean hpd_during_auth;
struct work_struct hpd_state_work;
- struct timer_list hpd_state_timer;
struct completion ddc_sw_done;
bool hdcp_enable;
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index fc512c1..a827d6a 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -2510,7 +2510,6 @@
#if defined(CONFIG_FB_MSM_MIPI_DSI) && defined(CONFIG_FB_MSM_MDP40)
struct mipi_panel_info *mipi;
#endif
- unsigned int mdp_r = 0;
if ((pdev->id == 0) && (pdev->num_resources > 0)) {
mdp_init_pdev = pdev;
@@ -2532,14 +2531,6 @@
}
mdp_rev = mdp_pdata->mdp_rev;
- if (mdp_rev == MDP_REV_42) {
- mdp_r = inpdw(MDP_BASE + 0x0);
- mdp_r = ((mdp_r & 0x30000) >> 16);
- if (mdp_r == 3) {
- mdp_rev = MDP_REV_43;
- mdp_pdata->mdp_rev = MDP_REV_43;
- }
- }
mdp_iommu_split_domain = mdp_pdata->mdp_iommu_split_domain;
@@ -3127,7 +3118,8 @@
{
mdp_suspend_sub();
#ifdef CONFIG_FB_MSM_DTV
- mdp4_dtv_set_black_screen(FALSE);
+ mdp4_solidfill_commit(MDP4_MIXER1);
+ mdp4_dtv_set_black_screen();
#endif
mdp_footswitch_ctrl(FALSE);
}
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index a707705..6edc9f2 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -531,7 +531,7 @@
}
#endif /* CONFIG_FB_MSM_DTV */
-void mdp4_dtv_set_black_screen(bool commit);
+void mdp4_dtv_set_black_screen(void);
int mdp4_overlay_dtv_set(struct msm_fb_data_type *mfd,
struct mdp4_overlay_pipe *pipe);
@@ -593,6 +593,7 @@
struct mdp4_overlay_pipe *mdp4_overlay_pipe_alloc(int ptype, int mixer);
void mdp4_overlay_dma_commit(int mixer);
void mdp4_overlay_vsync_commit(struct mdp4_overlay_pipe *pipe);
+void mdp4_solidfill_commit(int mixer);
void mdp4_mixer_stage_commit(int mixer);
void mdp4_dsi_cmd_do_update(int cndx, struct mdp4_overlay_pipe *pipe);
void mdp4_lcdc_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe);
diff --git a/drivers/video/msm/mdp4_dtv.c b/drivers/video/msm/mdp4_dtv.c
index bd0ce2f..4b83224 100644
--- a/drivers/video/msm/mdp4_dtv.c
+++ b/drivers/video/msm/mdp4_dtv.c
@@ -38,6 +38,7 @@
static int dtv_off(struct platform_device *pdev);
static int dtv_on(struct platform_device *pdev);
+static int dtv_off_sub(void);
static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST];
static int pdev_list_cnt;
@@ -45,6 +46,9 @@
static struct clk *tv_src_clk;
static struct clk *hdmi_clk;
static struct clk *mdp_tv_clk;
+static struct platform_device *dtv_pdev;
+static struct workqueue_struct *dtv_work_queue;
+static struct work_struct dtv_off_work;
static int mdp4_dtv_runtime_suspend(struct device *dev)
@@ -86,8 +90,48 @@
static int dtv_off(struct platform_device *pdev)
{
int ret = 0;
+ struct msm_fb_data_type *mfd = NULL;
- ret = panel_next_off(pdev);
+ if (!pdev) {
+ pr_err("%s: FAILED: invalid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ mfd = platform_get_drvdata(pdev);
+ if (!mfd) {
+ pr_err("%s: FAILED: invalid mfd\n", __func__);
+ return -EINVAL;
+ }
+
+ dtv_pdev = pdev;
+ /*
+ * If it's a suspend operation then handle the device
+ * power down synchronously.
+ * Otherwise, queue work item to handle power down sequence.
+ * This is needed since we need to wait for the audio engine
+ * to shutdown first before we turn off the DTV device.
+ */
+ if (!mfd->suspend.op_suspend) {
+ pr_debug("%s: Queuing work to turn off HDMI core\n", __func__);
+ queue_work(dtv_work_queue, &dtv_off_work);
+ } else {
+ pr_debug("%s: turning off HDMI core\n", __func__);
+ ret = dtv_off_sub();
+ }
+
+ return ret;
+}
+
+static int dtv_off_sub(void)
+{
+ int ret = 0;
+
+ if (!dtv_pdev) {
+ pr_err("%s: FAILED: invalid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = panel_next_off(dtv_pdev);
pr_info("%s\n", __func__);
@@ -112,12 +156,20 @@
return ret;
}
+static void dtv_off_work_func(struct work_struct *work)
+{
+ dtv_off_sub();
+}
+
static int dtv_on(struct platform_device *pdev)
{
int ret = 0;
struct msm_fb_data_type *mfd;
unsigned long panel_pixclock_freq , pm_qos_rate;
+ /* If a power down is already underway, wait for it to finish */
+ flush_work_sync(&dtv_off_work);
+
mfd = platform_get_drvdata(pdev);
panel_pixclock_freq = mfd->fbi->var.pixclock;
@@ -215,6 +267,8 @@
return 0;
}
+ dtv_work_queue = create_singlethread_workqueue("dtv_work");
+ INIT_WORK(&dtv_off_work, dtv_off_work_func);
mfd = platform_get_drvdata(pdev);
if (!mfd)
@@ -302,6 +356,8 @@
static int dtv_remove(struct platform_device *pdev)
{
+ if (dtv_work_queue)
+ destroy_workqueue(dtv_work_queue);
#ifdef CONFIG_MSM_BUS_SCALING
if (dtv_pdata && dtv_pdata->bus_scale_table &&
dtv_bus_scale_handle > 0)
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index e609969..59565c1 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -48,6 +48,7 @@
struct mdp4_overlay_pipe *stage[MDP4_MIXER_MAX][MDP4_MIXER_STAGE_MAX];
struct mdp4_overlay_pipe *baselayer[MDP4_MIXER_MAX];
struct blend_cfg blend[MDP4_MIXER_MAX][MDP4_MIXER_STAGE_MAX];
+ struct mdp4_overlay_pipe sf_plist[MDP4_MIXER_MAX][OVERLAY_PIPE_MAX];
uint32 mixer_cfg[MDP4_MIXER_MAX];
uint32 flush[MDP4_MIXER_MAX];
struct iommu_free_list iommu_free[MDP4_MIXER_MAX];
@@ -121,6 +122,7 @@
static struct ion_client *display_iclient;
+static void mdp4_overlay_bg_solidfill(struct blend_cfg *blend);
/*
* mdp4_overlay_iommu_unmap_freelist()
@@ -778,6 +780,7 @@
case MDP_Y_CRCB_H2V1:
case MDP_Y_CBCR_H2V1:
case MDP_Y_CRCB_H1V2:
+ case MDP_Y_CBCR_H1V2:
*luma_off = pipe->src_x +
(pipe->src_y * pipe->srcp0_ystride);
*chroma_off = pipe->src_x +
@@ -987,6 +990,7 @@
case MDP_Y_CRCB_H2V1:
case MDP_Y_CBCR_H2V1:
case MDP_Y_CRCB_H1V2:
+ case MDP_Y_CBCR_H1V2:
case MDP_Y_CRCB_H2V2:
case MDP_Y_CBCR_H2V2:
case MDP_Y_CBCR_H2V2_TILE:
@@ -1171,6 +1175,7 @@
case MDP_Y_CRCB_H2V1:
case MDP_Y_CBCR_H2V1:
case MDP_Y_CRCB_H1V2:
+ case MDP_Y_CBCR_H1V2:
case MDP_Y_CRCB_H2V2:
case MDP_Y_CBCR_H2V2:
case MDP_Y_CRCB_H1V1:
@@ -1211,6 +1216,10 @@
pipe->element1 = C1_B_Cb;
pipe->element0 = C2_R_Cr;
pipe->chroma_sample = MDP4_CHROMA_H1V2;
+ } else if (pipe->src_format == MDP_Y_CBCR_H1V2) {
+ pipe->element1 = C2_R_Cr;
+ pipe->element0 = C1_B_Cb;
+ pipe->chroma_sample = MDP4_CHROMA_H1V2;
} else if (pipe->src_format == MDP_Y_CRCB_H2V2) {
pipe->element1 = C1_B_Cb;
pipe->element0 = C2_R_Cr;
@@ -1372,6 +1381,7 @@
case MDP_Y_CRCB_H2V2:
case MDP_Y_CRCB_H2V1:
case MDP_Y_CRCB_H1V2:
+ case MDP_Y_CBCR_H1V2:
case MDP_Y_CRCB_H1V1:
case MDP_Y_CBCR_H1V1:
case MDP_YCRCB_H1V1:
@@ -1604,6 +1614,35 @@
return cnt;
}
+void mdp4_solidfill_commit(int mixer)
+{
+ struct blend_cfg bcfg;
+ struct mdp4_overlay_pipe *pp = NULL;
+ int i = 0;
+
+ for (i = 0; i < OVERLAY_PIPE_MAX; i++) {
+ pp = &ctrl->sf_plist[mixer][i];
+ if (pp->pipe_ndx && pp->solid_fill) {
+ bcfg.solidfill = 1;
+ bcfg.solidfill_pipe = pp;
+ mdp4_overlay_bg_solidfill(&bcfg);
+ mdp4_overlay_reg_flush(pp, 1);
+ mdp4_mixer_stage_up(pp, 0);
+ }
+ }
+ mdp4_mixer_stage_commit(MDP4_MIXER1);
+
+ for (i = 0; i < OVERLAY_PIPE_MAX; i++) {
+ pp = &ctrl->sf_plist[mixer][i];
+ if (pp->pipe_ndx && pp->solid_fill) {
+ mdp4_overlay_reg_flush(pp, 1);
+ mdp4_mixer_stage_down(pp, 0);
+ pp->solid_fill = 0;
+ }
+ }
+ mdp4_mixer_stage_commit(MDP4_MIXER1);
+}
+
void mdp4_mixer_stage_commit(int mixer)
{
struct mdp4_overlay_pipe *pipe;
@@ -1701,7 +1740,8 @@
ctrl->stage[mixer][i] = NULL; /* clear it */
}
- if (commit || (mixer > 0 && !hdmi_prim_display))
+ if (commit || ((mixer == 1) && !hdmi_prim_display) ||
+ (mixer == 2))
mdp4_mixer_stage_commit(mixer);
}
/*
@@ -1945,7 +1985,7 @@
struct mdp4_overlay_pipe *d_pipe;
struct mdp4_overlay_pipe *s_pipe;
struct blend_cfg *blend;
- int i, off, alpha_drop = 0;
+ int i, off, alpha_drop;
int d_alpha, s_alpha;
unsigned char *overlay_base;
uint32 c0, c1, c2, base_premulti;
@@ -1971,8 +2011,10 @@
d_alpha = 0;
continue;
}
+ alpha_drop = 0; /* per stage */
/* alpha channel is lost on VG pipe when using QSEED or M/N */
if (s_pipe->pipe_type == OVERLAY_TYPE_VIDEO &&
+ s_pipe->alpha_enable &&
((s_pipe->op_mode & MDP4_OP_SCALEY_EN) ||
(s_pipe->op_mode & MDP4_OP_SCALEX_EN)) &&
!(s_pipe->op_mode & (MDP4_OP_SCALEX_PIXEL_RPT |
@@ -2085,7 +2127,9 @@
outpdw(overlay_base + off + 0x108, blend->fg_alpha);
outpdw(overlay_base + off + 0x10c, blend->bg_alpha);
- if (mdp_rev >= MDP_REV_42)
+ if (mdp_rev >= MDP_REV_42 ||
+ ctrl->panel_mode & MDP4_PANEL_MDDI ||
+ ctrl->panel_mode & MDP4_PANEL_DSI_CMD)
outpdw(overlay_base + off + 0x104, blend->op);
outpdw(overlay_base + (off << 5) + 0x1004, blend->co3_sel);
@@ -3113,9 +3157,6 @@
__func__);
}
- if (hdmi_prim_display)
- fill_black_screen(FALSE, pipe->pipe_num, pipe->mixer_num);
-
mdp4_overlay_mdp_pipe_req(pipe, mfd);
mutex_unlock(&mfd->dma->ov_mutex);
@@ -3189,9 +3230,13 @@
} else { /* mixer1, DTV, ATV */
if (ctrl->panel_mode & MDP4_PANEL_DTV) {
- if (hdmi_prim_display)
- fill_black_screen(TRUE, pipe->pipe_num,
- pipe->mixer_num);
+ if (hdmi_prim_display) {
+ struct mdp4_overlay_pipe *pp;
+ pp = &ctrl->sf_plist[pipe->mixer_num]
+ [pipe->pipe_ndx - 1];
+ *pp = *pipe; /* clone it */
+ pp->solid_fill = 1;
+ }
mdp4_overlay_dtv_unset(mfd, pipe);
}
}
@@ -3396,7 +3441,8 @@
pipe->srcp0_ystride = pipe->src_width;
if ((pipe->src_format == MDP_Y_CRCB_H1V1) ||
(pipe->src_format == MDP_Y_CBCR_H1V1) ||
- (pipe->src_format == MDP_Y_CRCB_H1V2)) {
+ (pipe->src_format == MDP_Y_CRCB_H1V2) ||
+ (pipe->src_format == MDP_Y_CBCR_H1V2)) {
if (pipe->src_width > YUV_444_MAX_WIDTH)
pipe->srcp1_ystride = pipe->src_width << 2;
else
diff --git a/drivers/video/msm/mdp4_overlay_dtv.c b/drivers/video/msm/mdp4_overlay_dtv.c
index 398fafa..1de5d6e 100644
--- a/drivers/video/msm/mdp4_overlay_dtv.c
+++ b/drivers/video/msm/mdp4_overlay_dtv.c
@@ -991,22 +991,27 @@
spin_unlock(&vctrl->spin_lock);
}
-void mdp4_dtv_set_black_screen(bool commit)
+void mdp4_dtv_set_black_screen()
{
char *rgb_base;
/*Black color*/
uint32 color = 0x00000000;
uint32 temp_src_format;
- int cndx = 0;
+ int commit = 1, cndx = 0;
+ int pipe_num = OVERLAY_PIPE_RGB1;
struct vsycn_ctrl *vctrl;
vctrl = &vsync_ctrl_db[cndx];
- if (vctrl->base_pipe == NULL || !hdmi_prim_display) {
- pr_debug("dtv_pipe is not configured yet\n");
+ if (!hdmi_prim_display)
return;
- }
+
+ if (vctrl->base_pipe == NULL)
+ commit = 0;
+ else
+ pipe_num = vctrl->base_pipe->pipe_num;
+
rgb_base = MDP_BASE;
- rgb_base += (MDP4_RGB_OFF * (vctrl->base_pipe->pipe_num + 2));
+ rgb_base += (MDP4_RGB_OFF * (pipe_num + 2));
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
@@ -1021,13 +1026,12 @@
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
mdp4_overlay_reg_flush(vctrl->base_pipe, 1);
-
mdp4_mixer_stage_up(vctrl->base_pipe, 0);
mdp4_mixer_stage_commit(vctrl->base_pipe->mixer_num);
} else {
/* MDP_OVERLAY_REG_FLUSH for pipe*/
MDP_OUTP(MDP_BASE + 0x18000,
- BIT(vctrl->base_pipe->pipe_num + 2) | BIT(MDP4_MIXER1));
+ BIT(pipe_num + 2) | BIT(MDP4_MIXER1));
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
}
}
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.c b/drivers/video/msm/mdss/mdss_hdmi_tx.c
index 5d953c5..539cd49 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.c
@@ -66,6 +66,21 @@
.irq_handler = hdmi_tx_isr,
};
+struct dss_gpio hpd_gpio_config[] = {
+ {0, 1, COMPATIBLE_NAME "-hpd"},
+ {0, 1, COMPATIBLE_NAME "-ddc-clk"},
+ {0, 1, COMPATIBLE_NAME "-ddc-data"},
+ {0, 1, COMPATIBLE_NAME "-mux-en"},
+ {0, 0, COMPATIBLE_NAME "-mux-sel"}
+};
+
+struct dss_gpio core_gpio_config[] = {
+};
+
+struct dss_gpio cec_gpio_config[] = {
+ {0, 1, COMPATIBLE_NAME "-cec"}
+};
+
const char *hdmi_pm_name(enum hdmi_tx_power_module_type module)
{
switch (module) {
@@ -497,6 +512,9 @@
DEV_INFO("%s: Hdmi state switch to %d\n", __func__,
hdmi_ctrl->sdev.state);
}
+
+ if (!completion_done(&hdmi_ctrl->hpd_done))
+ complete_all(&hdmi_ctrl->hpd_done);
} /* hdmi_tx_hpd_int_work */
static int hdmi_tx_check_capability(struct dss_io_data *io)
@@ -1764,7 +1782,6 @@
mutex_lock(&hdmi_ctrl->mutex);
hdmi_ctrl->panel_power_on = true;
- /* todo: check hdmi_tx_is_controller_on when hpd is on */
if (hdmi_ctrl->hpd_state) {
DEV_DBG("%s: Turning HDMI on\n", __func__);
mutex_unlock(&hdmi_ctrl->mutex);
@@ -1984,9 +2001,13 @@
hdmi_ctrl->ddc_ctrl.io = &pdata->io[HDMI_TX_CORE_IO];
init_completion(&hdmi_ctrl->ddc_ctrl.ddc_sw_done);
+ hdmi_ctrl->panel_power_on = false;
+ hdmi_ctrl->panel_suspend = false;
+
hdmi_ctrl->hpd_state = false;
hdmi_ctrl->hpd_initialized = false;
hdmi_ctrl->hpd_off_pending = false;
+ init_completion(&hdmi_ctrl->hpd_done);
INIT_WORK(&hdmi_ctrl->hpd_int_work, hdmi_tx_hpd_int_work);
INIT_WORK(&hdmi_ctrl->power_off_work, hdmi_tx_power_off_work);
@@ -2020,20 +2041,100 @@
return rc;
} /* hdmi_tx_dev_init */
-static int hdmi_tx_event_handler(struct mdss_panel_data *panel_data,
- int event, void *arg)
+static int hdmi_tx_panel_event_handler(struct mdss_panel_data *panel_data,
+ int event, void *arg)
{
int rc = 0;
+ struct hdmi_tx_ctrl *hdmi_ctrl =
+ hdmi_tx_get_drvdata_from_panel_data(panel_data);
+
+ if (!hdmi_ctrl) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ DEV_DBG("%s: event = %d suspend=%d, hpd_feature=%d\n", __func__,
+ event, hdmi_ctrl->panel_suspend, hdmi_ctrl->hpd_feature_on);
+
switch (event) {
+ case MDSS_EVENT_RESUME:
+ if (hdmi_ctrl->hpd_feature_on) {
+ INIT_COMPLETION(hdmi_ctrl->hpd_done);
+
+ rc = hdmi_tx_hpd_on(hdmi_ctrl);
+ if (rc)
+ DEV_ERR("%s: hdmi_tx_hpd_on failed. rc=%d\n",
+ __func__, rc);
+ }
+ break;
+
+ case MDSS_EVENT_RESET:
+ if (hdmi_ctrl->panel_suspend) {
+ u32 timeout;
+ hdmi_ctrl->panel_suspend = false;
+
+ timeout = wait_for_completion_interruptible_timeout(
+ &hdmi_ctrl->hpd_done, HZ/10);
+ if (!timeout & !hdmi_ctrl->hpd_state) {
+ DEV_INFO("%s: cable removed during suspend\n",
+ __func__);
+
+ kobject_uevent(hdmi_ctrl->kobj, KOBJ_OFFLINE);
+ switch_set_state(&hdmi_ctrl->sdev, 0);
+
+ rc = -EPERM;
+ } else {
+ DEV_DBG("%s: cable present after resume\n",
+ __func__);
+ }
+ }
+ break;
+
case MDSS_EVENT_UNBLANK:
rc = hdmi_tx_power_on(panel_data);
+ if (rc)
+ DEV_ERR("%s: hdmi_tx_power_on failed. rc=%d\n",
+ __func__, rc);
break;
+
+ case MDSS_EVENT_TIMEGEN_ON:
+ break;
+
+ case MDSS_EVENT_SUSPEND:
+ if (!hdmi_ctrl->panel_power_on) {
+ if (hdmi_ctrl->hpd_feature_on)
+ hdmi_tx_hpd_off(hdmi_ctrl);
+ else
+ DEV_ERR("%s: invalid state\n", __func__);
+
+ hdmi_ctrl->panel_suspend = false;
+ } else {
+ hdmi_ctrl->hpd_off_pending = true;
+ hdmi_ctrl->panel_suspend = true;
+ }
+ break;
+
+ case MDSS_EVENT_BLANK:
+ if (hdmi_ctrl->panel_power_on) {
+ rc = hdmi_tx_power_off(panel_data);
+ if (rc)
+ DEV_ERR("%s: hdmi_tx_power_off failed.rc=%d\n",
+ __func__, rc);
+
+ } else {
+ DEV_DBG("%s: hdmi is already powered off\n", __func__);
+ }
+ break;
+
case MDSS_EVENT_TIMEGEN_OFF:
- rc = hdmi_tx_power_off(panel_data);
+ /* If a power off is already underway, wait for it to finish */
+ if (hdmi_ctrl->panel_suspend)
+ flush_work_sync(&hdmi_ctrl->power_off_work);
break;
}
+
return rc;
-}
+} /* hdmi_tx_panel_event_handler */
static int hdmi_tx_register_panel(struct hdmi_tx_ctrl *hdmi_ctrl)
{
@@ -2044,7 +2145,7 @@
return -EINVAL;
}
- hdmi_ctrl->panel_data.event_handler = hdmi_tx_event_handler;
+ hdmi_ctrl->panel_data.event_handler = hdmi_tx_panel_event_handler;
hdmi_ctrl->video_resolution = DEFAULT_VIDEO_RESOLUTION;
rc = hdmi_tx_init_panel_info(hdmi_ctrl->video_resolution,
@@ -2447,29 +2548,32 @@
static int hdmi_tx_get_dt_gpio_data(struct device *dev,
struct dss_module_power *mp, u32 module_type)
{
- int i, j, rc = 0;
- int dt_gpio_total = 0, mod_gpio_total = 0;
- u32 ndx_mask = 0;
- const char *mod_name = NULL;
+ int i, j;
+ int mp_gpio_cnt = 0, gpio_list_size = 0;
+ struct dss_gpio *gpio_list = NULL;
struct device_node *of_node = NULL;
- char prop_name[32];
- snprintf(prop_name, 32, "%s-%s", COMPATIBLE_NAME, "gpio-names");
+
+ DEV_DBG("%s: module: '%s'\n", __func__, hdmi_tx_pm_name(module_type));
if (!dev || !mp) {
DEV_ERR("%s: invalid input\n", __func__);
- rc = -EINVAL;
- goto error;
+ return -EINVAL;
}
+ of_node = dev->of_node;
+
switch (module_type) {
case HDMI_TX_HPD_PM:
- mod_name = "hpd";
+ gpio_list_size = ARRAY_SIZE(hpd_gpio_config);
+ gpio_list = hpd_gpio_config;
break;
case HDMI_TX_CORE_PM:
- mod_name = "core";
+ gpio_list_size = ARRAY_SIZE(core_gpio_config);
+ gpio_list = core_gpio_config;
break;
case HDMI_TX_CEC_PM:
- mod_name = "cec";
+ gpio_list_size = ARRAY_SIZE(cec_gpio_config);
+ gpio_list = cec_gpio_config;
break;
default:
DEV_ERR("%s: invalid module type=%d\n", __func__,
@@ -2477,90 +2581,49 @@
return -EINVAL;
}
- DEV_DBG("%s: module: '%s'\n", __func__, hdmi_tx_pm_name(module_type));
+ for (i = 0; i < gpio_list_size; i++)
+ if (of_find_property(of_node, gpio_list[i].gpio_name, NULL))
+ mp_gpio_cnt++;
- of_node = dev->of_node;
-
- dt_gpio_total = of_gpio_count(of_node);
- if (dt_gpio_total < 0) {
- DEV_ERR("%s: gpio not found. rc=%d\n", __func__,
- dt_gpio_total);
- rc = dt_gpio_total;
- goto error;
- }
-
- /* count how many gpio for particular hdmi module */
- for (i = 0; i < dt_gpio_total; i++) {
- const char *st = NULL;
-
- rc = of_property_read_string_index(of_node,
- prop_name, i, &st);
- if (rc) {
- DEV_ERR("%s: error reading name. i=%d, rc=%d\n",
- __func__, i, rc);
- goto error;
- }
-
- if (strnstr(st, mod_name, strlen(st))) {
- ndx_mask |= BIT(i);
- mod_gpio_total++;
- }
- }
-
- if (mod_gpio_total > 0) {
- mp->num_gpio = mod_gpio_total;
- mp->gpio_config = devm_kzalloc(dev, sizeof(struct dss_gpio) *
- mod_gpio_total, GFP_KERNEL);
- if (!mp->gpio_config) {
- DEV_ERR("%s: can't alloc '%s' gpio mem\n", __func__,
- hdmi_tx_pm_name(module_type));
- goto error;
- }
- } else {
+ if (!mp_gpio_cnt) {
DEV_DBG("%s: no gpio\n", __func__);
return 0;
}
+ DEV_DBG("%s: mp_gpio_cnt = %d\n", __func__, mp_gpio_cnt);
+ mp->num_gpio = mp_gpio_cnt;
- for (i = 0, j = 0; (i < dt_gpio_total) && (j < mod_gpio_total); i++) {
- const char *st = NULL;
+ mp->gpio_config = devm_kzalloc(dev, sizeof(struct dss_gpio) *
+ mp_gpio_cnt, GFP_KERNEL);
+ if (!mp->gpio_config) {
+ DEV_ERR("%s: can't alloc '%s' gpio mem\n", __func__,
+ hdmi_tx_pm_name(module_type));
- if (!(ndx_mask & BIT(0))) {
- ndx_mask >>= 1;
+ mp->num_gpio = 0;
+ return -ENOMEM;
+ }
+
+ for (i = 0, j = 0; i < gpio_list_size; i++) {
+ int gpio = of_get_named_gpio(of_node,
+ gpio_list[i].gpio_name, 0);
+ if (gpio < 0) {
+ DEV_DBG("%s: no gpio named %s\n", __func__,
+ gpio_list[i].gpio_name);
continue;
}
+ memcpy(&mp->gpio_config[j], &gpio_list[i],
+ sizeof(struct dss_gpio));
- /* gpio-name */
- rc = of_property_read_string_index(of_node,
- prop_name, i, &st);
- if (rc) {
- DEV_ERR("%s: error reading name. i=%d, rc=%d\n",
- __func__, i, rc);
- goto error;
- }
- snprintf(mp->gpio_config[j].gpio_name, 32, "%s", st);
+ mp->gpio_config[j].gpio = (unsigned)gpio;
- /* gpio-number */
- mp->gpio_config[j].gpio = of_get_gpio(of_node, i);
-
- DEV_DBG("%s: gpio num=%d, name=%s\n", __func__,
- mp->gpio_config[j].gpio,
- mp->gpio_config[j].gpio_name);
-
- ndx_mask >>= 1;
+ DEV_DBG("%s: gpio num=%d, name=%s, value=%d\n",
+ __func__, mp->gpio_config[j].gpio,
+ mp->gpio_config[j].gpio_name,
+ mp->gpio_config[j].value);
j++;
}
- return rc;
-
-error:
- if (mp->gpio_config) {
- devm_kfree(dev, mp->gpio_config);
- mp->gpio_config = NULL;
- }
- mp->num_gpio = 0;
-
- return rc;
+ return 0;
} /* hdmi_tx_get_dt_gpio_data */
static void hdmi_tx_put_dt_data(struct device *dev,
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.h b/drivers/video/msm/mdss/mdss_hdmi_tx.h
index ce19355..2d431b7 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.h
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.h
@@ -51,11 +51,13 @@
uint32_t video_resolution;
u32 panel_power_on;
+ u32 panel_suspend;
- u32 hpd_initialized;
u32 hpd_state;
u32 hpd_off_pending;
u32 hpd_feature_on;
+ u32 hpd_initialized;
+ struct completion hpd_done;
struct work_struct hpd_int_work;
struct work_struct power_off_work;
diff --git a/drivers/video/msm/mdss/mdss_io_util.c b/drivers/video/msm/mdss/mdss_io_util.c
index d7c19b4..2bf2d74 100644
--- a/drivers/video/msm/mdss/mdss_io_util.c
+++ b/drivers/video/msm/mdss/mdss_io_util.c
@@ -249,6 +249,10 @@
int i = 0, rc = 0;
if (enable) {
for (i = 0; i < num_gpio; i++) {
+ DEV_DBG("%pS->%s: %s enable\n",
+ __builtin_return_address(0), __func__,
+ in_gpio[i].gpio_name);
+
rc = gpio_request(in_gpio[i].gpio,
in_gpio[i].gpio_name);
if (rc < 0) {
@@ -257,10 +261,16 @@
in_gpio[i].gpio_name);
goto disable_gpio;
}
+ gpio_set_value(in_gpio[i].gpio, in_gpio[i].value);
}
} else {
- for (i = num_gpio-1; i >= 0; i--)
+ for (i = num_gpio-1; i >= 0; i--) {
+ DEV_DBG("%pS->%s: %s disable\n",
+ __builtin_return_address(0), __func__,
+ in_gpio[i].gpio_name);
+
gpio_free(in_gpio[i].gpio);
+ }
}
return rc;
diff --git a/drivers/video/msm/mdss/mdss_io_util.h b/drivers/video/msm/mdss/mdss_io_util.h
index 9d78d70..85826f7 100644
--- a/drivers/video/msm/mdss/mdss_io_util.h
+++ b/drivers/video/msm/mdss/mdss_io_util.h
@@ -58,6 +58,7 @@
struct dss_gpio {
unsigned gpio;
+ unsigned value;
char gpio_name[32];
};
diff --git a/drivers/video/msm/mipi_novatek.c b/drivers/video/msm/mipi_novatek.c
index 69ca0a3..ecac82d 100644
--- a/drivers/video/msm/mipi_novatek.c
+++ b/drivers/video/msm/mipi_novatek.c
@@ -460,6 +460,12 @@
{
struct dcs_cmd_req cmdreq;
+ if (mipi_novatek_pdata &&
+ mipi_novatek_pdata->gpio_set_backlight) {
+ mipi_novatek_pdata->gpio_set_backlight(mfd->bl_level);
+ return;
+ }
+
if ((mipi_novatek_pdata->enable_wled_bl_ctrl)
&& (wled_trigger_initialized)) {
led_trigger_event(bkl_led_trigger, mfd->bl_level);
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index e6e9624..993ec01 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -532,6 +532,7 @@
mfd->suspend.sw_refreshing_enable = mfd->sw_refreshing_enable;
mfd->suspend.op_enable = mfd->op_enable;
mfd->suspend.panel_power_on = mfd->panel_power_on;
+ mfd->suspend.op_suspend = true;
if (mfd->op_enable) {
ret =
@@ -597,6 +598,8 @@
MSM_FB_INFO("msm_fb_resume: can't turn on display!\n");
}
+ mfd->suspend.op_suspend = false;
+
return ret;
}
#endif
diff --git a/drivers/video/msm/msm_fb.h b/drivers/video/msm/msm_fb.h
index 2fd25cc..9c4f3d3 100644
--- a/drivers/video/msm/msm_fb.h
+++ b/drivers/video/msm/msm_fb.h
@@ -53,6 +53,7 @@
boolean op_enable;
boolean sw_refreshing_enable;
boolean panel_power_on;
+ boolean op_suspend;
};
struct msmfb_writeback_data_list {
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_sub.c b/drivers/video/msm/vidc/common/vcd/vcd_sub.c
index a9709fb..b84ae44 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_sub.c
+++ b/drivers/video/msm/vidc/common/vcd/vcd_sub.c
@@ -784,7 +784,11 @@
buf_pool->allocated--;
}
- memset(buf_entry, 0, sizeof(struct vcd_buffer_entry));
+ buf_entry->valid = buf_entry->allocated = buf_entry->in_use = 0;
+ buf_entry->alloc = buf_entry->virtual = buf_entry->physical = NULL;
+ buf_entry->sz = 0;
+ memset(&buf_entry->frame, 0, sizeof(struct vcd_frame_data));
+
buf_pool->validated--;
if (buf_pool->validated == 0)
vcd_free_buffer_pool_entries(buf_pool);
diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h
index 2dea611..4e9e1ce 100644
--- a/include/linux/mfd/wcd9xxx/core.h
+++ b/include/linux/mfd/wcd9xxx/core.h
@@ -131,7 +131,7 @@
u32 bit_width; /* sit width 16,24,32 */
struct list_head wcd9xxx_ch_list; /* channel list */
u16 grph; /* slimbus group handle */
- u32 ch_mask;
+ unsigned long ch_mask;
wait_queue_head_t dai_wait;
};
diff --git a/include/linux/mfd/wcd9xxx/wcd9320_registers.h b/include/linux/mfd/wcd9xxx/wcd9320_registers.h
index 4b8626a..f9966be 100644
--- a/include/linux/mfd/wcd9xxx/wcd9320_registers.h
+++ b/include/linux/mfd/wcd9xxx/wcd9320_registers.h
@@ -1334,9 +1334,16 @@
/* SLIMBUS Slave Registers */
#define TAIKO_SLIM_PGD_PORT_INT_EN0 (0x30)
-#define TAIKO_SLIM_PGD_PORT_INT_STATUS0 (0x34)
-#define TAIKO_SLIM_PGD_PORT_INT_CLR0 (0x38)
-#define TAIKO_SLIM_PGD_PORT_INT_SOURCE0 (0x60)
+#define TAIKO_SLIM_PGD_PORT_INT_STATUS_RX_0 (0x34)
+#define TAIKO_SLIM_PGD_PORT_INT_STATUS_RX_1 (0x35)
+#define TAIKO_SLIM_PGD_PORT_INT_STATUS_TX_0 (0x36)
+#define TAIKO_SLIM_PGD_PORT_INT_STATUS_TX_1 (0x37)
+#define TAIKO_SLIM_PGD_PORT_INT_CLR_RX_0 (0x38)
+#define TAIKO_SLIM_PGD_PORT_INT_CLR_RX_1 (0x39)
+#define TAIKO_SLIM_PGD_PORT_INT_CLR_TX_0 (0x3A)
+#define TAIKO_SLIM_PGD_PORT_INT_CLR_TX_1 (0x3B)
+#define TAIKO_SLIM_PGD_PORT_INT_RX_SOURCE0 (0x60)
+#define TAIKO_SLIM_PGD_PORT_INT_TX_SOURCE0 (0x70)
/* Macros for Packing Register Writes into a U32 */
#define TAIKO_PACKED_REG_SIZE sizeof(u32)
diff --git a/sound/soc/codecs/wcd9304.c b/sound/soc/codecs/wcd9304.c
index 412090f..fd9d825 100644
--- a/sound/soc/codecs/wcd9304.c
+++ b/sound/soc/codecs/wcd9304.c
@@ -2836,7 +2836,7 @@
if (dai->id <= NUM_CODEC_DAIS) {
if (sitar->dai[dai->id].ch_mask) {
active = 1;
- pr_debug("%s(): Codec DAI: chmask[%d] = 0x%x\n",
+ pr_debug("%s(): Codec DAI: chmask[%d] = 0x%lx\n",
__func__, dai->id,
sitar->dai[dai->id].ch_mask);
}
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index f28fd774..e11b985 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -3035,7 +3035,7 @@
msecs_to_jiffies(300));
}
/* apply the digital gain after the decimator is enabled*/
- if ((w->shift) < ARRAY_SIZE(rx_digital_gain_reg))
+ if ((w->shift + offset) < ARRAY_SIZE(tx_digital_gain_reg))
snd_soc_write(codec,
tx_digital_gain_reg[w->shift + offset],
snd_soc_read(codec,
@@ -4128,7 +4128,7 @@
if (dai->id <= NUM_CODEC_DAIS) {
if (tabla->dai[dai->id].ch_mask) {
active = 1;
- pr_debug("%s(): Codec DAI: chmask[%d] = 0x%x\n",
+ pr_debug("%s(): Codec DAI: chmask[%d] = 0x%lx\n",
__func__, dai->id, tabla->dai[dai->id].ch_mask);
}
}
@@ -7863,7 +7863,7 @@
port_id = i*8 + j;
for (k = 0; k < ARRAY_SIZE(tabla_dai); k++) {
ch_mask_temp = 1 << port_id;
- pr_debug("%s: tabla_p->dai[%d].ch_mask = 0x%x\n",
+ pr_debug("%s: tabla_p->dai[%d].ch_mask = 0x%lx\n",
__func__, k,
tabla_p->dai[k].ch_mask);
if (ch_mask_temp &
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index 8fdf4f2..5ffb60a 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -18,6 +18,8 @@
#include <linux/printk.h>
#include <linux/ratelimit.h>
#include <linux/debugfs.h>
+#include <linux/wait.h>
+#include <linux/bitops.h>
#include <linux/mfd/wcd9xxx/core.h>
#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
@@ -47,6 +49,12 @@
#define TAIKO_I2S_MASTER_MODE_MASK 0x08
#define TAIKO_MCLK_CLK_12P288MHZ 12288000
#define TAIKO_MCLK_CLK_9P6HZ 9600000
+
+#define TAIKO_SLIM_CLOSE_TIMEOUT 1000
+#define TAIKO_SLIM_IRQ_OVERFLOW (1 << 0)
+#define TAIKO_SLIM_IRQ_UNDERFLOW (1 << 1)
+#define TAIKO_SLIM_IRQ_PORT_CLOSED (1 << 2)
+
enum {
AIF1_PB = 0,
AIF1_CAP,
@@ -98,7 +106,8 @@
};
enum {
- COMPANDER_1 = 0,
+ COMPANDER_0,
+ COMPANDER_1,
COMPANDER_2,
COMPANDER_MAX,
};
@@ -204,6 +213,7 @@
};
static const u32 comp_shift[] = {
+ 4, /* Compander 0's clock source is on interpolator 7 */
0,
2,
};
@@ -215,31 +225,46 @@
COMPANDER_2,
COMPANDER_2,
COMPANDER_2,
+ COMPANDER_0,
COMPANDER_MAX,
};
static const struct comp_sample_dependent_params comp_samp_params[] = {
{
- .peak_det_timeout = 0x2,
- .rms_meter_div_fact = 0x8 << 4,
- .rms_meter_resamp_fact = 0x21,
+ /* 8 Khz */
+ .peak_det_timeout = 0x02,
+ .rms_meter_div_fact = 0x09,
+ .rms_meter_resamp_fact = 0x06,
},
{
- .peak_det_timeout = 0x3,
- .rms_meter_div_fact = 0x9 << 4,
+ /* 16 Khz */
+ .peak_det_timeout = 0x03,
+ .rms_meter_div_fact = 0x0A,
+ .rms_meter_resamp_fact = 0x0C,
+ },
+ {
+ /* 32 Khz */
+ .peak_det_timeout = 0x05,
+ .rms_meter_div_fact = 0x0B,
+ .rms_meter_resamp_fact = 0x1E,
+ },
+ {
+ /* 48 Khz */
+ .peak_det_timeout = 0x05,
+ .rms_meter_div_fact = 0x0B,
.rms_meter_resamp_fact = 0x28,
},
-
{
- .peak_det_timeout = 0x5,
- .rms_meter_div_fact = 0xB << 4,
- .rms_meter_resamp_fact = 0x28,
+ /* 96 Khz */
+ .peak_det_timeout = 0x06,
+ .rms_meter_div_fact = 0x0C,
+ .rms_meter_resamp_fact = 0x50,
},
-
{
- .peak_det_timeout = 0x5,
- .rms_meter_div_fact = 0xB << 4,
- .rms_meter_resamp_fact = 0x28,
+ /* 192 Khz */
+ .peak_det_timeout = 0x07,
+ .rms_meter_div_fact = 0xD,
+ .rms_meter_resamp_fact = 0xA0,
},
};
@@ -550,194 +575,167 @@
return 0;
}
-static int taiko_compander_gain_offset(
- struct snd_soc_codec *codec, u32 enable,
- unsigned int reg, int mask, int event)
-{
- int pa_mode = snd_soc_read(codec, reg) & mask;
- int gain_offset = 0;
- /* if PMU && enable is 1-> offset is 3
- * if PMU && enable is 0-> offset is 0
- * if PMD && pa_mode is PA -> offset is 0: PMU compander is off
- * if PMD && pa_mode is comp -> offset is -3: PMU compander is on.
- */
-
- if (SND_SOC_DAPM_EVENT_ON(event) && (enable != 0))
- gain_offset = TAIKO_COMP_DIGITAL_GAIN_OFFSET;
- if (SND_SOC_DAPM_EVENT_OFF(event) && (pa_mode == 0))
- gain_offset = -TAIKO_COMP_DIGITAL_GAIN_OFFSET;
- return gain_offset;
-}
-
-
-static int taiko_config_gain_compander(
- struct snd_soc_codec *codec,
- u32 compander, u32 enable, int event)
-{
- int value = 0;
- int mask = 1 << 5;
- int gain = 0;
- int gain_offset;
- if (compander >= COMPANDER_MAX) {
- pr_err("%s: Error, invalid compander channel\n", __func__);
- return -EINVAL;
- }
-
- if ((enable == 0) || SND_SOC_DAPM_EVENT_OFF(event))
- value = 1 << 4;
-
- if (compander == COMPANDER_1) {
- gain_offset = taiko_compander_gain_offset(codec, enable,
- TAIKO_A_RX_HPH_L_GAIN, mask, event);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_L_GAIN, mask, value);
- gain = snd_soc_read(codec, TAIKO_A_CDC_RX1_VOL_CTL_B2_CTL);
- snd_soc_update_bits(codec, TAIKO_A_CDC_RX1_VOL_CTL_B2_CTL,
- 0xFF, gain - gain_offset);
- gain_offset = taiko_compander_gain_offset(codec, enable,
- TAIKO_A_RX_HPH_R_GAIN, mask, event);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_R_GAIN, mask, value);
- gain = snd_soc_read(codec, TAIKO_A_CDC_RX2_VOL_CTL_B2_CTL);
- snd_soc_update_bits(codec, TAIKO_A_CDC_RX2_VOL_CTL_B2_CTL,
- 0xFF, gain - gain_offset);
- } else if (compander == COMPANDER_2) {
- gain_offset = taiko_compander_gain_offset(codec, enable,
- TAIKO_A_RX_LINE_1_GAIN, mask, event);
- snd_soc_update_bits(codec, TAIKO_A_RX_LINE_1_GAIN, mask, value);
- gain = snd_soc_read(codec, TAIKO_A_CDC_RX3_VOL_CTL_B2_CTL);
- snd_soc_update_bits(codec, TAIKO_A_CDC_RX3_VOL_CTL_B2_CTL,
- 0xFF, gain - gain_offset);
- gain_offset = taiko_compander_gain_offset(codec, enable,
- TAIKO_A_RX_LINE_3_GAIN, mask, event);
- snd_soc_update_bits(codec, TAIKO_A_RX_LINE_3_GAIN, mask, value);
- gain = snd_soc_read(codec, TAIKO_A_CDC_RX4_VOL_CTL_B2_CTL);
- snd_soc_update_bits(codec, TAIKO_A_CDC_RX4_VOL_CTL_B2_CTL,
- 0xFF, gain - gain_offset);
- gain_offset = taiko_compander_gain_offset(codec, enable,
- TAIKO_A_RX_LINE_2_GAIN, mask, event);
- snd_soc_update_bits(codec, TAIKO_A_RX_LINE_2_GAIN, mask, value);
- gain = snd_soc_read(codec, TAIKO_A_CDC_RX5_VOL_CTL_B2_CTL);
- snd_soc_update_bits(codec, TAIKO_A_CDC_RX5_VOL_CTL_B2_CTL,
- 0xFF, gain - gain_offset);
- gain_offset = taiko_compander_gain_offset(codec, enable,
- TAIKO_A_RX_LINE_4_GAIN, mask, event);
- snd_soc_update_bits(codec, TAIKO_A_RX_LINE_4_GAIN, mask, value);
- gain = snd_soc_read(codec, TAIKO_A_CDC_RX6_VOL_CTL_B2_CTL);
- snd_soc_update_bits(codec, TAIKO_A_CDC_RX6_VOL_CTL_B2_CTL,
- 0xFF, gain - gain_offset);
- }
- return 0;
-}
static int taiko_get_compander(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
int comp = ((struct soc_multi_mixer_control *)
- kcontrol->private_value)->max;
+ kcontrol->private_value)->shift;
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
ucontrol->value.integer.value[0] = taiko->comp_enabled[comp];
-
return 0;
}
static int taiko_set_compander(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
int comp = ((struct soc_multi_mixer_control *)
- kcontrol->private_value)->max;
+ kcontrol->private_value)->shift;
int value = ucontrol->value.integer.value[0];
- if (value == taiko->comp_enabled[comp]) {
- pr_debug("%s: compander #%d enable %d no change\n",
- __func__, comp, value);
- return 0;
- }
+ pr_debug("%s: Compander %d enable current %d, new %d\n",
+ __func__, comp, taiko->comp_enabled[comp], value);
taiko->comp_enabled[comp] = value;
return 0;
}
+static int taiko_config_gain_compander(struct snd_soc_codec *codec,
+ int comp, bool enable)
+{
+ int ret = 0;
+
+ switch (comp) {
+ case COMPANDER_0:
+ snd_soc_update_bits(codec, TAIKO_A_SPKR_DRV_GAIN,
+ 1 << 2, !enable << 2);
+ break;
+ case COMPANDER_1:
+ snd_soc_update_bits(codec, TAIKO_A_RX_HPH_L_GAIN,
+ 1 << 5, !enable << 5);
+ snd_soc_update_bits(codec, TAIKO_A_RX_HPH_R_GAIN,
+ 1 << 5, !enable << 5);
+ break;
+ case COMPANDER_2:
+ snd_soc_update_bits(codec, TAIKO_A_RX_LINE_1_GAIN,
+ 1 << 5, !enable << 5);
+ snd_soc_update_bits(codec, TAIKO_A_RX_LINE_3_GAIN,
+ 1 << 5, !enable << 5);
+ snd_soc_update_bits(codec, TAIKO_A_RX_LINE_2_GAIN,
+ 1 << 5, !enable << 5);
+ snd_soc_update_bits(codec, TAIKO_A_RX_LINE_4_GAIN,
+ 1 << 5, !enable << 5);
+ break;
+ default:
+ WARN_ON(1);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static void taiko_discharge_comp(struct snd_soc_codec *codec, int comp)
+{
+ /* Update RSM to 1, DIVF to 5 */
+ snd_soc_write(codec, TAIKO_A_CDC_COMP0_B3_CTL + (comp * 8), 1);
+ snd_soc_update_bits(codec, TAIKO_A_CDC_COMP0_B2_CTL + (comp * 8), 0xF0,
+ 1 << 5);
+ /* Wait for 1ms */
+ usleep_range(1000, 1000);
+}
static int taiko_config_compander(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol,
- int event)
+ struct snd_kcontrol *kcontrol, int event)
{
+ int mask, emask;
+ bool timedout;
+ unsigned long timeout;
struct snd_soc_codec *codec = w->codec;
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- u32 rate = taiko->comp_fs[w->shift];
+ const int comp = w->shift;
+ const u32 rate = taiko->comp_fs[comp];
+ const struct comp_sample_dependent_params *comp_params =
+ &comp_samp_params[rate];
- pr_debug("%s: %s event %d enabled = %d", __func__, w->name,
- event, taiko->comp_enabled[w->shift]);
+ pr_debug("%s: %s event %d compander %d, enabled %d", __func__,
+ w->name, event, comp, taiko->comp_enabled[comp]);
+
+ if (!taiko->comp_enabled[comp])
+ return 0;
+
+ /* Compander 0 has single channel */
+ mask = (comp == COMPANDER_0 ? 0x01 : 0x03);
+ emask = (comp == COMPANDER_0 ? 0x02 : 0x03);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- if (taiko->comp_enabled[w->shift] != 0) {
- /* Enable both L/R compander clocks */
- snd_soc_update_bits(codec,
- TAIKO_A_CDC_CLK_RX_B2_CTL,
- 0x03 << comp_shift[w->shift],
- 0x03 << comp_shift[w->shift]);
- /* Clar the HALT for the compander*/
- snd_soc_update_bits(codec,
- TAIKO_A_CDC_COMP1_B1_CTL +
- w->shift * 8, 1 << 2, 0);
- /* Toggle compander reset bits*/
- snd_soc_update_bits(codec,
- TAIKO_A_CDC_CLK_OTHR_RESET_B2_CTL,
- 0x03 << comp_shift[w->shift],
- 0x03 << comp_shift[w->shift]);
- snd_soc_update_bits(codec,
- TAIKO_A_CDC_CLK_OTHR_RESET_B2_CTL,
- 0x03 << comp_shift[w->shift], 0);
- taiko_config_gain_compander(codec, w->shift, 1, event);
- /* Update the RMS meter resampling*/
- snd_soc_update_bits(codec,
- TAIKO_A_CDC_COMP1_B3_CTL +
- w->shift * 8, 0xFF, 0x01);
- /* Wait for 1ms*/
- usleep_range(1000, 1000);
- }
+ /* Set gain source to compander */
+ taiko_config_gain_compander(codec, comp, true);
+ /* Enable RX interpolation path clocks */
+ snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_RX_B2_CTL,
+ mask << comp_shift[comp],
+ mask << comp_shift[comp]);
+
+ taiko_discharge_comp(codec, comp);
+
+ /* Clear compander halt */
+ snd_soc_update_bits(codec, TAIKO_A_CDC_COMP0_B1_CTL +
+ (comp * 8),
+ 1 << 2, 0);
+ /* Toggle compander reset bits */
+ snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_OTHR_RESET_B2_CTL,
+ mask << comp_shift[comp],
+ mask << comp_shift[comp]);
+ snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_OTHR_RESET_B2_CTL,
+ mask << comp_shift[comp], 0);
break;
case SND_SOC_DAPM_POST_PMU:
- /* Set sample rate dependent paramater*/
- if (taiko->comp_enabled[w->shift] != 0) {
- snd_soc_update_bits(codec, TAIKO_A_CDC_COMP1_FS_CFG +
- w->shift * 8, 0x03, rate);
- snd_soc_update_bits(codec, TAIKO_A_CDC_COMP1_B2_CTL +
- w->shift * 8, 0x0F,
- comp_samp_params[rate].peak_det_timeout);
- snd_soc_update_bits(codec, TAIKO_A_CDC_COMP1_B2_CTL +
- w->shift * 8, 0xF0,
- comp_samp_params[rate].rms_meter_div_fact);
- snd_soc_update_bits(codec, TAIKO_A_CDC_COMP1_B3_CTL +
- w->shift * 8, 0xFF,
- comp_samp_params[rate].rms_meter_resamp_fact);
- /* Compander enable -> 0x370/0x378*/
- snd_soc_update_bits(codec, TAIKO_A_CDC_COMP1_B1_CTL +
- w->shift * 8, 0x03, 0x03);
- }
+ /* Set sample rate dependent paramater */
+ snd_soc_update_bits(codec,
+ TAIKO_A_CDC_COMP0_FS_CFG + (comp * 8),
+ 0x07, rate);
+ snd_soc_write(codec, TAIKO_A_CDC_COMP0_B3_CTL + (comp * 8),
+ comp_params->rms_meter_resamp_fact);
+ snd_soc_update_bits(codec,
+ TAIKO_A_CDC_COMP0_B2_CTL + (comp * 8),
+ 0x0F, comp_params->peak_det_timeout);
+ snd_soc_update_bits(codec,
+ TAIKO_A_CDC_COMP0_B2_CTL + (comp * 8),
+ 0xF0, comp_params->rms_meter_div_fact << 4);
+ /* Compander enable */
+ snd_soc_update_bits(codec, TAIKO_A_CDC_COMP0_B1_CTL +
+ (comp * 8), emask, emask);
break;
case SND_SOC_DAPM_PRE_PMD:
- /* Halt the compander*/
- if (taiko->comp_enabled[w->shift] != 0) {
- snd_soc_update_bits(codec, TAIKO_A_CDC_COMP1_B1_CTL +
- w->shift * 8, 1 << 2, 1 << 2);
- }
+ /* Halt compander */
+ snd_soc_update_bits(codec,
+ TAIKO_A_CDC_COMP0_B1_CTL + (comp * 8),
+ 1 << 2, 1 << 2);
+ /* Wait up to a second for shutdown complete */
+ timeout = jiffies + HZ;
+ do {
+ if ((snd_soc_read(codec,
+ TAIKO_A_CDC_COMP0_SHUT_DOWN_STATUS +
+ (comp * 8)) & mask) == mask)
+ break;
+ } while (!(timedout = time_after(jiffies, timeout)));
+ pr_debug("%s: Compander %d shutdown %s in %dms\n", __func__,
+ comp, timedout ? "timedout" : "completed",
+ jiffies_to_msecs(timeout - HZ - jiffies));
break;
case SND_SOC_DAPM_POST_PMD:
- /* Restore the gain */
- if (taiko->comp_enabled[w->shift] != 0) {
- taiko_config_gain_compander(codec, w->shift,
- taiko->comp_enabled[w->shift], event);
- /* Disable the compander*/
- snd_soc_update_bits(codec, TAIKO_A_CDC_COMP1_B1_CTL +
- w->shift * 8, 0x03, 0x00);
- /* Turn off the clock for compander in pair*/
- snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_RX_B2_CTL,
- 0x03 << comp_shift[w->shift], 0);
- }
+ /* Disable compander */
+ snd_soc_update_bits(codec,
+ TAIKO_A_CDC_COMP0_B1_CTL + (comp * 8),
+ emask, 0x00);
+ /* Turn off the clock for compander in pair */
+ snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_RX_B2_CTL,
+ mask << comp_shift[comp], 0);
+ /* Set gain source to register */
+ taiko_config_gain_compander(codec, comp, false);
break;
}
return 0;
@@ -961,10 +959,12 @@
SOC_SINGLE_MULTI_EXT("IIR2 Band5", IIR2, BAND5, 255, 0, 5,
taiko_get_iir_band_audio_mixer, taiko_put_iir_band_audio_mixer),
- SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, 1, COMPANDER_1, 0,
- taiko_get_compander, taiko_set_compander),
- SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, 0, COMPANDER_2, 0,
- taiko_get_compander, taiko_set_compander),
+ SOC_SINGLE_EXT("COMP0 Switch", SND_SOC_NOPM, COMPANDER_0, 1, 0,
+ taiko_get_compander, taiko_set_compander),
+ SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0,
+ taiko_get_compander, taiko_set_compander),
+ SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0,
+ taiko_get_compander, taiko_set_compander),
};
@@ -2247,7 +2247,7 @@
msecs_to_jiffies(300));
}
/* apply the digital gain after the decimator is enabled*/
- if ((w->shift) < ARRAY_SIZE(rx_digital_gain_reg))
+ if ((w->shift + offset) < ARRAY_SIZE(tx_digital_gain_reg))
snd_soc_write(codec,
tx_digital_gain_reg[w->shift + offset],
snd_soc_read(codec,
@@ -2671,6 +2671,7 @@
{"LINEOUT4 DAC", NULL, "RX_BIAS"},
{"SPK DAC", NULL, "RX_BIAS"},
+ {"RX7 MIX1", NULL, "COMP0_CLK"},
{"RX1 MIX1", NULL, "COMP1_CLK"},
{"RX2 MIX1", NULL, "COMP1_CLK"},
{"RX3 MIX1", NULL, "COMP2_CLK"},
@@ -3629,6 +3630,37 @@
},
};
+static int taiko_codec_enable_slim_chmask(struct wcd9xxx_codec_dai_data *dai,
+ bool up)
+{
+ int ret = 0;
+ struct wcd9xxx_ch *ch;
+
+ if (up) {
+ list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) {
+ ret = wcd9xxx_get_slave_port(ch->ch_num);
+ if (ret < 0) {
+ pr_err("%s: Invalid slave port ID: %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ } else {
+ set_bit(ret, &dai->ch_mask);
+ }
+ }
+ } else {
+ ret = wait_event_timeout(dai->dai_wait, (dai->ch_mask == 0),
+ msecs_to_jiffies(
+ TAIKO_SLIM_CLOSE_TIMEOUT));
+ if (!ret) {
+ pr_err("%s: Slim close tx/rx wait timeout\n", __func__);
+ ret = -ETIMEDOUT;
+ } else {
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
static int taiko_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
@@ -3636,7 +3668,7 @@
struct wcd9xxx *core;
struct snd_soc_codec *codec = w->codec;
struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
- u32 ret = 0;
+ int ret = 0;
struct wcd9xxx_codec_dai_data *dai;
core = dev_get_drvdata(codec->dev->parent);
@@ -3655,6 +3687,7 @@
switch (event) {
case SND_SOC_DAPM_POST_PMU:
+ (void) taiko_codec_enable_slim_chmask(dai, true);
ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
dai->rate, dai->bit_width,
&dai->grph);
@@ -3662,7 +3695,14 @@
case SND_SOC_DAPM_POST_PMD:
ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
dai->grph);
- usleep_range(15000, 15000);
+ ret = taiko_codec_enable_slim_chmask(dai, false);
+ if (ret < 0) {
+ ret = wcd9xxx_disconnect_port(core,
+ &dai->wcd9xxx_ch_list,
+ dai->grph);
+ pr_debug("%s: Disconnect RX port, ret = %d\n",
+ __func__, ret);
+ }
break;
}
return ret;
@@ -3693,6 +3733,7 @@
dai = &taiko_p->dai[w->shift];
switch (event) {
case SND_SOC_DAPM_POST_PMU:
+ (void) taiko_codec_enable_slim_chmask(dai, true);
ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
dai->rate, dai->bit_width,
&dai->grph);
@@ -3700,6 +3741,14 @@
case SND_SOC_DAPM_POST_PMD:
ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
dai->grph);
+ ret = taiko_codec_enable_slim_chmask(dai, false);
+ if (ret < 0) {
+ ret = wcd9xxx_disconnect_port(core,
+ &dai->wcd9xxx_ch_list,
+ dai->grph);
+ pr_debug("%s: Disconnect RX port, ret = %d\n",
+ __func__, ret);
+ }
break;
}
return ret;
@@ -3943,10 +3992,13 @@
SND_SOC_DAPM_SUPPLY("LDO_H", TAIKO_A_LDO_H_MODE_1, 7, 0,
taiko_codec_enable_ldo_h, SND_SOC_DAPM_POST_PMU),
- SND_SOC_DAPM_SUPPLY("COMP1_CLK", SND_SOC_NOPM, 0, 0,
+ SND_SOC_DAPM_SUPPLY("COMP0_CLK", SND_SOC_NOPM, 0, 0,
taiko_config_compander, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
- SND_SOC_DAPM_SUPPLY("COMP2_CLK", SND_SOC_NOPM, 1, 0,
+ SND_SOC_DAPM_SUPPLY("COMP1_CLK", SND_SOC_NOPM, 1, 0,
+ taiko_config_compander, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("COMP2_CLK", SND_SOC_NOPM, 2, 0,
taiko_config_compander, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
@@ -4177,34 +4229,69 @@
};
-static unsigned long slimbus_value;
-
static irqreturn_t taiko_slimbus_irq(int irq, void *data)
{
struct taiko_priv *priv = data;
struct snd_soc_codec *codec = priv->codec;
- int i, j;
+ unsigned long status = 0;
+ int i, j, port_id, k;
+ u32 bit;
u8 val;
+ bool tx, cleared;
- for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++) {
- slimbus_value = wcd9xxx_interface_reg_read(codec->control_data,
- TAIKO_SLIM_PGD_PORT_INT_STATUS0 + i);
- for_each_set_bit(j, &slimbus_value, BITS_PER_BYTE) {
- val = wcd9xxx_interface_reg_read(codec->control_data,
- TAIKO_SLIM_PGD_PORT_INT_SOURCE0 + i*8 + j);
- if (val & 0x1)
- pr_err_ratelimited(
- "overflow error on port %x, value %x\n",
- i*8 + j, val);
- if (val & 0x2)
- pr_err_ratelimited(
- "underflow error on port %x, value %x\n",
- i*8 + j, val);
+ for (i = TAIKO_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0;
+ i <= TAIKO_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) {
+ val = wcd9xxx_interface_reg_read(codec->control_data, i);
+ status |= ((u32)val << (8 * j));
+ }
+
+ for_each_set_bit(j, &status, 32) {
+ tx = (j >= 16 ? true : false);
+ port_id = (tx ? j - 16 : j);
+ val = wcd9xxx_interface_reg_read(codec->control_data,
+ TAIKO_SLIM_PGD_PORT_INT_RX_SOURCE0 + j);
+ if (val & TAIKO_SLIM_IRQ_OVERFLOW)
+ pr_err_ratelimited(
+ "%s: overflow error on %s port %d, value %x\n",
+ __func__, (tx ? "TX" : "RX"), port_id, val);
+ if (val & TAIKO_SLIM_IRQ_UNDERFLOW)
+ pr_err_ratelimited(
+ "%s: underflow error on %s port %d, value %x\n",
+ __func__, (tx ? "TX" : "RX"), port_id, val);
+ if (val & TAIKO_SLIM_IRQ_PORT_CLOSED) {
+ /*
+ * INT SOURCE register starts from RX to TX
+ * but port number in the ch_mask is in opposite way
+ */
+ bit = (tx ? j - 16 : j + 16);
+ pr_debug("%s: %s port %d closed value %x, bit %u\n",
+ __func__, (tx ? "TX" : "RX"), port_id, val,
+ bit);
+ for (k = 0, cleared = false; k < NUM_CODEC_DAIS; k++) {
+ pr_debug("%s: priv->dai[%d].ch_mask = 0x%lx\n",
+ __func__, k, priv->dai[k].ch_mask);
+ if (test_and_clear_bit(bit,
+ &priv->dai[k].ch_mask)) {
+ cleared = true;
+ if (!priv->dai[k].ch_mask)
+ wake_up(&priv->dai[k].dai_wait);
+ /*
+ * There are cases when multiple DAIs
+ * might be using the same slimbus
+ * channel. Hence don't break here.
+ */
+ }
+ }
+ WARN(!cleared,
+ "Couldn't find slimbus %s port %d for closing\n",
+ (tx ? "TX" : "RX"), port_id);
}
wcd9xxx_interface_reg_write(codec->control_data,
- TAIKO_SLIM_PGD_PORT_INT_CLR0 + i, 0xFF);
-
+ TAIKO_SLIM_PGD_PORT_INT_CLR_RX_0 +
+ (j / 8),
+ 1 << (j % 8));
}
+
return IRQ_HANDLED;
}
@@ -4546,6 +4633,7 @@
{TAIKO_A_RX_LINE_2_GAIN, 0x20, 0x20},
{TAIKO_A_RX_LINE_3_GAIN, 0x20, 0x20},
{TAIKO_A_RX_LINE_4_GAIN, 0x20, 0x20},
+ {TAIKO_A_SPKR_DRV_GAIN, 0x04, 0x04},
/* CLASS H config */
{TAIKO_A_CDC_CONN_CLSH_CTL, 0x3C, 0x14},
@@ -4596,6 +4684,13 @@
{TAIKO_A_CDC_CLK_DMIC_B1_CTL, 0xEE, 0x22},
{TAIKO_A_CDC_CLK_DMIC_B2_CTL, 0x0E, 0x02},
+ /* Compander zone selection */
+ {TAIKO_A_CDC_COMP0_B4_CTL, 0x3F, 0x37},
+ {TAIKO_A_CDC_COMP1_B4_CTL, 0x3F, 0x37},
+ {TAIKO_A_CDC_COMP2_B4_CTL, 0x3F, 0x37},
+ {TAIKO_A_CDC_COMP0_B5_CTL, 0x7F, 0x7F},
+ {TAIKO_A_CDC_COMP1_B5_CTL, 0x7F, 0x7F},
+ {TAIKO_A_CDC_COMP2_B5_CTL, 0x7F, 0x7F},
};
static void taiko_codec_init_reg(struct snd_soc_codec *codec)
diff --git a/sound/soc/msm/mpq8064.c b/sound/soc/msm/mpq8064.c
index 90c96b4..d0bfb76 100644
--- a/sound/soc/msm/mpq8064.c
+++ b/sound/soc/msm/mpq8064.c
@@ -925,7 +925,6 @@
clk_put(mi2s_bit_clk);
mi2s_bit_clk = NULL;
}
- msm_mi2s_free_gpios();
}
static int configure_mi2s_gpio(void)
@@ -958,7 +957,6 @@
int ret = 0;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- configure_mi2s_gpio();
mi2s_bit_clk = clk_get(cpu_dai->dev, "bit_clk");
if (IS_ERR(mi2s_bit_clk))
return PTR_ERR(mi2s_bit_clk);
@@ -1138,7 +1136,6 @@
clk_put(sec_i2s_rx_osr_clk);
sec_i2s_rx_osr_clk = NULL;
}
- mpq8064_sec_i2s_rx_free_gpios();
}
pr_info("%s(): substream = %s stream = %d\n", __func__,
substream->name, substream->stream);
@@ -1177,7 +1174,6 @@
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- configure_sec_i2s_rx_gpio();
sec_i2s_rx_osr_clk = clk_get(cpu_dai->dev, "osr_clk");
if (IS_ERR(sec_i2s_rx_osr_clk)) {
pr_err("Failed to get sec_i2s_rx_osr_clk\n");
@@ -1695,7 +1691,8 @@
kfree(mbhc_cfg.calibration);
return ret;
}
-
+ configure_sec_i2s_rx_gpio();
+ configure_mi2s_gpio();
return ret;
}
@@ -1707,6 +1704,8 @@
pr_err("%s: Not the right machine type\n", __func__);
return ;
}
+ mpq8064_sec_i2s_rx_free_gpios();
+ msm_mi2s_free_gpios();
platform_device_unregister(msm_snd_device);
kfree(mbhc_cfg.calibration);
}
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 23eee9d..79ce671 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -438,7 +438,6 @@
mutex_lock(&routing_lock);
- adm_pseudo_close(PSEUDOPORT_01);
for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
if (!is_be_dai_extproc(i) &&
(msm_bedais[i].active) &&
@@ -480,8 +479,12 @@
if (!is_be_dai_extproc(i) &&
(afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
(msm_bedais[i].active) &&
- (test_bit(fedai_id, &msm_bedais[i].fe_sessions)))
- adm_close(msm_bedais[i].port_id);
+ (test_bit(fedai_id, &msm_bedais[i].fe_sessions))) {
+ if (msm_bedais[i].port_id == PSEUDOPORT_01)
+ adm_pseudo_close(msm_bedais[i].port_id);
+ else
+ adm_close(msm_bedais[i].port_id);
+ }
}
fe_dai_map[fedai_id][session_type] = INVALID_SESSION;
diff --git a/sound/soc/msm/qdsp6/q6afe.c b/sound/soc/msm/qdsp6/q6afe.c
index 6cabc97..2d44a41 100644
--- a/sound/soc/msm/qdsp6/q6afe.c
+++ b/sound/soc/msm/qdsp6/q6afe.c
@@ -1790,6 +1790,9 @@
goto fail_cmd;
}
pr_debug("%s: port_id=%d\n", __func__, port_id);
+ if ((port_id == RT_PROXY_DAI_001_RX) ||
+ (port_id == RT_PROXY_DAI_002_TX))
+ return 0;
port_id = afe_convert_virtual_to_portid(port_id);
stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
index 7ba6514..94c1c85 100644
--- a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
@@ -457,6 +457,7 @@
pr_debug("%s: Trigger start\n", __func__);
q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
atomic_set(&prtd->start, 1);
+ atomic_set(&prtd->pending_buffer, 1);
break;
case SNDRV_PCM_TRIGGER_STOP:
pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 4c6a5a4..f02e5c5 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -297,9 +297,37 @@
{
u64 config = evsel->attr.config;
int type = evsel->attr.type;
+ char *buf;
+ size_t buf_sz;
- if (evsel->name)
+ if (evsel->name) {
+ /* Make new space for the modifier bits. */
+ buf_sz = strlen(evsel->name) + 3;
+ buf = malloc(buf_sz);
+ if (!buf)
+ /*
+ * Always return what was already in 'name'.
+ */
+ return evsel->name;
+
+ strlcpy(buf, evsel->name, buf_sz);
+
+ free(evsel->name);
+
+ evsel->name = buf;
+
+ /* User mode profiling. */
+ if (!evsel->attr.exclude_user && evsel->attr.exclude_kernel)
+ strlcpy(&evsel->name[strlen(evsel->name)], ":u",
+ buf_sz);
+ /* Kernel mode profiling. */
+ else if (!evsel->attr.exclude_kernel &&
+ evsel->attr.exclude_user)
+ strlcpy(&evsel->name[strlen(evsel->name)], ":k",
+ buf_sz);
+
return evsel->name;
+ }
return __event_name(type, config, NULL);
}