Merge "9625: i2c: Support I2C mini core on BLSP1 QUP2"
diff --git a/Documentation/devicetree/bindings/arm/msm/lpm-resources.txt b/Documentation/devicetree/bindings/arm/msm/lpm-resources.txt
index b16d40f..7f2a21b 100644
--- a/Documentation/devicetree/bindings/arm/msm/lpm-resources.txt
+++ b/Documentation/devicetree/bindings/arm/msm/lpm-resources.txt
@@ -8,13 +8,25 @@
that need to be monitored for usage requirement to check if a given low power
state can be entered.Each resource is identified by a combination of the name,
id,type and key which is also used by the RPM to identify a shared resource.
+The name and resource-type are required nodes; the type, id and key are
+optional nodes which are needed if the resource type is RPM shared resource
+(MSM_LPM_RPM_RS_TYPE).
-The required nodes for lpm-resources are:
+The nodes for lpm-resources are:
+
+Required Nodes:
- compatible: "qcom,lpm-resources"
- reg: The numeric level id
- qcom,name: The name of the low power resource represented
as a string.
+- qcom,resource-type: The type of the LPM resource.
+ MSM_LPM_RPM_RS_TYPE = 0
+ MSM_LPM_LOCAL_RS_TYPE = 1
+
+
+Optional Nodes:
+
- qcom,type: The type of resource used like smps or pxo
represented as a hex value.
- qcom,id: The id representing a device within a resource type.
@@ -25,6 +37,7 @@
qcom,lpm-resources@0 {
reg = <0x0>;
qcom,name = "vdd-dig";
+ qcom,resource-type = <0>;
qcom,type = <0x62706d73>; /* "smpb" */
qcom,id = <0x02>;
qcom,key = <0x6e726f63>; /* "corn" */
diff --git a/Documentation/devicetree/bindings/gpio/gpio_keys.txt b/Documentation/devicetree/bindings/gpio/gpio_keys.txt
index 5c2c021..4e810e1 100644
--- a/Documentation/devicetree/bindings/gpio/gpio_keys.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio_keys.txt
@@ -4,6 +4,7 @@
- compatible = "gpio-keys";
Optional properties:
+ - input-name: input name of the device
- autorepeat: Boolean, Enable auto repeat feature of Linux input
subsystem.
@@ -28,6 +29,7 @@
#address-cells = <1>;
#size-cells = <0>;
autorepeat;
+ input-name = "gpio-keys";
button@21 {
label = "GPIO Key UP";
linux,code = <103>;
diff --git a/Documentation/devicetree/bindings/iommu/msm_iommu.txt b/Documentation/devicetree/bindings/iommu/msm_iommu.txt
index 7a90cc0..dcf023d 100644
--- a/Documentation/devicetree/bindings/iommu/msm_iommu.txt
+++ b/Documentation/devicetree/bindings/iommu/msm_iommu.txt
@@ -13,7 +13,6 @@
- qcom,iommu-ctx-sids : List of stream identifiers associated with this
translation context.
- label : Name of the context bank
- - qcom,iommu-smt-size : Number of SMR entries in the SMT of this HW block
- vdd-supply : vdd-supply: phandle to GDSC regulator controlling this IOMMU.
Optional properties:
diff --git a/Documentation/devicetree/bindings/qseecom/qseecom.txt b/Documentation/devicetree/bindings/qseecom/qseecom.txt
index 8b17ba9..5e7c42a 100644
--- a/Documentation/devicetree/bindings/qseecom/qseecom.txt
+++ b/Documentation/devicetree/bindings/qseecom/qseecom.txt
@@ -2,9 +2,21 @@
Required properties:
- compatible : Should be "qcom,qseecom"
+- qcom, msm_bus,name: Should be "qseecom-noc"
+- qcom, msm_bus,num_cases: Depends on the use cases for bus scaling
+- qcom, msm_bus,num_paths: The paths for source and destination ports
+- qcom, msm_bus,vectors: Vectors for bus topology.
Example:
-
qcom,qseecom@fe806000 {
compatible = "qcom,qseecom";
+ qcom,msm_bus,name = "qseecom-noc";
+ qcom,msm_bus,num_cases = <4>;
+ qcom,msm_bus,active_only = <0>;
+ qcom,msm_bus,num_paths = <1>;
+ qcom,msm_bus,vectors =
+ <55 512 0 0>,
+ <55 512 3936000000 393600000>,
+ <55 512 3936000000 393600000>,
+ <55 512 3936000000 393600000>;
};
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 2864fd1..cb2c1e1 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -123,15 +123,36 @@
- compatible : "qcom,msm-ocmem-audio"
- - qcom,msm-ocmem-audio-src-id: Master port id
+ - qcom,msm_bus,name: Client name
- - qcom,msm-ocmem-audio-dst-id: Slave port id
+ - qcom,msm_bus,num_cases: Total number of use cases
- - qcom,msm-ocmem-audio-ab: arbitrated bandwidth
- in Bytes/s
+ - qcom,msm_bus,active_only: Context flag for requests in active or
+ dual (active & sleep) contex
- - qcom,msm-ocmem-audio-ib: instantaneous bandwidth
- in Bytes/s
+ - qcom,msm_bus,num_paths: Total number of master-slave pairs
+
+ - qcom,msm_bus,vectors: Arrays of unsigned integers representing:
+ master-id, slave-id, arbitrated bandwidth,
+ instantaneous bandwidth
+* wcd9xxx_intc
+
+Required properties:
+
+ - compatible : "qcom,wcd9xxx-irq"
+
+ - interrupt-controller : Mark this device node as an interrupt
+ controller
+
+ - #interrupt-cells : Should be 1
+
+ - interrupt-parent : Parent interrupt controller
+
+ - interrupts : Interrupt number on the parent
+ interrupt controller
+
+ - interrupt-names : Name of interrupt on the parent
+ interrupt controller
Example:
@@ -234,10 +255,22 @@
qcom,msm-ocmem-audio {
compatible = "qcom,msm-ocmem-audio";
- qcom,msm-ocmem-audio-src-id = <11>;
- qcom,msm-ocmem-audio-dst-id = <604>;
- qcom,msm-ocmem-audio-ab = <209715200>;
- qcom,msm-ocmem-audio-ib = <471859200>;
+ qcom,msm_bus,name = "audio-ocmem";
+ qcom,msm_bus,num_cases = <2>;
+ qcom,msm_bus,active_only = <0>;
+ qcom,msm_bus,num_paths = <1>;
+ qcom,msm_bus,vectors =
+ <11 604 0 0>,
+ <11 604 32505856 325058560>;
+ };
+
+ wcd9xxx_intc: wcd9xxx-irq {
+ compatible = "qcom,wcd9xxx-irq";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <72 0>;
+ interrupt-names = "cdc-int";
};
* MSM8974 ASoC Machine driver
diff --git a/Documentation/mmc/mmc-dev-attrs.txt b/Documentation/mmc/mmc-dev-attrs.txt
index 22ae844..7dde34f 100644
--- a/Documentation/mmc/mmc-dev-attrs.txt
+++ b/Documentation/mmc/mmc-dev-attrs.txt
@@ -8,6 +8,32 @@
force_ro Enforce read-only access even if write protect switch is off.
+ num_wr_reqs_to_start_packing This attribute is used to determine
+ the trigger for activating the write packing, in case the write
+ packing control feature is enabled.
+
+ When the MMC manages to reach a point where num_wr_reqs_to_start_packing
+ write requests could be packed, it enables the write packing feature.
+ This allows us to start the write packing only when it is beneficial
+ and has minimum affect on the read latency.
+
+ The number of potential packed requests that will trigger the packing
+ can be configured via sysfs by writing the required value to:
+ /sys/block/<block_dev_name>/num_wr_reqs_to_start_packing.
+
+ The default value of num_wr_reqs_to_start_packing was determined by
+ running parallel lmdd write and lmdd read operations and calculating
+ the max number of packed writes requests.
+
+ min_sectors_to_check_bkops_status This attribute is used to
+ determine whether the status bit that indicates the need for BKOPS
+ should be checked. The value is stored in this attribute represents
+ the minimum number of sectors that needs to be changed in the device
+ (written or discarded) in order to require the status-bit of BKOPS
+ to be checked. The value can modified via sysfs by writing the
+ required value to:
+ /sys/block/<block_dev_name>/min_sectors_to_check_bkops_status
+
SD and MMC Device Attributes
============================
diff --git a/arch/arm/boot/dts/msm-iommu.dtsi b/arch/arm/boot/dts/msm-iommu.dtsi
index e907de8..709f40a 100755
--- a/arch/arm/boot/dts/msm-iommu.dtsi
+++ b/arch/arm/boot/dts/msm-iommu.dtsi
@@ -18,7 +18,6 @@
ranges;
reg = <0xfda64000 0x10000>;
vdd-supply = <&gdsc_jpeg>;
- qcom,iommu-smt-size = <16>;
status = "disabled";
qcom,iommu-ctx@fda6c000 {
@@ -50,7 +49,6 @@
ranges;
reg = <0xfd928000 0x10000>;
vdd-supply = <&gdsc_mdss>;
- qcom,iommu-smt-size = <16>;
status = "disabled";
qcom,iommu-ctx@fd930000 {
@@ -75,7 +73,6 @@
ranges;
reg = <0xfdc84000 0x10000>;
vdd-supply = <&gdsc_venus>;
- qcom,iommu-smt-size = <16>;
qcom,needs-alt-core-clk;
status = "disabled";
@@ -108,10 +105,35 @@
ranges;
reg = <0xfdb10000 0x10000>;
vdd-supply = <&gdsc_oxili_cx>;
- qcom,iommu-smt-size = <32>;
qcom,needs-alt-core-clk;
status = "disabled";
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2050
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2008>;
+
+ qcom,iommu-bfb-data = <0xffffffff
+ 0xffffffff
+ 0x00000004
+ 0x00000010
+ 0x00000000
+ 0x00000000
+ 0x00000001
+ 0x00000021
+ 0x0
+ 0x1
+ 0x81
+ 0x0>;
+
qcom,iommu-ctx@fdb18000 {
reg = <0xfdb18000 0x1000>;
interrupts = <0 241 0>;
@@ -134,7 +156,6 @@
ranges;
reg = <0xfda44000 0x10000>;
vdd-supply = <&gdsc_vfe>;
- qcom,iommu-smt-size = <32>;
status = "disabled";
qcom,iommu-ctx@fda4c000 {
diff --git a/arch/arm/boot/dts/msm8226-sim.dts b/arch/arm/boot/dts/msm8226-sim.dts
index 4330849..aeda1d8 100644
--- a/arch/arm/boot/dts/msm8226-sim.dts
+++ b/arch/arm/boot/dts/msm8226-sim.dts
@@ -58,4 +58,21 @@
reg = <0xf995e000 0x1000>;
interrupts = <0 114 0>;
};
+
+ usb@f9a55000 {
+ compatible = "qcom,hsusb-otg";
+ reg = <0xf9a55000 0x400>;
+ interrupts = <0 134 0>;
+ interrupt-names = "core_irq";
+
+ qcom,hsusb-otg-phy-type = <2>;
+ qcom,hsusb-otg-mode = <1>;
+ qcom,hsusb-otg-otg-control = <1>;
+ qcom,hsusb-otg-disable-reset;
+ };
+
+ android_usb {
+ compatible = "qcom,android-usb";
+ };
+
};
diff --git a/arch/arm/boot/dts/msm8910-sim.dts b/arch/arm/boot/dts/msm8910-sim.dts
new file mode 100644
index 0000000..268e1f8
--- /dev/null
+++ b/arch/arm/boot/dts/msm8910-sim.dts
@@ -0,0 +1,51 @@
+/* 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.
+ */
+
+/dts-v1/;
+/include/ "skeleton.dtsi"
+
+/ {
+ model = "Qualcomm MSM 8910 Simulator";
+ compatible = "qcom,msm8910-sim", "qcom,msm8910";
+ qcom,msm-id = <147 1 0>;
+ interrupt-parent = <&intc>;
+
+ intc: interrupt-controller@f9000000 {
+ compatible = "qcom,msm-qgic2";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ reg = <0xf9000000 0x1000>,
+ <0xf9002000 0x1000>;
+ };
+
+ msmgpio: gpio@fd510000 {
+ compatible = "qcom,msm-gpio";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ reg = <0xfd510000 0x4000>;
+ #gpio-cells = <2>;
+ };
+
+ timer: msm-qtimer@f9021000 {
+ compatible = "qcom,msm-qtimer", "arm,armv7-timer";
+ reg = <0xf9021000 0x1000>;
+ interrupts = <0 7 0 0 8 0>;
+ irq-is-not-percpu;
+ clock-frequency = <19200000>;
+ };
+
+ serial@f991f000 {
+ compatible = "qcom,msm-lsuart-v14";
+ reg = <0xf991f000 0x1000>;
+ interrupts = <0 109 0>;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8974-cdp.dts b/arch/arm/boot/dts/msm8974-cdp.dts
index aff0adc..05fcc4f 100644
--- a/arch/arm/boot/dts/msm8974-cdp.dts
+++ b/arch/arm/boot/dts/msm8974-cdp.dts
@@ -112,6 +112,7 @@
gpio_keys {
compatible = "gpio-keys";
+ input-name = "gpio-keys";
camera_snapshot {
label = "camera_snapshot";
diff --git a/arch/arm/boot/dts/msm8974-liquid.dts b/arch/arm/boot/dts/msm8974-liquid.dts
index 2abc1d5..bf4adaf 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dts
+++ b/arch/arm/boot/dts/msm8974-liquid.dts
@@ -22,27 +22,59 @@
serial@f991e000 {
status = "ok";
};
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ input-name = "gpio-keys";
+
+ home {
+ label = "home";
+ gpios = <&pm8941_gpios 1 0x1>;
+ linux,input-type = <1>;
+ linux,code = <102>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ vol_down {
+ label = "volume_down";
+ gpios = <&pm8941_gpios 2 0x1>;
+ linux,input-type = <1>;
+ linux,code = <114>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&pm8941_gpios 5 0x1>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+ };
};
&pm8941_gpios {
gpio@c000 { /* GPIO 1 */
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <2>;
+ qcom,select = <0>;
};
gpio@c100 { /* GPIO 2 */
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <2>;
+ qcom,select = <0>;
};
gpio@c200 { /* GPIO 3 */
- qcom,mode = <0>;
- qcom,pull = <0>;
- qcom,vin-sel = <2>;
- qcom,select = <0>;
};
gpio@c300 { /* GPIO 4 */
- qcom,mode = <0>;
- qcom,pull = <0>;
- qcom,vin-sel = <2>;
- qcom,select = <0>;
};
gpio@c400 { /* GPIO 5 */
diff --git a/arch/arm/boot/dts/msm8974-mtp.dts b/arch/arm/boot/dts/msm8974-mtp.dts
index 00aec9f..e0d6ad3 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dts
+++ b/arch/arm/boot/dts/msm8974-mtp.dts
@@ -112,6 +112,7 @@
gpio_keys {
compatible = "gpio-keys";
+ input-name = "gpio-keys";
camera_snapshot {
label = "camera_snapshot";
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index b992e86..9770ecf 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -42,6 +42,15 @@
reg = <0xfd510000 0x4000>;
};
+ wcd9xxx_intc: wcd9xxx-irq {
+ compatible = "qcom,wcd9xxx-irq";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <72 0>;
+ interrupt-names = "cdc-int";
+ };
+
timer {
compatible = "qcom,msm-qtimer", "arm,armv7-timer";
interrupts = <1 2 0 1 3 0>;
@@ -268,6 +277,9 @@
compatible = "qcom,taiko-slim-pgd";
elemental-addr = [00 01 A0 00 17 02];
+ interrupt-parent = <&wcd9xxx_intc>;
+ interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28>;
+
qcom,cdc-reset-gpio = <&msmgpio 63 0>;
cdc-vdd-buck-supply = <&pm8941_s2>;
@@ -761,10 +773,13 @@
qcom,msm-ocmem-audio {
compatible = "qcom,msm-ocmem-audio";
- qcom,msm-ocmem-audio-src-id = <11>;
- qcom,msm-ocmem-audio-dst-id = <604>;
- qcom,msm-ocmem-audio-ab = <32505856>;
- qcom,msm-ocmem-audio-ib = <32505856>;
+ qcom,msm_bus,name = "audio-ocmem";
+ qcom,msm_bus,num_cases = <2>;
+ qcom,msm_bus,active_only = <0>;
+ qcom,msm_bus,num_paths = <1>;
+ qcom,msm_bus,vectors =
+ <11 604 0 0>,
+ <11 604 32505856 32505856>;
};
qcom,mss@fc880000 {
@@ -883,6 +898,15 @@
qcom,qseecom@fe806000 {
compatible = "qcom,qseecom";
+ qcom,msm_bus,name = "qseecom-noc";
+ qcom,msm_bus,num_cases = <4>;
+ qcom,msm_bus,active_only = <0>;
+ qcom,msm_bus,num_paths = <1>;
+ qcom,msm_bus,vectors =
+ <55 512 0 0>,
+ <55 512 3936000000 393600000>,
+ <55 512 3936000000 393600000>,
+ <55 512 3936000000 393600000>;
};
qcom,wdt@f9017000 {
@@ -894,9 +918,9 @@
qcom,ipi-ping = <1>;
};
- qcom,tz-log@fe805720 {
+ qcom,tz-log@fc03000 {
compatible = "qcom,tz-log";
- reg = <0xfe805720 0x1000>;
+ reg = <0x0fc03000 0x1000>;
};
qcom,venus@fdce0000 {
diff --git a/arch/arm/boot/dts/msm8974_pm.dtsi b/arch/arm/boot/dts/msm8974_pm.dtsi
index e39a72a..b2f3fec 100644
--- a/arch/arm/boot/dts/msm8974_pm.dtsi
+++ b/arch/arm/boot/dts/msm8974_pm.dtsi
@@ -28,6 +28,8 @@
qcom,saw2-spm-dly= <0x20000400>;
qcom,saw2-spm-ctl = <0x1>;
qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d0 c0 a0 90 03 d0 98 a2 c0
+ 0b 00 42 1b 0f];
qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
10 0b 30 06 26 30 0f];
qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
@@ -49,6 +51,8 @@
qcom,saw2-spm-dly= <0x20000400>;
qcom,saw2-spm-ctl = <0x1>;
qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d0 c0 a0 90 03 d0 98 a2 c0
+ 0b 00 42 1b 0f];
qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
10 0b 30 06 26 30 0f];
qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
@@ -70,6 +74,8 @@
qcom,saw2-spm-dly= <0x20000400>;
qcom,saw2-spm-ctl = <0x1>;
qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d0 c0 a0 90 03 d0 98 a2 c0
+ 0b 00 42 1b 0f];
qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
10 0b 30 06 26 30 0f];
qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
@@ -91,6 +97,8 @@
qcom,saw2-spm-dly= <0x20000400>;
qcom,saw2-spm-ctl = <0x1>;
qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d0 c0 a0 90 03 d0 98 a2 c0
+ 0b 00 42 1b 0f];
qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
10 0b 30 06 26 30 0f];
qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
@@ -130,6 +138,7 @@
qcom,lpm-resources@0 {
reg = <0x0>;
qcom,name = "vdd-dig";
+ qcom,resource-type = <0>;
qcom,type = <0x62706d73>; /* "smpb" */
qcom,id = <0x02>;
qcom,key = <0x6e726f63>; /* "corn" */
@@ -138,6 +147,7 @@
qcom,lpm-resources@1 {
reg = <0x1>;
qcom,name = "vdd-mem";
+ qcom,resource-type = <0>;
qcom,type = <0x62706d73>; /* "smpb" */
qcom,id = <0x01>;
qcom,key = <0x7675>; /* "uv" */
@@ -146,10 +156,17 @@
qcom,lpm-resources@2 {
reg = <0x2>;
qcom,name = "pxo";
+ qcom,resource-type = <0>;
qcom,type = <0x306b6c63>; /* "clk0" */
qcom,id = <0x00>;
qcom,key = <0x62616e45>; /* "Enab" */
};
+
+ qcom,lpm-resources@3 {
+ reg = <0x3>;
+ qcom,name = "l2";
+ qcom,resource-type = <1>;
+ };
};
qcom,lpm-levels {
@@ -174,6 +191,22 @@
qcom,lpm-level@1 {
reg = <0x1>;
+ qcom,mode = <4>; /* MSM_PM_SLEEP_MODE_RETENTION*/
+ qcom,xo = <1>; /* ON */
+ qcom,l2 = <3>; /* ACTIVE */
+ qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+ qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+ qcom,vdd-dig-upper-bound = <5>; /* MAX */
+ qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
+ qcom,latency-us = <1500>;
+ qcom,ss-power = <200>;
+ qcom,energy-overhead = <576000>;
+ qcom,time-overhead = <2000>;
+ };
+
+
+ qcom,lpm-level@2 {
+ reg = <0x2>;
qcom,mode = <2>; /* MSM_PM_SLEEP_MODE_STANDALONE_POWER_COLLAPSE */
qcom,xo = <1>; /* ON */
qcom,l2 = <3>; /* ACTIVE */
@@ -187,8 +220,8 @@
qcom,time-overhead = <2000>;
};
- qcom,lpm-level@2 {
- reg = <0x2>;
+ qcom,lpm-level@3 {
+ reg = <0x3>;
qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <1>; /* ON */
qcom,l2 = <1>; /* GDHS */
@@ -202,8 +235,8 @@
qcom,time-overhead = <8500>;
};
- qcom,lpm-level@3 {
- reg = <0x3>;
+ qcom,lpm-level@4 {
+ reg = <0x4>;
qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <1>; /* ON */
qcom,l2 = <0>; /* OFF */
@@ -217,8 +250,8 @@
qcom,time-overhead = <9000>;
};
- qcom,lpm-level@4 {
- reg = <0x4>;
+ qcom,lpm-level@5 {
+ reg = <0x5>;
qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <1>; /* ON */
qcom,l2 = <0>; /* OFF */
@@ -232,8 +265,8 @@
qcom,time-overhead = <10000>;
};
- qcom,lpm-level@5 {
- reg = <0x5>;
+ qcom,lpm-level@6 {
+ reg = <0x6>;
qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <0>; /* OFF */
qcom,l2 = <1>; /* GDHS */
@@ -247,8 +280,8 @@
qcom,time-overhead = <12000>;
};
- qcom,lpm-level@6 {
- reg = <0x6>;
+ qcom,lpm-level@7 {
+ reg = <0x7>;
qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <0>; /* OFF */
qcom,l2 = <0>; /* OFF */
@@ -262,8 +295,8 @@
qcom,time-overhead = <18000>;
};
- qcom,lpm-level@7 {
- reg = <0x7>;
+ qcom,lpm-level@8 {
+ reg = <0x8>;
qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <0>; /* OFF */
qcom,l2 = <0>; /* OFF */
@@ -277,8 +310,8 @@
qcom,time-overhead = <23500>;
};
- qcom,lpm-level@8 {
- reg = <0x8>;
+ qcom,lpm-level@9 {
+ reg = <0x9>;
qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <0>; /* OFF */
qcom,l2 = <0>; /* OFF */
diff --git a/arch/arm/boot/dts/msm9625-regulator.dtsi b/arch/arm/boot/dts/msm9625-regulator.dtsi
index c42af2c..1b5b03b 100644
--- a/arch/arm/boot/dts/msm9625-regulator.dtsi
+++ b/arch/arm/boot/dts/msm9625-regulator.dtsi
@@ -10,154 +10,242 @@
* GNU General Public License for more details.
*/
-&spmi_bus {
- qcom,pm8019@1 {
- pm8019_s1: regulator@1400 {
+&rpm_bus {
+ rpm-regulator-smpa1 {
+ status = "okay";
+ pm8019_s1: regulator-s1 {
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <1050000>;
- qcom,enable-time = <500>;
+ qcom,init-voltage = <1050000>;
status = "okay";
};
+ };
- pm8019_s2: regulator@1700 {
+ rpm-regulator-smpa2 {
+ status = "okay";
+ qcom,allow-atomic = <1>;
+ pm8019_s2: regulator-s2 {
regulator-min-microvolt = <1250000>;
regulator-max-microvolt = <1250000>;
+ qcom,init-voltage = <1250000>;
+ qcom,init-current = <100>;
qcom,system-load = <100000>;
- qcom,enable-time = <500>;
regulator-always-on;
status = "okay";
};
+ };
- pm8019_s3: regulator@1a00 {
- regulator-min-microvolt = <1100000>;
+ rpm-regulator-smpa3 {
+ status = "okay";
+ pm8019_s3: regulator-s3 {
+ regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <1100000>;
+ qcom,init-voltage = <1100000>;
+ qcom,init-current = <100>;
qcom,system-load = <100000>;
- qcom,enable-time = <500>;
regulator-always-on;
status = "okay";
};
+ pm8019_s3_ao: regulator-s3-ao {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8019_s3_ao";
+ qcom,set = <1>;
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1100000>;
+ status = "okay";
+ };
+ };
- pm8019_s4: regulator@1d00 {
+ rpm-regulator-smpa4 {
+ status = "okay";
+ pm8019_s4: regulator-s4 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <2075000>;
+ qcom,init-voltage = <2075000>;
+ qcom,init-current = <100>;
qcom,system-load = <100000>;
- qcom,enable-time = <500>;
regulator-always-on;
status = "okay";
};
+ };
- pm8019_l1: regulator@4000 {
+ rpm-regulator-ldoa1 {
+ status = "okay";
+ pm8019_l1: regulator-l1 {
parent-supply = <&pm8019_s2>;
regulator-min-microvolt = <1225000>;
regulator-max-microvolt = <1225000>;
- qcom,enable-time = <200>;
+ qcom,init-voltage = <1225000>;
status = "okay";
};
+ };
- pm8019_l2: regulator@4100 {
+ rpm-regulator-ldoa2 {
+ status = "okay";
+ pm8019_l2: regulator-l2 {
parent-supply = <&pm8019_s4>;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
- qcom,enable-time = <200>;
+ qcom,init-voltage = <1800000>;
status = "okay";
};
+ };
- pm8019_l3: regulator@4200 {
+ rpm-regulator-ldoa3 {
+ status = "okay";
+ pm8019_l3: regulator-l3 {
parent-supply = <&pm8019_s4>;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
- qcom,enable-time = <200>;
+ qcom,init-voltage = <1800000>;
status = "okay";
};
+ };
- pm8019_l4: regulator@4300 {
+ rpm-regulator-ldoa4 {
+ status = "okay";
+ pm8019_l4: regulator-l4 {
regulator-min-microvolt = <3075000>;
regulator-max-microvolt = <3075000>;
- qcom,enable-time = <200>;
+ qcom,init-voltage = <3075000>;
status = "okay";
};
+ };
- pm8019_l5: regulator@4400 {
+ rpm-regulator-ldoa5 {
+ status = "okay";
+ pm8019_l5: regulator-l5 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <2850000>;
- qcom,enable-time = <200>;
+ qcom,init-voltage = <1800000>;
status = "okay";
};
+ };
- pm8019_l6: regulator@4500 {
+ rpm-regulator-ldoa6 {
+ status = "okay";
+ pm8019_l6: regulator-l6 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <2850000>;
- qcom,enable-time = <200>;
+ qcom,init-voltage = <1800000>;
status = "okay";
};
+ };
- pm8019_l7: regulator@4600 {
+ rpm-regulator-ldoa7 {
+ status = "okay";
+ pm8019_l7: regulator-l7 {
parent-supply = <&pm8019_s4>;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
- qcom,enable-time = <200>;
+ qcom,init-voltage = <1800000>;
status = "okay";
};
+ };
- pm8019_l8: regulator@4700 {
+ rpm-regulator-ldoa8 {
+ status = "okay";
+ pm8019_l8: regulator-l8 {
parent-supply = <&pm8019_s4>;
regulator-min-microvolt = <2050000>;
regulator-max-microvolt = <2050000>;
- qcom,enable-time = <200>;
+ qcom,init-voltage = <2050000>;
status = "okay";
};
+ };
- pm8019_l9: regulator@4800 {
+ rpm-regulator-ldoa9 {
+ status = "okay";
+ pm8019_l9: regulator-l9 {
parent-supply = <&pm8019_s2>;
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1200000>;
+ qcom,init-voltage = <1200000>;
+ qcom,init-current = <10>;
qcom,system-load = <10000>;
- qcom,enable-time = <200>;
regulator-always-on;
status = "okay";
};
+ };
- pm8019_l10: regulator@4900 {
+ rpm-regulator-ldoa10 {
+ status = "okay";
+ pm8019_l10: regulator-l10 {
parent-supply = <&pm8019_s3>;
- regulator-min-microvolt = <1050000>;
+ regulator-min-microvolt = <500000>;
regulator-max-microvolt = <1050000>;
- qcom,system-load = <10000>;
- qcom,enable-time = <200>;
- regulator-always-on;
status = "okay";
};
+ pm8019_l10_corner: regulator-l10-corner {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8019_l10_corner";
+ qcom,set = <3>;
+ regulator-min-microvolt = <1>;
+ regulator-max-microvolt = <7>;
+ qcom,use-voltage-corner;
+ status = "okay";
+ qcom,consumer-supplies = "vdd_dig", "";
+ };
+ pm8019_l10_corner_ao: regulator-l10-corner-ao {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8019_l10_corner_ao";
+ qcom,set = <1>;
+ regulator-min-microvolt = <1>;
+ regulator-max-microvolt = <7>;
+ qcom,use-voltage-corner;
+ status = "okay";
+ };
+ };
- pm8019_l11: regulator@4a00 {
+ rpm-regulator-ldoa11 {
+ status = "okay";
+ pm8019_l11: regulator-l11 {
parent-supply = <&pm8019_s4>;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
+ qcom,init-current = <10>;
qcom,system-load = <10000>;
- qcom,enable-time = <200>;
regulator-always-on;
status = "okay";
};
+ };
- pm8019_l12: regulator@4b00 {
+ rpm-regulator-ldoa12 {
+ status = "okay";
+ pm8019_l12: regulator-l12 {
parent-supply = <&pm8019_s3>;
- regulator-min-microvolt = <1050000>;
+ regulator-min-microvolt = <750000>;
regulator-max-microvolt = <1050000>;
- qcom,system-load = <10000>;
- qcom,enable-time = <200>;
- regulator-always-on;
status = "okay";
};
+ pm8019_l12_ao: regulator-l12-ao {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8019_l12_ao";
+ qcom,set = <1>;
+ parent-supply = <&pm8019_s3_ao>;
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <1050000>;
+ status = "okay";
+ };
+ };
- pm8019_l13: regulator@4c00 {
+ rpm-regulator-ldoa13 {
+ status = "okay";
+ pm8019_l13: regulator-l13 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <2950000>;
- qcom,enable-time = <200>;
+ qcom,init-voltage = <2950000>;
status = "okay";
};
+ };
- pm8019_l14: regulator@4d00 {
+ rpm-regulator-ldoa14 {
+ status = "okay";
+ pm8019_l14: regulator-l14 {
regulator-min-microvolt = <2700000>;
regulator-max-microvolt = <2700000>;
- qcom,enable-time = <200>;
+ qcom,init-voltage = <2700000>;
status = "okay";
};
};
diff --git a/arch/arm/configs/msm8910_defconfig b/arch/arm/configs/msm8910_defconfig
new file mode 100644
index 0000000..ebcc6f5
--- /dev/null
+++ b/arch/arm/configs/msm8910_defconfig
@@ -0,0 +1,100 @@
+# CONFIG_ARM_PATCH_PHYS_VIRT is not set
+CONFIG_EXPERIMENTAL=y
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SYSVIPC=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_SCHED=y
+# CONFIG_FAIR_GROUP_SCHED is not set
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_RD_BZIP2=y
+CONFIG_RD_LZMA=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=m
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_ARCH_MSM=y
+CONFIG_ARCH_MSM8910=y
+# CONFIG_MSM_STACKED_MEMORY is not set
+CONFIG_CPU_HAS_L2_PMU=y
+# CONFIG_MSM_FIQ_SUPPORT is not set
+# CONFIG_MSM_PROC_COMM is not set
+CONFIG_MSM_DIRECT_SCLK_ACCESS=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_ARM_ARCH_TIMER=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+CONFIG_VMALLOC_RESERVE=0x19000000
+CONFIG_USE_OF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_SUSPEND is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+# CONFIG_ANDROID_PMEM is not set
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=m
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_MSM_HSL=y
+CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+CONFIG_HW_RANDOM=y
+CONFIG_DEBUG_GPIO=y
+CONFIG_GPIO_SYSFS=y
+# CONFIG_HWMON is not set
+# CONFIG_HID_SUPPORT is not set
+# CONFIG_USB_SUPPORT is not set
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_FS=y
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_USER=y
+CONFIG_KEYS=y
+CONFIG_CRYPTO_AUTHENC=y
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=y
+CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_ARC4=y
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_DEFLATE=y
+# CONFIG_CRYPTO_HW is not set
+CONFIG_CRC_CCITT=y
+CONFIG_CRC16=y
+CONFIG_LIBCRC32C=y
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index 45d52e4..6a6bfda 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -71,7 +71,8 @@
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_AVS_HW=y
# CONFIG_MSM_HW3D is not set
-CONFIG_MSM_PIL_QDSP6V4=y
+CONFIG_MSM_PIL_LPASS_QDSP6V4=y
+CONFIG_MSM_PIL_MODEM_QDSP6V4=y
CONFIG_MSM_PIL_RIVA=y
CONFIG_MSM_PIL_TZAPPS=y
CONFIG_MSM_PIL_DSPS=y
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index 465598f..cf2dd23 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -70,7 +70,8 @@
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_AVS_HW=y
# CONFIG_MSM_HW3D is not set
-CONFIG_MSM_PIL_QDSP6V4=y
+CONFIG_MSM_PIL_LPASS_QDSP6V4=y
+CONFIG_MSM_PIL_MODEM_QDSP6V4=y
CONFIG_MSM_PIL_RIVA=y
CONFIG_MSM_PIL_TZAPPS=y
CONFIG_MSM_PIL_DSPS=y
diff --git a/arch/arm/configs/msm9615_defconfig b/arch/arm/configs/msm9615_defconfig
index 2cc801e..7c3d5b0 100644
--- a/arch/arm/configs/msm9615_defconfig
+++ b/arch/arm/configs/msm9615_defconfig
@@ -49,6 +49,8 @@
CONFIG_MSM_SUBSYSTEM_RESTART=y
# CONFIG_MSM_SYSMON_COMM is not set
CONFIG_MSM_MODEM_8960=y
+CONFIG_MSM_PIL_LPASS_QDSP6V4=y
+CONFIG_MSM_PIL_MODEM_QDSP6V4=y
CONFIG_MSM_LPASS_8960=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index 284f5fc..7abc135 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -35,6 +35,9 @@
CONFIG_CPU_HAS_L2_PMU=y
# CONFIG_MSM_FIQ_SUPPORT is not set
# CONFIG_MSM_PROC_COMM is not set
+CONFIG_MSM_SMD=y
+CONFIG_MSM_SMD_PKG4=y
+CONFIG_MSM_RPM_REGULATOR_SMD=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_WATCHDOG_V2=y
CONFIG_NO_HZ=y
@@ -45,10 +48,11 @@
CONFIG_HIGHMEM=y
CONFIG_VMALLOC_RESERVE=0x19000000
CONFIG_USE_OF=y
+CONFIG_CPU_IDLE=y
CONFIG_VFP=y
CONFIG_NEON=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-# CONFIG_SUSPEND is not set
+CONFIG_PM_RUNTIME=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
@@ -102,7 +106,11 @@
# CONFIG_HWMON is not set
CONFIG_REGULATOR=y
CONFIG_REGULATOR_QPNP=y
-# CONFIG_HID_SUPPORT is not set
+CONFIG_ION=y
+CONFIG_ION_MSM=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
CONFIG_USB_GADGET=y
CONFIG_USB_CI13XXX_MSM=y
CONFIG_USB_G_ANDROID=y
@@ -147,3 +155,13 @@
# CONFIG_CRYPTO_HW is not set
CONFIG_CRC_CCITT=y
CONFIG_LIBCRC32C=y
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_TEST=m
+CONFIG_MMC_MSM=y
+CONFIG_MMC_MSM_SPS_SUPPORT=y
+CONFIG_MMC_UNSAFE_RESUME=y
diff --git a/arch/arm/kernel/perf_event_msm.c b/arch/arm/kernel/perf_event_msm.c
index 90c9c9e..8f58adf 100644
--- a/arch/arm/kernel/perf_event_msm.c
+++ b/arch/arm/kernel/perf_event_msm.c
@@ -709,8 +709,6 @@
static struct arm_pmu scorpion_pmu = {
.handle_irq = armv7pmu_handle_irq,
- .request_pmu_irq = msm_request_irq,
- .free_pmu_irq = msm_free_irq,
.enable = scorpion_pmu_enable_event,
.disable = scorpion_pmu_disable_event,
.read_counter = armv7pmu_read_counter,
@@ -731,6 +729,9 @@
scorpion_pmu.name = "ARMv7 Scorpion";
scorpion_pmu.num_events = armv7_read_num_pmnc_events();
scorpion_pmu.pmu.attr_groups = msm_l1_pmu_attr_grps;
+ /* Unicore can't use the percpu IRQ API. */
+ scorpion_pmu.request_pmu_irq = armpmu_generic_request_irq;
+ scorpion_pmu.free_pmu_irq = armpmu_generic_free_irq;
scorpion_clear_pmuregs();
return &scorpion_pmu;
}
@@ -741,6 +742,8 @@
scorpion_pmu.name = "ARMv7 Scorpion-MP";
scorpion_pmu.num_events = armv7_read_num_pmnc_events();
scorpion_pmu.pmu.attr_groups = msm_l1_pmu_attr_grps;
+ scorpion_pmu.request_pmu_irq = msm_request_irq;
+ scorpion_pmu.free_pmu_irq = msm_free_irq;
scorpion_clear_pmuregs();
return &scorpion_pmu;
}
diff --git a/arch/arm/kernel/perf_event_msm_krait.c b/arch/arm/kernel/perf_event_msm_krait.c
index 8d8f47a..eec614b 100644
--- a/arch/arm/kernel/perf_event_msm_krait.c
+++ b/arch/arm/kernel/perf_event_msm_krait.c
@@ -540,8 +540,8 @@
int err = 0;
int cpu;
- err = request_percpu_irq(irq, *handle_irq, "krait-l1-armpmu",
- &cpu_hw_events);
+ err = request_percpu_irq(irq, *handle_irq, "l1-armpmu",
+ &cpu_hw_events);
if (!err) {
for_each_cpu(cpu, cpu_online_mask) {
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 6f5528e..cdc435c 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -315,6 +315,7 @@
select GIC_SECURE
select ARCH_MSM_CORTEX_A5
select CPU_V7
+ select MIGHT_HAVE_CACHE_L2X0
select GPIO_MSM_V2
select MSM_GPIOMUX
select MSM_RPM
@@ -355,10 +356,17 @@
select MSM_SMP
select CPU_V7
select MSM_GPIOMUX
+ select MSM_RPM_SMD
+ select MSM_NATIVE_RESTART
+ select MSM_RESTART_V2
+ select MSM_SPM_V2
+ select MSM_PM8X60 if PM
+ select MSM_SCM if SMP
select MULTI_IRQ_HANDLER
select GPIO_MSM_V3
select MAY_HAVE_SPARSE_IRQ
select SPARSE_IRQ
+ select MSM_MULTIMEDIA_USE_ION
config ARCH_MSM8910
bool "MSM8910"
@@ -1929,13 +1937,25 @@
Support for booting and shutting down QDSP6v3 processors (hexagon).
The QDSP6 is a low power DSP used in audio software applications.
-config MSM_PIL_QDSP6V4
- tristate "QDSP6v4 (Hexagon) Boot Support"
- depends on MSM_PIL
+config MSM_PIL_LPASS_QDSP6V4
+ tristate "LPASS QDSP6v4 (Hexagon) Boot Support"
+ depends on MSM_SUBSYSTEM_RESTART
help
- Support for booting and shutting down QDSP6v4 processors (hexagon).
- The QDSP6 is a low power DSP used in audio, modem firmware, and modem
- software applications.
+ Support for booting and shutting down QDSP6v4 processors (hexagon)
+ in low power audio subsystems. If you would like to record or
+ play audio then say Y here.
+
+ If unsure, say N.
+
+config MSM_PIL_MODEM_QDSP6V4
+ tristate "Modem QDSP6v4 (Hexagon) Boot Support"
+ depends on MSM_SUBSYSTEM_RESTART
+ help
+ Support for booting and shutting down QDSP6v4 processors (hexagon)
+ in modem subsystems. If you would like to make or receive phone
+ calls then say Y here.
+
+ If unsure, say N.
config MSM_PIL_LPASS_QDSP6V5
tristate "LPASS QDSP6v5 (Hexagon) Boot Support"
@@ -2017,22 +2037,6 @@
bool "Secure Channel Manager (SCM) support"
default n
-config MSM_MODEM_8960
- bool "MSM 8960 Modem driver"
- depends on (ARCH_MSM8960 || ARCH_MSM9615)
- help
- This option enables the modem driver for the MSM8960 and MSM9615, which monitors
- modem hardware watchdog interrupt lines and plugs into the subsystem
- restart and PIL drivers. For MSM9615, it only supports a full chip reset.
-
-config MSM_LPASS_8960
- tristate "MSM 8960 Lpass driver"
- depends on (ARCH_MSM8960 || ARCH_MSM9615)
- help
- This option enables the lpass driver for the MSM8960 and MSM9615. This monitors
- lpass hardware watchdog interrupt lines and plugs into the subsystem
- restart and PIL drivers. For MSM9615, it only supports a full chip reset.
-
config MSM_MODEM_SSR_8974
bool "MSM 8974 Modem restart driver"
depends on (ARCH_MSM8974)
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 7dece76..e6c6ca6 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -72,7 +72,8 @@
obj-$(CONFIG_MSM_PIL) += peripheral-loader.o
obj-$(CONFIG_MSM_PIL) += scm-pas.o
obj-$(CONFIG_MSM_PIL_QDSP6V3) += pil-q6v3.o
-obj-$(CONFIG_MSM_PIL_QDSP6V4) += pil-q6v4.o
+obj-$(CONFIG_MSM_PIL_LPASS_QDSP6V4) += pil-q6v4.o pil-q6v4-lpass.o
+obj-$(CONFIG_MSM_PIL_MODEM_QDSP6V4) += pil-q6v4.o pil-q6v4-mss.o
obj-$(CONFIG_MSM_PIL_LPASS_QDSP6V5) += pil-q6v5.o pil-q6v5-lpass.o
obj-$(CONFIG_MSM_PIL_MSS_QDSP6V5) += pil-q6v5.o pil-q6v5-mss.o
obj-$(CONFIG_MSM_PIL_MBA) += pil-mba.o
@@ -106,6 +107,7 @@
ifndef CONFIG_ARCH_MSM8226
ifndef CONFIG_ARCH_MSM9625
ifndef CONFIG_ARCH_MPQ8092
+ifndef CONFIG_ARCH_MSM8910
obj-y += nand_partitions.o
endif
endif
@@ -114,6 +116,7 @@
endif
endif
endif
+endif
obj-$(CONFIG_MSM_SDIO_TTY) += sdio_tty.o
obj-$(CONFIG_MSM_SMD_TTY) += smd_tty.o
obj-$(CONFIG_MSM_SMD_QMI) += smd_qmi.o
@@ -197,16 +200,16 @@
obj-y += ramdump.o
endif
obj-$(CONFIG_MSM_SYSMON_COMM) += sysmon.o
-obj-$(CONFIG_MSM_MODEM_8960) += modem-8960.o
obj-$(CONFIG_MSM_MODEM_SSR_8974) += modem-ssr-8974.o
-obj-$(CONFIG_MSM_LPASS_8960) += lpass-8960.o
obj-$(CONFIG_MSM_ADSP_SSR_8974) += adsp-8974.o
+obj-$(CONFIG_MSM_WCNSS_SSR_8974) += wcnss-ssr-8974.o
ifdef CONFIG_CPU_IDLE
obj-$(CONFIG_ARCH_APQ8064) += cpuidle.o
obj-$(CONFIG_ARCH_MSM8960) += cpuidle.o
obj-$(CONFIG_ARCH_MSM8X60) += cpuidle.o
obj-$(CONFIG_ARCH_MSM9615) += cpuidle.o
+ obj-$(CONFIG_ARCH_MSM9625) += cpuidle.o
obj-$(CONFIG_ARCH_MSM8974) += cpuidle.o
endif
@@ -289,6 +292,7 @@
obj-$(CONFIG_ARCH_MSM8930) += acpuclock-8930.o acpuclock-8627.o acpuclock-8930aa.o
obj-$(CONFIG_ARCH_MPQ8092) += board-8092.o board-8092-gpiomux.o
obj-$(CONFIG_ARCH_MSM8226) += board-8226.o board-8226-gpiomux.o
+obj-$(CONFIG_ARCH_MSM8910) += board-8910.o
obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire.o board-sapphire-gpio.o
obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-keypad.o board-sapphire-panel.o
@@ -315,6 +319,7 @@
endif
ifdef CONFIG_MSM_RPM_SMD
obj-$(CONFIG_ARCH_MSM8974) += lpm_levels.o lpm_resources.o
+ obj-$(CONFIG_ARCH_MSM9625) += lpm_levels.o lpm_resources.o
endif
obj-$(CONFIG_MSM_MPM_OF) += mpm-of.o
obj-$(CONFIG_MSM_MPM) += mpm.o
@@ -346,9 +351,10 @@
obj-$(CONFIG_ARCH_MSM9625) += gpiomux-v2.o gpiomux.o
obj-$(CONFIG_ARCH_MPQ8092) += gpiomux-v2.o gpiomux.o
obj-$(CONFIG_ARCH_MSM8226) += gpiomux-v2.o gpiomux.o
+obj-$(CONFIG_ARCH_MSM8910) += gpiomux-v2.o gpiomux.o
obj-$(CONFIG_MSM_SLEEP_STATS_DEVICE) += idle_stats_device.o
-obj-$(CONFIG_MSM_DCVS) += msm_dcvs_scm.o msm_dcvs.o msm_dcvs_idle.o msm_mpdecision.o
+obj-$(CONFIG_MSM_DCVS) += msm_dcvs_scm.o msm_dcvs.o msm_mpdecision.o
obj-$(CONFIG_MSM_RUN_QUEUE_STATS) += msm_rq_stats.o
obj-$(CONFIG_MSM_SHOW_RESUME_IRQ) += msm_show_resume_irq.o
obj-$(CONFIG_BT_MSM_PINTEST) += btpintest.o
diff --git a/arch/arm/mach-msm/Makefile.boot b/arch/arm/mach-msm/Makefile.boot
index 9234b2c..cf1f401 100644
--- a/arch/arm/mach-msm/Makefile.boot
+++ b/arch/arm/mach-msm/Makefile.boot
@@ -65,3 +65,5 @@
# MPQ8092
zreladdr-$(CONFIG_ARCH_MPQ8092) := 0x00008000
+# MSM8910
+ zreladdr-$(CONFIG_ARCH_MSM8910) := 0x00008000
diff --git a/arch/arm/mach-msm/acpuclock-8064.c b/arch/arm/mach-msm/acpuclock-8064.c
index d10211bc..d1613d9 100644
--- a/arch/arm/mach-msm/acpuclock-8064.c
+++ b/arch/arm/mach-msm/acpuclock-8064.c
@@ -116,7 +116,7 @@
};
static struct l2_level l2_freq_tbl[] __initdata = {
- [0] = { { 384000, PLL_8, 0, 0x00 }, 1050000, 1050000, 1 },
+ [0] = { { 384000, PLL_8, 0, 0x00 }, 950000, 1050000, 1 },
[1] = { { 432000, HFPLL, 2, 0x20 }, 1050000, 1050000, 2 },
[2] = { { 486000, HFPLL, 2, 0x24 }, 1050000, 1050000, 2 },
[3] = { { 540000, HFPLL, 2, 0x28 }, 1050000, 1050000, 2 },
diff --git a/arch/arm/mach-msm/board-8064-camera.c b/arch/arm/mach-msm/board-8064-camera.c
index c79f82f..0a95e51 100644
--- a/arch/arm/mach-msm/board-8064-camera.c
+++ b/arch/arm/mach-msm/board-8064-camera.c
@@ -503,6 +503,34 @@
.i2c_mux_mode = MODE_L,
};
+static struct msm_camera_sensor_flash_data flash_imx135 = {
+ .flash_type = MSM_CAMERA_FLASH_NONE,
+};
+
+static struct msm_camera_csi_lane_params imx135_csi_lane_params = {
+ .csi_lane_assign = 0xE4,
+ .csi_lane_mask = 0xF,
+};
+
+static struct msm_camera_sensor_platform_info sensor_board_info_imx135 = {
+ .mount_angle = 90,
+ .cam_vreg = apq_8064_cam_vreg,
+ .num_vreg = ARRAY_SIZE(apq_8064_cam_vreg),
+ .gpio_conf = &apq8064_back_cam_gpio_conf,
+ .i2c_conf = &apq8064_back_cam_i2c_conf,
+ .csi_lane_params = &imx135_csi_lane_params,
+};
+
+static struct msm_camera_sensor_info msm_camera_sensor_imx135_data = {
+ .sensor_name = "imx135",
+ .pdata = &msm_camera_csi_device_data[0],
+ .flash_data = &flash_imx135,
+ .sensor_platform_info = &sensor_board_info_imx135,
+ .csi_if = 1,
+ .camera_type = BACK_CAMERA_2D,
+ .sensor_type = BAYER_SENSOR,
+};
+
static struct msm_camera_sensor_flash_data flash_imx074 = {
.flash_type = MSM_CAMERA_FLASH_LED,
.flash_src = &msm_flash_src
@@ -700,6 +728,10 @@
.platform_data = &msm_camera_sensor_imx074_data,
},
{
+ I2C_BOARD_INFO("imx135", 0x10),
+ .platform_data = &msm_camera_sensor_imx135_data,
+ },
+ {
I2C_BOARD_INFO("mt9m114", 0x48),
.platform_data = &msm_camera_sensor_mt9m114_data,
},
diff --git a/arch/arm/mach-msm/board-8064-gpu.c b/arch/arm/mach-msm/board-8064-gpu.c
index c7075bc..fad7092 100644
--- a/arch/arm/mach-msm/board-8064-gpu.c
+++ b/arch/arm/mach-msm/board-8064-gpu.c
@@ -31,13 +31,14 @@
};
static struct msm_dcvs_core_info grp3d_core_info = {
- .freq_tbl = &grp3d_freq[0],
- .num_cores = 1,
- .sensors = (int[]){0},
- .core_param = {
+ .freq_tbl = &grp3d_freq[0],
+ .num_cores = 1,
+ .sensors = (int[]){0},
+ .thermal_poll_ms = 60000,
+ .core_param = {
.core_type = MSM_DCVS_CORE_TYPE_GPU,
},
- .algo_param = {
+ .algo_param = {
.disable_pc_threshold = 0,
.em_win_size_min_us = 100000,
.em_win_size_max_us = 300000,
@@ -53,8 +54,7 @@
.ss_iobusy_conv = 100,
},
-
- .energy_coeffs = {
+ .energy_coeffs = {
.leakage_coeff_a = -17720,
.leakage_coeff_b = 37,
.leakage_coeff_c = 3329,
@@ -65,7 +65,7 @@
.active_coeff_c = 0
},
- .power_param = {
+ .power_param = {
.current_temp = 25,
.num_freq = ARRAY_SIZE(grp3d_freq),
}
diff --git a/arch/arm/mach-msm/board-8064-regulator.c b/arch/arm/mach-msm/board-8064-regulator.c
index ef3c81d..851f7d9 100644
--- a/arch/arm/mach-msm/board-8064-regulator.c
+++ b/arch/arm/mach-msm/board-8064-regulator.c
@@ -64,6 +64,7 @@
VREG_CONSUMERS(L8) = {
REGULATOR_SUPPLY("8921_l8", NULL),
REGULATOR_SUPPLY("cam_vana", "4-001a"),
+ REGULATOR_SUPPLY("cam_vana", "4-0010"),
REGULATOR_SUPPLY("cam_vana", "4-0048"),
REGULATOR_SUPPLY("cam_vana", "4-006c"),
REGULATOR_SUPPLY("cam_vana", "4-0034"),
@@ -84,6 +85,7 @@
};
VREG_CONSUMERS(L12) = {
REGULATOR_SUPPLY("cam_vdig", "4-001a"),
+ REGULATOR_SUPPLY("cam_vdig", "4-0010"),
REGULATOR_SUPPLY("cam_vdig", "4-0048"),
REGULATOR_SUPPLY("cam_vdig", "4-006c"),
REGULATOR_SUPPLY("cam_vdig", "4-0034"),
@@ -102,6 +104,7 @@
VREG_CONSUMERS(L16) = {
REGULATOR_SUPPLY("8921_l16", NULL),
REGULATOR_SUPPLY("cam_vaf", "4-001a"),
+ REGULATOR_SUPPLY("cam_vaf", "4-0010"),
REGULATOR_SUPPLY("cam_vaf", "4-0048"),
REGULATOR_SUPPLY("cam_vaf", "4-006c"),
REGULATOR_SUPPLY("cam_vaf", "4-0034"),
@@ -121,8 +124,6 @@
};
VREG_CONSUMERS(L23) = {
REGULATOR_SUPPLY("8921_l23", NULL),
- REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.1"),
- REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.2"),
REGULATOR_SUPPLY("HSUSB_1p8", "msm_ehci_host.0"),
REGULATOR_SUPPLY("HSUSB_1p8", "msm_ehci_host.1"),
};
@@ -141,15 +142,13 @@
};
VREG_CONSUMERS(L26) = {
REGULATOR_SUPPLY("8921_l26", NULL),
- REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.0"),
+ REGULATOR_SUPPLY("core_vdd", "pil-q6v4-lpass"),
};
VREG_CONSUMERS(L27) = {
REGULATOR_SUPPLY("8921_l27", NULL),
- REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.2"),
};
VREG_CONSUMERS(L28) = {
REGULATOR_SUPPLY("8921_l28", NULL),
- REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.1"),
};
VREG_CONSUMERS(L29) = {
REGULATOR_SUPPLY("8921_l29", NULL),
@@ -218,6 +217,7 @@
VREG_CONSUMERS(LVS5) = {
REGULATOR_SUPPLY("8921_lvs5", NULL),
REGULATOR_SUPPLY("cam_vio", "4-001a"),
+ REGULATOR_SUPPLY("cam_vio", "4-0010"),
REGULATOR_SUPPLY("cam_vio", "4-0048"),
REGULATOR_SUPPLY("cam_vio", "4-006c"),
REGULATOR_SUPPLY("cam_vio", "4-0034"),
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index cc9dcbb..b3add3b 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -605,6 +605,11 @@
#endif
}
+static void __init reserve_mpdcvs_memory(void)
+{
+ apq8064_reserve_table[MEMTYPE_EBI1].size += SZ_32K;
+}
+
static void __init apq8064_calculate_reserve_sizes(void)
{
size_pmem_devices();
@@ -613,6 +618,7 @@
reserve_mdp_memory();
reserve_rtb_memory();
reserve_cache_dump_memory();
+ reserve_mpdcvs_memory();
}
static struct reserve_info apq8064_reserve_info __initdata = {
@@ -2556,6 +2562,7 @@
&apq8064_rpm_device,
&apq8064_rpm_log_device,
&apq8064_rpm_stat_device,
+ &apq8064_rpm_master_stat_device,
&apq_device_tz_log,
&msm_bus_8064_apps_fabric,
&msm_bus_8064_sys_fabric,
@@ -2568,7 +2575,6 @@
&msm_pil_vidc,
&msm_gss,
&apq8064_rtb_device,
- &apq8064_cpu_idle_device,
&apq8064_msm_gov_device,
&apq8064_device_cache_erp,
&msm8960_device_ebi1_ch0_erp,
diff --git a/arch/arm/mach-msm/board-8226.c b/arch/arm/mach-msm/board-8226.c
index 9545c7a..767736f 100644
--- a/arch/arm/mach-msm/board-8226.c
+++ b/arch/arm/mach-msm/board-8226.c
@@ -46,6 +46,8 @@
static struct clk_lookup msm_clocks_dummy[] = {
CLK_DUMMY("core_clk", BLSP1_UART_CLK, "msm_serial_hsl.0", OFF),
CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "msm_serial_hsl.0", OFF),
+ CLK_DUMMY("iface_clk", HSUSB_IFACE_CLK, "msm_otg", OFF),
+ CLK_DUMMY("core_clk", HSUSB_CORE_CLK, "msm_otg", OFF),
};
struct clock_init_data msm_dummy_clock_init_data __initdata = {
@@ -62,6 +64,8 @@
static struct of_dev_auxdata msm8226_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("qcom,msm-lsuart-v14", 0xF991F000, \
"msm_serial_hsl.0", NULL),
+ OF_DEV_AUXDATA("qcom,hsusb-otg", 0xF9A55000, \
+ "msm_otg", NULL),
{}
};
diff --git a/arch/arm/mach-msm/board-8910.c b/arch/arm/mach-msm/board-8910.c
new file mode 100644
index 0000000..18463e3
--- /dev/null
+++ b/arch/arm/mach-msm/board-8910.c
@@ -0,0 +1,94 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/of_fdt.h>
+#include <linux/of_irq.h>
+#include <linux/memory.h>
+#include <asm/mach/map.h>
+#include <asm/arch_timer.h>
+#include <asm/hardware/gic.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <mach/board.h>
+#include <mach/gpiomux.h>
+#include <mach/msm_iomap.h>
+#ifdef CONFIG_ION_MSM
+#include <mach/ion.h>
+#endif
+#include <mach/socinfo.h>
+#include <mach/board.h>
+#include <mach/clk-provider.h>
+#include "clock.h"
+
+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),
+};
+
+struct clock_init_data msm_dummy_clock_init_data __initdata = {
+ .table = msm_clocks_dummy,
+ .size = ARRAY_SIZE(msm_clocks_dummy),
+};
+
+static struct of_device_id irq_match[] __initdata = {
+ { .compatible = "qcom,msm-qgic2", .data = gic_of_init, },
+ { .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, },
+ {},
+};
+
+static void __init msm8910_dt_timer_init(void)
+{
+ arch_timer_of_register();
+}
+
+static struct sys_timer msm8910_dt_timer = {
+ .init = msm8910_dt_timer_init
+};
+
+void __init msm8910_init_irq(void)
+{
+ of_irq_init(irq_match);
+}
+
+void __init msm8910_init(void)
+{
+ 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);
+}
+
+static const char *msm8910_dt_match[] __initconst = {
+ "qcom,msm8910",
+ NULL
+};
+
+DT_MACHINE_START(MSM8910_DT, "Qualcomm MSM 8910 (Flattened Device Tree)")
+ .map_io = msm_map_msm8910_io,
+ .init_irq = msm8910_init_irq,
+ .init_machine = msm8910_init,
+ .handle_irq = gic_handle_irq,
+ .timer = &msm8910_dt_timer,
+ .dt_compat = msm8910_dt_match,
+MACHINE_END
diff --git a/arch/arm/mach-msm/board-8930-regulator-pm8038.c b/arch/arm/mach-msm/board-8930-regulator-pm8038.c
index 208f15b..16a82b4 100644
--- a/arch/arm/mach-msm/board-8930-regulator-pm8038.c
+++ b/arch/arm/mach-msm/board-8930-regulator-pm8038.c
@@ -117,7 +117,7 @@
};
VREG_CONSUMERS(L16) = {
REGULATOR_SUPPLY("8038_l16", NULL),
- REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.2"),
+ REGULATOR_SUPPLY("sw_core_vdd", "pil-q6v4-modem"),
};
VREG_CONSUMERS(L17) = {
REGULATOR_SUPPLY("8038_l17", NULL),
@@ -127,7 +127,7 @@
};
VREG_CONSUMERS(L19) = {
REGULATOR_SUPPLY("8038_l19", NULL),
- REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.1"),
+ REGULATOR_SUPPLY("fw_core_vdd", "pil-q6v4-modem"),
};
VREG_CONSUMERS(L20) = {
REGULATOR_SUPPLY("8038_l20", NULL),
@@ -151,8 +151,7 @@
REGULATOR_SUPPLY("hdmi_avdd", "hdmi_msm.0"),
REGULATOR_SUPPLY("hdmi_vcc", "hdmi_msm.0"),
REGULATOR_SUPPLY("pll_vdd", "pil_riva"),
- REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.1"),
- REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.2"),
+ REGULATOR_SUPPLY("pll_vdd", "pil-q6v4-modem"),
};
VREG_CONSUMERS(L24) = {
REGULATOR_SUPPLY("8038_l24", NULL),
@@ -166,7 +165,7 @@
};
VREG_CONSUMERS(L27) = {
REGULATOR_SUPPLY("8038_l27", NULL),
- REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.0"),
+ REGULATOR_SUPPLY("core_vdd", "pil-q6v4-lpass"),
};
VREG_CONSUMERS(S1) = {
REGULATOR_SUPPLY("8038_s1", NULL),
diff --git a/arch/arm/mach-msm/board-8930-regulator-pm8917.c b/arch/arm/mach-msm/board-8930-regulator-pm8917.c
index db40e5d..8898b50 100644
--- a/arch/arm/mach-msm/board-8930-regulator-pm8917.c
+++ b/arch/arm/mach-msm/board-8930-regulator-pm8917.c
@@ -547,11 +547,11 @@
RPM_SMPS(S2, 0, 1, 0, 1300000, 1300000, NULL, 0, 1p60, NONE, NONE),
RPM_SMPS(S3, 0, 1, 1, 500000, 1150000, NULL, 100000, 4p80, AUTO, LPM),
RPM_SMPS(S4, 1, 1, 0, 1800000, 1800000, NULL, 100000, 1p60, AUTO, LPM),
- RPM_SMPS(S7, 0, 1, 0, 1150000, 1150000, NULL, 100000, 3p20, NONE, NONE),
+ RPM_SMPS(S7, 0, 1, 0, 1150000, 1150000, NULL, 100000, 3p20, AUTO, AUTO),
RPM_SMPS(S8, 1, 1, 1, 2050000, 2050000, NULL, 100000, 1p60, NONE, NONE),
/* ID a_on pd ss min_uV max_uV supply sys_uA init_ip */
- RPM_LDO(L1, 1, 1, 0, 1050000, 1050000, "8917_s4", 0, 10000),
+ RPM_LDO(L1, 0, 1, 0, 1050000, 1050000, "8917_s4", 0, 10000),
RPM_LDO(L2, 0, 1, 0, 1200000, 1200000, "8917_s4", 0, 0),
RPM_LDO(L3, 0, 1, 0, 3075000, 3075000, NULL, 0, 0),
RPM_LDO(L4, 1, 1, 0, 1800000, 1800000, NULL, 10000, 10000),
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index a6a90a7..f0f59ec 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -2347,8 +2347,7 @@
static struct platform_device *common_devices[] __initdata = {
&msm_8960_q6_lpass,
- &msm_8960_q6_mss_fw,
- &msm_8960_q6_mss_sw,
+ &msm_8960_q6_mss,
&msm_8960_riva,
&msm_pil_tzapps,
&msm_pil_vidc,
@@ -2398,6 +2397,7 @@
&msm8930_rpm_log_device,
&msm8930_rpm_rbcpr_device,
&msm8930_rpm_stat_device,
+ &msm8930_rpm_master_stat_device,
#ifdef CONFIG_ION_MSM
&msm8930_ion_dev,
#endif
@@ -2413,7 +2413,6 @@
&gpio_keys_8930,
#endif
&msm8930_rtb_device,
- &msm8930_cpu_idle_device,
&msm_bus_8930_apps_fabric,
&msm_bus_8930_sys_fabric,
&msm_bus_8930_mm_fabric,
diff --git a/arch/arm/mach-msm/board-8960-camera.c b/arch/arm/mach-msm/board-8960-camera.c
index 9bb6e09..88fd527 100644
--- a/arch/arm/mach-msm/board-8960-camera.c
+++ b/arch/arm/mach-msm/board-8960-camera.c
@@ -752,6 +752,33 @@
.eeprom_info = &imx091_eeprom_info,
};
+static struct msm_camera_sensor_flash_data flash_imx135 = {
+ .flash_type = MSM_CAMERA_FLASH_NONE,
+};
+
+static struct msm_camera_csi_lane_params imx135_csi_lane_params = {
+ .csi_lane_assign = 0xE4,
+ .csi_lane_mask = 0xF,
+};
+
+static struct msm_camera_sensor_platform_info sensor_board_info_imx135 = {
+ .mount_angle = 90,
+ .cam_vreg = msm_8960_cam_vreg,
+ .num_vreg = ARRAY_SIZE(msm_8960_cam_vreg),
+ .gpio_conf = &msm_8960_back_cam_gpio_conf,
+ .csi_lane_params = &imx135_csi_lane_params,
+};
+
+static struct msm_camera_sensor_info msm_camera_sensor_imx135_data = {
+ .sensor_name = "imx135",
+ .pdata = &msm_camera_csi_device_data[0],
+ .flash_data = &flash_imx135,
+ .sensor_platform_info = &sensor_board_info_imx135,
+ .csi_if = 1,
+ .camera_type = BACK_CAMERA_2D,
+ .sensor_type = BAYER_SENSOR,
+};
+
static struct pm8xxx_mpp_config_data privacy_light_on_config = {
.type = PM8XXX_MPP_TYPE_SINK,
.level = PM8XXX_MPP_CS_OUT_5MA,
@@ -838,6 +865,10 @@
.platform_data = &msm_camera_sensor_ov2720_data,
},
{
+ I2C_BOARD_INFO("imx135", 0x10),
+ .platform_data = &msm_camera_sensor_imx135_data,
+ },
+ {
I2C_BOARD_INFO("mt9m114", 0x48),
.platform_data = &msm_camera_sensor_mt9m114_data,
},
diff --git a/arch/arm/mach-msm/board-8960-regulator.c b/arch/arm/mach-msm/board-8960-regulator.c
index 2fa98b6..f9e2c8e 100644
--- a/arch/arm/mach-msm/board-8960-regulator.c
+++ b/arch/arm/mach-msm/board-8960-regulator.c
@@ -78,6 +78,7 @@
REGULATOR_SUPPLY("cam_vana", "4-0048"),
REGULATOR_SUPPLY("cam_vana", "4-0020"),
REGULATOR_SUPPLY("cam_vana", "4-0034"),
+ REGULATOR_SUPPLY("cam_vana", "4-0010"),
};
VREG_CONSUMERS(L12) = {
REGULATOR_SUPPLY("8921_l12", NULL),
@@ -86,6 +87,7 @@
REGULATOR_SUPPLY("cam_vdig", "4-0048"),
REGULATOR_SUPPLY("cam_vdig", "4-0020"),
REGULATOR_SUPPLY("cam_vdig", "4-0034"),
+ REGULATOR_SUPPLY("cam_vdig", "4-0010"),
};
VREG_CONSUMERS(L14) = {
REGULATOR_SUPPLY("8921_l14", NULL),
@@ -101,6 +103,7 @@
REGULATOR_SUPPLY("cam_vaf", "4-0048"),
REGULATOR_SUPPLY("cam_vaf", "4-0020"),
REGULATOR_SUPPLY("cam_vaf", "4-0034"),
+ REGULATOR_SUPPLY("cam_vaf", "4-0010"),
};
VREG_CONSUMERS(L17) = {
REGULATOR_SUPPLY("8921_l17", NULL),
@@ -120,8 +123,7 @@
REGULATOR_SUPPLY("dsi_pll_vddio", "mdp.0"),
REGULATOR_SUPPLY("hdmi_avdd", "hdmi_msm.0"),
REGULATOR_SUPPLY("pll_vdd", "pil_riva"),
- REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.1"),
- REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.2"),
+ REGULATOR_SUPPLY("pll_vdd", "pil-q6v4-modem"),
};
VREG_CONSUMERS(L24) = {
REGULATOR_SUPPLY("8921_l24", NULL),
@@ -136,15 +138,15 @@
};
VREG_CONSUMERS(L26) = {
REGULATOR_SUPPLY("8921_l26", NULL),
- REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.0"),
+ REGULATOR_SUPPLY("core_vdd", "pil-q6v4-lpass"),
};
VREG_CONSUMERS(L27) = {
REGULATOR_SUPPLY("8921_l27", NULL),
- REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.2"),
+ REGULATOR_SUPPLY("sw_core_vdd", "pil-q6v4-modem"),
};
VREG_CONSUMERS(L28) = {
REGULATOR_SUPPLY("8921_l28", NULL),
- REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.1"),
+ REGULATOR_SUPPLY("fw_core_vdd", "pil-q6v4-modem"),
};
VREG_CONSUMERS(L29) = {
REGULATOR_SUPPLY("8921_l29", NULL),
@@ -221,6 +223,7 @@
REGULATOR_SUPPLY("cam_vio", "4-0048"),
REGULATOR_SUPPLY("cam_vio", "4-0020"),
REGULATOR_SUPPLY("cam_vio", "4-0034"),
+ REGULATOR_SUPPLY("cam_vio", "4-0010"),
};
/* This mapping is used for CDP only. */
VREG_CONSUMERS(CDP_LVS6) = {
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 7115e40..7e96edf 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -2753,6 +2753,7 @@
&msm8960_rpm_device,
&msm8960_rpm_log_device,
&msm8960_rpm_stat_device,
+ &msm8960_rpm_master_stat_device,
&msm_device_tz_log,
&coresight_tpiu_device,
&coresight_etb_device,
@@ -2762,7 +2763,6 @@
&msm_device_dspcrashd_8960,
&msm8960_device_watchdog,
&msm8960_rtb_device,
- &msm8960_cpu_idle_device,
&msm8960_device_cache_erp,
&msm8960_device_ebi1_ch0_erp,
&msm8960_device_ebi1_ch1_erp,
@@ -3281,10 +3281,8 @@
msm8960_pm8921_gpio_mpp_init();
/* Don't add modem devices on APQ targets */
- if (socinfo_get_id() != 124) {
- platform_device_register(&msm_8960_q6_mss_fw);
- platform_device_register(&msm_8960_q6_mss_sw);
- }
+ if (socinfo_get_id() != 124)
+ platform_device_register(&msm_8960_q6_mss);
platform_add_devices(cdp_devices, ARRAY_SIZE(cdp_devices));
msm8960_init_smsc_hub();
msm8960_init_hsic();
diff --git a/arch/arm/mach-msm/board-8974-gpiomux.c b/arch/arm/mach-msm/board-8974-gpiomux.c
index 8568340..50b59e1 100644
--- a/arch/arm/mach-msm/board-8974-gpiomux.c
+++ b/arch/arm/mach-msm/board-8974-gpiomux.c
@@ -128,6 +128,12 @@
.dir = GPIOMUX_OUT_LOW,
};
+static struct gpiomux_setting taiko_int = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
static struct msm_gpiomux_config msm_touch_configs[] __initdata = {
{
.gpio = 60, /* TOUCH RESET */
@@ -344,6 +350,21 @@
},
};
+static struct gpiomux_setting sd_card_det_config = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_IN,
+};
+
+static struct msm_gpiomux_config sd_card_det __initdata = {
+ .gpio = 62,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sd_card_det_config,
+ [GPIOMUX_SUSPENDED] = &sd_card_det_config,
+ },
+};
+
static struct msm_gpiomux_config msm_sensor_configs[] __initdata = {
{
.gpio = 15, /* CAM_MCLK0 */
@@ -516,7 +537,13 @@
.settings = {
[GPIOMUX_SUSPENDED] = &taiko_reset,
},
- }
+ },
+ {
+ .gpio = 72, /* CDC_INT */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &taiko_int,
+ },
+ },
};
void __init msm_8974_init_gpiomux(void)
@@ -543,6 +570,8 @@
msm_gpiomux_install(msm_sensor_configs, ARRAY_SIZE(msm_sensor_configs));
+ msm_gpiomux_install(&sd_card_det, 1);
+
msm_gpiomux_install(msm_taiko_config, ARRAY_SIZE(msm_taiko_config));
msm_gpiomux_install(msm_hdmi_configs, ARRAY_SIZE(msm_hdmi_configs));
diff --git a/arch/arm/mach-msm/board-8974.c b/arch/arm/mach-msm/board-8974.c
index dcc0d01..7fe45b8 100644
--- a/arch/arm/mach-msm/board-8974.c
+++ b/arch/arm/mach-msm/board-8974.c
@@ -27,6 +27,7 @@
#include <linux/regulator/machine.h>
#include <linux/regulator/krait-regulator.h>
#include <linux/msm_thermal.h>
+#include <linux/mfd/wcd9xxx/core.h>
#include <asm/mach/map.h>
#include <asm/hardware/gic.h>
#include <mach/board.h>
@@ -488,6 +489,7 @@
{ .compatible = "qcom,msm-qgic2", .data = gic_of_init, },
{ .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, },
{ .compatible = "qcom,spmi-pmic-arb", .data = qpnpint_of_init, },
+ { .compatible = "qcom,wcd9xxx-irq", .data = wcd9xxx_irq_of_init, },
{}
};
static struct of_device_id mpm_match[] __initdata = {
diff --git a/arch/arm/mach-msm/board-9615.c b/arch/arm/mach-msm/board-9615.c
index ca95b62..b9da615 100644
--- a/arch/arm/mach-msm/board-9615.c
+++ b/arch/arm/mach-msm/board-9615.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -870,6 +870,8 @@
#ifdef CONFIG_LTC4088_CHARGER
&msm_device_charger,
#endif
+ &msm_9615_q6_lpass,
+ &msm_9615_q6_mss,
&msm_device_otg,
&msm_device_hsic_peripheral,
&msm_device_gadget_peripheral,
@@ -908,6 +910,8 @@
&msm_voip,
&msm_i2s_cpudai0,
&msm_i2s_cpudai1,
+ &msm_i2s_cpudai4,
+ &msm_i2s_cpudai5,
&msm_pcm_hostless,
&msm_cpudai_afe_01_rx,
&msm_cpudai_afe_01_tx,
@@ -933,6 +937,7 @@
&msm_bus_def_fab,
&msm9615_rpm_log_device,
&msm9615_rpm_stat_device,
+ &msm9615_rpm_master_stat_device,
&msm_tsens_device,
};
diff --git a/arch/arm/mach-msm/board-9625.c b/arch/arm/mach-msm/board-9625.c
index 37e93b6..90428ce 100644
--- a/arch/arm/mach-msm/board-9625.c
+++ b/arch/arm/mach-msm/board-9625.c
@@ -28,11 +28,19 @@
#include <asm/mach/time.h>
#include <mach/socinfo.h>
#include <mach/board.h>
+#include <mach/restart.h>
#include <mach/gpio.h>
#include <mach/clk-provider.h>
#include <mach/qpnp-int.h>
#include <mach/msm_memtypes.h>
+#include <mach/msm_iomap.h>
+#include <mach/msm_smd.h>
+#include <mach/rpm-smd.h>
+#include <mach/rpm-regulator-smd.h>
#include "clock.h"
+#include "modem_notifier.h"
+#include "lpm_resources.h"
+#include "spm.h"
#define MSM_KERNEL_EBI_SIZE 0x51000
@@ -63,7 +71,6 @@
.paddr_to_memtype = msm9625_paddr_to_memtype,
};
-
#define L2CC_AUX_CTRL ((0x1 << L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT) | \
(0x2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) | \
(0x1 << L2X0_AUX_CTRL_EVNT_MON_BUS_EN_SHIFT))
@@ -134,6 +141,155 @@
msm_reserve();
}
+static struct resource smd_resource[] = {
+ {
+ .name = "modem_smd_in",
+ .start = 32 + 25, /* mss_sw_to_kpss_ipc_irq0 */
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "modem_smsm_in",
+ .start = 32 + 26, /* mss_sw_to_kpss_ipc_irq1 */
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "adsp_smd_in",
+ .start = 32 + 156, /* lpass_to_kpss_ipc_irq0 */
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "adsp_smsm_in",
+ .start = 32 + 157, /* lpass_to_kpss_ipc_irq1 */
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "rpm_smd_in",
+ .start = 32 + 168, /* rpm_to_kpss_ipc_irq4 */
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct smd_subsystem_config smd_config_list[] = {
+ {
+ .irq_config_id = SMD_MODEM,
+ .subsys_name = "modem",
+ .edge = SMD_APPS_MODEM,
+
+ .smd_int.irq_name = "modem_smd_in",
+ .smd_int.flags = IRQF_TRIGGER_RISING,
+ .smd_int.irq_id = -1,
+ .smd_int.device_name = "smd_dev",
+ .smd_int.dev_id = 0,
+ .smd_int.out_bit_pos = 1 << 12,
+ .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
+ .smd_int.out_offset = 0x8,
+
+ .smsm_int.irq_name = "modem_smsm_in",
+ .smsm_int.flags = IRQF_TRIGGER_RISING,
+ .smsm_int.irq_id = -1,
+ .smsm_int.device_name = "smsm_dev",
+ .smsm_int.dev_id = 0,
+ .smsm_int.out_bit_pos = 1 << 13,
+ .smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
+ .smsm_int.out_offset = 0x8,
+ },
+ {
+ .irq_config_id = SMD_Q6,
+ .subsys_name = "adsp",
+ .edge = SMD_APPS_QDSP,
+
+ .smd_int.irq_name = "adsp_smd_in",
+ .smd_int.flags = IRQF_TRIGGER_RISING,
+ .smd_int.irq_id = -1,
+ .smd_int.device_name = "smd_dev",
+ .smd_int.dev_id = 0,
+ .smd_int.out_bit_pos = 1 << 8,
+ .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
+ .smd_int.out_offset = 0x8,
+
+ .smsm_int.irq_name = "adsp_smsm_in",
+ .smsm_int.flags = IRQF_TRIGGER_RISING,
+ .smsm_int.irq_id = -1,
+ .smsm_int.device_name = "smsm_dev",
+ .smsm_int.dev_id = 0,
+ .smsm_int.out_bit_pos = 1 << 9,
+ .smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
+ .smsm_int.out_offset = 0x8,
+ },
+ {
+ .irq_config_id = SMD_RPM,
+ .subsys_name = NULL, /* do not use PIL to load RPM */
+ .edge = SMD_APPS_RPM,
+
+ .smd_int.irq_name = "rpm_smd_in",
+ .smd_int.flags = IRQF_TRIGGER_RISING,
+ .smd_int.irq_id = -1,
+ .smd_int.device_name = "smd_dev",
+ .smd_int.dev_id = 0,
+ .smd_int.out_bit_pos = 1 << 0,
+ .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
+ .smd_int.out_offset = 0x8,
+
+ .smsm_int.irq_name = NULL, /* RPM does not support SMSM */
+ .smsm_int.flags = 0,
+ .smsm_int.irq_id = 0,
+ .smsm_int.device_name = NULL,
+ .smsm_int.dev_id = 0,
+ .smsm_int.out_bit_pos = 0,
+ .smsm_int.out_base = NULL,
+ .smsm_int.out_offset = 0,
+ },
+};
+
+static struct smd_smem_regions aux_smem_areas[] = {
+ {
+ .phys_addr = (void *)(0xfc428000),
+ .size = 0x4000,
+ },
+};
+
+static struct smd_subsystem_restart_config smd_ssr_cfg = {
+ .disable_smsm_reset_handshake = 1,
+};
+
+static struct smd_platform smd_platform_data = {
+ .num_ss_configs = ARRAY_SIZE(smd_config_list),
+ .smd_ss_configs = smd_config_list,
+ .smd_ssr_config = &smd_ssr_cfg,
+ .num_smem_areas = ARRAY_SIZE(aux_smem_areas),
+ .smd_smem_areas = aux_smem_areas,
+};
+
+struct platform_device msm_device_smd_9625 = {
+ .name = "msm_smd",
+ .id = -1,
+ .resource = smd_resource,
+ .num_resources = ARRAY_SIZE(smd_resource),
+ .dev = {
+ .platform_data = &smd_platform_data,
+ }
+};
+
+void __init msm9625_add_devices(void)
+{
+ platform_device_register(&msm_device_smd_9625);
+}
+
+/*
+ * Used to satisfy dependencies for devices that need to be
+ * run early or in a particular order. Most likely your device doesn't fall
+ * into this category, and thus the driver should not be added here.
+ * EPROBE_DEFER can satisfy most dependency problems.
+ */
+void __init msm9625_add_drivers(void)
+{
+ msm_init_modem_notifier_list();
+ msm_smd_init();
+ msm_rpm_driver_init();
+ msm_lpmrs_module_init();
+ rpm_regulator_smd_driver_init();
+ msm_spm_device_init();
+}
void __init msm9625_init(void)
{
@@ -144,6 +300,8 @@
msm_clock_init(&msm_dummy_clock_init_data);
of_platform_populate(NULL, of_default_bus_match_table,
msm9625_auxdata_lookup, NULL);
+ msm9625_add_devices();
+ msm9625_add_drivers();
}
DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)")
@@ -154,4 +312,5 @@
.timer = &msm_dt_timer,
.dt_compat = msm9625_dt_match,
.reserve = msm9625_reserve,
+ .restart = msm_restart,
MACHINE_END
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index 2cd2cd4..285b037 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -5216,9 +5216,7 @@
CLK_LOOKUP("pwm_clk", cxo_clk.c, "0-0048"),
CLK_LOOKUP("cxo", cxo_clk.c, "wcnss_wlan.0"),
CLK_LOOKUP("cxo", cxo_clk.c, "pil_riva"),
- CLK_LOOKUP("xo", pxo_clk.c, "pil_qdsp6v4.0"),
- CLK_LOOKUP("xo", cxo_clk.c, "pil_qdsp6v4.1"),
- CLK_LOOKUP("xo", cxo_clk.c, "pil_qdsp6v4.2"),
+ CLK_LOOKUP("xo", pxo_clk.c, "pil-q6v4-lpass"),
CLK_LOOKUP("xo", cxo_clk.c, "pil_gss"),
CLK_LOOKUP("xo", cxo_clk.c, "BAM_RMNT"),
CLK_LOOKUP("xo", cxo_clk.c, "msm_xo"),
@@ -5353,6 +5351,7 @@
CLK_LOOKUP("core_clk", pmic_ssbi2_clk.c, ""),
CLK_LOOKUP("mem_clk", rpm_msg_ram_p_clk.c, ""),
CLK_LOOKUP("cam_clk", cam0_clk.c, "4-001a"),
+ CLK_LOOKUP("cam_clk", cam0_clk.c, "4-0010"),
CLK_LOOKUP("cam_clk", cam0_clk.c, "4-0034"),
CLK_LOOKUP("cam_clk", cam0_clk.c, "4-0020"),
CLK_LOOKUP("cam_clk", cam1_clk.c, "4-0048"),
@@ -5562,9 +5561,8 @@
CLK_LOOKUP("xo", pxo_a_clk.c, ""),
CLK_LOOKUP("cxo", cxo_clk.c, "wcnss_wlan.0"),
CLK_LOOKUP("cxo", cxo_clk.c, "pil_riva"),
- CLK_LOOKUP("xo", pxo_clk.c, "pil_qdsp6v4.0"),
- CLK_LOOKUP("xo", cxo_clk.c, "pil_qdsp6v4.1"),
- CLK_LOOKUP("xo", cxo_clk.c, "pil_qdsp6v4.2"),
+ CLK_LOOKUP("xo", pxo_clk.c, "pil-q6v4-lpass"),
+ CLK_LOOKUP("xo", cxo_clk.c, "pil-q6v4-modem"),
CLK_LOOKUP("xo", cxo_clk.c, "BAM_RMNT"),
CLK_LOOKUP("xo", cxo_clk.c, "msm_xo"),
CLK_LOOKUP("vref_buff", cxo_clk.c, "rpm-regulator"),
@@ -5714,6 +5712,7 @@
CLK_LOOKUP("cam_clk", cam2_clk.c, NULL),
CLK_LOOKUP("cam_clk", cam0_clk.c, "4-0020"),
CLK_LOOKUP("cam_clk", cam0_clk.c, "4-0034"),
+ CLK_LOOKUP("cam_clk", cam0_clk.c, "4-0010"),
CLK_LOOKUP("csi_src_clk", csi0_src_clk.c, "msm_csid.0"),
CLK_LOOKUP("csi_src_clk", csi1_src_clk.c, "msm_csid.1"),
CLK_LOOKUP("csi_src_clk", csi2_src_clk.c, "msm_csid.2"),
@@ -5915,9 +5914,8 @@
CLK_LOOKUP("xo", cxo_clk.c, "msm_xo"),
CLK_LOOKUP("cxo", cxo_clk.c, "wcnss_wlan.0"),
CLK_LOOKUP("cxo", cxo_clk.c, "pil_riva"),
- CLK_LOOKUP("xo", pxo_clk.c, "pil_qdsp6v4.0"),
- CLK_LOOKUP("xo", cxo_clk.c, "pil_qdsp6v4.1"),
- CLK_LOOKUP("xo", cxo_clk.c, "pil_qdsp6v4.2"),
+ CLK_LOOKUP("xo", pxo_clk.c, "pil-q6v4-lpass"),
+ CLK_LOOKUP("xo", cxo_clk.c, "pil-q6v4-modem"),
CLK_LOOKUP("xo", cxo_clk.c, "BAM_RMNT"),
CLK_LOOKUP("vref_buff", cxo_clk.c, "rpm-regulator"),
CLK_LOOKUP("pll2", pll2_clk.c, NULL),
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index 10de231..b21e60e 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -632,7 +632,6 @@
#define CXO_ID 0x0
#define QDSS_ID 0x1
-#define RPM_SCALING_ENABLE_ID 0x2
#define PNOC_ID 0x0
#define SNOC_ID 0x1
@@ -789,7 +788,6 @@
static DEFINE_CLK_VOTER(pnoc_sdcc4_clk, &pnoc_clk.c, 0);
static DEFINE_CLK_VOTER(pnoc_sps_clk, &pnoc_clk.c, 0);
-static DEFINE_CLK_VOTER(pnoc_qseecom_clk, &pnoc_clk.c, 0);
static struct clk_freq_tbl ftbl_gcc_usb30_master_clk[] = {
F(125000000, gpll0, 1, 5, 24),
@@ -2933,7 +2931,7 @@
};
static struct clk_freq_tbl ftbl_mdss_edplink_clk[] = {
- F_MDSS(135000000, edppll_270, 2, 0, 0),
+ F_MDSS(162000000, edppll_270, 2, 0, 0),
F_MDSS(270000000, edppll_270, 11, 0, 0),
F_END
};
@@ -2953,7 +2951,7 @@
};
static struct clk_freq_tbl ftbl_mdss_edppixel_clk[] = {
- F_MDSS(175000000, edppll_350, 2, 0, 0),
+ F_MDSS(148500000, edppll_350, 2, 0, 0),
F_MDSS(350000000, edppll_350, 11, 0, 0),
F_END
};
@@ -5079,6 +5077,11 @@
CLK_LOOKUP("bus_clk", gcc_ce2_axi_clk.c, "qcrypto.0"),
CLK_LOOKUP("core_clk_src", ce2_clk_src.c, "qcrypto.0"),
+ CLK_LOOKUP("core_clk", gcc_ce1_clk.c, "qseecom"),
+ CLK_LOOKUP("iface_clk", gcc_ce1_ahb_clk.c, "qseecom"),
+ CLK_LOOKUP("bus_clk", gcc_ce1_axi_clk.c, "qseecom"),
+ CLK_LOOKUP("core_clk_src", ce1_clk_src.c, "qseecom"),
+
CLK_LOOKUP("core_clk", gcc_gp1_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_gp2_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_gp3_clk.c, ""),
@@ -5122,8 +5125,9 @@
/* Multimedia clocks */
CLK_LOOKUP("bus_clk_src", axi_clk_src.c, ""),
CLK_LOOKUP("bus_clk", mmss_mmssnoc_axi_clk.c, ""),
- CLK_LOOKUP("core_clk", mdss_edpaux_clk.c, ""),
- CLK_LOOKUP("core_clk", mdss_edppixel_clk.c, ""),
+ CLK_LOOKUP("core_clk", mdss_edpaux_clk.c, "fd923400.qcom,mdss_edp"),
+ CLK_LOOKUP("pixel_clk", mdss_edppixel_clk.c, "fd923400.qcom,mdss_edp"),
+ CLK_LOOKUP("link_clk", mdss_edplink_clk.c, "fd923400.qcom,mdss_edp"),
CLK_LOOKUP("byte_clk", mdss_byte0_clk.c, "fd922800.qcom,mdss_dsi"),
CLK_LOOKUP("byte_clk", mdss_byte1_clk.c, ""),
CLK_LOOKUP("core_clk", mdss_esc0_clk.c, "fd922800.qcom,mdss_dsi"),
@@ -5283,6 +5287,7 @@
CLK_LOOKUP("core_clk", camss_vfe_cpp_clk.c, "fda44000.qcom,iommu"),
CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "mdp.0"),
CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "mdss_dsi_clk_ctrl"),
+ CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "fd923400.qcom,mdss_edp"),
CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "fd928000.qcom,iommu"),
CLK_LOOKUP("core_clk", mdss_axi_clk.c, "fd928000.qcom,iommu"),
CLK_LOOKUP("bus_clk", mdss_axi_clk.c, "mdp.0"),
@@ -5361,7 +5366,6 @@
CLK_LOOKUP("core_clk", gcc_prng_ahb_clk.c, "msm_rng"),
CLK_LOOKUP("dfab_clk", pnoc_sps_clk.c, "msm_sps"),
- CLK_LOOKUP("bus_clk", pnoc_qseecom_clk.c, "qseecom"),
CLK_LOOKUP("bus_clk", snoc_clk.c, ""),
CLK_LOOKUP("bus_clk", pnoc_clk.c, ""),
@@ -5746,24 +5750,6 @@
#define APCS_GCC_CC_PHYS 0xF9011000
#define APCS_GCC_CC_SIZE SZ_4K
-static void __init enable_rpm_scaling(void)
-{
- int rc, value = 0x1;
- struct msm_rpm_kvp kvp = {
- .key = RPM_SMD_KEY_ENABLE,
- .data = (void *)&value,
- .length = sizeof(value),
- };
-
- rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_SLEEP_SET,
- RPM_MISC_CLK_TYPE, RPM_SCALING_ENABLE_ID, &kvp, 1);
- WARN(rc < 0, "RPM clock scaling (sleep set) did not enable!\n");
-
- rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_ACTIVE_SET,
- RPM_MISC_CLK_TYPE, RPM_SCALING_ENABLE_ID, &kvp, 1);
- WARN(rc < 0, "RPM clock scaling (active set) did not enable!\n");
-}
-
static void __init msm8974_clock_pre_init(void)
{
virt_bases[GCC_BASE] = ioremap(GCC_CC_PHYS, GCC_CC_SIZE);
diff --git a/arch/arm/mach-msm/clock-rpm.c b/arch/arm/mach-msm/clock-rpm.c
index e06eb4b..daf83e2 100644
--- a/arch/arm/mach-msm/clock-rpm.c
+++ b/arch/arm/mach-msm/clock-rpm.c
@@ -297,6 +297,27 @@
return HANDOFF_ENABLED_CLK;
}
+#define RPM_MISC_CLK_TYPE 0x306b6c63
+#define RPM_SCALING_ENABLE_ID 0x2
+
+void enable_rpm_scaling(void)
+{
+ int rc, value = 0x1;
+ struct msm_rpm_kvp kvp = {
+ .key = RPM_SMD_KEY_ENABLE,
+ .data = (void *)&value,
+ .length = sizeof(value),
+ };
+
+ rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_SLEEP_SET,
+ RPM_MISC_CLK_TYPE, RPM_SCALING_ENABLE_ID, &kvp, 1);
+ WARN(rc < 0, "RPM clock scaling (sleep set) did not enable!\n");
+
+ rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_ACTIVE_SET,
+ RPM_MISC_CLK_TYPE, RPM_SCALING_ENABLE_ID, &kvp, 1);
+ WARN(rc < 0, "RPM clock scaling (active set) did not enable!\n");
+}
+
struct clk_ops clk_ops_rpm = {
.enable = rpm_clk_enable,
.disable = rpm_clk_disable,
diff --git a/arch/arm/mach-msm/clock-rpm.h b/arch/arm/mach-msm/clock-rpm.h
index 2f0b729..252e8cb 100644
--- a/arch/arm/mach-msm/clock-rpm.h
+++ b/arch/arm/mach-msm/clock-rpm.h
@@ -54,6 +54,12 @@
return container_of(clk, struct rpm_clk, c);
}
+/*
+ * RPM scaling enable function used for target that has an RPM resource for
+ * rpm clock scaling enable.
+ */
+void enable_rpm_scaling(void);
+
extern struct clk_rpmrs_data clk_rpmrs_data;
extern struct clk_rpmrs_data clk_rpmrs_data_smd;
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index 3cd4b2f..a49a145 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -101,6 +101,7 @@
#define PCIE20_SIZE SZ_4K
#define MSM8064_PC_CNTR_PHYS (APQ8064_IMEM_PHYS + 0x664)
#define MSM8064_PC_CNTR_SIZE 0x40
+#define MSM8064_RPM_MASTER_STATS_BASE 0x10BB00
static struct resource msm8064_resources_pccntr[] = {
{
@@ -2351,6 +2352,37 @@
},
};
+static struct resource resources_rpm_master_stats[] = {
+ {
+ .start = MSM8064_RPM_MASTER_STATS_BASE,
+ .end = MSM8064_RPM_MASTER_STATS_BASE + SZ_256,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static char *master_names[] = {
+ "KPSS",
+ "MPSS",
+ "LPASS",
+ "RIVA",
+ "DSPS",
+};
+
+static struct msm_rpm_master_stats_platform_data msm_rpm_master_stat_pdata = {
+ .masters = master_names,
+ .nomasters = ARRAY_SIZE(master_names),
+};
+
+struct platform_device apq8064_rpm_master_stat_device = {
+ .name = "msm_rpm_master_stat",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(resources_rpm_master_stats),
+ .resource = resources_rpm_master_stats,
+ .dev = {
+ .platform_data = &msm_rpm_master_stat_pdata,
+ },
+};
+
static struct msm_rpm_log_platform_data msm_rpm_log_pdata = {
.phys_addr_base = 0x0010C000,
.reg_offsets = {
@@ -2649,15 +2681,6 @@
.num_resources = ARRAY_SIZE(i2s_mdm_resources),
.resource = i2s_mdm_resources,
};
-static int apq8064_LPM_latency = 1000; /* >100 usec for WFI */
-
-struct platform_device apq8064_cpu_idle_device = {
- .name = "msm_cpu_idle",
- .id = -1,
- .dev = {
- .platform_data = &apq8064_LPM_latency,
- },
-};
static struct msm_dcvs_freq_entry apq8064_freq[] = {
{ 384000, 900, 0, 0, 0},
@@ -2670,13 +2693,14 @@
};
static struct msm_dcvs_core_info apq8064_core_info = {
- .freq_tbl = &apq8064_freq[0],
- .num_cores = 4,
- .sensors = (int[]){7, 8, 9, 10},
- .core_param = {
+ .freq_tbl = &apq8064_freq[0],
+ .num_cores = 4,
+ .sensors = (int[]){7, 8, 9, 10},
+ .thermal_poll_ms = 60000,
+ .core_param = {
.core_type = MSM_DCVS_CORE_TYPE_CPU,
},
- .algo_param = {
+ .algo_param = {
.disable_pc_threshold = 1458000,
.em_win_size_min_us = 100000,
.em_win_size_max_us = 300000,
@@ -2692,7 +2716,7 @@
.ss_win_size_max_us = 1000000,
.ss_util_pct = 95,
},
- .energy_coeffs = {
+ .energy_coeffs = {
.active_coeff_a = 336,
.active_coeff_b = 0,
.active_coeff_c = 0,
@@ -2702,17 +2726,24 @@
.leakage_coeff_c = 3329,
.leakage_coeff_d = -277,
},
- .power_param = {
+ .power_param = {
.current_temp = 25,
.num_freq = ARRAY_SIZE(apq8064_freq),
}
};
+#define APQ8064_LPM_LATENCY 1000 /* >100 usec for WFI */
+
+static struct msm_gov_platform_data gov_platform_data = {
+ .info = &apq8064_core_info,
+ .latency = APQ8064_LPM_LATENCY,
+};
+
struct platform_device apq8064_msm_gov_device = {
.name = "msm_dcvs_gov",
.id = -1,
.dev = {
- .platform_data = &apq8064_core_info,
+ .platform_data = &gov_platform_data,
},
};
diff --git a/arch/arm/mach-msm/devices-8930.c b/arch/arm/mach-msm/devices-8930.c
index d062ff4..30a99cd 100644
--- a/arch/arm/mach-msm/devices-8930.c
+++ b/arch/arm/mach-msm/devices-8930.c
@@ -38,6 +38,7 @@
#endif
#define MSM8930_PC_CNTR_PHYS (MSM8930_IMEM_PHYS + 0x664)
#define MSM8930_PC_CNTR_SIZE 0x40
+#define MSM8930_RPM_MASTER_STATS_BASE 0x10B100
static struct resource msm8930_resources_pccntr[] = {
{
@@ -558,6 +559,36 @@
},
};
+static struct resource resources_rpm_master_stats[] = {
+ {
+ .start = MSM8930_RPM_MASTER_STATS_BASE,
+ .end = MSM8930_RPM_MASTER_STATS_BASE + SZ_256,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static char *master_names[] = {
+ "KPSS",
+ "MPSS",
+ "LPASS",
+ "RIVA",
+};
+
+static struct msm_rpm_master_stats_platform_data msm_rpm_master_stat_pdata = {
+ .masters = master_names,
+ .nomasters = ARRAY_SIZE(master_names),
+};
+
+struct platform_device msm8930_rpm_master_stat_device = {
+ .name = "msm_rpm_master_stat",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(resources_rpm_master_stats),
+ .resource = resources_rpm_master_stats,
+ .dev = {
+ .platform_data = &msm_rpm_master_stat_pdata,
+ },
+};
+
static struct resource msm_rpm_rbcpr_resource = {
.start = 0x0010DB00,
.end = 0x0010DB00 + SZ_8K - 1,
@@ -583,16 +614,6 @@
.resource = &msm_rpm_rbcpr_resource,
};
-static int msm8930_LPM_latency = 1000; /* >100 usec for WFI */
-
-struct platform_device msm8930_cpu_idle_device = {
- .name = "msm_cpu_idle",
- .id = -1,
- .dev = {
- .platform_data = &msm8930_LPM_latency,
- },
-};
-
struct platform_device msm_bus_8930_sys_fabric = {
.name = "msm_bus_fabric",
.id = MSM_BUS_FAB_SYSTEM,
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index 72da3d8..5b04fa7 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -105,6 +105,7 @@
#define MSM8960_PC_CNTR_PHYS (MSM8960_IMEM_PHYS + 0x664)
#define MSM8960_PC_CNTR_SIZE 0x40
+#define MSM8960_RPM_MASTER_STATS_BASE 0x10BB00
static struct resource msm8960_resources_pccntr[] = {
{
@@ -1334,108 +1335,106 @@
},
};
-#define MSM_LPASS_QDSP6SS_PHYS 0x28800000
-#define SFAB_LPASS_Q6_ACLK_CTL (MSM_CLK_CTL_BASE + 0x23A0)
-
static struct resource msm_8960_q6_lpass_resources[] = {
{
- .start = MSM_LPASS_QDSP6SS_PHYS,
- .end = MSM_LPASS_QDSP6SS_PHYS + SZ_256 - 1,
+ .start = 0x28800000,
+ .end = 0x28800000 + SZ_256 - 1,
.flags = IORESOURCE_MEM,
},
+ {
+ .start = LPASS_Q6SS_WDOG_EXPIRED,
+ .end = LPASS_Q6SS_WDOG_EXPIRED,
+ .flags = IORESOURCE_IRQ,
+ },
};
static struct pil_q6v4_pdata msm_8960_q6_lpass_data = {
.strap_tcm_base = 0x01460000,
.strap_ahb_upper = 0x00290000,
.strap_ahb_lower = 0x00000280,
- .aclk_reg = SFAB_LPASS_Q6_ACLK_CTL,
+ .aclk_reg = MSM_CLK_CTL_BASE + 0x23A0,
.name = "q6",
.pas_id = PAS_Q6,
.bus_port = MSM_BUS_MASTER_LPASS_PROC,
};
struct platform_device msm_8960_q6_lpass = {
- .name = "pil_qdsp6v4",
- .id = 0,
+ .name = "pil-q6v4-lpass",
+ .id = -1,
.num_resources = ARRAY_SIZE(msm_8960_q6_lpass_resources),
.resource = msm_8960_q6_lpass_resources,
.dev.platform_data = &msm_8960_q6_lpass_data,
};
-#define MSM_MSS_ENABLE_PHYS 0x08B00000
-#define MSM_FW_QDSP6SS_PHYS 0x08800000
-#define MSS_Q6FW_JTAG_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C6C)
-#define SFAB_MSS_Q6_FW_ACLK_CTL (MSM_CLK_CTL_BASE + 0x2044)
-
-static struct resource msm_8960_q6_mss_fw_resources[] = {
+static struct resource msm_8960_q6_mss_resources[] = {
{
- .start = MSM_FW_QDSP6SS_PHYS,
- .end = MSM_FW_QDSP6SS_PHYS + SZ_256 - 1,
+ .start = 0x08800000,
+ .end = 0x08800000 + SZ_256 - 1,
.flags = IORESOURCE_MEM,
},
{
- .start = MSM_MSS_ENABLE_PHYS,
- .end = MSM_MSS_ENABLE_PHYS + 4 - 1,
- .flags = IORESOURCE_MEM,
- },
-};
-
-static struct pil_q6v4_pdata msm_8960_q6_mss_fw_data = {
- .strap_tcm_base = 0x00400000,
- .strap_ahb_upper = 0x00090000,
- .strap_ahb_lower = 0x00000080,
- .aclk_reg = SFAB_MSS_Q6_FW_ACLK_CTL,
- .jtag_clk_reg = MSS_Q6FW_JTAG_CLK_CTL,
- .name = "modem_fw",
- .depends = "q6",
- .pas_id = PAS_MODEM_FW,
- .bus_port = MSM_BUS_MASTER_MSS_FW_PROC,
-};
-
-struct platform_device msm_8960_q6_mss_fw = {
- .name = "pil_qdsp6v4",
- .id = 1,
- .num_resources = ARRAY_SIZE(msm_8960_q6_mss_fw_resources),
- .resource = msm_8960_q6_mss_fw_resources,
- .dev.platform_data = &msm_8960_q6_mss_fw_data,
-};
-
-#define MSM_SW_QDSP6SS_PHYS 0x08900000
-#define SFAB_MSS_Q6_SW_ACLK_CTL (MSM_CLK_CTL_BASE + 0x2040)
-#define MSS_Q6SW_JTAG_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C68)
-
-static struct resource msm_8960_q6_mss_sw_resources[] = {
- {
- .start = MSM_SW_QDSP6SS_PHYS,
- .end = MSM_SW_QDSP6SS_PHYS + SZ_256 - 1,
+ .start = 0x08B00000,
+ .end = 0x08B00000 + SZ_256 - 1,
.flags = IORESOURCE_MEM,
},
{
- .start = MSM_MSS_ENABLE_PHYS,
- .end = MSM_MSS_ENABLE_PHYS + 4 - 1,
+ .start = 0x08882000,
+ .end = 0x08882000 + SZ_256 - 1,
.flags = IORESOURCE_MEM,
},
+ {
+ .start = 0x08900000,
+ .end = 0x08900000 + SZ_256 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = 0x08982000,
+ .end = 0x08982000 + SZ_256 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = Q6FW_WDOG_EXPIRED_IRQ,
+ .end = Q6FW_WDOG_EXPIRED_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = Q6SW_WDOG_EXPIRED_IRQ,
+ .end = Q6SW_WDOG_EXPIRED_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
};
-static struct pil_q6v4_pdata msm_8960_q6_mss_sw_data = {
- .strap_tcm_base = 0x00420000,
- .strap_ahb_upper = 0x00090000,
- .strap_ahb_lower = 0x00000080,
- .aclk_reg = SFAB_MSS_Q6_SW_ACLK_CTL,
- .jtag_clk_reg = MSS_Q6SW_JTAG_CLK_CTL,
- .name = "modem",
- .depends = "modem_fw",
- .pas_id = PAS_MODEM_SW,
- .bus_port = MSM_BUS_MASTER_MSS_SW_PROC,
+static struct pil_q6v4_pdata msm_8960_q6_mss_data[2] = {
+ {
+ .strap_tcm_base = 0x00400000,
+ .strap_ahb_upper = 0x00090000,
+ .strap_ahb_lower = 0x00000080,
+ .aclk_reg = MSM_CLK_CTL_BASE + 0x2C6C,
+ .jtag_clk_reg = MSM_CLK_CTL_BASE + 0x2044,
+ .name = "modem_fw",
+ .depends = "q6",
+ .pas_id = PAS_MODEM_FW,
+ .bus_port = MSM_BUS_MASTER_MSS_FW_PROC,
+ },
+ {
+ .strap_tcm_base = 0x00420000,
+ .strap_ahb_upper = 0x00090000,
+ .strap_ahb_lower = 0x00000080,
+ .aclk_reg = MSM_CLK_CTL_BASE + 0x2040,
+ .jtag_clk_reg = MSM_CLK_CTL_BASE + 0x2C68,
+ .name = "modem",
+ .depends = "modem_fw",
+ .pas_id = PAS_MODEM_SW,
+ .bus_port = MSM_BUS_MASTER_MSS_SW_PROC,
+ }
};
-struct platform_device msm_8960_q6_mss_sw = {
- .name = "pil_qdsp6v4",
- .id = 2,
- .num_resources = ARRAY_SIZE(msm_8960_q6_mss_sw_resources),
- .resource = msm_8960_q6_mss_sw_resources,
- .dev.platform_data = &msm_8960_q6_mss_sw_data,
+struct platform_device msm_8960_q6_mss = {
+ .name = "pil-q6v4-modem",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(msm_8960_q6_mss_resources),
+ .resource = msm_8960_q6_mss_resources,
+ .dev.platform_data = msm_8960_q6_mss_data,
};
static struct resource msm_8960_riva_resources[] = {
@@ -3747,6 +3746,37 @@
},
};
+static struct resource resources_rpm_master_stats[] = {
+ {
+ .start = MSM8960_RPM_MASTER_STATS_BASE,
+ .end = MSM8960_RPM_MASTER_STATS_BASE + SZ_256,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static char *master_names[] = {
+ "KPSS",
+ "GPSS",
+ "LPASS",
+ "RIVA",
+ "DSPS",
+};
+
+static struct msm_rpm_master_stats_platform_data msm_rpm_master_stat_pdata = {
+ .masters = master_names,
+ .nomasters = ARRAY_SIZE(master_names),
+};
+
+struct platform_device msm8960_rpm_master_stat_device = {
+ .name = "msm_rpm_master_stat",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(resources_rpm_master_stats),
+ .resource = resources_rpm_master_stats,
+ .dev = {
+ .platform_data = &msm_rpm_master_stat_pdata,
+ },
+};
+
struct platform_device msm_bus_sys_fabric = {
.name = "msm_bus_fabric",
.id = MSM_BUS_FAB_SYSTEM,
@@ -4071,16 +4101,6 @@
.resource = msm_ebi1_ch1_erp_resources,
};
-static int msm8960_LPM_latency = 1000; /* >100 usec for WFI */
-
-struct platform_device msm8960_cpu_idle_device = {
- .name = "msm_cpu_idle",
- .id = -1,
- .dev = {
- .platform_data = &msm8960_LPM_latency,
- },
-};
-
static struct resource msm_cache_erp_resources[] = {
{
.name = "l1_irq",
diff --git a/arch/arm/mach-msm/devices-9615.c b/arch/arm/mach-msm/devices-9615.c
index 46853ac..fe3a4d5 100644
--- a/arch/arm/mach-msm/devices-9615.c
+++ b/arch/arm/mach-msm/devices-9615.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -64,6 +64,7 @@
#define MSM_GPIO_I2C_CLK 16
#define MSM_GPIO_I2C_SDA 17
+#define MSM9615_RPM_MASTER_STATS_BASE 0x10A700
static struct msm_watchdog_pdata msm_watchdog_pdata = {
.pet_time = 10000,
@@ -570,6 +571,15 @@
.name = "msm-dai-q6",
.id = PRIMARY_I2S_TX,
};
+struct platform_device msm_i2s_cpudai4 = {
+ .name = "msm-dai-q6",
+ .id = SECONDARY_I2S_RX,
+};
+
+struct platform_device msm_i2s_cpudai5 = {
+ .name = "msm-dai-q6",
+ .id = SECONDARY_I2S_TX,
+};
struct platform_device msm_voip = {
.name = "msm-voip-dsp",
.id = -1,
@@ -704,6 +714,41 @@
.id = -1,
};
+static struct resource msm_9615_q6_lpass_resources[] = {
+ {
+ .start = LPASS_Q6SS_WDOG_EXPIRED,
+ .end = LPASS_Q6SS_WDOG_EXPIRED,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device msm_9615_q6_lpass = {
+ .name = "pil-q6v4-lpass",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(msm_9615_q6_lpass_resources),
+ .resource = msm_9615_q6_lpass_resources,
+};
+
+static struct resource msm_9615_q6_mss_resources[] = {
+ {
+ .start = Q6FW_WDOG_EXPIRED_IRQ,
+ .end = Q6FW_WDOG_EXPIRED_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = Q6SW_WDOG_EXPIRED_IRQ,
+ .end = Q6SW_WDOG_EXPIRED_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device msm_9615_q6_mss = {
+ .name = "pil-q6v4-modem",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(msm_9615_q6_mss_resources),
+ .resource = msm_9615_q6_mss_resources,
+};
+
#ifdef CONFIG_HW_RANDOM_MSM
/* PRNG device */
#define MSM_PRNG_PHYS 0x1A500000
@@ -1328,6 +1373,35 @@
},
};
+static struct resource resources_rpm_master_stats[] = {
+ {
+ .start = MSM9615_RPM_MASTER_STATS_BASE,
+ .end = MSM9615_RPM_MASTER_STATS_BASE + SZ_256,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static char *master_names[] = {
+ "KPSS",
+ "MPSS",
+ "LPASS",
+};
+
+static struct msm_rpm_master_stats_platform_data msm_rpm_master_stat_pdata = {
+ .masters = master_names,
+ .nomasters = ARRAY_SIZE(master_names),
+};
+
+struct platform_device msm9615_rpm_master_stat_device = {
+ .name = "msm_rpm_master_stat",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(resources_rpm_master_stats),
+ .resource = resources_rpm_master_stats,
+ .dev = {
+ .platform_data = &msm_rpm_master_stat_pdata,
+ },
+};
+
static struct msm_rpm_log_platform_data msm_rpm_log_pdata = {
.phys_addr_base = 0x0010AC00,
.reg_offsets = {
diff --git a/arch/arm/mach-msm/devices-msm7x27a.c b/arch/arm/mach-msm/devices-msm7x27a.c
index 50ab26f..c0ad39b 100644
--- a/arch/arm/mach-msm/devices-msm7x27a.c
+++ b/arch/arm/mach-msm/devices-msm7x27a.c
@@ -1606,6 +1606,26 @@
},
};
+static struct resource pl310_resources[] = {
+ {
+ .start = 0xC0400000,
+ .end = 0xC0400000 + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "l2_irq",
+ .start = MSM8625_INT_L2CC_INTR,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device pl310_erp_device = {
+ .name = "pl310_erp",
+ .id = -1,
+ .resource = pl310_resources,
+ .num_resources = ARRAY_SIZE(pl310_resources),
+};
+
enum {
MSM8625,
MSM8625A,
@@ -1954,6 +1974,11 @@
(SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 2))
msm_cpr_init();
+ if (!cpu_is_msm8625())
+ pl310_resources[1].start = INT_L2CC_INTR;
+
+ platform_device_register(&pl310_erp_device);
+
return 0;
}
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index 6f3dda3..8f02050 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -244,15 +244,17 @@
extern struct platform_device msm_cpudai_incall_record_tx;
extern struct platform_device msm_i2s_cpudai0;
extern struct platform_device msm_i2s_cpudai1;
-
+extern struct platform_device msm_i2s_cpudai4;
+extern struct platform_device msm_i2s_cpudai5;
extern struct platform_device msm_pil_q6v3;
extern struct platform_device msm_pil_modem;
extern struct platform_device msm_pil_tzapps;
extern struct platform_device msm_pil_dsps;
extern struct platform_device msm_pil_vidc;
extern struct platform_device msm_8960_q6_lpass;
-extern struct platform_device msm_8960_q6_mss_fw;
-extern struct platform_device msm_8960_q6_mss_sw;
+extern struct platform_device msm_9615_q6_lpass;
+extern struct platform_device msm_8960_q6_mss;
+extern struct platform_device msm_9615_q6_mss;
extern struct platform_device msm_8960_riva;
extern struct platform_device msm_gss;
@@ -335,10 +337,12 @@
extern struct platform_device msm8960_rpm_device;
extern struct platform_device msm8960_rpm_stat_device;
+extern struct platform_device msm8960_rpm_master_stat_device;
extern struct platform_device msm8960_rpm_log_device;
extern struct platform_device msm8930_rpm_device;
extern struct platform_device msm8930_rpm_stat_device;
+extern struct platform_device msm8930_rpm_master_stat_device;
extern struct platform_device msm8930_rpm_log_device;
extern struct platform_device msm8930_rpm_rbcpr_device;
@@ -348,10 +352,12 @@
extern struct platform_device msm9615_rpm_device;
extern struct platform_device msm9615_rpm_stat_device;
+extern struct platform_device msm9615_rpm_master_stat_device;
extern struct platform_device msm9615_rpm_log_device;
extern struct platform_device apq8064_rpm_device;
extern struct platform_device apq8064_rpm_stat_device;
+extern struct platform_device apq8064_rpm_master_stat_device;
extern struct platform_device apq8064_rpm_log_device;
extern struct platform_device msm_device_rng;
@@ -394,10 +400,6 @@
extern struct platform_device *msm_8974_stub_regulator_devices[];
extern int msm_8974_stub_regulator_devices_len;
-extern struct platform_device msm8960_cpu_idle_device;
-extern struct platform_device msm8930_cpu_idle_device;
-extern struct platform_device apq8064_cpu_idle_device;
-
extern struct platform_device apq8064_msm_gov_device;
extern struct platform_device msm_bus_8930_apps_fabric;
diff --git a/arch/arm/mach-msm/idle-v7.S b/arch/arm/mach-msm/idle-v7.S
index 8eed48d..1d8f313 100644
--- a/arch/arm/mach-msm/idle-v7.S
+++ b/arch/arm/mach-msm/idle-v7.S
@@ -330,9 +330,14 @@
ldr pc, [r1] /* jump */
ENTRY(msm_pm_set_l2_flush_flag)
- ldr r1, =msm_pm_flush_l2_flag
- str r0, [r1]
- bx lr
+ ldr r1, =msm_pm_flush_l2_flag
+ str r0, [r1]
+ bx lr
+
+ENTRY(msm_pm_get_l2_flush_flag)
+ ldr r1, =msm_pm_flush_l2_flag
+ ldr r0, [r1]
+ bx lr
.data
diff --git a/arch/arm/mach-msm/idle.h b/arch/arm/mach-msm/idle.h
index 4abdd04..7a939ab 100644
--- a/arch/arm/mach-msm/idle.h
+++ b/arch/arm/mach-msm/idle.h
@@ -38,6 +38,7 @@
#ifdef CONFIG_CPU_V7
void msm_pm_boot_entry(void);
void msm_pm_set_l2_flush_flag(unsigned int flag);
+int msm_pm_get_l2_flush_flag(void);
extern unsigned long msm_pm_pc_pgd;
extern unsigned long msm_pm_boot_vector[NR_CPUS];
extern uint32_t target_type;
diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h
index 433fee3..0b53bad 100644
--- a/arch/arm/mach-msm/include/mach/board.h
+++ b/arch/arm/mach-msm/include/mach/board.h
@@ -586,6 +586,8 @@
void msm_map_msm8226_io(void);
void msm8226_init_irq(void);
void msm8226_init_gpiomux(void);
+void msm_map_msm8910_io(void);
+void msm8910_init_irq(void);
struct mmc_platform_data;
int msm_add_sdcc(unsigned int controller,
diff --git a/arch/arm/mach-msm/include/mach/iommu.h b/arch/arm/mach-msm/include/mach/iommu.h
index f63af64..41a59af6 100644
--- a/arch/arm/mach-msm/include/mach/iommu.h
+++ b/arch/arm/mach-msm/include/mach/iommu.h
@@ -88,7 +88,6 @@
* @aclk: Alternate clock for this IOMMU core, if any
* @name: Human-readable name of this IOMMU device
* @gdsc: Regulator needed to power this HW block (v2 only)
- * @nsmr: Size of the SMT on this HW block (v2 only)
* @bfb_settings: Optional BFB performance tuning parameters
*
* A msm_iommu_drvdata holds the global driver data about a single piece
@@ -103,7 +102,6 @@
struct clk *aclk;
const char *name;
struct regulator *gdsc;
- unsigned int nsmr;
struct msm_iommu_bfb_settings *bfb_settings;
};
diff --git a/arch/arm/mach-msm/include/mach/irqs-8910.h b/arch/arm/mach-msm/include/mach/irqs-8910.h
new file mode 100644
index 0000000..22fdc16
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/irqs-8910.h
@@ -0,0 +1,40 @@
+/* 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 __ASM_ARCH_MSM_IRQS_8910_H
+#define __ASM_ARCH_MSM_IRQS_8910_H
+
+/* MSM ACPU Interrupt Numbers */
+
+/*
+ * 0-15: STI/SGI (software triggered/generated interrupts)
+ * 16-31: PPI (private peripheral interrupts)
+ * 32+: SPI (shared peripheral interrupts)
+ */
+
+#define GIC_PPI_START 16
+#define GIC_SPI_START 32
+
+#define INT_ARMQC_PERFMON (GIC_PPI_START + 10)
+
+#define APCC_QGICL2PERFMONIRPTREQ (GIC_SPI_START + 1)
+#define SC_SICL2PERFMONIRPTREQ APCC_QGICL2PERFMONIRPTREQ
+#define TLMM_MSM_SUMMARY_IRQ (GIC_SPI_START + 208)
+
+#define NR_MSM_IRQS 256
+#define NR_GPIO_IRQS 146
+#define NR_QPNP_IRQS 32768 /* SPARSE_IRQ is required to support this */
+#define NR_BOARD_IRQS NR_QPNP_IRQS
+#define NR_TLMM_MSM_DIR_CONN_IRQ 8
+#define NR_MSM_GPIOS NR_GPIO_IRQS
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/irqs.h b/arch/arm/mach-msm/include/mach/irqs.h
index f562c40..7aff770 100644
--- a/arch/arm/mach-msm/include/mach/irqs.h
+++ b/arch/arm/mach-msm/include/mach/irqs.h
@@ -60,6 +60,8 @@
#if defined(CONFIG_ARCH_MSM8974)
#include "irqs-8974.h"
+#elif defined(CONFIG_ARCH_MSM8910)
+#include "irqs-8910.h"
#elif defined(CONFIG_ARCH_MPQ8092)
#include "irqs-8092.h"
#elif defined(CONFIG_ARCH_MSM9615)
diff --git a/arch/arm/mach-msm/include/mach/msm_dcvs.h b/arch/arm/mach-msm/include/mach/msm_dcvs.h
index 3ac0e74..e81cee4 100644
--- a/arch/arm/mach-msm/include/mach/msm_dcvs.h
+++ b/arch/arm/mach-msm/include/mach/msm_dcvs.h
@@ -19,6 +19,10 @@
#define CORES_MAX (10)
#define CPU_OFFSET 1 /* used to notify TZ the core number */
+#define GPU_OFFSET (CORES_MAX * 2/3) /* there will be more cpus than gpus,
+ * let the GPU be assigned fewer core
+ * elements and start later
+ */
enum msm_core_idle_state {
MSM_DCVS_IDLE_ENTER,
@@ -32,54 +36,14 @@
MSM_DCVS_DISABLE_HIGH_LATENCY_MODES,
};
-/**
- * struct msm_dcvs_idle
- *
- * API for idle code to register and send idle enter/exit
- * notifications to msm_dcvs driver.
- */
-struct msm_dcvs_idle {
- const char *core_name;
- /* Enable/Disable idle state/notifications */
- int (*enable)(struct msm_dcvs_idle *self,
- enum msm_core_control_event event);
+struct msm_gov_platform_data {
+ struct msm_dcvs_core_info *info;
+ int latency;
};
/**
- * struct msm_dcvs_freq
- *
- * API for clock driver code to register and receive frequency change
- * request for the core from the msm_dcvs driver.
- */
-struct msm_dcvs_freq {
- const char *core_name;
- /* Callback from msm_dcvs to set the core frequency */
-};
-
-/**
- * msm_dcvs_idle_source_register
- * @drv: Pointer to the source driver
- * @return: Handle to be used for sending idle state notifications.
- *
- * Register the idle driver with the msm_dcvs driver to send idle
- * state notifications for the core.
- */
-extern int msm_dcvs_idle_source_register(struct msm_dcvs_idle *drv);
-
-/**
- * msm_dcvs_idle_source_unregister
- * @drv: Pointer to the source driver
- * @return:
- * 0 on success
- * -EINVAL
- *
- * Description: Unregister the idle driver with the msm_dcvs driver
- */
-extern int msm_dcvs_idle_source_unregister(struct msm_dcvs_idle *drv);
-
-/**
* msm_dcvs_idle
- * @handle: Handle provided back at registration
+ * @dcvs_core_id: The id returned by msm_dcvs_register_core
* @state: The enter/exit idle state the core is in
* @iowaited: iowait in us
* on iMSM_DCVS_IDLE_EXIT.
@@ -91,7 +55,7 @@
*
* Send idle state notifications to the msm_dcvs driver
*/
-int msm_dcvs_idle(int handle, enum msm_core_idle_state state,
+int msm_dcvs_idle(int dcvs_core_id, enum msm_core_idle_state state,
uint32_t iowaited);
/**
@@ -103,6 +67,7 @@
struct msm_dcvs_core_info {
int num_cores;
int *sensors;
+ int thermal_poll_ms;
struct msm_dcvs_freq_entry *freq_tbl;
struct msm_dcvs_core_param core_param;
struct msm_dcvs_algo_param algo_param;
@@ -112,7 +77,8 @@
/**
* msm_dcvs_register_core
- * @core_name: Unique name identifier for the core.
+ * @type: whether this is a CPU or a GPU
+ * @type_core_num: The number of the core for a type
* @info: The core specific algorithm parameters.
* @sensor: The thermal sensor number of the core in question
* @return :
@@ -125,10 +91,13 @@
* Cores that need to run synchronously must share the same group id.
*/
extern int msm_dcvs_register_core(
- const char *core_name,
+ enum msm_dcvs_core_type type,
+ int type_core_num,
struct msm_dcvs_core_info *info,
- int (*set_frequency)(struct msm_dcvs_freq *self, unsigned int freq),
- unsigned int (*get_frequency)(struct msm_dcvs_freq *self),
+ int (*set_frequency)(int type_core_num, unsigned int freq),
+ unsigned int (*get_frequency)(int type_core_num),
+ int (*idle_enable)(int type_core_num,
+ enum msm_core_control_event event),
int sensor);
/**
@@ -139,7 +108,7 @@
* Register the clock driver code with the msm_dvs driver to get notified about
* frequency change requests.
*/
-extern int msm_dcvs_freq_sink_start(struct msm_dcvs_freq *drv);
+extern int msm_dcvs_freq_sink_start(int dcvs_core_id);
/**
* msm_dcvs_freq_sink_stop
@@ -151,7 +120,7 @@
* Unregister the sink driver for the core. This will cause the source driver
* for the core to stop sending idle pulses.
*/
-extern int msm_dcvs_freq_sink_stop(struct msm_dcvs_freq *drv);
+extern int msm_dcvs_freq_sink_stop(int dcvs_core_id);
/**
* msm_dcvs_update_limits
@@ -159,5 +128,5 @@
*
* Update the frequency known to dcvs when the limits are changed.
*/
-extern void msm_dcvs_update_limits(struct msm_dcvs_freq *drv);
+extern void msm_dcvs_update_limits(int dcvs_core_id);
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h b/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h
index 82542b2..831b40e 100644
--- a/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h
+++ b/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h
@@ -100,6 +100,8 @@
#define CONFIG_MAX_PKT(n) ((n) << 16)
#define CONFIG_ZLT (1 << 29) /* stop on zero-len xfer */
#define CONFIG_IOS (1 << 15) /* IRQ on setup */
+#define CONFIG_MULT (3 << 30)
+#define CONFIG_MULT_SHIFT 11
struct ept_queue_item {
unsigned next;
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-9625.h b/arch/arm/mach-msm/include/mach/msm_iomap-9625.h
index 765de13..652563c 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-9625.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-9625.h
@@ -43,6 +43,9 @@
#define MSM9625_IMEM_PHYS 0xFC42B000
#define MSM9625_IMEM_SIZE SZ_2K
+#define MSM9625_MPM2_PSHOLD_PHYS 0xFC4AB000
+#define MSM9625_MPM2_PSHOLD_SIZE SZ_4K
+
#ifdef CONFIG_DEBUG_MSM9625_UART
#define MSM_DEBUG_UART_BASE IOMEM(0xFA71E000)
#define MSM_DEBUG_UART_PHYS 0xF991E000
diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/codec_utils.h b/arch/arm/mach-msm/include/mach/qdsp5v2/codec_utils.h
index 92dfe12..2cfdabf 100644
--- a/arch/arm/mach-msm/include/mach/qdsp5v2/codec_utils.h
+++ b/arch/arm/mach-msm/include/mach/qdsp5v2/codec_utils.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010, 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010, 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
@@ -123,7 +123,8 @@
uint64_t bytecount_given;
uint64_t bytecount_query;
- struct list_head pmem_region_queue; /* protected by lock */
+ struct list_head ion_region_queue; /* protected by lock */
+ struct ion_client *client;
int eq_enable;
int eq_needs_commit;
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h
index 487e814..da639ce 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h
@@ -78,7 +78,7 @@
} __packed;
/* Max number of static located transparent data (bytes) */
-#define USM_MAX_CFG_DATA_SIZE 20
+#define USM_MAX_CFG_DATA_SIZE 100
struct usm_encode_cfg_blk {
u32 frames_per_buf;
u32 format_id;
diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c
index 8ebead8..4b81ce7 100644
--- a/arch/arm/mach-msm/io.c
+++ b/arch/arm/mach-msm/io.c
@@ -461,6 +461,7 @@
static struct map_desc msm9625_io_desc[] __initdata = {
MSM_CHIP_DEVICE(APCS_GCC, MSM9625),
MSM_CHIP_DEVICE(TLMM, MSM9625),
+ MSM_CHIP_DEVICE(MPM2_PSHOLD, MSM9625),
MSM_CHIP_DEVICE(TMR, MSM9625),
MSM_CHIP_DEVICE(IMEM, MSM9625),
{
diff --git a/arch/arm/mach-msm/lpass-8960.c b/arch/arm/mach-msm/lpass-8960.c
deleted file mode 100644
index b714a7f..0000000
--- a/arch/arm/mach-msm/lpass-8960.c
+++ /dev/null
@@ -1,290 +0,0 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/reboot.h>
-#include <linux/workqueue.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/err.h>
-
-#include <mach/irqs.h>
-#include <mach/scm.h>
-#include <mach/peripheral-loader.h>
-#include <mach/subsystem_restart.h>
-#include <mach/subsystem_notif.h>
-
-#include "smd_private.h"
-#include "ramdump.h"
-#include "sysmon.h"
-
-#define SCM_Q6_NMI_CMD 0x1
-#define MODULE_NAME "lpass_8960"
-#define MAX_BUF_SIZE 0x51
-
-/* Subsystem restart: QDSP6 data, functions */
-static void lpass_fatal_fn(struct work_struct *);
-static DECLARE_WORK(lpass_fatal_work, lpass_fatal_fn);
-struct lpass_ssr {
- void *lpass_ramdump_dev;
-} lpass_ssr;
-
-static struct lpass_ssr lpass_ssr_8960;
-static int q6_crash_shutdown;
-
-static int riva_notifier_cb(struct notifier_block *this, unsigned long code,
- void *ss_handle)
-{
- int ret;
- switch (code) {
- case SUBSYS_BEFORE_SHUTDOWN:
- pr_debug("%s: R-Notify: Shutdown started\n", __func__);
- ret = sysmon_send_event(SYSMON_SS_LPASS, "wcnss",
- SUBSYS_BEFORE_SHUTDOWN);
- if (ret < 0)
- pr_err("%s: sysmon_send_event error %d", __func__,
- ret);
- break;
- }
- return NOTIFY_DONE;
-}
-
-static void *ssr_notif_hdle;
-static struct notifier_block rnb = {
- .notifier_call = riva_notifier_cb,
-};
-
-static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
- void *ss_handle)
-{
- int ret;
- switch (code) {
- case SUBSYS_BEFORE_SHUTDOWN:
- pr_debug("%s: M-Notify: Shutdown started\n", __func__);
- ret = sysmon_send_event(SYSMON_SS_LPASS, "modem",
- SUBSYS_BEFORE_SHUTDOWN);
- if (ret < 0)
- pr_err("%s: sysmon_send_event error %d", __func__,
- ret);
- break;
- }
- return NOTIFY_DONE;
-}
-
-static void *ssr_modem_notif_hdle;
-static struct notifier_block mnb = {
- .notifier_call = modem_notifier_cb,
-};
-
-static void lpass_log_failure_reason(void)
-{
- char *reason;
- char buffer[MAX_BUF_SIZE];
- unsigned size;
-
- reason = smem_get_entry(SMEM_SSR_REASON_LPASS0, &size);
-
- if (!reason) {
- pr_err("%s: subsystem failure reason: (unknown, smem_get_entry failed).",
- MODULE_NAME);
- return;
- }
-
- if (reason[0] == '\0') {
- pr_err("%s: subsystem failure reason: (unknown, init value found)",
- MODULE_NAME);
- return;
- }
-
- size = size < MAX_BUF_SIZE ? size : (MAX_BUF_SIZE-1);
- memcpy(buffer, reason, size);
- buffer[size] = '\0';
- pr_err("%s: subsystem failure reason: %s", MODULE_NAME, buffer);
- memset((void *)reason, 0x0, size);
- wmb();
-}
-
-static void lpass_fatal_fn(struct work_struct *work)
-{
- pr_err("%s %s: Watchdog bite received from Q6!\n", MODULE_NAME,
- __func__);
- lpass_log_failure_reason();
- panic(MODULE_NAME ": Resetting the SoC");
-}
-
-static void lpass_smsm_state_cb(void *data, uint32_t old_state,
- uint32_t new_state)
-{
- /* Ignore if we're the one that set SMSM_RESET */
- if (q6_crash_shutdown)
- return;
-
- if (new_state & SMSM_RESET) {
- pr_err("%s: LPASS SMSM state changed to SMSM_RESET,"
- " new_state = 0x%x, old_state = 0x%x\n", __func__,
- new_state, old_state);
- lpass_log_failure_reason();
- panic(MODULE_NAME ": Resetting the SoC");
- }
-}
-
-static void send_q6_nmi(void)
-{
- /* Send NMI to QDSP6 via an SCM call. */
- uint32_t cmd = 0x1;
-
- scm_call(SCM_SVC_UTIL, SCM_Q6_NMI_CMD,
- &cmd, sizeof(cmd), NULL, 0);
-
- /* Q6 requires worstcase 100ms to dump caches etc.*/
- mdelay(100);
- pr_debug("%s: Q6 NMI was sent.\n", __func__);
-}
-
-static int lpass_shutdown(const struct subsys_desc *subsys)
-{
- send_q6_nmi();
- pil_force_shutdown("q6");
- disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED);
-
- return 0;
-}
-
-static int lpass_powerup(const struct subsys_desc *subsys)
-{
- int ret = pil_force_boot("q6");
- enable_irq(LPASS_Q6SS_WDOG_EXPIRED);
- return ret;
-}
-/* RAM segments - address and size for 8960 */
-static struct ramdump_segment q6_segments[] = { {0x8da00000, 0x8f200000 -
- 0x8da00000}, {0x28400000, 0x20000} };
-static int lpass_ramdump(int enable, const struct subsys_desc *subsys)
-{
- pr_debug("%s: enable[%d]\n", __func__, enable);
- if (enable)
- return do_ramdump(lpass_ssr_8960.lpass_ramdump_dev,
- q6_segments,
- ARRAY_SIZE(q6_segments));
- else
- return 0;
-}
-
-static void lpass_crash_shutdown(const struct subsys_desc *subsys)
-{
- q6_crash_shutdown = 1;
- send_q6_nmi();
-}
-
-static irqreturn_t lpass_wdog_bite_irq(int irq, void *dev_id)
-{
- int ret;
-
- pr_debug("%s: rxed irq[0x%x]", __func__, irq);
- disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED);
- ret = schedule_work(&lpass_fatal_work);
-
- return IRQ_HANDLED;
-}
-
-static struct subsys_device *lpass_8960_dev;
-
-static struct subsys_desc lpass_8960 = {
- .name = "lpass",
- .shutdown = lpass_shutdown,
- .powerup = lpass_powerup,
- .ramdump = lpass_ramdump,
- .crash_shutdown = lpass_crash_shutdown
-};
-
-static int __init lpass_restart_init(void)
-{
- lpass_8960_dev = subsys_register(&lpass_8960);
- if (IS_ERR(lpass_8960_dev))
- return PTR_ERR(lpass_8960_dev);
- return 0;
-}
-
-static int __init lpass_fatal_init(void)
-{
- int ret;
-
- ret = smsm_state_cb_register(SMSM_Q6_STATE, SMSM_RESET,
- lpass_smsm_state_cb, 0);
-
- if (ret < 0)
- pr_err("%s: Unable to register SMSM callback! (%d)\n",
- __func__, ret);
-
- ret = request_irq(LPASS_Q6SS_WDOG_EXPIRED, lpass_wdog_bite_irq,
- IRQF_TRIGGER_RISING, "q6_wdog", NULL);
-
- if (ret < 0) {
- pr_err("%s: Unable to request LPASS_Q6SS_WDOG_EXPIRED irq.",
- __func__);
- goto out;
- }
- ret = lpass_restart_init();
- if (ret < 0) {
- pr_err("%s: Unable to reg with lpass ssr. (%d)\n",
- __func__, ret);
- goto out;
- }
-
- lpass_ssr_8960.lpass_ramdump_dev = create_ramdump_device("lpass");
-
- if (!lpass_ssr_8960.lpass_ramdump_dev) {
- pr_err("%s: Unable to create ramdump device.\n",
- __func__);
- ret = -ENOMEM;
- goto out;
- }
- ssr_notif_hdle = subsys_notif_register_notifier("riva",
- &rnb);
- if (IS_ERR(ssr_notif_hdle) < 0) {
- ret = PTR_ERR(ssr_notif_hdle);
- pr_err("%s: subsys_register_notifier for Riva: err = %d\n",
- __func__, ret);
- free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
- goto out;
- }
-
- ssr_modem_notif_hdle = subsys_notif_register_notifier("modem",
- &mnb);
- if (IS_ERR(ssr_modem_notif_hdle) < 0) {
- ret = PTR_ERR(ssr_modem_notif_hdle);
- pr_err("%s: subsys_register_notifier for Modem: err = %d\n",
- __func__, ret);
- subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
- free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
- goto out;
- }
-
- pr_info("%s: lpass SSR driver init'ed.\n", __func__);
-out:
- return ret;
-}
-
-static void __exit lpass_fatal_exit(void)
-{
- subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
- subsys_notif_unregister_notifier(ssr_modem_notif_hdle, &mnb);
- subsys_unregister(lpass_8960_dev);
- free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
-}
-
-module_init(lpass_fatal_init);
-module_exit(lpass_fatal_exit);
-
-MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/lpm_levels.c b/arch/arm/mach-msm/lpm_levels.c
index 8218a42..4854fc48 100644
--- a/arch/arm/mach-msm/lpm_levels.c
+++ b/arch/arm/mach-msm/lpm_levels.c
@@ -22,6 +22,18 @@
#include "pm.h"
#include "rpm-notifier.h"
+
+enum {
+ MSM_LPM_LVL_DBG_SUSPEND_LIMITS = BIT(0),
+ MSM_LPM_LVL_DBG_IDLE_LIMITS = BIT(1),
+};
+
+static int msm_lpm_lvl_dbg_msk;
+
+module_param_named(
+ debug_mask, msm_lpm_lvl_dbg_msk, int, S_IRUGO | S_IWUSR | S_IWGRP
+);
+
static struct msm_rpmrs_level *msm_lpm_levels;
static int msm_lpm_level_count;
@@ -41,6 +53,7 @@
bool from_idle, bool notify_rpm)
{
int ret = 0;
+ int debug_mask;
struct msm_rpmrs_limits *l = (struct msm_rpmrs_limits *)limits;
ret = msm_rpm_enter_sleep();
@@ -49,6 +62,21 @@
__func__, ret);
goto bail;
}
+ if (from_idle)
+ debug_mask = msm_lpm_lvl_dbg_msk &
+ MSM_LPM_LVL_DBG_IDLE_LIMITS;
+ else
+ debug_mask = msm_lpm_lvl_dbg_msk &
+ MSM_LPM_LVL_DBG_SUSPEND_LIMITS;
+
+ if (debug_mask)
+ pr_info("%s(): pxo:%d l2:%d mem:0x%x(0x%x) dig:0x%x(0x%x)\n",
+ __func__, l->pxo, l->l2_cache,
+ l->vdd_mem_lower_bound,
+ l->vdd_mem_upper_bound,
+ l->vdd_dig_lower_bound,
+ l->vdd_dig_upper_bound);
+
ret = msm_lpmrs_enter_sleep(sclk_count, l, from_idle, notify_rpm);
bail:
return ret;
diff --git a/arch/arm/mach-msm/lpm_resources.c b/arch/arm/mach-msm/lpm_resources.c
index 48d31f3..364f297 100644
--- a/arch/arm/mach-msm/lpm_resources.c
+++ b/arch/arm/mach-msm/lpm_resources.c
@@ -104,6 +104,11 @@
struct msm_rpm_request *handle;
};
+enum {
+ MSM_LPM_RPM_RS_TYPE = 0,
+ MSM_LPM_LOCAL_RS_TYPE = 1,
+};
+
struct msm_lpm_resource {
struct msm_lpm_rs_data rs_data;
uint32_t sleep_value;
@@ -126,7 +131,7 @@
.aggregate = msm_lpm_aggregate_l2,
.flush = msm_lpm_flush_l2,
.notify = NULL,
- .valid = true,
+ .valid = false,
.rs_data = {
.value = MSM_LPM_L2_CACHE_ACTIVE,
.default_value = MSM_LPM_L2_CACHE_ACTIVE,
@@ -787,6 +792,7 @@
struct msm_lpm_resource *rs = NULL;
const char *val;
int i;
+ uint32_t resource_type;
key = "qcom,name";
ret = of_property_read_string(node, key, &val);
@@ -810,49 +816,73 @@
continue;
}
- key = "qcom,type";
- ret = of_property_read_u32(node, key, &rs->rs_data.type);
+ key = "qcom,resource-type";
+ ret = of_property_read_u32(node, key, &resource_type);
if (ret) {
- pr_err("Failed to read type\n");
+ pr_err("Failed to read resource-type\n");
goto fail;
}
- key = "qcom,id";
- ret = of_property_read_u32(node, key, &rs->rs_data.id);
- if (ret) {
- pr_err("Failed to read id\n");
+ switch (resource_type) {
+ case MSM_LPM_RPM_RS_TYPE:
+ key = "qcom,type";
+ ret = of_property_read_u32(node, key,
+ &rs->rs_data.type);
+ if (ret) {
+ pr_err("Failed to read type\n");
+ goto fail;
+ }
+
+ key = "qcom,id";
+ ret = of_property_read_u32(node, key, &rs->rs_data.id);
+ if (ret) {
+ pr_err("Failed to read id\n");
+ goto fail;
+ }
+
+ key = "qcom,key";
+ ret = of_property_read_u32(node, key, &rs->rs_data.key);
+ if (ret) {
+ pr_err("Failed to read key\n");
+ goto fail;
+ }
+
+ rs->rs_data.handle = msm_lpm_create_rpm_request(
+ rs->rs_data.type,
+ rs->rs_data.id);
+
+ if (!rs->rs_data.handle) {
+ pr_err("%s: Failed to allocate handle for %s\n",
+ __func__, rs->name);
+ ret = -1;
+ goto fail;
+ }
+ /* fall through */
+
+ case MSM_LPM_LOCAL_RS_TYPE:
+ rs->valid = true;
+ break;
+ default:
+ pr_err("%s: Invalid resource type %d", __func__,
+ resource_type);
goto fail;
}
-
- key = "qcom,key";
- ret = of_property_read_u32(node, key, &rs->rs_data.key);
- if (ret) {
- pr_err("Failed to read key\n");
- goto fail;
- }
-
- rs->rs_data.handle = msm_lpm_create_rpm_request(
- rs->rs_data.type, rs->rs_data.id);
-
- if (!rs->rs_data.handle) {
- pr_err("%s: Failed to allocate handle for %s\n",
- __func__, rs->name);
- ret = -1;
- goto fail;
- }
-
- rs->valid = true;
}
msm_rpm_register_notifier(&msm_lpm_rpm_nblk);
msm_lpm_init_rpm_ctl();
- register_hotcpu_notifier(&msm_lpm_cpu_nblk);
- /* For UP mode, set the default to HSFS OPEN*/
- if (num_possible_cpus() == 1) {
- msm_lpm_l2.rs_data.default_value = MSM_LPM_L2_CACHE_HSFS_OPEN;
- msm_lpm_l2.rs_data.value = MSM_LPM_L2_CACHE_HSFS_OPEN;
- }
- msm_pm_set_l2_flush_flag(0);
- return 0;
+
+ if (msm_lpm_l2.valid) {
+ register_hotcpu_notifier(&msm_lpm_cpu_nblk);
+ /* For UP mode, set the default to HSFS OPEN*/
+ if (num_possible_cpus() == 1) {
+ msm_lpm_l2.rs_data.default_value =
+ MSM_LPM_L2_CACHE_HSFS_OPEN;
+ msm_lpm_l2.rs_data.value = MSM_LPM_L2_CACHE_HSFS_OPEN;
+ }
+ msm_pm_set_l2_flush_flag(0);
+ } else
+ msm_pm_set_l2_flush_flag(1);
+
fail:
return ret;
}
diff --git a/arch/arm/mach-msm/modem-8960.c b/arch/arm/mach-msm/modem-8960.c
deleted file mode 100644
index 83b3bc4..0000000
--- a/arch/arm/mach-msm/modem-8960.c
+++ /dev/null
@@ -1,332 +0,0 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/reboot.h>
-#include <linux/workqueue.h>
-#include <linux/io.h>
-#include <linux/jiffies.h>
-#include <linux/stringify.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/debugfs.h>
-
-#include <mach/irqs.h>
-#include <mach/scm.h>
-#include <mach/peripheral-loader.h>
-#include <mach/subsystem_restart.h>
-#include <mach/subsystem_notif.h>
-#include <mach/socinfo.h>
-#include <mach/msm_smsm.h>
-
-#include "smd_private.h"
-#include "modem_notifier.h"
-#include "ramdump.h"
-
-static int crash_shutdown;
-
-static struct subsys_device *modem_8960_dev;
-
-#define MAX_SSR_REASON_LEN 81U
-
-static void log_modem_sfr(void)
-{
- u32 size;
- char *smem_reason, reason[MAX_SSR_REASON_LEN];
-
- smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
- if (!smem_reason || !size) {
- pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n");
- return;
- }
- if (!smem_reason[0]) {
- pr_err("modem subsystem failure reason: (unknown, init string found).\n");
- return;
- }
-
- size = min(size, MAX_SSR_REASON_LEN-1);
- memcpy(reason, smem_reason, size);
- reason[size] = '\0';
- pr_err("modem subsystem failure reason: %s.\n", reason);
-
- smem_reason[0] = '\0';
- wmb();
-}
-
-static void restart_modem(void)
-{
- log_modem_sfr();
- subsystem_restart_dev(modem_8960_dev);
-}
-
-static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
-{
- /* Ignore if we're the one that set SMSM_RESET */
- if (crash_shutdown)
- return;
-
- if (new_state & SMSM_RESET) {
- pr_err("Probable fatal error on the modem.\n");
- restart_modem();
- }
-}
-
-#define Q6_FW_WDOG_ENABLE 0x08882024
-#define Q6_SW_WDOG_ENABLE 0x08982024
-
-static int modem_shutdown(const struct subsys_desc *subsys)
-{
- void __iomem *q6_fw_wdog_addr;
- void __iomem *q6_sw_wdog_addr;
-
- /*
- * Disable the modem watchdog since it keeps running even after the
- * modem is shutdown.
- */
- q6_fw_wdog_addr = ioremap_nocache(Q6_FW_WDOG_ENABLE, 4);
- if (!q6_fw_wdog_addr)
- return -ENOMEM;
-
- q6_sw_wdog_addr = ioremap_nocache(Q6_SW_WDOG_ENABLE, 4);
- if (!q6_sw_wdog_addr) {
- iounmap(q6_fw_wdog_addr);
- return -ENOMEM;
- }
-
- writel_relaxed(0x0, q6_fw_wdog_addr);
- writel_relaxed(0x0, q6_sw_wdog_addr);
- mb();
- iounmap(q6_sw_wdog_addr);
- iounmap(q6_fw_wdog_addr);
-
- pil_force_shutdown("modem");
- pil_force_shutdown("modem_fw");
- disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
- disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
-
- return 0;
-}
-
-#define MODEM_WDOG_CHECK_TIMEOUT_MS 10000
-
-static int modem_powerup(const struct subsys_desc *subsys)
-{
- pil_force_boot("modem_fw");
- pil_force_boot("modem");
- enable_irq(Q6FW_WDOG_EXPIRED_IRQ);
- enable_irq(Q6SW_WDOG_EXPIRED_IRQ);
- return 0;
-}
-
-void modem_crash_shutdown(const struct subsys_desc *subsys)
-{
- crash_shutdown = 1;
- smsm_reset_modem(SMSM_RESET);
-}
-
-/* FIXME: Get address, size from PIL */
-static struct ramdump_segment modemsw_segments[] = {
- {0x89000000, 0x8D400000 - 0x89000000},
-};
-
-static struct ramdump_segment modemfw_segments[] = {
- {0x8D400000, 0x8DA00000 - 0x8D400000},
-};
-
-static struct ramdump_segment smem_segments[] = {
- {0x80000000, 0x00200000},
-};
-
-static void *modemfw_ramdump_dev;
-static void *modemsw_ramdump_dev;
-static void *smem_ramdump_dev;
-
-static int modem_ramdump(int enable, const struct subsys_desc *crashed_subsys)
-{
- int ret = 0;
-
- if (enable) {
- ret = do_ramdump(modemsw_ramdump_dev, modemsw_segments,
- ARRAY_SIZE(modemsw_segments));
-
- if (ret < 0) {
- pr_err("Unable to dump modem sw memory (rc = %d).\n",
- ret);
- goto out;
- }
-
- ret = do_ramdump(modemfw_ramdump_dev, modemfw_segments,
- ARRAY_SIZE(modemfw_segments));
-
- if (ret < 0) {
- pr_err("Unable to dump modem fw memory (rc = %d).\n",
- ret);
- goto out;
- }
-
- ret = do_ramdump(smem_ramdump_dev, smem_segments,
- ARRAY_SIZE(smem_segments));
-
- if (ret < 0) {
- pr_err("Unable to dump smem memory (rc = %d).\n", ret);
- goto out;
- }
- }
-
-out:
- return ret;
-}
-
-static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
-{
- switch (irq) {
-
- case Q6SW_WDOG_EXPIRED_IRQ:
- pr_err("Watchdog bite received from modem software!\n");
- restart_modem();
- break;
- case Q6FW_WDOG_EXPIRED_IRQ:
- pr_err("Watchdog bite received from modem firmware!\n");
- restart_modem();
- break;
- break;
-
- default:
- pr_err("%s: Unknown IRQ!\n", __func__);
- }
-
- return IRQ_HANDLED;
-}
-
-static struct subsys_desc modem_8960 = {
- .name = "modem",
- .shutdown = modem_shutdown,
- .powerup = modem_powerup,
- .ramdump = modem_ramdump,
- .crash_shutdown = modem_crash_shutdown
-};
-
-static int modem_subsystem_restart_init(void)
-{
- modem_8960_dev = subsys_register(&modem_8960);
- if (IS_ERR(modem_8960_dev))
- return PTR_ERR(modem_8960_dev);
- return 0;
-}
-
-static int modem_debug_set(void *data, u64 val)
-{
- if (val == 1)
- subsystem_restart_dev(modem_8960_dev);
-
- return 0;
-}
-
-static int modem_debug_get(void *data, u64 *val)
-{
- *val = 0;
- return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(modem_debug_fops, modem_debug_get, modem_debug_set,
- "%llu\n");
-
-static int modem_debugfs_init(void)
-{
- struct dentry *dent;
- dent = debugfs_create_dir("modem_debug", 0);
-
- if (IS_ERR(dent))
- return PTR_ERR(dent);
-
- debugfs_create_file("reset_modem", 0644, dent, NULL,
- &modem_debug_fops);
- return 0;
-}
-
-static int __init modem_8960_init(void)
-{
- int ret;
-
- if (cpu_is_apq8064() || cpu_is_apq8064ab())
- return -ENODEV;
-
- ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
- smsm_state_cb, 0);
-
- if (ret < 0)
- pr_err("%s: Unable to register SMSM callback! (%d)\n",
- __func__, ret);
-
- ret = request_irq(Q6FW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
- IRQF_TRIGGER_RISING, "modem_wdog_fw", NULL);
-
- if (ret < 0) {
- pr_err("%s: Unable to request q6fw watchdog IRQ. (%d)\n",
- __func__, ret);
- goto out;
- }
-
- ret = request_irq(Q6SW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
- IRQF_TRIGGER_RISING, "modem_wdog_sw", NULL);
-
- if (ret < 0) {
- pr_err("%s: Unable to request q6sw watchdog IRQ. (%d)\n",
- __func__, ret);
- disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
- goto out;
- }
-
- ret = modem_subsystem_restart_init();
-
- if (ret < 0) {
- pr_err("%s: Unable to reg with subsystem restart. (%d)\n",
- __func__, ret);
- goto out;
- }
-
- modemfw_ramdump_dev = create_ramdump_device("modem_fw");
-
- if (!modemfw_ramdump_dev) {
- pr_err("%s: Unable to create modem fw ramdump device. (%d)\n",
- __func__, -ENOMEM);
- ret = -ENOMEM;
- goto out;
- }
-
- modemsw_ramdump_dev = create_ramdump_device("modem_sw");
-
- if (!modemsw_ramdump_dev) {
- pr_err("%s: Unable to create modem sw ramdump device. (%d)\n",
- __func__, -ENOMEM);
- ret = -ENOMEM;
- goto out;
- }
-
- smem_ramdump_dev = create_ramdump_device("smem-modem");
-
- if (!smem_ramdump_dev) {
- pr_err("%s: Unable to create smem ramdump device. (%d)\n",
- __func__, -ENOMEM);
- ret = -ENOMEM;
- goto out;
- }
-
- ret = modem_debugfs_init();
-
- pr_info("%s: modem fatal driver init'ed.\n", __func__);
-out:
- return ret;
-}
-
-module_init(modem_8960_init);
diff --git a/arch/arm/mach-msm/msm-krait-l2-accessors.c b/arch/arm/mach-msm/msm-krait-l2-accessors.c
index 3da155a..2c66ea0 100644
--- a/arch/arm/mach-msm/msm-krait-l2-accessors.c
+++ b/arch/arm/mach-msm/msm-krait-l2-accessors.c
@@ -18,75 +18,12 @@
DEFINE_RAW_SPINLOCK(l2_access_lock);
-#define L2CPMR 0x500
-#define L2CPUCPMR 0x501
-#define L2CPUVRF8 0x708
-#define CPUNDX_MASK (0x7 << 12)
-
-/*
- * For Krait versions found in APQ8064v1.x, save L2CPUVRF8 before
- * L2CPMR or L2CPUCPMR writes and restore it after to work around an
- * issue where L2CPUVRF8 becomes corrupt.
- */
-static bool l2cpuvrf8_needs_fix(u32 reg_addr)
-{
- switch (read_cpuid_id()) {
- case 0x510F06F0: /* KR28M4A10 */
- case 0x510F06F1: /* KR28M4A10B */
- case 0x510F06F2: /* KR28M4A11 */
- break;
- default:
- return false;
- };
-
- switch (reg_addr & ~CPUNDX_MASK) {
- case L2CPMR:
- case L2CPUCPMR:
- return true;
- default:
- return false;
- }
-}
-
-static u32 l2cpuvrf8_fix_save(u32 reg_addr, u32 *l2cpuvrf8_val)
-{
- u32 l2cpuvrf8_addr = L2CPUVRF8 | (reg_addr & CPUNDX_MASK);
-
- mb();
- asm volatile ("mcr p15, 3, %[l2cpselr], c15, c0, 6\n\t"
- "isb\n\t"
- "mrc p15, 3, %[l2cpdr], c15, c0, 7\n\t"
- : [l2cpdr]"=r" (*l2cpuvrf8_val)
- : [l2cpselr]"r" (l2cpuvrf8_addr)
- );
-
- return l2cpuvrf8_addr;
-}
-
-static void l2cpuvrf8_fix_restore(u32 l2cpuvrf8_addr, u32 l2cpuvrf8_val)
-{
- mb();
- asm volatile ("mcr p15, 3, %[l2cpselr], c15, c0, 6\n\t"
- "isb\n\t"
- "mcr p15, 3, %[l2cpdr], c15, c0, 7\n\t"
- "isb\n\t"
- :
- : [l2cpselr]"r" (l2cpuvrf8_addr),
- [l2cpdr]"r" (l2cpuvrf8_val)
- );
-}
-
u32 set_get_l2_indirect_reg(u32 reg_addr, u32 val)
{
unsigned long flags;
- u32 uninitialized_var(l2cpuvrf8_val), l2cpuvrf8_addr = 0;
u32 ret_val;
raw_spin_lock_irqsave(&l2_access_lock, flags);
-
- if (l2cpuvrf8_needs_fix(reg_addr))
- l2cpuvrf8_addr = l2cpuvrf8_fix_save(reg_addr, &l2cpuvrf8_val);
-
mb();
asm volatile ("mcr p15, 3, %[l2cpselr], c15, c0, 6\n\t"
"isb\n\t"
@@ -96,10 +33,6 @@
: [l2cpdr_read]"=r" (ret_val)
: [l2cpselr]"r" (reg_addr), [l2cpdr]"r" (val)
);
-
- if (l2cpuvrf8_addr)
- l2cpuvrf8_fix_restore(l2cpuvrf8_addr, l2cpuvrf8_val);
-
raw_spin_unlock_irqrestore(&l2_access_lock, flags);
return ret_val;
@@ -109,13 +42,8 @@
void set_l2_indirect_reg(u32 reg_addr, u32 val)
{
unsigned long flags;
- u32 uninitialized_var(l2cpuvrf8_val), l2cpuvrf8_addr = 0;
raw_spin_lock_irqsave(&l2_access_lock, flags);
-
- if (l2cpuvrf8_needs_fix(reg_addr))
- l2cpuvrf8_addr = l2cpuvrf8_fix_save(reg_addr, &l2cpuvrf8_val);
-
mb();
asm volatile ("mcr p15, 3, %[l2cpselr], c15, c0, 6\n\t"
"isb\n\t"
@@ -124,10 +52,6 @@
:
: [l2cpselr]"r" (reg_addr), [l2cpdr]"r" (val)
);
-
- if (l2cpuvrf8_addr)
- l2cpuvrf8_fix_restore(l2cpuvrf8_addr, l2cpuvrf8_val);
-
raw_spin_unlock_irqrestore(&l2_access_lock, flags);
}
EXPORT_SYMBOL(set_l2_indirect_reg);
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
index 2072cb1..97299a0 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
@@ -485,7 +485,7 @@
};
#define M_PRIOLVL_OVERRIDE_ADDR(b, n) \
- (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000220)
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000230)
enum bimc_m_priolvl_override {
M_PRIOLVL_OVERRIDE_RMSK = 0x301,
M_PRIOLVL_OVERRIDE_BMSK = 0x300,
@@ -495,10 +495,10 @@
};
#define M_RD_CMD_OVERRIDE_ADDR(b, n) \
- (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000230)
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000240)
enum bimc_m_read_command_override {
- M_RD_CMD_OVERRIDE_RMSK = 0x37f3f,
- M_RD_CMD_OVERRIDE_AREQPRIO_BMSK = 0x300000,
+ M_RD_CMD_OVERRIDE_RMSK = 0x3071f7f,
+ M_RD_CMD_OVERRIDE_AREQPRIO_BMSK = 0x3000000,
M_RD_CMD_OVERRIDE_AREQPRIO_SHFT = 0x18,
M_RD_CMD_OVERRIDE_AMEMTYPE_BMSK = 0x70000,
M_RD_CMD_OVERRIDE_AMEMTYPE_SHFT = 0x10,
@@ -529,13 +529,15 @@
};
#define M_WR_CMD_OVERRIDE_ADDR(b, n) \
- (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000240)
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000250)
enum bimc_m_write_command_override {
- M_WR_CMD_OVERRIDE_RMSK = 0x37f3f,
- M_WR_CMD_OVERRIDE_AREQPRIO_BMSK = 0x30000,
- M_WR_CMD_OVERRIDE_AREQPRIO_SHFT = 0x10,
- M_WR_CMD_OVERRIDE_AMEMTYPE_BMSK = 0x7000,
- M_WR_CMD_OVERRIDE_AMEMTYPE_SHFT = 0xc,
+ M_WR_CMD_OVERRIDE_RMSK = 0x3071f7f,
+ M_WR_CMD_OVERRIDE_AREQPRIO_BMSK = 0x3000000,
+ M_WR_CMD_OVERRIDE_AREQPRIO_SHFT = 0x18,
+ M_WR_CMD_OVERRIDE_AMEMTYPE_BMSK = 0x70000,
+ M_WR_CMD_OVERRIDE_AMEMTYPE_SHFT = 0x10,
+ M_WR_CMD_OVERRIDE_ATRANSIENT_BMSK = 0x1000,
+ M_WR_CMD_OVERRIDE_ATRANSIENT_SHFT = 0xc,
M_WR_CMD_OVERRIDE_ASHARED_BMSK = 0x800,
M_WR_CMD_OVERRIDE_ASHARED_SHFT = 0xb,
M_WR_CMD_OVERRIDE_AREDIRECT_BMSK = 0x400,
@@ -544,8 +546,10 @@
M_WR_CMD_OVERRIDE_AOOO_SHFT = 0x9,
M_WR_CMD_OVERRIDE_AINNERSHARED_BMSK = 0x100,
M_WR_CMD_OVERRIDE_AINNERSHARED_SHFT = 0x8,
- M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK = 0x20,
- M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_SHFT = 0x5,
+ M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK = 0x40,
+ M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_SHFT = 0x6,
+ M_WR_CMD_OVERRIDE_OVERRIDE_ATRANSIENT_BMSK = 0x20,
+ M_WR_CMD_OVERRIDE_OVERRIDE_ATRANSIENT_SHFT = 0x5,
M_WR_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_BMSK = 0x10,
M_WR_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_SHFT = 0x4,
M_WR_CMD_OVERRIDE_OVERRIDE_ASHARED_BMSK = 0x8,
@@ -1454,7 +1458,7 @@
* boundary in future
*/
wmb();
- set_qos_mode(binfo->base, mas_index, 1, 1, 1);
+ set_qos_mode(binfo->base, mas_index, 0, 1, 1);
break;
case BIMC_QOS_MODE_BYPASS:
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 1b8c07e..cfd84eb 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
@@ -641,6 +641,7 @@
.mode = NOC_QOS_MODE_FIXED,
.qport = qports_crypto_c0,
.mas_hw_id = MAS_CRYPTO_CORE0,
+ .hw_sel = MSM_BUS_NOC,
},
{
.id = MSM_BUS_MASTER_CRYPTO_CORE1,
@@ -651,6 +652,7 @@
.mode = NOC_QOS_MODE_FIXED,
.qport = qports_crypto_c1,
.mas_hw_id = MAS_CRYPTO_CORE1,
+ .hw_sel = MSM_BUS_NOC,
},
{
.id = MSM_BUS_MASTER_LPASS_PROC,
@@ -719,6 +721,7 @@
.mas_hw_id = MAS_USB3,
.prio_rd = 2,
.prio_wr = 2,
+ .hw_sel = MSM_BUS_NOC,
},
{
.id = MSM_BUS_SLAVE_AMPSS,
@@ -1046,9 +1049,8 @@
.qport = qports_kmpss,
.ws = 10000,
.mas_hw_id = MAS_APPSS_PROC,
- .prio_lvl = 0,
- .prio_rd = 2,
- .prio_wr = 2,
+ .prio_rd = 1,
+ .prio_wr = 1,
},
{
.id = MSM_BUS_MASTER_AMPSS_M1,
@@ -1061,6 +1063,8 @@
.qport = qports_kmpss,
.ws = 10000,
.mas_hw_id = MAS_APPSS_PROC,
+ .prio_rd = 1,
+ .prio_wr = 1,
},
{
.id = MSM_BUS_MASTER_MSS_PROC,
diff --git a/arch/arm/mach-msm/msm_dcvs.c b/arch/arm/mach-msm/msm_dcvs.c
index 288d6bb..310197e 100644
--- a/arch/arm/mach-msm/msm_dcvs.c
+++ b/arch/arm/mach-msm/msm_dcvs.c
@@ -14,7 +14,6 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/kthread.h>
#include <linux/kobject.h>
#include <linux/ktime.h>
@@ -27,23 +26,14 @@
#include <asm/atomic.h>
#include <asm/page.h>
#include <mach/msm_dcvs.h>
+#include <trace/events/mpdcvs_trace.h>
#define CORE_HANDLE_OFFSET (0xA0)
#define __err(f, ...) pr_err("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
#define __info(f, ...) pr_info("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
#define MAX_PENDING (5)
-enum {
- MSM_DCVS_DEBUG_NOTIFIER = BIT(0),
- MSM_DCVS_DEBUG_IDLE_PULSE = BIT(1),
- MSM_DCVS_DEBUG_FREQ_CHANGE = BIT(2),
-};
-
struct core_attribs {
- struct kobj_attribute core_id;
- struct kobj_attribute idle_enabled;
- struct kobj_attribute freq_change_enabled;
- struct kobj_attribute actual_freq;
struct kobj_attribute freq_change_us;
struct kobj_attribute disable_pc_threshold;
@@ -69,10 +59,46 @@
struct kobj_attribute leakage_coeff_c;
struct kobj_attribute leakage_coeff_d;
+ struct kobj_attribute thermal_poll_ms;
+
struct attribute_group attrib_group;
};
+enum pending_freq_state {
+ /*
+ * used by the thread to check if pending_freq was updated while it was
+ * setting previous frequency - this is written to and used by the
+ * freq updating thread
+ */
+ NO_OUTSTANDING_FREQ_CHANGE = 0,
+
+ /*
+ * This request is set to indicate that the governor is stopped and no
+ * more frequency change requests are accepted untill it starts again.
+ * This is checked/used by the threads that want to change the freq
+ */
+ STOP_FREQ_CHANGE = -1,
+
+ /*
+ * Any other +ve value means that a freq change was requested and the
+ * thread has not gotten around to update it
+ *
+ * Any other -ve value means that this is the last freq change i.e. a
+ * freq change was requested but the thread has not run yet and
+ * meanwhile the governor was stopped.
+ */
+};
+
struct dcvs_core {
+ spinlock_t idle_state_change_lock;
+ /* 0 when not idle (busy) 1 when idle and -1 when governor starts and
+ * we dont know whether the next call is going to be idle enter or exit
+ */
+ int idle_entered;
+
+ enum msm_dcvs_core_type type;
+ /* this is the number in each type for example cpu 0,1,2 and gpu 0,1 */
+ int type_core_num;
char core_name[CORE_NAME_MAX];
uint32_t actual_freq;
uint32_t freq_change_us;
@@ -81,112 +107,200 @@
struct msm_dcvs_algo_param algo_param;
struct msm_dcvs_energy_curve_coeffs coeffs;
- struct msm_dcvs_idle *idle_driver;
- struct msm_dcvs_freq *freq_driver;
/* private */
- int64_t time_start;
- struct mutex lock;
- spinlock_t cpu_lock;
+ ktime_t time_start;
struct task_struct *task;
struct core_attribs attrib;
- uint32_t handle;
- struct hrtimer timer;
- int32_t timer_disabled;
+ uint32_t dcvs_core_id;
struct msm_dcvs_core_info *info;
int sensor;
- int pending_freq;
wait_queue_head_t wait_q;
- int (*set_frequency)(struct msm_dcvs_freq *self, unsigned int freq);
- unsigned int (*get_frequency)(struct msm_dcvs_freq *self);
+
+ int (*set_frequency)(int type_core_num, unsigned int freq);
+ unsigned int (*get_frequency)(int type_core_num);
+ int (*idle_enable)(int type_core_num,
+ enum msm_core_control_event event);
+
+ spinlock_t pending_freq_lock;
+ int pending_freq;
+
+ struct hrtimer slack_timer;
+ struct delayed_work temperature_work;
};
-static int msm_dcvs_debug;
static int msm_dcvs_enabled = 1;
module_param_named(enable, msm_dcvs_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP);
static struct dentry *debugfs_base;
static struct dcvs_core core_list[CORES_MAX];
-static DEFINE_MUTEX(core_list_lock);
static struct kobject *cores_kobj;
-static struct dcvs_core *core_handles[CORES_MAX];
-/* Change core frequency, called with core mutex locked */
+static void force_stop_slack_timer(struct dcvs_core *core)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&core->idle_state_change_lock, flags);
+ hrtimer_cancel(&core->slack_timer);
+ spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
+}
+
+static void force_start_slack_timer(struct dcvs_core *core, int slack_us)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&core->idle_state_change_lock, flags);
+
+ /*
+ * only start the timer if governor is not stopped
+ */
+ if (slack_us != 0) {
+ ret = hrtimer_start(&core->slack_timer,
+ ktime_set(0, slack_us * 1000),
+ HRTIMER_MODE_REL_PINNED);
+ if (ret) {
+ pr_err("%s Failed to start timer ret = %d\n",
+ core->core_name, ret);
+ }
+ }
+
+ spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
+}
+
+static void stop_slack_timer(struct dcvs_core *core)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&core->idle_state_change_lock, flags);
+ /* err only for cpu type's GPU's can do idle exit consecutively */
+ if (core->idle_entered == 1 && !(core->dcvs_core_id >= GPU_OFFSET))
+ __err("%s trying to reenter idle", core->core_name);
+ core->idle_entered = 1;
+ hrtimer_cancel(&core->slack_timer);
+ core->idle_entered = 1;
+ spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
+}
+
+static void start_slack_timer(struct dcvs_core *core, int slack_us)
+{
+ unsigned long flags1, flags2;
+ int ret;
+
+ spin_lock_irqsave(&core->idle_state_change_lock, flags2);
+
+ spin_lock_irqsave(&core->pending_freq_lock, flags1);
+
+ /* err only for cpu type's GPU's can do idle enter consecutively */
+ if (core->idle_entered == 0 && !(core->dcvs_core_id >= GPU_OFFSET))
+ __err("%s trying to reexit idle", core->core_name);
+ core->idle_entered = 0;
+ /*
+ * only start the timer if governor is not stopped
+ */
+ if (slack_us != 0
+ && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
+ ret = hrtimer_start(&core->slack_timer,
+ ktime_set(0, slack_us * 1000),
+ HRTIMER_MODE_REL_PINNED);
+ if (ret) {
+ pr_err("%s Failed to start timer ret = %d\n",
+ core->core_name, ret);
+ }
+ }
+ spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
+
+ spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
+}
+
+static void restart_slack_timer(struct dcvs_core *core, int slack_us)
+{
+ unsigned long flags1, flags2;
+ int ret;
+
+ spin_lock_irqsave(&core->idle_state_change_lock, flags2);
+
+ hrtimer_cancel(&core->slack_timer);
+
+ spin_lock_irqsave(&core->pending_freq_lock, flags1);
+
+ /*
+ * only start the timer if idle is not entered
+ * and governor is not stopped
+ */
+ if (slack_us != 0 && (core->idle_entered != 1)
+ && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
+ ret = hrtimer_start(&core->slack_timer,
+ ktime_set(0, slack_us * 1000),
+ HRTIMER_MODE_REL_PINNED);
+ if (ret) {
+ pr_err("%s Failed to start timer ret = %d\n",
+ core->core_name, ret);
+ }
+ }
+ spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
+ spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
+}
+
static int __msm_dcvs_change_freq(struct dcvs_core *core)
{
int ret = 0;
unsigned long flags = 0;
- unsigned int requested_freq = 0;
- unsigned int prev_freq = 0;
- int64_t time_start = 0;
- int64_t time_end = 0;
+ int requested_freq = 0;
+ ktime_t time_start;
uint32_t slack_us = 0;
uint32_t ret1 = 0;
- if (!core->freq_driver || !core->set_frequency) {
- /* Core may have unregistered or hotplugged */
- return -ENODEV;
- }
- spin_lock_irqsave(&core->cpu_lock, flags);
+ spin_lock_irqsave(&core->pending_freq_lock, flags);
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 = 0;
- /**
- * Cancel the timers, we dont want the timer firing as we are
- * changing the clock rate. Dont let idle_exit and others setup
- * timers as well.
- */
- hrtimer_cancel(&core->timer);
- core->timer_disabled = 1;
+ 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;
+ }
+
if (requested_freq == core->actual_freq)
goto out;
- spin_unlock_irqrestore(&core->cpu_lock, flags);
-
+ spin_unlock_irqrestore(&core->pending_freq_lock, flags);
/**
* Call the frequency sink driver to change the frequency
* We will need to get back the actual frequency in KHz and
* the record the time taken to change it.
*/
- ret = core->set_frequency(core->freq_driver, requested_freq);
- if (ret <= 0) {
+ ret = core->set_frequency(core->type_core_num, requested_freq);
+ if (ret <= 0)
__err("Core %s failed to set freq %u\n",
core->core_name, requested_freq);
/* continue to call TZ to get updated slack timer */
- } else {
- prev_freq = core->actual_freq;
+ else
core->actual_freq = ret;
- }
- time_end = ktime_to_ns(ktime_get());
- if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
- __info("Core %s Time end %llu Time start: %llu\n",
- core->core_name, time_end, time_start);
- time_end -= time_start;
- do_div(time_end, NSEC_PER_USEC);
- core->freq_change_us = (uint32_t)time_end;
+ core->freq_change_us = (uint32_t)ktime_to_us(
+ ktime_sub(ktime_get(), time_start));
/**
* Disable low power modes if the actual frequency is >
* disable_pc_threshold.
*/
- if (core->actual_freq >
- core->algo_param.disable_pc_threshold) {
- core->idle_driver->enable(core->idle_driver,
+ if (core->actual_freq > core->algo_param.disable_pc_threshold) {
+ core->idle_enable(core->type_core_num,
MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
- if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
- __info("Disabling LPM for %s\n", core->core_name);
- } else if (core->actual_freq <=
- core->algo_param.disable_pc_threshold) {
- core->idle_driver->enable(core->idle_driver,
+ } else if (core->actual_freq <= core->algo_param.disable_pc_threshold) {
+ core->idle_enable(core->type_core_num,
MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
- if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
- __info("Enabling LPM for %s\n", core->core_name);
}
/**
@@ -194,66 +308,75 @@
* to this frequency and that will get us the new slack
* timer
*/
- ret = msm_dcvs_scm_event(core->handle, MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
- core->actual_freq, (uint32_t)time_end, &slack_us, &ret1);
- if (!ret) {
- /* Reset the slack timer */
- if (slack_us) {
- core->timer_disabled = 0;
- ret = hrtimer_start(&core->timer,
- ktime_set(0, slack_us * 1000),
- HRTIMER_MODE_REL_PINNED);
- if (ret)
- __err("Failed to register timer for core %s\n",
- core->core_name);
- }
- } else {
- __err("Error sending core (%s) freq change (%u)\n",
- core->core_name, core->actual_freq);
+ ret = msm_dcvs_scm_event(core->dcvs_core_id,
+ MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
+ core->actual_freq, core->freq_change_us,
+ &slack_us, &ret1);
+ if (ret) {
+ __err("Error sending core (%s) dcvs_core_id = %d freq change (%u) reqfreq = %d slack_us=%d ret = %d\n",
+ core->core_name, core->dcvs_core_id,
+ core->actual_freq, requested_freq,
+ slack_us, ret);
}
- if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
- __info("Freq %u requested for core %s (actual %u prev %u) "
- "change time %u us slack time %u us\n",
- requested_freq, core->core_name,
- core->actual_freq, prev_freq,
- core->freq_change_us, slack_us);
+ /* TODO confirm that we get a valid freq from SM even when the above
+ * FREQ_UPDATE fails
+ */
+ restart_slack_timer(core, slack_us);
+ spin_lock_irqsave(&core->pending_freq_lock, flags);
- spin_lock_irqsave(&core->cpu_lock, flags);
/**
* By the time we are done with freq changes, we could be asked to
* change again. Check before exiting.
*/
- if (core->pending_freq)
+ if (core->pending_freq != NO_OUTSTANDING_FREQ_CHANGE
+ && core->pending_freq != STOP_FREQ_CHANGE) {
goto repeat;
+ }
-
-out: /* should always be jumped to with the spin_lock held */
- core->pending_freq = 0;
- spin_unlock_irqrestore(&core->cpu_lock, flags);
+out: /* should always be jumped to with the spin_lock held */
+ spin_unlock_irqrestore(&core->pending_freq_lock, flags);
return ret;
}
-static int __msm_dcvs_report_temp(struct dcvs_core *core)
+static void msm_dcvs_report_temp_work(struct work_struct *work)
{
+ struct dcvs_core *core = container_of(work,
+ struct dcvs_core,
+ temperature_work.work);
struct msm_dcvs_core_info *info = core->info;
struct tsens_device tsens_dev;
int ret;
unsigned long temp = 0;
+ int interval_ms;
tsens_dev.sensor_num = core->sensor;
ret = tsens_get_temp(&tsens_dev, &temp);
- if (!ret) {
+ if (!temp) {
tsens_dev.sensor_num = 0;
ret = tsens_get_temp(&tsens_dev, &temp);
- if (!ret)
- return -ENODEV;
+ if (!temp)
+ goto out;
}
- ret = msm_dcvs_scm_set_power_params(core->handle, &info->power_param,
+ if (temp == info->power_param.current_temp)
+ goto out;
+ info->power_param.current_temp = temp;
+
+ ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
+ &info->power_param,
&info->freq_tbl[0], &core->coeffs);
- return ret;
+out:
+ if (info->thermal_poll_ms == 0)
+ interval_ms = 60000;
+ else if (info->thermal_poll_ms < 1000)
+ interval_ms = 1000;
+ else
+ interval_ms = info->thermal_poll_ms;
+
+ schedule_delayed_work(&core->temperature_work,
+ msecs_to_jiffies(interval_ms));
}
static int msm_dcvs_do_freq(void *data)
@@ -271,25 +394,57 @@
if (kthread_should_stop())
break;
- mutex_lock(&core->lock);
__msm_dcvs_change_freq(core);
- __msm_dcvs_report_temp(core);
- mutex_unlock(&core->lock);
}
return 0;
}
+/* freq_pending_lock should be held */
+static void request_freq_change(struct dcvs_core *core, int new_freq)
+{
+ if (new_freq == NO_OUTSTANDING_FREQ_CHANGE) {
+ if (core->pending_freq != STOP_FREQ_CHANGE) {
+ __err("%s gov started with earlier pending freq %d\n",
+ core->core_name, core->pending_freq);
+ }
+ core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
+ return;
+ }
+
+ 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;
+ return;
+ }
+
+ if (core->pending_freq < 0) {
+ /* a value less than 0 means that the governor has stopped
+ * and no more freq changes should be requested
+ */
+ return;
+ }
+
+ if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
+ core->pending_freq = new_freq;
+ core->time_start = ktime_get();
+ wake_up(&core->wait_q);
+ }
+}
+
static int msm_dcvs_update_freq(struct dcvs_core *core,
enum msm_dcvs_scm_event event, uint32_t param0,
- uint32_t *ret1, int *freq_changed)
+ uint32_t *ret1)
{
int ret = 0;
unsigned long flags = 0;
- uint32_t new_freq = 0;
+ uint32_t new_freq = -EINVAL;
- spin_lock_irqsave(&core->cpu_lock, flags);
- ret = msm_dcvs_scm_event(core->handle, event, param0,
+ spin_lock_irqsave(&core->pending_freq_lock, flags);
+
+ ret = msm_dcvs_scm_event(core->dcvs_core_id, event, param0,
core->actual_freq, &new_freq, ret1);
if (ret) {
if (ret == -13)
@@ -300,18 +455,18 @@
goto out;
}
- if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
- core->pending_freq = new_freq;
- core->time_start = ktime_to_ns(ktime_get());
-
- if (core->task)
- wake_up(&core->wait_q);
- } else {
- if (freq_changed)
- *freq_changed = 0;
+ if (new_freq == 0) {
+ /*
+ * sometimes TZ gives us a 0 freq back,
+ * do not queue up a request
+ */
+ goto out;
}
+
+ request_freq_change(core, new_freq);
+
out:
- spin_unlock_irqrestore(&core->cpu_lock, flags);
+ spin_unlock_irqrestore(&core->pending_freq_lock, flags);
return ret;
}
@@ -319,19 +474,17 @@
static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
{
int ret = 0;
- struct dcvs_core *core = container_of(timer, struct dcvs_core, timer);
+ struct dcvs_core *core = container_of(timer,
+ struct dcvs_core, slack_timer);
uint32_t ret1;
- uint32_t ret2;
- if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
- __info("Slack timer fired for core %s\n", core->core_name);
-
+ trace_printk("dcvs: Slack timer fired for core=%s\n", core->core_name);
/**
* Timer expired, notify TZ
* Dont care about the third arg.
*/
ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
- &ret1, &ret2);
+ &ret1);
if (ret)
__err("Timer expired for core %s but failed to notify.\n",
core->core_name);
@@ -352,6 +505,28 @@
return snprintf(buf, PAGE_SIZE, "%d\n", v); \
}
+#define DCVS_PARAM_STORE(_name) \
+static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
+ struct kobj_attribute *attr, char *buf) \
+{ \
+ struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
+ return snprintf(buf, PAGE_SIZE, "%d\n", core->info->_name); \
+} \
+static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
+ struct kobj_attribute *attr, const char *buf, size_t count) \
+{ \
+ int ret = 0; \
+ uint32_t val = 0; \
+ struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
+ ret = kstrtouint(buf, 10, &val); \
+ if (ret) { \
+ __err("Invalid input %s for %s\n", buf, __stringify(_name));\
+ } else { \
+ core->info->_name = val; \
+ } \
+ return count; \
+}
+
#define DCVS_ALGO_PARAM(_name) \
static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
struct kobj_attribute *attr, char *buf) \
@@ -365,14 +540,13 @@
int ret = 0; \
uint32_t val = 0; \
struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
- mutex_lock(&core->lock); \
ret = kstrtouint(buf, 10, &val); \
if (ret) { \
__err("Invalid input %s for %s\n", buf, __stringify(_name));\
} else { \
uint32_t old_val = core->algo_param._name; \
core->algo_param._name = val; \
- ret = msm_dcvs_scm_set_algo_params(core->handle, \
+ ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id, \
&core->algo_param); \
if (ret) { \
core->algo_param._name = old_val; \
@@ -380,7 +554,6 @@
ret, val, __stringify(_name)); \
} \
} \
- mutex_unlock(&core->lock); \
return count; \
}
@@ -397,14 +570,13 @@
int ret = 0; \
int32_t val = 0; \
struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
- mutex_lock(&core->lock); \
ret = kstrtoint(buf, 10, &val); \
if (ret) { \
__err("Invalid input %s for %s\n", buf, __stringify(_name));\
} else { \
int32_t old_val = core->coeffs._name; \
core->coeffs._name = val; \
- ret = msm_dcvs_scm_set_power_params(core->handle, \
+ ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id, \
&core->info->power_param, &core->info->freq_tbl[0], \
&core->coeffs); \
if (ret) { \
@@ -413,7 +585,6 @@
ret, val, __stringify(_name)); \
} \
} \
- mutex_unlock(&core->lock); \
return count; \
}
@@ -435,10 +606,6 @@
* Function declarations for different attributes.
* Gets used when setting the attribute show and store parameters.
*/
-DCVS_PARAM_SHOW(core_id, core->handle)
-DCVS_PARAM_SHOW(idle_enabled, (core->idle_driver != NULL))
-DCVS_PARAM_SHOW(freq_change_enabled, (core->freq_driver != NULL))
-DCVS_PARAM_SHOW(actual_freq, (core->actual_freq))
DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
DCVS_ALGO_PARAM(disable_pc_threshold)
@@ -464,11 +631,13 @@
DCVS_ENERGY_PARAM(leakage_coeff_c)
DCVS_ENERGY_PARAM(leakage_coeff_d)
+DCVS_PARAM_STORE(thermal_poll_ms)
+
static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
{
int ret = 0;
struct kobject *core_kobj = NULL;
- const int attr_count = 27;
+ const int attr_count = 24;
BUG_ON(!cores_kobj);
@@ -480,37 +649,33 @@
goto done;
}
+ DCVS_RO_ATTRIB(0, freq_change_us);
- DCVS_RO_ATTRIB(0, core_id);
- DCVS_RO_ATTRIB(1, idle_enabled);
- DCVS_RO_ATTRIB(2, freq_change_enabled);
- DCVS_RO_ATTRIB(3, actual_freq);
- DCVS_RO_ATTRIB(4, freq_change_us);
+ DCVS_RW_ATTRIB(1, disable_pc_threshold);
+ DCVS_RW_ATTRIB(2, em_win_size_min_us);
+ DCVS_RW_ATTRIB(3, em_win_size_max_us);
+ DCVS_RW_ATTRIB(4, em_max_util_pct);
+ DCVS_RW_ATTRIB(5, group_id);
+ DCVS_RW_ATTRIB(6, max_freq_chg_time_us);
+ DCVS_RW_ATTRIB(7, slack_mode_dynamic);
+ DCVS_RW_ATTRIB(8, slack_weight_thresh_pct);
+ DCVS_RW_ATTRIB(9, slack_time_min_us);
+ DCVS_RW_ATTRIB(10, slack_time_max_us);
+ DCVS_RW_ATTRIB(11, ss_iobusy_conv);
+ DCVS_RW_ATTRIB(12, ss_win_size_min_us);
+ DCVS_RW_ATTRIB(13, ss_win_size_max_us);
+ DCVS_RW_ATTRIB(14, ss_util_pct);
- DCVS_RW_ATTRIB(5, disable_pc_threshold);
- DCVS_RW_ATTRIB(6, em_win_size_min_us);
- DCVS_RW_ATTRIB(7, em_win_size_max_us);
- DCVS_RW_ATTRIB(8, em_max_util_pct);
- DCVS_RW_ATTRIB(9, group_id);
- DCVS_RW_ATTRIB(10, max_freq_chg_time_us);
- DCVS_RW_ATTRIB(11, slack_mode_dynamic);
- DCVS_RW_ATTRIB(12, slack_time_min_us);
- DCVS_RW_ATTRIB(13, slack_time_max_us);
- DCVS_RW_ATTRIB(14, slack_weight_thresh_pct);
- DCVS_RW_ATTRIB(15, ss_iobusy_conv);
- DCVS_RW_ATTRIB(16, ss_win_size_min_us);
- DCVS_RW_ATTRIB(17, ss_win_size_max_us);
- DCVS_RW_ATTRIB(18, ss_util_pct);
+ DCVS_RW_ATTRIB(15, active_coeff_a);
+ DCVS_RW_ATTRIB(16, active_coeff_b);
+ DCVS_RW_ATTRIB(17, active_coeff_c);
+ DCVS_RW_ATTRIB(18, leakage_coeff_a);
+ DCVS_RW_ATTRIB(19, leakage_coeff_b);
+ DCVS_RW_ATTRIB(20, leakage_coeff_c);
+ DCVS_RW_ATTRIB(21, leakage_coeff_d);
+ DCVS_RW_ATTRIB(22, thermal_poll_ms);
- DCVS_RW_ATTRIB(19, active_coeff_a);
- DCVS_RW_ATTRIB(20, active_coeff_b);
- DCVS_RW_ATTRIB(21, active_coeff_c);
- DCVS_RW_ATTRIB(22, leakage_coeff_a);
- DCVS_RW_ATTRIB(23, leakage_coeff_b);
- DCVS_RW_ATTRIB(24, leakage_coeff_c);
- DCVS_RW_ATTRIB(25, leakage_coeff_d);
-
- core->attrib.attrib_group.attrs[26] = NULL;
+ core->attrib.attrib_group.attrs[23] = NULL;
core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
if (!core_kobj) {
@@ -521,8 +686,6 @@
ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
if (ret)
__err("Cannot create core %s attr group\n", core->core_name);
- else if (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER)
- __info("Setting up attributes for core %s\n", core->core_name);
done:
if (ret) {
@@ -533,71 +696,95 @@
return ret;
}
-/* Return the core if found or add to list if @add_to_list is true */
-static struct dcvs_core *msm_dcvs_get_core(const char *name, int add_to_list)
+static int get_core_offset(enum msm_dcvs_core_type type, int num)
+{
+ int offset = -EINVAL;
+
+ switch (type) {
+ case MSM_DCVS_CORE_TYPE_CPU:
+ offset = CPU_OFFSET + num;
+ BUG_ON(offset >= GPU_OFFSET);
+ break;
+ case MSM_DCVS_CORE_TYPE_GPU:
+ offset = GPU_OFFSET + num;
+ BUG_ON(offset >= CORES_MAX);
+ break;
+ default:
+ BUG();
+ }
+
+ return offset;
+}
+
+/* Return the core and initialize non platform data specific numbers in it */
+static struct dcvs_core *msm_dcvs_add_core(enum msm_dcvs_core_type type,
+ int num)
{
struct dcvs_core *core = NULL;
int i;
- int empty = -1;
+ char name[CORE_NAME_MAX];
- if (!name[0] ||
- (strnlen(name, CORE_NAME_MAX - 1) == CORE_NAME_MAX - 1))
- return core;
-
- mutex_lock(&core_list_lock);
- for (i = 0; i < CORES_MAX; i++) {
- core = &core_list[i];
- if ((empty < 0) && !core->core_name[0]) {
- empty = i;
- continue;
- }
- if (!strncmp(name, core->core_name, CORE_NAME_MAX))
- break;
- }
-
- /* Check for core_list full */
- if ((i == CORES_MAX) && (empty < 0)) {
- mutex_unlock(&core_list_lock);
+ i = get_core_offset(type, num);
+ if (i < 0)
return NULL;
- }
- if (i == CORES_MAX && add_to_list) {
- core = &core_list[empty];
- strlcpy(core->core_name, name, CORE_NAME_MAX);
- mutex_init(&core->lock);
- spin_lock_init(&core->cpu_lock);
- core->handle = empty + CORE_HANDLE_OFFSET;
- hrtimer_init(&core->timer,
- CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
- core->timer.function = msm_dcvs_core_slack_timer;
- }
- mutex_unlock(&core_list_lock);
+ if (type == MSM_DCVS_CORE_TYPE_CPU)
+ snprintf(name, CORE_NAME_MAX, "cpu%d", num);
+ else
+ snprintf(name, CORE_NAME_MAX, "gpu%d", num);
+ core = &core_list[i];
+ core->dcvs_core_id = i;
+ strlcpy(core->core_name, name, CORE_NAME_MAX);
+ spin_lock_init(&core->pending_freq_lock);
+ spin_lock_init(&core->idle_state_change_lock);
+ hrtimer_init(&core->slack_timer,
+ CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
+ core->slack_timer.function = msm_dcvs_core_slack_timer;
return core;
}
-int msm_dcvs_register_core(const char *core_name,
+/* Return the core if found or add to list if @add_to_list is true */
+static struct dcvs_core *msm_dcvs_get_core(int offset)
+{
+ /* if the handle is still not set bug */
+ BUG_ON(core_list[offset].dcvs_core_id == -1);
+ return &core_list[offset];
+}
+
+
+int msm_dcvs_register_core(
+ enum msm_dcvs_core_type type,
+ int type_core_num,
struct msm_dcvs_core_info *info,
- int (*set_frequency)(struct msm_dcvs_freq *self, unsigned int freq),
- unsigned int (*get_frequency)(struct msm_dcvs_freq *self),
+ int (*set_frequency)(int type_core_num, unsigned int freq),
+ unsigned int (*get_frequency)(int type_core_num),
+ int (*idle_enable)(int type_core_num,
+ enum msm_core_control_event event),
int sensor)
{
int ret = -EINVAL;
+ int offset;
struct dcvs_core *core = NULL;
uint32_t ret1;
uint32_t ret2;
- if (!core_name || !core_name[0])
+ offset = get_core_offset(type, type_core_num);
+ if (offset < 0)
return ret;
+ if (core_list[offset].dcvs_core_id != -1)
+ return core_list[offset].dcvs_core_id;
- core = msm_dcvs_get_core(core_name, true);
+ core = msm_dcvs_add_core(type, type_core_num);
if (!core)
return ret;
- mutex_lock(&core->lock);
-
+ core->type = type;
+ core->type_core_num = type_core_num;
core->set_frequency = set_frequency;
core->get_frequency = get_frequency;
+ core->idle_enable = idle_enable;
+ core->pending_freq = STOP_FREQ_CHANGE;
core->info = info;
memcpy(&core->algo_param, &info->algo_param,
@@ -606,23 +793,39 @@
memcpy(&core->coeffs, &info->energy_coeffs,
sizeof(struct msm_dcvs_energy_curve_coeffs));
- pr_debug("registering core with sensor %d\n", sensor);
+ /*
+ * The tz expects cpu0 to represent bit 0 in the mask, however the
+ * dcvs_core_id needs to start from 1, dcvs_core_id = 0 is used to
+ * indicate that this request is not associated with any core.
+ * mpdecision
+ */
+ info->core_param.core_bitmask_id
+ = 1 << (core->dcvs_core_id - CPU_OFFSET);
core->sensor = sensor;
- ret = msm_dcvs_scm_register_core(core->handle, &info->core_param);
- if (ret)
+ ret = msm_dcvs_scm_register_core(core->dcvs_core_id, &info->core_param);
+ if (ret) {
+ __err("%s: scm register core fail handle = %d ret = %d\n",
+ __func__, core->dcvs_core_id, ret);
goto bail;
+ }
- ret = msm_dcvs_scm_set_algo_params(core->handle, &info->algo_param);
- if (ret)
+ ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id,
+ &info->algo_param);
+ if (ret) {
+ __err("%s: scm algo params failed ret = %d\n", __func__, ret);
goto bail;
+ }
- ret = msm_dcvs_scm_set_power_params(core->handle, &info->power_param,
+ ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
+ &info->power_param,
&info->freq_tbl[0], &core->coeffs);
- if (ret)
+ if (ret) {
+ __err("%s: scm power params failed ret = %d\n", __func__, ret);
goto bail;
+ }
- ret = msm_dcvs_scm_event(core->handle, MSM_DCVS_SCM_CORE_ONLINE,
+ ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE,
core->actual_freq, 0, &ret1, &ret2);
if (ret)
goto bail;
@@ -630,196 +833,164 @@
ret = msm_dcvs_setup_core_sysfs(core);
if (ret) {
__err("Unable to setup core %s sysfs\n", core->core_name);
- core_handles[core->handle - CORE_HANDLE_OFFSET] = NULL;
goto bail;
}
+ core->idle_entered = -1;
init_waitqueue_head(&core->wait_q);
core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
- "msm_dcvs/%d", core->handle);
-bail:
- mutex_unlock(&core->lock);
+ "msm_dcvs/%d", core->dcvs_core_id);
+ ret = core->dcvs_core_id;
+
+ INIT_DELAYED_WORK(&core->temperature_work, msm_dcvs_report_temp_work);
+ schedule_delayed_work(&core->temperature_work,
+ msecs_to_jiffies(info->thermal_poll_ms));
return ret;
+bail:
+ core->dcvs_core_id = -1;
+ return -EINVAL;
}
EXPORT_SYMBOL(msm_dcvs_register_core);
-void msm_dcvs_update_limits(struct msm_dcvs_freq *drv)
+void msm_dcvs_update_limits(int dcvs_core_id)
{
struct dcvs_core *core;
- if (!drv || !drv->core_name)
+ if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
+ __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
+ __func__, dcvs_core_id);
return;
+ }
- core = msm_dcvs_get_core(drv->core_name, false);
- core->actual_freq = core->get_frequency(drv);
+ core = msm_dcvs_get_core(dcvs_core_id);
+ core->actual_freq = core->get_frequency(core->type_core_num);
}
-int msm_dcvs_freq_sink_start(struct msm_dcvs_freq *drv)
+int msm_dcvs_freq_sink_start(int dcvs_core_id)
{
int ret = -EINVAL;
struct dcvs_core *core = NULL;
uint32_t ret1;
- uint32_t ret2;
+ unsigned long flags;
+ int new_freq;
+ int timer_interval_us;
- if (!drv || !drv->core_name)
- return ret;
+ if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
+ __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
+ __func__, dcvs_core_id);
+ return -EINVAL;
+ }
- core = msm_dcvs_get_core(drv->core_name, true);
+ core = msm_dcvs_get_core(dcvs_core_id);
if (!core)
return ret;
- mutex_lock(&core->lock);
- if (core->freq_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER))
- __info("Frequency notifier for %s being replaced\n",
- core->core_name);
- core->freq_driver = drv;
- if (IS_ERR(core->task)) {
- mutex_unlock(&core->lock);
- return -EFAULT;
+ core->actual_freq = core->get_frequency(core->type_core_num);
+
+ spin_lock_irqsave(&core->pending_freq_lock, flags);
+ /* mark that we are ready to accept new frequencies */
+ request_freq_change(core, NO_OUTSTANDING_FREQ_CHANGE);
+ spin_unlock_irqrestore(&core->pending_freq_lock, flags);
+
+ spin_lock_irqsave(&core->idle_state_change_lock, flags);
+ core->idle_entered = -1;
+ spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
+
+ /* Notify TZ to start receiving idle info for the core */
+ ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1, &ret1);
+
+ ret = msm_dcvs_scm_event(
+ core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE, core->actual_freq,
+ 0, &new_freq, &timer_interval_us);
+ if (ret)
+ __err("Error (%d) DCVS sending online for %s\n",
+ ret, core->core_name);
+
+ if (new_freq != 0) {
+ spin_lock_irqsave(&core->pending_freq_lock, flags);
+ request_freq_change(core, new_freq);
+ spin_unlock_irqrestore(&core->pending_freq_lock, flags);
}
+ force_start_slack_timer(core, timer_interval_us);
- if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
- __info("Enabling idle pulse for %s\n", core->core_name);
- if (core->idle_driver) {
- core->actual_freq = core->get_frequency(drv);
- /* Notify TZ to start receiving idle info for the core */
- ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1,
- &ret1, &ret2);
- core->idle_driver->enable(core->idle_driver,
- MSM_DCVS_ENABLE_IDLE_PULSE);
- }
-
- mutex_unlock(&core->lock);
-
- return core->handle;
+ core->idle_enable(core->type_core_num, MSM_DCVS_ENABLE_IDLE_PULSE);
+ return 0;
}
EXPORT_SYMBOL(msm_dcvs_freq_sink_start);
-int msm_dcvs_freq_sink_stop(struct msm_dcvs_freq *drv)
+int msm_dcvs_freq_sink_stop(int dcvs_core_id)
{
int ret = -EINVAL;
struct dcvs_core *core = NULL;
uint32_t ret1;
- uint32_t ret2;
+ uint32_t freq;
+ unsigned long flags;
- if (!drv || !drv->core_name)
- return ret;
-
- core = msm_dcvs_get_core(drv->core_name, false);
- if (!core)
- return ret;
-
- mutex_lock(&core->lock);
- if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
- __info("Disabling idle pulse for %s\n", core->core_name);
- if (core->idle_driver) {
- core->idle_driver->enable(core->idle_driver,
- MSM_DCVS_DISABLE_IDLE_PULSE);
- /* Notify TZ to stop receiving idle info for the core */
- ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 0,
- &ret1, &ret2);
- hrtimer_cancel(&core->timer);
- core->idle_driver->enable(core->idle_driver,
- MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
- if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
- __info("Enabling LPM for %s\n", core->core_name);
+ if (dcvs_core_id < 0 || dcvs_core_id > CORES_MAX) {
+ pr_err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
+ __func__, dcvs_core_id);
+ return -EINVAL;
}
- core->freq_driver = NULL;
- mutex_unlock(&core->lock);
+
+ core = msm_dcvs_get_core(dcvs_core_id);
+ if (!core) {
+ __err("couldn't find core for coreid = %d\n", dcvs_core_id);
+ return ret;
+ }
+
+ core->idle_enable(core->type_core_num, MSM_DCVS_DISABLE_IDLE_PULSE);
+ /* Notify TZ to stop receiving idle info for the core */
+ ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_DCVS_ENABLE,
+ 0, core->actual_freq, &freq, &ret1);
+ core->idle_enable(core->type_core_num,
+ MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
+ 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);
+ force_stop_slack_timer(core);
return 0;
}
EXPORT_SYMBOL(msm_dcvs_freq_sink_stop);
-int msm_dcvs_idle_source_register(struct msm_dcvs_idle *drv)
-{
- int ret = -EINVAL;
- struct dcvs_core *core = NULL;
-
- if (!drv || !drv->core_name)
- return ret;
-
- core = msm_dcvs_get_core(drv->core_name, true);
- if (!core)
- return ret;
-
- mutex_lock(&core->lock);
- if (core->idle_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER))
- __info("Idle notifier for %s being replaced\n",
- core->core_name);
- core->idle_driver = drv;
- mutex_unlock(&core->lock);
-
- return core->handle;
-}
-EXPORT_SYMBOL(msm_dcvs_idle_source_register);
-
-int msm_dcvs_idle_source_unregister(struct msm_dcvs_idle *drv)
-{
- int ret = -EINVAL;
- struct dcvs_core *core = NULL;
-
- if (!drv || !drv->core_name)
- return ret;
-
- core = msm_dcvs_get_core(drv->core_name, false);
- if (!core)
- return ret;
-
- mutex_lock(&core->lock);
- core->idle_driver = NULL;
- mutex_unlock(&core->lock);
-
- return 0;
-}
-EXPORT_SYMBOL(msm_dcvs_idle_source_unregister);
-
-int msm_dcvs_idle(int handle, enum msm_core_idle_state state, uint32_t iowaited)
+int msm_dcvs_idle(int dcvs_core_id, enum msm_core_idle_state state,
+ uint32_t iowaited)
{
int ret = 0;
struct dcvs_core *core = NULL;
uint32_t timer_interval_us = 0;
uint32_t r0, r1;
- uint32_t freq_changed = 0;
- if (handle >= CORE_HANDLE_OFFSET &&
- (handle - CORE_HANDLE_OFFSET) < CORES_MAX)
- core = &core_list[handle - CORE_HANDLE_OFFSET];
+ if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
+ pr_err("invalid dcvs_core_id = %d ret -EINVAL\n", dcvs_core_id);
+ return -EINVAL;
+ }
- BUG_ON(!core);
-
- if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
- __info("Core %s idle state %d\n", core->core_name, state);
+ core = msm_dcvs_get_core(dcvs_core_id);
switch (state) {
case MSM_DCVS_IDLE_ENTER:
- hrtimer_cancel(&core->timer);
- ret = msm_dcvs_scm_event(core->handle,
+ stop_slack_timer(core);
+ ret = msm_dcvs_scm_event(core->dcvs_core_id,
MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
- if (ret)
+ if (ret < 0 && ret != -13)
__err("Error (%d) sending idle enter for %s\n",
ret, core->core_name);
+ trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 1);
break;
case MSM_DCVS_IDLE_EXIT:
- hrtimer_cancel(&core->timer);
ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
- iowaited, &timer_interval_us, &freq_changed);
+ iowaited, &timer_interval_us);
if (ret)
__err("Error (%d) sending idle exit for %s\n",
ret, core->core_name);
- /* only start slack timer if change_freq won't */
- if (freq_changed)
- break;
- if (timer_interval_us && !core->timer_disabled) {
- ret = hrtimer_start(&core->timer,
- ktime_set(0, timer_interval_us * 1000),
- HRTIMER_MODE_REL_PINNED);
-
- if (ret)
- __err("Failed to register timer for core %s\n",
- core->core_name);
- }
+ start_slack_timer(core, timer_interval_us);
+ trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 0);
+ trace_msm_dcvs_iowait("iowait", core->core_name, iowaited);
+ trace_msm_dcvs_slack_time("slack_timer_dcvs", core->core_name,
+ timer_interval_us);
break;
}
@@ -854,13 +1025,6 @@
goto err;
}
- if (!debugfs_create_u32("debug_mask", S_IRUGO | S_IWUSR,
- debugfs_base, &msm_dcvs_debug)) {
- __err("Cannot create debugfs entry %s\n", "debug_mask");
- ret = -ENOMEM;
- goto err;
- }
-
err:
if (ret) {
kobject_del(cores_kobj);
@@ -875,16 +1039,24 @@
static int __init msm_dcvs_early_init(void)
{
int ret = 0;
+ int i;
if (!msm_dcvs_enabled) {
__info("Not enabled (%d)\n", msm_dcvs_enabled);
return 0;
}
- ret = msm_dcvs_scm_init(10 * 1024);
- if (ret)
- __err("Unable to initialize DCVS err=%d\n", ret);
+ /* Only need about 32kBytes for normal operation */
+ ret = msm_dcvs_scm_init(SZ_32K);
+ if (ret) {
+ __err("Unable to initialize DCVS err=%d\n", ret);
+ goto done;
+ }
+
+ for (i = 0; i < CORES_MAX; i++)
+ core_list[i].dcvs_core_id = -1;
+done:
return ret;
}
postcore_initcall(msm_dcvs_early_init);
diff --git a/arch/arm/mach-msm/msm_dcvs_idle.c b/arch/arm/mach-msm/msm_dcvs_idle.c
deleted file mode 100644
index 179e170..0000000
--- a/arch/arm/mach-msm/msm_dcvs_idle.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/cpu_pm.h>
-#include <linux/platform_device.h>
-#include <linux/pm_qos.h>
-#include <linux/hrtimer.h>
-#include <linux/tick.h>
-#include <mach/msm_dcvs.h>
-
-struct cpu_idle_info {
- int cpu;
- int enabled;
- int handle;
- struct msm_dcvs_idle dcvs_notifier;
-};
-
-static DEFINE_PER_CPU_SHARED_ALIGNED(struct cpu_idle_info, cpu_idle_info);
-static DEFINE_PER_CPU_SHARED_ALIGNED(u64, iowait_on_cpu);
-static char core_name[NR_CPUS][10];
-static struct pm_qos_request qos_req;
-static uint32_t latency;
-
-static int msm_dcvs_idle_notifier(struct msm_dcvs_idle *self,
- enum msm_core_control_event event)
-{
- struct cpu_idle_info *info = container_of(self,
- struct cpu_idle_info, dcvs_notifier);
-
- switch (event) {
- case MSM_DCVS_ENABLE_IDLE_PULSE:
- info->enabled = true;
- break;
-
- case MSM_DCVS_DISABLE_IDLE_PULSE:
- info->enabled = false;
- break;
-
- case MSM_DCVS_ENABLE_HIGH_LATENCY_MODES:
- pm_qos_update_request(&qos_req, PM_QOS_DEFAULT_VALUE);
- break;
-
- case MSM_DCVS_DISABLE_HIGH_LATENCY_MODES:
- pm_qos_update_request(&qos_req, latency);
- break;
- }
-
- return 0;
-}
-
-static int msm_cpuidle_notifier(struct notifier_block *self, unsigned long cmd,
- void *v)
-{
- struct cpu_idle_info *info =
- &per_cpu(cpu_idle_info, smp_processor_id());
- u64 io_wait_us = 0;
- u64 prev_io_wait_us = 0;
- u64 last_update_time = 0;
- u64 val = 0;
- uint32_t iowaited = 0;
-
- if (!info->enabled)
- return NOTIFY_OK;
-
- switch (cmd) {
- case CPU_PM_ENTER:
- val = get_cpu_iowait_time_us(smp_processor_id(),
- &last_update_time);
- /* val could be -1 when NOHZ is not enabled */
- if (val == (u64)-1)
- val = 0;
- per_cpu(iowait_on_cpu, smp_processor_id()) = val;
- msm_dcvs_idle(info->handle, MSM_DCVS_IDLE_ENTER, 0);
- break;
-
- case CPU_PM_ENTER_FAILED:
- case CPU_PM_EXIT:
- prev_io_wait_us = per_cpu(iowait_on_cpu, smp_processor_id());
- val = get_cpu_iowait_time_us(smp_processor_id(),
- &last_update_time);
- if (val == (u64)-1)
- val = 0;
- io_wait_us = val;
- iowaited = (io_wait_us - prev_io_wait_us);
- msm_dcvs_idle(info->handle, MSM_DCVS_IDLE_EXIT, iowaited);
- break;
- }
-
- return NOTIFY_OK;
-}
-
-static struct notifier_block idle_nb = {
- .notifier_call = msm_cpuidle_notifier,
-};
-
-static int msm_dcvs_idle_probe(struct platform_device *pdev)
-{
- int cpu;
- struct cpu_idle_info *info = NULL;
- struct msm_dcvs_idle *inotify = NULL;
-
- for_each_possible_cpu(cpu) {
- info = &per_cpu(cpu_idle_info, cpu);
- info->cpu = cpu;
- inotify = &info->dcvs_notifier;
- snprintf(core_name[cpu], 10, "cpu%d", cpu);
- inotify->core_name = core_name[cpu];
- inotify->enable = msm_dcvs_idle_notifier;
- info->handle = msm_dcvs_idle_source_register(inotify);
- BUG_ON(info->handle < 0);
- }
-
- latency = *((uint32_t *)pdev->dev.platform_data);
- pm_qos_add_request(&qos_req, PM_QOS_CPU_DMA_LATENCY,
- PM_QOS_DEFAULT_VALUE);
-
- return cpu_pm_register_notifier(&idle_nb);
-}
-
-static int msm_dcvs_idle_remove(struct platform_device *pdev)
-{
- int ret = 0;
- int rc = 0;
- int cpu = 0;
- struct msm_dcvs_idle *inotify = NULL;
- struct cpu_idle_info *info = NULL;
-
- rc = cpu_pm_unregister_notifier(&idle_nb);
-
- for_each_possible_cpu(cpu) {
- info = &per_cpu(cpu_idle_info, cpu);
- inotify = &info->dcvs_notifier;
- ret = msm_dcvs_idle_source_unregister(inotify);
- if (ret) {
- rc = -EFAULT;
- pr_err("Error de-registering core %d idle notifier.\n",
- cpu);
- }
- }
-
- return rc;
-}
-
-static struct platform_driver idle_pdrv = {
- .probe = msm_dcvs_idle_probe,
- .remove = __devexit_p(msm_dcvs_idle_remove),
- .driver = {
- .name = "msm_cpu_idle",
- .owner = THIS_MODULE,
- },
-};
-
-static int msm_dcvs_idle_init(void)
-{
- return platform_driver_register(&idle_pdrv);
-}
-late_initcall(msm_dcvs_idle_init);
diff --git a/arch/arm/mach-msm/msm_dcvs_scm.c b/arch/arm/mach-msm/msm_dcvs_scm.c
index df6c44f..78d62ac 100644
--- a/arch/arm/mach-msm/msm_dcvs_scm.c
+++ b/arch/arm/mach-msm/msm_dcvs_scm.c
@@ -20,6 +20,7 @@
#include <mach/memory.h>
#include <mach/scm.h>
#include <mach/msm_dcvs_scm.h>
+#include <trace/events/mpdcvs_trace.h>
#define DCVS_CMD_REGISTER_CORE 2
#define DCVS_CMD_SET_ALGO_PARAM 3
@@ -225,6 +226,9 @@
ret = scm_call_atomic4_3(SCM_SVC_DCVS, DCVS_CMD_EVENT,
core_id, event_id, param0, param1, ret0, ret1);
+ trace_msm_dcvs_scm_event(core_id, (int)event_id, param0, param1,
+ *ret0, *ret1);
+
return ret;
}
EXPORT_SYMBOL(msm_dcvs_scm_event);
diff --git a/arch/arm/mach-msm/msm_mpdecision.c b/arch/arm/mach-msm/msm_mpdecision.c
index 056e4eb..9f6cc11 100644
--- a/arch/arm/mach-msm/msm_mpdecision.c
+++ b/arch/arm/mach-msm/msm_mpdecision.c
@@ -37,6 +37,8 @@
#include <asm/page.h>
#include <mach/msm_dcvs.h>
#include <mach/msm_dcvs_scm.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/mpdcvs_trace.h>
#define DEFAULT_RQ_AVG_POLL_MS (1)
@@ -168,6 +170,8 @@
if (nr > num_present_hundreds)
nr = num_present_hundreds;
+ trace_msm_mp_runq("nr_running", nr);
+
if (ok_to_update_tz(nr, last_nr)) {
hrtimer_try_to_cancel(&msm_mpd.slack_timer);
msm_mpd.data.nr = nr;
@@ -197,9 +201,6 @@
time_taken_ms = ktime_to_ms(ktime_get()) - cpu_action_time_ms;
if (time_taken_ms > hp_latencies.hp_up_max_ms)
hp_latencies.hp_up_max_ms = time_taken_ms;
- if (time_taken_ms > 5)
- pr_warn("cpu_up for cpu%d exceeded 5ms (%d)\n",
- cpu, time_taken_ms);
hp_latencies.hp_up_ms += time_taken_ms;
hp_latencies.hp_up_count++;
ret = msm_dcvs_scm_event(
@@ -228,9 +229,6 @@
time_taken_ms = ktime_to_ms(ktime_get()) - cpu_action_time_ms;
if (time_taken_ms > hp_latencies.hp_dw_max_ms)
hp_latencies.hp_dw_max_ms = time_taken_ms;
- if (time_taken_ms > 5)
- pr_warn("cpu_down for cpu%d exceeded 5ms (%d)\n",
- cpu, time_taken_ms);
hp_latencies.hp_dw_ms += time_taken_ms;
hp_latencies.hp_dw_count++;
ret = msm_dcvs_scm_event(
@@ -263,6 +261,8 @@
return ret;
}
+ trace_msm_mp_cpusonline("cpu_online_mp", req_cpu_mask);
+ trace_msm_mp_slacktime("slack_time_mp", slack_us);
msm_mpd.slack_us = slack_us;
atomic_set(&msm_mpd.algo_cpu_mask, req_cpu_mask);
msm_mpd.hpupdate = HPUPDATE_SCHEDULED;
@@ -286,6 +286,8 @@
{
unsigned long flags;
+ trace_printk("mpd:slack_timer_fired!\n");
+
spin_lock_irqsave(&rq_avg_lock, flags);
if (msm_mpd.data.event == MSM_DCVS_SCM_RUNQ_UPDATE)
goto out;
diff --git a/arch/arm/mach-msm/pil-q6v4-lpass.c b/arch/arm/mach-msm/pil-q6v4-lpass.c
new file mode 100644
index 0000000..a0432550
--- /dev/null
+++ b/arch/arm/mach-msm/pil-q6v4-lpass.c
@@ -0,0 +1,402 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+
+#include <mach/scm.h>
+#include <mach/peripheral-loader.h>
+#include <mach/subsystem_restart.h>
+#include <mach/subsystem_notif.h>
+
+#include "smd_private.h"
+#include "ramdump.h"
+#include "sysmon.h"
+#include "peripheral-loader.h"
+#include "pil-q6v4.h"
+#include "scm-pas.h"
+
+struct lpass_q6v4 {
+ struct q6v4_data q6;
+ void *riva_notif_hdle;
+ void *modem_notif_hdle;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
+ int crash_shutdown;
+ void *ramdump_dev;
+ struct work_struct work;
+ int loadable;
+};
+
+static int pil_q6v4_lpass_boot(struct pil_desc *pil)
+{
+ struct q6v4_data *drv = pil_to_q6v4_data(pil);
+ int err;
+
+ err = pil_q6v4_power_up(drv);
+ if (err)
+ return err;
+
+ return pil_q6v4_boot(pil);
+}
+
+static int pil_q6v4_lpass_shutdown(struct pil_desc *pil)
+{
+ struct q6v4_data *drv = pil_to_q6v4_data(pil);
+ int ret;
+
+ ret = pil_q6v4_shutdown(pil);
+ if (ret)
+ return ret;
+ pil_q6v4_power_down(drv);
+ return 0;
+}
+
+static struct pil_reset_ops pil_q6v4_lpass_ops = {
+ .init_image = pil_q6v4_init_image,
+ .auth_and_reset = pil_q6v4_lpass_boot,
+ .shutdown = pil_q6v4_lpass_shutdown,
+ .proxy_vote = pil_q6v4_make_proxy_votes,
+ .proxy_unvote = pil_q6v4_remove_proxy_votes,
+};
+
+static struct pil_reset_ops pil_q6v4_lpass_ops_trusted = {
+ .init_image = pil_q6v4_init_image_trusted,
+ .auth_and_reset = pil_q6v4_boot_trusted,
+ .shutdown = pil_q6v4_shutdown_trusted,
+ .proxy_vote = pil_q6v4_make_proxy_votes,
+ .proxy_unvote = pil_q6v4_remove_proxy_votes,
+};
+
+static int riva_notifier_cb(struct notifier_block *this, unsigned long code,
+ void *ss_handle)
+{
+ int ret;
+ switch (code) {
+ case SUBSYS_BEFORE_SHUTDOWN:
+ pr_debug("%s: R-Notify: Shutdown started\n", __func__);
+ ret = sysmon_send_event(SYSMON_SS_LPASS, "wcnss",
+ SUBSYS_BEFORE_SHUTDOWN);
+ if (ret < 0)
+ pr_err("%s: sysmon_send_event error %d", __func__, ret);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block rnb = {
+ .notifier_call = riva_notifier_cb,
+};
+
+static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
+ void *ss_handle)
+{
+ int ret;
+ switch (code) {
+ case SUBSYS_BEFORE_SHUTDOWN:
+ pr_debug("%s: M-Notify: Shutdown started\n", __func__);
+ ret = sysmon_send_event(SYSMON_SS_LPASS, "modem",
+ SUBSYS_BEFORE_SHUTDOWN);
+ if (ret < 0)
+ pr_err("%s: sysmon_send_event error %d", __func__, ret);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block mnb = {
+ .notifier_call = modem_notifier_cb,
+};
+
+static void lpass_log_failure_reason(void)
+{
+ char *reason;
+ char buffer[81];
+ unsigned size;
+
+ reason = smem_get_entry(SMEM_SSR_REASON_LPASS0, &size);
+
+ if (!reason) {
+ pr_err("LPASS subsystem failure reason: (unknown, smem_get_entry failed).");
+ return;
+ }
+
+ if (reason[0] == '\0') {
+ pr_err("LPASS subsystem failure reason: (unknown, init value found)");
+ return;
+ }
+
+ size = min(size, sizeof(buffer) - 1);
+ memcpy(buffer, reason, size);
+ buffer[size] = '\0';
+ pr_err("LPASS subsystem failure reason: %s", buffer);
+ memset((void *)reason, 0x0, size);
+ wmb();
+}
+
+static void lpass_fatal_fn(struct work_struct *work)
+{
+ pr_err("Watchdog bite received from lpass Q6!\n");
+ lpass_log_failure_reason();
+ panic("Q6 Resetting the SoC");
+}
+
+static void lpass_smsm_state_cb(void *data, uint32_t old_state,
+ uint32_t new_state)
+{
+ struct lpass_q6v4 *drv = data;
+
+ /* Ignore if we're the one that set SMSM_RESET */
+ if (drv->crash_shutdown)
+ return;
+
+ if (new_state & SMSM_RESET) {
+ pr_err("%s: LPASS SMSM state changed to SMSM_RESET, new_state = %#x, old_state = %#x\n",
+ __func__, new_state, old_state);
+ lpass_log_failure_reason();
+ panic("Q6 Resetting the SoC");
+ }
+}
+
+#define SCM_Q6_NMI_CMD 0x1
+
+static void send_q6_nmi(void)
+{
+ /* Send NMI to QDSP6 via an SCM call. */
+ uint32_t cmd = 0x1;
+
+ scm_call(SCM_SVC_UTIL, SCM_Q6_NMI_CMD,
+ &cmd, sizeof(cmd), NULL, 0);
+
+ /* Q6 requires worstcase 100ms to dump caches etc.*/
+ mdelay(100);
+ pr_debug("%s: Q6 NMI was sent.\n", __func__);
+}
+
+#define subsys_to_lpass(d) container_of(d, struct lpass_q6v4, subsys_desc)
+
+static int lpass_shutdown(const struct subsys_desc *subsys)
+{
+ struct lpass_q6v4 *drv = subsys_to_lpass(subsys);
+
+ send_q6_nmi();
+ if (drv->loadable)
+ pil_force_shutdown("q6");
+ disable_irq_nosync(drv->q6.wdog_irq);
+
+ return 0;
+}
+
+static int lpass_powerup(const struct subsys_desc *subsys)
+{
+ struct lpass_q6v4 *drv = subsys_to_lpass(subsys);
+ int ret = 0;
+
+ if (drv->loadable)
+ ret = pil_force_boot("q6");
+ enable_irq(drv->q6.wdog_irq);
+
+ return ret;
+}
+
+static struct ramdump_segment segments[] = {
+ {0x8da00000, 0x8f200000 - 0x8da00000},
+ {0x28400000, 0x20000}
+};
+
+static int lpass_ramdump(int enable, const struct subsys_desc *subsys)
+{
+ struct lpass_q6v4 *drv = subsys_to_lpass(subsys);
+
+ if (!enable)
+ return 0;
+ return do_ramdump(drv->ramdump_dev, segments, ARRAY_SIZE(segments));
+}
+
+static void lpass_crash_shutdown(const struct subsys_desc *subsys)
+{
+ struct lpass_q6v4 *drv = subsys_to_lpass(subsys);
+
+ drv->crash_shutdown = 1;
+ send_q6_nmi();
+}
+
+static irqreturn_t lpass_wdog_bite_irq(int irq, void *dev_id)
+{
+ struct lpass_q6v4 *drv = dev_id;
+
+ disable_irq_nosync(drv->q6.wdog_irq);
+ schedule_work(&drv->work);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit pil_q6v4_lpass_driver_probe(struct platform_device *pdev)
+{
+ const struct pil_q6v4_pdata *pdata = pdev->dev.platform_data;
+ struct lpass_q6v4 *drv;
+ struct q6v4_data *q6;
+ struct pil_desc *desc;
+ struct resource *res;
+ int ret;
+
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, drv);
+ q6 = &drv->q6;
+ desc = &q6->desc;
+
+ q6->wdog_irq = platform_get_irq(pdev, 0);
+ if (q6->wdog_irq < 0)
+ return q6->wdog_irq;
+
+ drv->loadable = !!pdata; /* No pdata = don't use PIL */
+ if (drv->loadable) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+ q6->base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!q6->base)
+ return -ENOMEM;
+
+ q6->vreg = devm_regulator_get(&pdev->dev, "core_vdd");
+ if (IS_ERR(q6->vreg))
+ return PTR_ERR(q6->vreg);
+
+ q6->xo = devm_clk_get(&pdev->dev, "xo");
+ if (IS_ERR(q6->xo))
+ return PTR_ERR(q6->xo);
+
+ desc->name = pdata->name;
+ desc->dev = &pdev->dev;
+ desc->owner = THIS_MODULE;
+ desc->proxy_timeout = 10000;
+ pil_q6v4_init(q6, pdata);
+
+ if (pas_supported(pdata->pas_id) > 0) {
+ desc->ops = &pil_q6v4_lpass_ops_trusted;
+ dev_info(&pdev->dev, "using secure boot\n");
+ } else {
+ desc->ops = &pil_q6v4_lpass_ops;
+ dev_info(&pdev->dev, "using non-secure boot\n");
+ }
+
+ q6->pil = msm_pil_register(desc);
+ if (IS_ERR(q6->pil))
+ return PTR_ERR(q6->pil);
+ }
+
+ drv->subsys_desc.name = "lpass";
+ drv->subsys_desc.shutdown = lpass_shutdown;
+ drv->subsys_desc.powerup = lpass_powerup;
+ drv->subsys_desc.ramdump = lpass_ramdump;
+ drv->subsys_desc.crash_shutdown = lpass_crash_shutdown;
+
+ INIT_WORK(&drv->work, lpass_fatal_fn);
+
+ drv->ramdump_dev = create_ramdump_device("lpass");
+ if (!drv->ramdump_dev) {
+ ret = -ENOMEM;
+ goto err_ramdump;
+ }
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ ret = PTR_ERR(drv->subsys);
+ goto err_subsys;
+ }
+
+ ret = devm_request_irq(&pdev->dev, q6->wdog_irq, lpass_wdog_bite_irq,
+ IRQF_TRIGGER_RISING, dev_name(&pdev->dev), drv);
+ if (ret)
+ goto err_irq;
+
+ ret = smsm_state_cb_register(SMSM_Q6_STATE, SMSM_RESET,
+ lpass_smsm_state_cb, drv);
+ if (ret < 0)
+ goto err_smsm;
+
+ drv->riva_notif_hdle = subsys_notif_register_notifier("riva", &rnb);
+ if (IS_ERR(drv->riva_notif_hdle)) {
+ ret = PTR_ERR(drv->riva_notif_hdle);
+ goto err_notif_riva;
+ }
+
+ drv->modem_notif_hdle = subsys_notif_register_notifier("modem", &mnb);
+ if (IS_ERR(drv->modem_notif_hdle)) {
+ ret = PTR_ERR(drv->modem_notif_hdle);
+ goto err_notif_modem;
+ }
+ return 0;
+err_notif_modem:
+ subsys_notif_unregister_notifier(drv->riva_notif_hdle, &rnb);
+err_notif_riva:
+ smsm_state_cb_deregister(SMSM_Q6_STATE, SMSM_RESET,
+ lpass_smsm_state_cb, drv);
+err_smsm:
+err_irq:
+ subsys_unregister(drv->subsys);
+err_subsys:
+ destroy_ramdump_device(drv->ramdump_dev);
+err_ramdump:
+ if (drv->loadable)
+ msm_pil_unregister(q6->pil);
+ return ret;
+}
+
+static int __devexit pil_q6v4_lpass_driver_exit(struct platform_device *pdev)
+{
+ struct lpass_q6v4 *drv = platform_get_drvdata(pdev);
+ subsys_notif_unregister_notifier(drv->riva_notif_hdle, &rnb);
+ subsys_notif_unregister_notifier(drv->modem_notif_hdle, &mnb);
+ smsm_state_cb_deregister(SMSM_Q6_STATE, SMSM_RESET,
+ lpass_smsm_state_cb, drv);
+ subsys_unregister(drv->subsys);
+ destroy_ramdump_device(drv->ramdump_dev);
+ if (drv->loadable)
+ msm_pil_unregister(drv->q6.pil);
+ return 0;
+}
+
+static struct platform_driver pil_q6v4_lpass_driver = {
+ .probe = pil_q6v4_lpass_driver_probe,
+ .remove = __devexit_p(pil_q6v4_lpass_driver_exit),
+ .driver = {
+ .name = "pil-q6v4-lpass",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pil_q6v4_lpass_init(void)
+{
+ return platform_driver_register(&pil_q6v4_lpass_driver);
+}
+module_init(pil_q6v4_lpass_init);
+
+static void __exit pil_q6v4_lpass_exit(void)
+{
+ platform_driver_unregister(&pil_q6v4_lpass_driver);
+}
+module_exit(pil_q6v4_lpass_exit);
+
+MODULE_DESCRIPTION("Support for booting QDSP6v4 (Hexagon) processors");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/pil-q6v4-mss.c b/arch/arm/mach-msm/pil-q6v4-mss.c
new file mode 100644
index 0000000..61b5536
--- /dev/null
+++ b/arch/arm/mach-msm/pil-q6v4-mss.c
@@ -0,0 +1,501 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+
+#include <mach/msm_iomap.h>
+#include <mach/subsystem_restart.h>
+#include <mach/msm_smsm.h>
+#include <mach/peripheral-loader.h>
+
+#include "smd_private.h"
+#include "ramdump.h"
+#include "peripheral-loader.h"
+#include "pil-q6v4.h"
+#include "scm-pas.h"
+
+#define MSS_S_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2C70)
+#define MSS_SLP_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C60)
+#define SFAB_MSS_M_ACLK_CTL (MSM_CLK_CTL_BASE + 0x2340)
+#define SFAB_MSS_S_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2C00)
+#define MSS_RESET (MSM_CLK_CTL_BASE + 0x2C64)
+
+struct q6v4_modem {
+ struct q6v4_data q6_fw;
+ struct q6v4_data q6_sw;
+ void __iomem *modem_base;
+ void *fw_ramdump_dev;
+ void *sw_ramdump_dev;
+ void *smem_ramdump_dev;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
+ int crash_shutdown;
+ int loadable;
+};
+
+static DEFINE_MUTEX(pil_q6v4_modem_lock);
+static unsigned pil_q6v4_modem_count;
+
+/* Bring modem subsystem out of reset */
+static void pil_q6v4_init_modem(void __iomem *base, void __iomem *jtag_clk)
+{
+ mutex_lock(&pil_q6v4_modem_lock);
+ if (!pil_q6v4_modem_count) {
+ /* Enable MSS clocks */
+ writel_relaxed(0x10, SFAB_MSS_M_ACLK_CTL);
+ writel_relaxed(0x10, SFAB_MSS_S_HCLK_CTL);
+ writel_relaxed(0x10, MSS_S_HCLK_CTL);
+ writel_relaxed(0x10, MSS_SLP_CLK_CTL);
+ /* Wait for clocks to enable */
+ mb();
+ udelay(10);
+
+ /* De-assert MSS reset */
+ writel_relaxed(0x0, MSS_RESET);
+ mb();
+ udelay(10);
+ /* Enable MSS */
+ writel_relaxed(0x7, base);
+ }
+
+ /* Enable JTAG clocks */
+ /* TODO: Remove if/when Q6 software enables them? */
+ writel_relaxed(0x10, jtag_clk);
+
+ pil_q6v4_modem_count++;
+ mutex_unlock(&pil_q6v4_modem_lock);
+}
+
+/* Put modem subsystem back into reset */
+static void pil_q6v4_shutdown_modem(void)
+{
+ mutex_lock(&pil_q6v4_modem_lock);
+ if (pil_q6v4_modem_count)
+ pil_q6v4_modem_count--;
+ if (pil_q6v4_modem_count == 0)
+ writel_relaxed(0x1, MSS_RESET);
+ mutex_unlock(&pil_q6v4_modem_lock);
+}
+
+static int pil_q6v4_modem_boot(struct pil_desc *pil)
+{
+ struct q6v4_data *drv = pil_to_q6v4_data(pil);
+ struct q6v4_modem *mdm = dev_get_drvdata(pil->dev);
+ int err;
+
+ err = pil_q6v4_power_up(drv);
+ if (err)
+ return err;
+
+ pil_q6v4_init_modem(mdm->modem_base, drv->jtag_clk_reg);
+ return pil_q6v4_boot(pil);
+}
+
+static int pil_q6v4_modem_shutdown(struct pil_desc *pil)
+{
+ struct q6v4_data *drv = pil_to_q6v4_data(pil);
+ int ret;
+
+ ret = pil_q6v4_shutdown(pil);
+ if (ret)
+ return ret;
+ pil_q6v4_shutdown_modem();
+ pil_q6v4_power_down(drv);
+ return 0;
+}
+
+static struct pil_reset_ops pil_q6v4_modem_ops = {
+ .init_image = pil_q6v4_init_image,
+ .auth_and_reset = pil_q6v4_modem_boot,
+ .shutdown = pil_q6v4_modem_shutdown,
+ .proxy_vote = pil_q6v4_make_proxy_votes,
+ .proxy_unvote = pil_q6v4_remove_proxy_votes,
+};
+
+static struct pil_reset_ops pil_q6v4_modem_ops_trusted = {
+ .init_image = pil_q6v4_init_image_trusted,
+ .auth_and_reset = pil_q6v4_boot_trusted,
+ .shutdown = pil_q6v4_shutdown_trusted,
+ .proxy_vote = pil_q6v4_make_proxy_votes,
+ .proxy_unvote = pil_q6v4_remove_proxy_votes,
+};
+
+static void log_modem_sfr(void)
+{
+ u32 size;
+ char *smem_reason, reason[81];
+
+ smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
+ if (!smem_reason || !size) {
+ pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n");
+ return;
+ }
+ if (!smem_reason[0]) {
+ pr_err("modem subsystem failure reason: (unknown, init string found).\n");
+ return;
+ }
+
+ size = min(size, sizeof(reason)-1);
+ memcpy(reason, smem_reason, size);
+ reason[size] = '\0';
+ pr_err("modem subsystem failure reason: %s.\n", reason);
+
+ smem_reason[0] = '\0';
+ wmb();
+}
+
+static void restart_modem(struct q6v4_modem *drv)
+{
+ log_modem_sfr();
+ subsystem_restart_dev(drv->subsys);
+}
+
+#define desc_to_modem(d) container_of(d, struct q6v4_modem, subsys_desc)
+
+static int modem_shutdown(const struct subsys_desc *subsys)
+{
+ struct q6v4_modem *drv = desc_to_modem(subsys);
+
+ /* The watchdogs keep running even after the modem is shutdown */
+ writel_relaxed(0x0, drv->q6_fw.wdog_base + 0x24);
+ writel_relaxed(0x0, drv->q6_sw.wdog_base + 0x24);
+ mb();
+
+ if (drv->loadable) {
+ pil_force_shutdown("modem");
+ pil_force_shutdown("modem_fw");
+ }
+
+ disable_irq_nosync(drv->q6_fw.wdog_irq);
+ disable_irq_nosync(drv->q6_sw.wdog_irq);
+
+ return 0;
+}
+
+static int modem_powerup(const struct subsys_desc *subsys)
+{
+ struct q6v4_modem *drv = desc_to_modem(subsys);
+
+ if (drv->loadable) {
+ pil_force_boot("modem_fw");
+ pil_force_boot("modem");
+ }
+ enable_irq(drv->q6_fw.wdog_irq);
+ enable_irq(drv->q6_sw.wdog_irq);
+ return 0;
+}
+
+void modem_crash_shutdown(const struct subsys_desc *subsys)
+{
+ struct q6v4_modem *drv = desc_to_modem(subsys);
+
+ drv->crash_shutdown = 1;
+ smsm_reset_modem(SMSM_RESET);
+}
+
+static struct ramdump_segment sw_segments[] = {
+ {0x89000000, 0x8D400000 - 0x89000000},
+};
+
+static struct ramdump_segment fw_segments[] = {
+ {0x8D400000, 0x8DA00000 - 0x8D400000},
+};
+
+static struct ramdump_segment smem_segments[] = {
+ {0x80000000, 0x00200000},
+};
+
+static int modem_ramdump(int enable, const struct subsys_desc *subsys)
+{
+ struct q6v4_modem *drv = desc_to_modem(subsys);
+ int ret;
+
+ if (!enable)
+ return 0;
+
+ ret = do_ramdump(drv->sw_ramdump_dev, sw_segments,
+ ARRAY_SIZE(sw_segments));
+ if (ret < 0)
+ return ret;
+
+ ret = do_ramdump(drv->fw_ramdump_dev, fw_segments,
+ ARRAY_SIZE(fw_segments));
+ if (ret < 0)
+ return ret;
+
+ ret = do_ramdump(drv->smem_ramdump_dev, smem_segments,
+ ARRAY_SIZE(smem_segments));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
+{
+ struct q6v4_modem *drv = data;
+
+ /* Ignore if we're the one that set SMSM_RESET */
+ if (drv->crash_shutdown)
+ return;
+
+ if (new_state & SMSM_RESET) {
+ pr_err("Probable fatal error on the modem.\n");
+ restart_modem(drv);
+ }
+}
+
+static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
+{
+ struct q6v4_modem *drv = dev_id;
+ restart_modem(drv);
+ return IRQ_HANDLED;
+}
+
+static int __devinit
+pil_q6v4_proc_init(struct q6v4_data *drv, struct platform_device *pdev, int i)
+{
+ static const char *name[2] = { "fw", "sw" };
+ const struct pil_q6v4_pdata *pdata_p = pdev->dev.platform_data;
+ const struct pil_q6v4_pdata *pdata = pdata_p + i;
+ char reg_name[12];
+ struct pil_desc *desc;
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1 + (i * 2));
+ if (!res)
+ return -EINVAL;
+
+ drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!drv->base)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2 + (i * 2));
+ if (!res)
+ return -EINVAL;
+
+ drv->wdog_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!drv->wdog_base)
+ return -ENOMEM;
+
+ snprintf(reg_name, sizeof(reg_name), "%s_core_vdd", name[i]);
+ drv->vreg = devm_regulator_get(&pdev->dev, reg_name);
+ if (IS_ERR(drv->vreg))
+ return PTR_ERR(drv->vreg);
+
+ drv->xo = devm_clk_get(&pdev->dev, "xo");
+ if (IS_ERR(drv->xo))
+ return PTR_ERR(drv->xo);
+
+ desc = &drv->desc;
+ desc->name = pdata->name;
+ desc->depends_on = pdata->depends;
+ desc->dev = &pdev->dev;
+ desc->owner = THIS_MODULE;
+ desc->proxy_timeout = 10000;
+ pil_q6v4_init(drv, pdata);
+
+ if (pas_supported(pdata->pas_id) > 0) {
+ desc->ops = &pil_q6v4_modem_ops_trusted;
+ dev_info(&pdev->dev, "using secure boot for %s\n", name[i]);
+ } else {
+ desc->ops = &pil_q6v4_modem_ops;
+ dev_info(&pdev->dev, "using non-secure boot for %s\n", name[i]);
+ }
+ return 0;
+}
+
+static int __devinit pil_q6v4_modem_driver_probe(struct platform_device *pdev)
+{
+ struct q6v4_data *drv_fw, *drv_sw;
+ struct q6v4_modem *drv;
+ struct resource *res;
+ struct regulator *pll_supply;
+ int ret;
+ const struct pil_q6v4_pdata *pdata = pdev->dev.platform_data;
+
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, drv);
+
+ drv_fw = &drv->q6_fw;
+ drv_sw = &drv->q6_sw;
+
+ drv_fw->wdog_irq = platform_get_irq(pdev, 0);
+ if (drv_fw->wdog_irq < 0)
+ return drv_fw->wdog_irq;
+
+ drv_sw->wdog_irq = platform_get_irq(pdev, 1);
+ if (drv_sw->wdog_irq < 0)
+ return drv_sw->wdog_irq;
+
+ drv->loadable = !!pdata; /* No pdata = don't use PIL */
+ if (drv->loadable) {
+ ret = pil_q6v4_proc_init(drv_fw, pdev, 0);
+ if (ret)
+ return ret;
+
+ ret = pil_q6v4_proc_init(drv_sw, pdev, 1);
+ if (ret)
+ return ret;
+
+ pll_supply = devm_regulator_get(&pdev->dev, "pll_vdd");
+ drv_fw->pll_supply = drv_sw->pll_supply = pll_supply;
+ if (IS_ERR(pll_supply))
+ return PTR_ERR(pll_supply);
+
+ ret = regulator_set_voltage(pll_supply, 1800000, 1800000);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to set pll voltage\n");
+ return ret;
+ }
+
+ ret = regulator_set_optimum_mode(pll_supply, 100000);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to set pll optimum mode\n");
+ return ret;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ drv->modem_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!drv->modem_base)
+ return -ENOMEM;
+
+ drv_fw->pil = msm_pil_register(&drv_fw->desc);
+ if (IS_ERR(drv_fw->pil))
+ return PTR_ERR(drv_fw->pil);
+
+ drv_sw->pil = msm_pil_register(&drv_sw->desc);
+ if (IS_ERR(drv_sw->pil)) {
+ ret = PTR_ERR(drv_sw->pil);
+ goto err_pil_sw;
+ }
+ }
+
+ drv->subsys_desc.name = "modem";
+ drv->subsys_desc.shutdown = modem_shutdown;
+ drv->subsys_desc.powerup = modem_powerup;
+ drv->subsys_desc.ramdump = modem_ramdump;
+ drv->subsys_desc.crash_shutdown = modem_crash_shutdown;
+
+ drv->fw_ramdump_dev = create_ramdump_device("modem_fw");
+ if (!drv->fw_ramdump_dev) {
+ ret = -ENOMEM;
+ goto err_fw_ramdump;
+ }
+
+ drv->sw_ramdump_dev = create_ramdump_device("modem_sw");
+ if (!drv->sw_ramdump_dev) {
+ ret = -ENOMEM;
+ goto err_sw_ramdump;
+ }
+
+ drv->smem_ramdump_dev = create_ramdump_device("smem-modem");
+ if (!drv->smem_ramdump_dev) {
+ ret = -ENOMEM;
+ goto err_smem_ramdump;
+ }
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ ret = PTR_ERR(drv->subsys);
+ goto err_subsys;
+ }
+
+ ret = devm_request_irq(&pdev->dev, drv_fw->wdog_irq,
+ modem_wdog_bite_irq, IRQF_TRIGGER_RISING,
+ dev_name(&pdev->dev), drv);
+ if (ret)
+ goto err_irq;
+
+ ret = devm_request_irq(&pdev->dev, drv_sw->wdog_irq,
+ modem_wdog_bite_irq, IRQF_TRIGGER_RISING,
+ dev_name(&pdev->dev), drv);
+ if (ret)
+ goto err_irq;
+
+ ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
+ smsm_state_cb, drv);
+ if (ret)
+ goto err_irq;
+ return 0;
+
+err_irq:
+ subsys_unregister(drv->subsys);
+err_subsys:
+ destroy_ramdump_device(drv->smem_ramdump_dev);
+err_smem_ramdump:
+ destroy_ramdump_device(drv->sw_ramdump_dev);
+err_sw_ramdump:
+ destroy_ramdump_device(drv->fw_ramdump_dev);
+err_fw_ramdump:
+ if (drv->loadable)
+ msm_pil_unregister(drv_sw->pil);
+err_pil_sw:
+ msm_pil_unregister(drv_fw->pil);
+ return ret;
+}
+
+static int __devexit pil_q6v4_modem_driver_exit(struct platform_device *pdev)
+{
+ struct q6v4_modem *drv = platform_get_drvdata(pdev);
+
+ smsm_state_cb_deregister(SMSM_MODEM_STATE, SMSM_RESET,
+ smsm_state_cb, drv);
+ subsys_unregister(drv->subsys);
+ destroy_ramdump_device(drv->smem_ramdump_dev);
+ destroy_ramdump_device(drv->sw_ramdump_dev);
+ destroy_ramdump_device(drv->fw_ramdump_dev);
+ if (drv->loadable) {
+ msm_pil_unregister(drv->q6_sw.pil);
+ msm_pil_unregister(drv->q6_fw.pil);
+ }
+ return 0;
+}
+
+static struct platform_driver pil_q6v4_modem_driver = {
+ .probe = pil_q6v4_modem_driver_probe,
+ .remove = __devexit_p(pil_q6v4_modem_driver_exit),
+ .driver = {
+ .name = "pil-q6v4-modem",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pil_q6v4_modem_init(void)
+{
+ return platform_driver_register(&pil_q6v4_modem_driver);
+}
+module_init(pil_q6v4_modem_init);
+
+static void __exit pil_q6v4_modem_exit(void)
+{
+ platform_driver_unregister(&pil_q6v4_modem_driver);
+}
+module_exit(pil_q6v4_modem_exit);
+
+MODULE_DESCRIPTION("Support for booting QDSP6v4 (Hexagon) processors");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/pil-q6v4.c b/arch/arm/mach-msm/pil-q6v4.c
index 32cce1d..47033fc 100644
--- a/arch/arm/mach-msm/pil-q6v4.c
+++ b/arch/arm/mach-msm/pil-q6v4.c
@@ -22,7 +22,6 @@
#include <linux/clk.h>
#include <mach/msm_bus.h>
-#include <mach/msm_iomap.h>
#include "peripheral-loader.h"
#include "pil-q6v4.h"
@@ -35,12 +34,6 @@
#define QDSP6SS_GFMUX_CTL 0x30
#define QDSP6SS_PWR_CTL 0x38
-#define MSS_S_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2C70)
-#define MSS_SLP_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C60)
-#define SFAB_MSS_M_ACLK_CTL (MSM_CLK_CTL_BASE + 0x2340)
-#define SFAB_MSS_S_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2C00)
-#define MSS_RESET (MSM_CLK_CTL_BASE + 0x2C64)
-
#define Q6SS_SS_ARES BIT(0)
#define Q6SS_CORE_ARES BIT(1)
#define Q6SS_ISDB_ARES BIT(2)
@@ -59,29 +52,19 @@
#define Q6SS_CLK_ENA BIT(1)
#define Q6SS_SRC_SWITCH_CLK_OVR BIT(8)
-struct q6v4_data {
- void __iomem *base;
- void __iomem *modem_base;
- unsigned long start_addr;
- struct regulator *vreg;
- struct regulator *pll_supply;
- bool vreg_enabled;
- struct clk *xo;
- struct pil_device *pil;
-};
-
-static int pil_q6v4_init_image(struct pil_desc *pil, const u8 *metadata,
+int pil_q6v4_init_image(struct pil_desc *pil, const u8 *metadata,
size_t size)
{
const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
- struct q6v4_data *drv = dev_get_drvdata(pil->dev);
+ struct q6v4_data *drv = pil_to_q6v4_data(pil);
drv->start_addr = ehdr->e_entry;
return 0;
}
+EXPORT_SYMBOL(pil_q6v4_init_image);
-static int pil_q6v4_make_proxy_votes(struct pil_desc *pil)
+int pil_q6v4_make_proxy_votes(struct pil_desc *pil)
{
- const struct q6v4_data *drv = dev_get_drvdata(pil->dev);
+ const struct q6v4_data *drv = pil_to_q6v4_data(pil);
int ret;
ret = clk_prepare_enable(drv->xo);
@@ -102,19 +85,21 @@
err:
return ret;
}
+EXPORT_SYMBOL(pil_q6v4_make_proxy_votes);
-static void pil_q6v4_remove_proxy_votes(struct pil_desc *pil)
+void pil_q6v4_remove_proxy_votes(struct pil_desc *pil)
{
- const struct q6v4_data *drv = dev_get_drvdata(pil->dev);
+ const struct q6v4_data *drv = pil_to_q6v4_data(pil);
if (drv->pll_supply)
regulator_disable(drv->pll_supply);
clk_disable_unprepare(drv->xo);
}
+EXPORT_SYMBOL(pil_q6v4_remove_proxy_votes);
-static int pil_q6v4_power_up(struct device *dev)
+int pil_q6v4_power_up(struct q6v4_data *drv)
{
int err;
- struct q6v4_data *drv = dev_get_drvdata(dev);
+ struct device *dev = drv->desc.dev;
err = regulator_set_voltage(drv->vreg, 743750, 743750);
if (err) {
@@ -141,68 +126,27 @@
drv->vreg_enabled = true;
return 0;
}
+EXPORT_SYMBOL(pil_q6v4_power_up);
-static DEFINE_MUTEX(pil_q6v4_modem_lock);
-static unsigned pil_q6v4_modem_count;
-
-/* Bring modem subsystem out of reset */
-static void pil_q6v4_init_modem(void __iomem *base, void __iomem *jtag_clk)
+void pil_q6v4_power_down(struct q6v4_data *drv)
{
- mutex_lock(&pil_q6v4_modem_lock);
- if (!pil_q6v4_modem_count) {
- /* Enable MSS clocks */
- writel_relaxed(0x10, SFAB_MSS_M_ACLK_CTL);
- writel_relaxed(0x10, SFAB_MSS_S_HCLK_CTL);
- writel_relaxed(0x10, MSS_S_HCLK_CTL);
- writel_relaxed(0x10, MSS_SLP_CLK_CTL);
- /* Wait for clocks to enable */
- mb();
- udelay(10);
-
- /* De-assert MSS reset */
- writel_relaxed(0x0, MSS_RESET);
- mb();
- udelay(10);
- /* Enable MSS */
- writel_relaxed(0x7, base);
+ if (drv->vreg_enabled) {
+ regulator_disable(drv->vreg);
+ drv->vreg_enabled = false;
}
-
- /* Enable JTAG clocks */
- /* TODO: Remove if/when Q6 software enables them? */
- writel_relaxed(0x10, jtag_clk);
-
- pil_q6v4_modem_count++;
- mutex_unlock(&pil_q6v4_modem_lock);
}
+EXPORT_SYMBOL(pil_q6v4_power_down);
-/* Put modem subsystem back into reset */
-static void pil_q6v4_shutdown_modem(void)
-{
- mutex_lock(&pil_q6v4_modem_lock);
- if (pil_q6v4_modem_count)
- pil_q6v4_modem_count--;
- if (pil_q6v4_modem_count == 0)
- writel_relaxed(0x1, MSS_RESET);
- mutex_unlock(&pil_q6v4_modem_lock);
-}
-
-static int pil_q6v4_reset(struct pil_desc *pil)
+int pil_q6v4_boot(struct pil_desc *pil)
{
u32 reg, err;
- const struct q6v4_data *drv = dev_get_drvdata(pil->dev);
- const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
+ const struct q6v4_data *drv = pil_to_q6v4_data(pil);
- err = pil_q6v4_power_up(pil->dev);
- if (err)
- return err;
/* Enable Q6 ACLK */
- writel_relaxed(0x10, pdata->aclk_reg);
-
- if (drv->modem_base)
- pil_q6v4_init_modem(drv->modem_base, pdata->jtag_clk_reg);
+ writel_relaxed(0x10, drv->aclk_reg);
/* Unhalt bus port */
- err = msm_bus_axi_portunhalt(pdata->bus_port);
+ err = msm_bus_axi_portunhalt(drv->bus_port);
if (err)
dev_err(pil->dev, "Failed to unhalt bus port\n");
@@ -216,8 +160,8 @@
drv->base + QDSP6SS_RST_EVB);
/* Program TCM and AHB address ranges */
- writel_relaxed(pdata->strap_tcm_base, drv->base + QDSP6SS_STRAP_TCM);
- writel_relaxed(pdata->strap_ahb_upper | pdata->strap_ahb_lower,
+ writel_relaxed(drv->strap_tcm_base, drv->base + QDSP6SS_STRAP_TCM);
+ writel_relaxed(drv->strap_ahb_upper | drv->strap_ahb_lower,
drv->base + QDSP6SS_STRAP_AHB);
/* Turn off Q6 core clock */
@@ -257,15 +201,15 @@
return 0;
}
+EXPORT_SYMBOL(pil_q6v4_boot);
-static int pil_q6v4_shutdown(struct pil_desc *pil)
+int pil_q6v4_shutdown(struct pil_desc *pil)
{
u32 reg;
- struct q6v4_data *drv = dev_get_drvdata(pil->dev);
- const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
+ struct q6v4_data *drv = pil_to_q6v4_data(pil);
/* Make sure bus port is halted */
- msm_bus_axi_porthalt(pdata->bus_port);
+ msm_bus_axi_porthalt(drv->bus_port);
/* Turn off Q6 core clock */
writel_relaxed(Q6SS_SRC_SWITCH_CLK_OVR,
@@ -279,188 +223,67 @@
/* Turn off Q6 memories */
writel_relaxed(Q6SS_CLAMP_IO, drv->base + QDSP6SS_PWR_CTL);
- if (drv->modem_base)
- pil_q6v4_shutdown_modem();
-
- if (drv->vreg_enabled) {
- regulator_disable(drv->vreg);
- drv->vreg_enabled = false;
- }
-
return 0;
}
+EXPORT_SYMBOL(pil_q6v4_shutdown);
-static struct pil_reset_ops pil_q6v4_ops = {
- .init_image = pil_q6v4_init_image,
- .auth_and_reset = pil_q6v4_reset,
- .shutdown = pil_q6v4_shutdown,
- .proxy_vote = pil_q6v4_make_proxy_votes,
- .proxy_unvote = pil_q6v4_remove_proxy_votes,
-};
-
-static int pil_q6v4_init_image_trusted(struct pil_desc *pil,
+int pil_q6v4_init_image_trusted(struct pil_desc *pil,
const u8 *metadata, size_t size)
{
- const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
- return pas_init_image(pdata->pas_id, metadata, size);
+ struct q6v4_data *drv = pil_to_q6v4_data(pil);
+ return pas_init_image(drv->pas_id, metadata, size);
}
+EXPORT_SYMBOL(pil_q6v4_init_image_trusted);
-static int pil_q6v4_reset_trusted(struct pil_desc *pil)
+int pil_q6v4_boot_trusted(struct pil_desc *pil)
{
- const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
+ struct q6v4_data *drv = pil_to_q6v4_data(pil);
int err;
- err = pil_q6v4_power_up(pil->dev);
+ err = pil_q6v4_power_up(drv);
if (err)
return err;
/* Unhalt bus port */
- err = msm_bus_axi_portunhalt(pdata->bus_port);
+ err = msm_bus_axi_portunhalt(drv->bus_port);
if (err)
dev_err(pil->dev, "Failed to unhalt bus port\n");
- return pas_auth_and_reset(pdata->pas_id);
+ return pas_auth_and_reset(drv->pas_id);
}
+EXPORT_SYMBOL(pil_q6v4_boot_trusted);
-static int pil_q6v4_shutdown_trusted(struct pil_desc *pil)
+int pil_q6v4_shutdown_trusted(struct pil_desc *pil)
{
int ret;
- struct q6v4_data *drv = dev_get_drvdata(pil->dev);
- struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
+ struct q6v4_data *drv = pil_to_q6v4_data(pil);
/* Make sure bus port is halted */
- msm_bus_axi_porthalt(pdata->bus_port);
+ msm_bus_axi_porthalt(drv->bus_port);
- ret = pas_shutdown(pdata->pas_id);
+ ret = pas_shutdown(drv->pas_id);
if (ret)
return ret;
- if (drv->vreg_enabled) {
- regulator_disable(drv->vreg);
- drv->vreg_enabled = false;
- }
+ pil_q6v4_power_down(drv);
return ret;
}
+EXPORT_SYMBOL(pil_q6v4_shutdown_trusted);
-static struct pil_reset_ops pil_q6v4_ops_trusted = {
- .init_image = pil_q6v4_init_image_trusted,
- .auth_and_reset = pil_q6v4_reset_trusted,
- .shutdown = pil_q6v4_shutdown_trusted,
- .proxy_vote = pil_q6v4_make_proxy_votes,
- .proxy_unvote = pil_q6v4_remove_proxy_votes,
-};
-
-static int __devinit pil_q6v4_driver_probe(struct platform_device *pdev)
+void __devinit
+pil_q6v4_init(struct q6v4_data *drv, const struct pil_q6v4_pdata *pdata)
{
- const struct pil_q6v4_pdata *pdata = pdev->dev.platform_data;
- struct q6v4_data *drv;
- struct resource *res;
- struct pil_desc *desc;
- int ret;
+ drv->strap_tcm_base = pdata->strap_tcm_base;
+ drv->strap_ahb_upper = pdata->strap_ahb_upper;
+ drv->strap_ahb_lower = pdata->strap_ahb_lower;
+ drv->aclk_reg = pdata->aclk_reg;
+ drv->jtag_clk_reg = pdata->jtag_clk_reg;
+ drv->pas_id = pdata->pas_id;
+ drv->bus_port = pdata->bus_port;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -EINVAL;
-
- drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
- if (!drv)
- return -ENOMEM;
- platform_set_drvdata(pdev, drv);
-
- drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
- if (!drv->base)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (res) {
- drv->modem_base = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!drv->modem_base)
- return -ENOMEM;
- }
-
- desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
- if (!desc)
- return -ENOMEM;
-
- drv->pll_supply = devm_regulator_get(&pdev->dev, "pll_vdd");
- if (IS_ERR(drv->pll_supply)) {
- drv->pll_supply = NULL;
- } else {
- ret = regulator_set_voltage(drv->pll_supply, 1800000, 1800000);
- if (ret) {
- dev_err(&pdev->dev, "failed to set pll voltage\n");
- return ret;
- }
-
- ret = regulator_set_optimum_mode(drv->pll_supply, 100000);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to set pll optimum mode\n");
- return ret;
- }
- }
-
- desc->name = pdata->name;
- desc->depends_on = pdata->depends;
- desc->dev = &pdev->dev;
- desc->owner = THIS_MODULE;
- desc->proxy_timeout = 10000;
-
- if (pas_supported(pdata->pas_id) > 0) {
- desc->ops = &pil_q6v4_ops_trusted;
- dev_info(&pdev->dev, "using secure boot\n");
- } else {
- desc->ops = &pil_q6v4_ops;
- dev_info(&pdev->dev, "using non-secure boot\n");
- }
-
- drv->vreg = devm_regulator_get(&pdev->dev, "core_vdd");
- if (IS_ERR(drv->vreg))
- return PTR_ERR(drv->vreg);
-
- ret = regulator_set_optimum_mode(drv->vreg, 100000);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to set regulator's mode.\n");
- return ret;
- }
-
- drv->xo = devm_clk_get(&pdev->dev, "xo");
- if (IS_ERR(drv->xo))
- return PTR_ERR(drv->xo);
-
- drv->pil = msm_pil_register(desc);
- if (IS_ERR(drv->pil))
- return PTR_ERR(drv->pil);
- return 0;
+ regulator_set_optimum_mode(drv->vreg, 100000);
}
-
-static int __devexit pil_q6v4_driver_exit(struct platform_device *pdev)
-{
- struct q6v4_data *drv = platform_get_drvdata(pdev);
- msm_pil_unregister(drv->pil);
- return 0;
-}
-
-static struct platform_driver pil_q6v4_driver = {
- .probe = pil_q6v4_driver_probe,
- .remove = __devexit_p(pil_q6v4_driver_exit),
- .driver = {
- .name = "pil_qdsp6v4",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init pil_q6v4_init(void)
-{
- return platform_driver_register(&pil_q6v4_driver);
-}
-module_init(pil_q6v4_init);
-
-static void __exit pil_q6v4_exit(void)
-{
- platform_driver_unregister(&pil_q6v4_driver);
-}
-module_exit(pil_q6v4_exit);
+EXPORT_SYMBOL(pil_q6v4_init);
MODULE_DESCRIPTION("Support for booting QDSP6v4 (Hexagon) processors");
MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/pil-q6v4.h b/arch/arm/mach-msm/pil-q6v4.h
index b0b97d0..0395bed 100644
--- a/arch/arm/mach-msm/pil-q6v4.h
+++ b/arch/arm/mach-msm/pil-q6v4.h
@@ -12,6 +12,8 @@
#ifndef __MSM_PIL_Q6V4_H
#define __MSM_PIL_Q6V4_H
+#include "peripheral-loader.h"
+
struct pil_q6v4_pdata {
const unsigned long strap_tcm_base;
const unsigned long strap_ahb_upper;
@@ -23,4 +25,52 @@
const unsigned pas_id;
int bus_port;
};
+
+struct clk;
+struct pil_device;
+struct regulator;
+
+/**
+ * struct q6v4_data - Q6 processor
+ */
+struct q6v4_data {
+ void __iomem *base;
+ void __iomem *wdog_base;
+ unsigned long start_addr;
+ unsigned long strap_tcm_base;
+ unsigned long strap_ahb_upper;
+ unsigned long strap_ahb_lower;
+ void __iomem *aclk_reg;
+ void __iomem *jtag_clk_reg;
+ unsigned pas_id;
+ int bus_port;
+ int wdog_irq;
+
+ struct regulator *vreg;
+ struct regulator *pll_supply;
+ bool vreg_enabled;
+ struct clk *xo;
+
+ struct pil_device *pil;
+ struct pil_desc desc;
+};
+
+#define pil_to_q6v4_data(p) container_of(p, struct q6v4_data, desc)
+
+extern int pil_q6v4_init_image(struct pil_desc *pil, const u8 *metadata,
+ size_t size);
+extern int pil_q6v4_make_proxy_votes(struct pil_desc *pil);
+extern void pil_q6v4_remove_proxy_votes(struct pil_desc *pil);
+extern int pil_q6v4_power_up(struct q6v4_data *drv);
+extern void pil_q6v4_power_down(struct q6v4_data *drv);
+extern int pil_q6v4_boot(struct pil_desc *pil);
+extern int pil_q6v4_shutdown(struct pil_desc *pil);
+
+extern int pil_q6v4_init_image_trusted(struct pil_desc *pil,
+ const u8 *metadata, size_t size);
+extern int pil_q6v4_boot_trusted(struct pil_desc *pil);
+extern int pil_q6v4_shutdown_trusted(struct pil_desc *pil);
+extern void __devinit
+pil_q6v4_init(struct q6v4_data *drv, const struct pil_q6v4_pdata *p);
+
#endif
diff --git a/arch/arm/mach-msm/pil-q6v5-mss.c b/arch/arm/mach-msm/pil-q6v5-mss.c
index 7b45a0e..9ff1234 100644
--- a/arch/arm/mach-msm/pil-q6v5-mss.c
+++ b/arch/arm/mach-msm/pil-q6v5-mss.c
@@ -79,7 +79,6 @@
static int pil_mss_enable_clks(struct q6v5_data *drv)
{
int ret;
- void __iomem *mpll1_config_ctl;
ret = clk_prepare_enable(drv->ahb_clk);
if (ret)
@@ -91,12 +90,6 @@
if (ret)
goto err_rom_clk;
- /* TODO: Remove when support for 8974v1.0 HW is dropped. */
- mpll1_config_ctl = ioremap(0xFC981034, 0x4);
- writel_relaxed(0x0300403D, mpll1_config_ctl);
- mb();
- iounmap(mpll1_config_ctl);
-
return 0;
err_rom_clk:
diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index 60ee8f0..5c40750 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.c
@@ -30,6 +30,7 @@
#include <mach/system.h>
#include <mach/scm.h>
#include <mach/socinfo.h>
+#include <mach/msm-krait-l2-accessors.h>
#include <asm/cacheflush.h>
#include <asm/hardware/gic.h>
#include <asm/pgtable.h>
@@ -413,10 +414,63 @@
}
EXPORT_SYMBOL(msm_pm_set_max_sleep_time);
+struct reg_data {
+ uint32_t reg;
+ uint32_t val;
+};
-/******************************************************************************
- *
- *****************************************************************************/
+static struct reg_data reg_saved_state[] = {
+ { .reg = 0x4501, },
+ { .reg = 0x5501, },
+ { .reg = 0x6501, },
+ { .reg = 0x7501, },
+ { .reg = 0x0500, },
+};
+
+static unsigned int active_vdd;
+static bool msm_pm_save_cp15;
+static const unsigned int pc_vdd = 0x98;
+
+static void msm_pm_save_cpu_reg(void)
+{
+ int i;
+
+ /* Only on core0 */
+ if (smp_processor_id())
+ return;
+
+ /**
+ * On some targets, L2 PC will turn off may reset the core
+ * configuration for the mux and the default may not make the core
+ * happy when it resumes.
+ * Save the active vdd, and set the core vdd to QSB max vdd, so that
+ * when the core resumes, it is capable of supporting the current QSB
+ * rate. Then restore the active vdd before switching the acpuclk rate.
+ */
+ if (msm_pm_get_l2_flush_flag() == 1) {
+ active_vdd = msm_spm_get_vdd(0);
+ for (i = 0; i < ARRAY_SIZE(reg_saved_state); i++)
+ reg_saved_state[i].val =
+ get_l2_indirect_reg(reg_saved_state[i].reg);
+ msm_spm_set_vdd(0, pc_vdd);
+ }
+}
+
+static void msm_pm_restore_cpu_reg(void)
+{
+ int i;
+
+ /* Only on core0 */
+ if (smp_processor_id())
+ return;
+
+ if (msm_pm_get_l2_flush_flag() == 1) {
+ for (i = 0; i < ARRAY_SIZE(reg_saved_state); i++)
+ set_l2_indirect_reg(reg_saved_state[i].reg,
+ reg_saved_state[i].val);
+ msm_spm_set_vdd(0, active_vdd);
+ }
+}
static void *msm_pm_idle_rs_limits;
static bool msm_pm_use_qtimer;
@@ -558,8 +612,14 @@
pr_info("CPU%u: %s: change clock rate (old rate = %lu)\n",
cpu, __func__, saved_acpuclk_rate);
+ if (msm_pm_save_cp15)
+ msm_pm_save_cpu_reg();
+
collapsed = msm_pm_spm_power_collapse(cpu, from_idle, true);
+ if (msm_pm_save_cp15)
+ msm_pm_restore_cpu_reg();
+
if (cpu_online(cpu)) {
if (MSM_PM_DEBUG_CLOCK & msm_pm_debug_mask)
pr_info("CPU%u: %s: restore clock rate to %lu\n",
@@ -593,9 +653,12 @@
return collapsed;
}
-static void msm_pm_qtimer_available(void)
+static void msm_pm_target_init(void)
{
- if (machine_is_msm8974())
+ if (cpu_is_apq8064())
+ msm_pm_save_cp15 = true;
+
+ if (cpu_is_msm8974())
msm_pm_use_qtimer = true;
}
@@ -718,11 +781,17 @@
switch (mode) {
case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
+ if (num_online_cpus() > 1) {
+ allow = false;
+ break;
+ }
+ /* fall through */
case MSM_PM_SLEEP_MODE_RETENTION:
if (!allow)
break;
- if (num_online_cpus() > 1) {
+ if (msm_pm_retention_tz_call &&
+ num_online_cpus() > 1) {
allow = false;
break;
}
@@ -1087,7 +1156,7 @@
msm_pm_add_stats(enable_stats, ARRAY_SIZE(enable_stats));
suspend_set_ops(&msm_pm_ops);
- msm_pm_qtimer_available();
+ msm_pm_target_init();
hrtimer_init(&pm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
msm_cpuidle_init();
platform_driver_register(&msm_pc_counter_driver);
diff --git a/arch/arm/mach-msm/pm-boot.c b/arch/arm/mach-msm/pm-boot.c
index ed15a0c..7bc4fe0 100644
--- a/arch/arm/mach-msm/pm-boot.c
+++ b/arch/arm/mach-msm/pm-boot.c
@@ -211,7 +211,10 @@
char *key = NULL;
uint32_t val = 0;
int ret = 0;
- int flag = 0;
+ uint32_t vaddr_val;
+
+ pdata.p_addr = 0;
+ vaddr_val = 0;
key = "qcom,mode";
ret = of_property_read_u32(pdev->dev.of_node, key, &val);
@@ -223,24 +226,43 @@
key = "qcom,phy-addr";
ret = of_property_read_u32(pdev->dev.of_node, key, &val);
- if (ret && pdata.mode == MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS)
- goto fail;
- if (!ret) {
+ if (!ret)
pdata.p_addr = val;
- flag++;
- }
+
key = "qcom,virt-addr";
- ret = of_property_read_u32(pdev->dev.of_node, key, &val);
- if (ret && pdata.mode == MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT)
- goto fail;
- if (!ret) {
- pdata.v_addr = (void *)val;
- flag++;
- }
+ ret = of_property_read_u32(pdev->dev.of_node, key, &vaddr_val);
- if (pdata.mode == MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR && (flag != 2)) {
- key = "addresses for boot remap";
+ switch (pdata.mode) {
+ case MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS:
+ if (!pdata.p_addr) {
+ key = "qcom,phy-addr";
+ goto fail;
+ }
+ break;
+ case MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT:
+ if (!vaddr_val)
+ goto fail;
+
+ pdata.v_addr = (void *)vaddr_val;
+ break;
+ case MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR:
+ if (!vaddr_val)
+ goto fail;
+
+ pdata.v_addr = ioremap_nocache(vaddr_val, SZ_8);
+
+ pdata.p_addr = allocate_contiguous_ebi_nomap(SZ_8, SZ_64K);
+ if (!pdata.p_addr) {
+ key = "qcom,phy-addr";
+ goto fail;
+ }
+ break;
+ case MSM_PM_BOOT_CONFIG_TZ:
+ break;
+ default:
+ pr_err("%s: Unsupported boot mode %d",
+ __func__, pdata.mode);
goto fail;
}
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c b/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c
index 733b7a1..e396186 100644
--- a/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c
+++ b/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c
@@ -5,6 +5,7 @@
*
* Copyright (C) 2008 HTC Corporation
* Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2012 The Linux Foundation. All rights reserved.
*
* All source code in this file is licensed under the following license except
* where indicated.
@@ -34,7 +35,7 @@
#include <linux/dma-mapping.h>
#include <linux/msm_audio.h>
#include <linux/msm_audio_sbc.h>
-#include <linux/android_pmem.h>
+#include <linux/msm_ion.h>
#include <linux/memory_alloc.h>
#include <mach/iommu.h>
@@ -115,6 +116,8 @@
int stopped; /* set when stopped, cleared on flush */
int abort; /* set when error, like sample rate mismatch */
char *build_id;
+ struct ion_client *client;
+ struct ion_handle *output_buff_handle;
};
static struct audio_a2dp_in the_audio_a2dp_in;
@@ -848,10 +851,11 @@
audio->audrec = NULL;
audio->opened = 0;
if (audio->data) {
- iounmap(audio->msm_map);
- free_contiguous_memory_by_paddr(audio->phys);
+ ion_unmap_kernel(audio->client, audio->output_buff_handle);
+ ion_free(audio->client, audio->output_buff_handle);
audio->data = NULL;
}
+ ion_client_destroy(audio->client);
mutex_unlock(&audio->lock);
return 0;
}
@@ -861,6 +865,11 @@
struct audio_a2dp_in *audio = &the_audio_a2dp_in;
int rc;
int encid;
+ int len = 0;
+ unsigned long ionflag = 0;
+ ion_phys_addr_t addr = 0;
+ struct ion_handle *handle = NULL;
+ struct ion_client *client = NULL;
mutex_lock(&audio->lock);
if (audio->opened) {
@@ -868,22 +877,56 @@
goto done;
}
- audio->phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K);
- if (audio->phys) {
- audio->msm_map = ioremap(audio->phys, DMASZ);
- if (IS_ERR(audio->msm_map)) {
- MM_ERR("could not map the phys address to kernel"
- "space\n");
+ client = msm_ion_client_create(UINT_MAX, "Audio_a2dp_in_client");
+ if (IS_ERR_OR_NULL(client)) {
+ MM_ERR("Unable to create ION client\n");
rc = -ENOMEM;
- free_contiguous_memory_by_paddr(audio->phys);
- goto done;
- }
- audio->data = (u8 *)audio->msm_map;
- } else {
- MM_ERR("could not allocate DMA buffers\n");
- rc = -ENOMEM;
- goto done;
+ goto client_create_error;
}
+ audio->client = client;
+
+ MM_DBG("allocating mem sz = %d\n", DMASZ);
+ handle = ion_alloc(client, DMASZ, SZ_4K,
+ ION_HEAP(ION_AUDIO_HEAP_ID), 0);
+ if (IS_ERR_OR_NULL(handle)) {
+ MM_ERR("Unable to create allocate O/P buffers\n");
+ rc = -ENOMEM;
+ goto output_buff_alloc_error;
+ }
+
+ audio->output_buff_handle = handle;
+
+ rc = ion_phys(client , handle, &addr, &len);
+ if (rc) {
+ MM_ERR("O/P buffers:Invalid phy: %x sz: %x\n",
+ (unsigned int) addr, (unsigned int) len);
+ rc = -ENOMEM;
+ goto output_buff_get_phys_error;
+ } else {
+ MM_INFO("O/P buffers:valid phy: %x sz: %x\n",
+ (unsigned int) addr, (unsigned int) len);
+ }
+ audio->phys = (int32_t)addr;
+
+ rc = ion_handle_get_flags(client, handle, &ionflag);
+ if (rc) {
+ MM_ERR("could not get flags for the handle\n");
+ rc = -ENOMEM;
+ goto output_buff_get_flags_error;
+ }
+
+ audio->msm_map = ion_map_kernel(client, handle);
+ if (IS_ERR(audio->data)) {
+ MM_ERR("could not map read buffers,freeing instance 0x%08x\n",
+ (int)audio);
+ rc = -ENOMEM;
+ goto output_buff_map_error;
+ }
+ MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n",
+ audio->phys, (int)audio->data);
+
+ audio->data = (char *)audio->msm_map;
+
MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\
(int) audio->data, (int) audio->phys);
@@ -953,6 +996,13 @@
mutex_unlock(&audio->lock);
return rc;
evt_error:
+output_buff_map_error:
+output_buff_get_phys_error:
+output_buff_get_flags_error:
+ ion_free(client, audio->output_buff_handle);
+output_buff_alloc_error:
+ ion_client_destroy(client);
+client_create_error:
msm_adsp_put(audio->audrec);
audpreproc_aenc_free(audio->enc_id);
mutex_unlock(&audio->lock);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_lpa.c b/arch/arm/mach-msm/qdsp5v2/audio_lpa.c
index 60f43b9..7ec0617 100644
--- a/arch/arm/mach-msm/qdsp5v2/audio_lpa.c
+++ b/arch/arm/mach-msm/qdsp5v2/audio_lpa.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-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
@@ -27,7 +27,7 @@
#include <linux/delay.h>
#include <linux/earlysuspend.h>
#include <linux/list.h>
-#include <linux/android_pmem.h>
+#include <linux/msm_ion.h>
#include <asm/atomic.h>
#include <asm/ioctls.h>
#include <mach/msm_adsp.h>
@@ -136,9 +136,9 @@
union msm_audio_event_payload payload;
};
-struct audlpa_pmem_region {
+struct audlpa_ion_region {
struct list_head list;
- struct file *file;
+ struct ion_handle *handle;
int fd;
void *vaddr;
unsigned long paddr;
@@ -170,7 +170,7 @@
static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
static void audlpa_post_event(struct audio *audio, int type,
union msm_audio_event_payload payload);
-static unsigned long audlpa_pmem_fixup(struct audio *audio, void *addr,
+static unsigned long audlpa_ion_fixup(struct audio *audio, void *addr,
unsigned long len, int ref_up);
static void audlpa_async_send_data(struct audio *audio, unsigned needed,
uint32_t *payload);
@@ -778,7 +778,7 @@
if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE ||
drv_evt->event_type == AUDIO_EVENT_READ_DONE) {
mutex_lock(&audio->lock);
- audlpa_pmem_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
+ audlpa_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
drv_evt->payload.aio_buf.buf_len, 0);
mutex_unlock(&audio->lock);
}
@@ -788,94 +788,118 @@
return rc;
}
-static int audlpa_pmem_check(struct audio *audio,
+static int audlpa_ion_check(struct audio *audio,
void *vaddr, unsigned long len)
{
- struct audlpa_pmem_region *region_elt;
- struct audlpa_pmem_region t = { .vaddr = vaddr, .len = len };
+ struct audlpa_ion_region *region_elt;
+ struct audlpa_ion_region t = {.vaddr = vaddr, .len = len };
- list_for_each_entry(region_elt, &audio->pmem_region_queue, list) {
+ list_for_each_entry(region_elt, &audio->ion_region_queue, list) {
if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
OVERLAPS(region_elt, &t)) {
- MM_ERR("region (vaddr %p len %ld)"
+ MM_ERR("[%p]:region (vaddr %p len %ld)"
" clashes with registered region"
" (vaddr %p paddr %p len %ld)\n",
- vaddr, len,
+ audio, vaddr, len,
region_elt->vaddr,
- (void *)region_elt->paddr,
- region_elt->len);
+ (void *)region_elt->paddr, region_elt->len);
return -EINVAL;
}
}
return 0;
}
-
-static int audlpa_pmem_add(struct audio *audio,
- struct msm_audio_pmem_info *info)
+static int audlpa_ion_add(struct audio *audio,
+ struct msm_audio_ion_info *info)
{
- unsigned long paddr, kvaddr, len;
- struct file *file;
- struct audlpa_pmem_region *region;
+ ion_phys_addr_t paddr;
+ size_t len;
+ unsigned long kvaddr;
+ struct audlpa_ion_region *region;
int rc = -EINVAL;
+ struct ion_handle *handle;
+ unsigned long ionflag;
- MM_DBG("\n"); /* Macro prints the file name and function */
+ MM_ERR("\n"); /* Macro prints the file name and function */
region = kmalloc(sizeof(*region), GFP_KERNEL);
if (!region) {
rc = -ENOMEM;
goto end;
}
-
- if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) {
- kfree(region);
- goto end;
+ handle = ion_import_dma_buf(audio->client, info->fd);
+ if (IS_ERR_OR_NULL(handle)) {
+ pr_err("%s: could not get handle of the given fd\n", __func__);
+ goto import_error;
}
-
- rc = audlpa_pmem_check(audio, info->vaddr, len);
+ rc = ion_handle_get_flags(audio->client, handle, &ionflag);
+ if (rc) {
+ pr_err("%s: could not get flags for the handle\n", __func__);
+ goto flag_error;
+ }
+ kvaddr = (unsigned long)ion_map_kernel(audio->client, handle);
+ if (IS_ERR_OR_NULL((void *)kvaddr)) {
+ pr_err("%s: could not get virtual address\n", __func__);
+ goto map_error;
+ }
+ rc = ion_phys(audio->client, handle, &paddr, &len);
+ if (rc) {
+ pr_err("%s: could not get physical address\n", __func__);
+ goto ion_error;
+ }
+ rc = audlpa_ion_check(audio, info->vaddr, len);
if (rc < 0) {
- put_pmem_file(file);
- kfree(region);
- goto end;
+ MM_ERR("audpcm_ion_check failed\n");
+ goto ion_error;
}
-
+ region->handle = handle;
region->vaddr = info->vaddr;
region->fd = info->fd;
region->paddr = paddr;
region->kvaddr = kvaddr;
region->len = len;
- region->file = file;
region->ref_cnt = 0;
- MM_DBG("add region paddr %lx vaddr %p, len %lu\n", region->paddr,
- region->vaddr, region->len);
- list_add_tail(®ion->list, &audio->pmem_region_queue);
+ MM_DBG("[%p]:add region paddr %lx vaddr %p, len %lu kvaddr %lx\n",
+ audio, region->paddr, region->vaddr,
+ region->len, region->kvaddr);
+ list_add_tail(®ion->list, &audio->ion_region_queue);
+
+ return rc;
+
+ion_error:
+ ion_unmap_kernel(audio->client, handle);
+map_error:
+flag_error:
+ ion_free(audio->client, handle);
+import_error:
+ kfree(region);
end:
return rc;
}
-static int audlpa_pmem_remove(struct audio *audio,
- struct msm_audio_pmem_info *info)
+static int audlpa_ion_remove(struct audio *audio,
+ struct msm_audio_ion_info *info)
{
- struct audlpa_pmem_region *region;
+ struct audlpa_ion_region *region;
struct list_head *ptr, *next;
int rc = -EINVAL;
- MM_DBG("info fd %d vaddr %p\n", info->fd, info->vaddr);
+ list_for_each_safe(ptr, next, &audio->ion_region_queue) {
+ region = list_entry(ptr, struct audlpa_ion_region, list);
- list_for_each_safe(ptr, next, &audio->pmem_region_queue) {
- region = list_entry(ptr, struct audlpa_pmem_region, list);
-
- if ((region->fd == info->fd) &&
+ if (region != NULL && (region->fd == info->fd) &&
(region->vaddr == info->vaddr)) {
if (region->ref_cnt) {
- MM_DBG("region %p in use ref_cnt %d\n",
- region, region->ref_cnt);
+ MM_DBG("%s[%p]:region %p in use ref_cnt %d\n",
+ __func__, audio, region,
+ region->ref_cnt);
break;
}
MM_DBG("remove region fd %d vaddr %p\n",
info->fd, info->vaddr);
list_del(®ion->list);
- put_pmem_file(region->file);
+ ion_unmap_kernel(audio->client, region->handle);
+ ion_free(audio->client, region->handle);
kfree(region);
rc = 0;
break;
@@ -885,23 +909,20 @@
return rc;
}
-static int audlpa_pmem_lookup_vaddr(struct audio *audio, void *addr,
- unsigned long len, struct audlpa_pmem_region **region)
+static int audlpa_ion_lookup_vaddr(struct audio *audio, void *addr,
+ unsigned long len, struct audlpa_ion_region **region)
{
- struct audlpa_pmem_region *region_elt;
-
+ struct audlpa_ion_region *region_elt;
int match_count = 0;
-
*region = NULL;
/* returns physical address or zero */
- list_for_each_entry(region_elt, &audio->pmem_region_queue,
- list) {
+ list_for_each_entry(region_elt, &audio->ion_region_queue, list) {
if (addr >= region_elt->vaddr &&
addr < region_elt->vaddr + region_elt->len &&
addr + len <= region_elt->vaddr + region_elt->len) {
/* offset since we could pass vaddr inside a registerd
- * pmem buffer
+ * ion buffer
*/
match_count++;
@@ -911,13 +932,16 @@
}
if (match_count > 1) {
- MM_ERR("multiple hits for vaddr %p, len %ld\n", addr, len);
- list_for_each_entry(region_elt,
- &audio->pmem_region_queue, list) {
+ MM_ERR("%s[%p]:multiple hits for vaddr %p, len %ld\n",
+ __func__, audio, addr, len);
+ list_for_each_entry(region_elt, &audio->ion_region_queue,
+ list) {
if (addr >= region_elt->vaddr &&
addr < region_elt->vaddr + region_elt->len &&
addr + len <= region_elt->vaddr + region_elt->len)
- MM_ERR("\t%p, %ld --> %p\n", region_elt->vaddr,
+ MM_ERR("\t%s[%p]:%p, %ld --> %p\n",
+ __func__, audio,
+ region_elt->vaddr,
region_elt->len,
(void *)region_elt->paddr);
}
@@ -925,17 +949,17 @@
return *region ? 0 : -1;
}
-
-unsigned long audlpa_pmem_fixup(struct audio *audio, void *addr,
+static unsigned long audlpa_ion_fixup(struct audio *audio, void *addr,
unsigned long len, int ref_up)
{
- struct audlpa_pmem_region *region;
+ struct audlpa_ion_region *region;
unsigned long paddr;
int ret;
- ret = audlpa_pmem_lookup_vaddr(audio, addr, len, ®ion);
+ ret = audlpa_ion_lookup_vaddr(audio, addr, len, ®ion);
if (ret) {
- MM_ERR("lookup (%p, %ld) failed\n", addr, len);
+ MM_ERR("%s[%p]:lookup (%p, %ld) failed\n",
+ __func__, audio, addr, len);
return 0;
}
if (ref_up)
@@ -969,7 +993,7 @@
buf_node->buf.buf_addr, buf_node->buf.buf_len,
buf_node->buf.data_len);
- buf_node->paddr = audlpa_pmem_fixup(
+ buf_node->paddr = audlpa_ion_fixup(
audio, buf_node->buf.buf_addr,
buf_node->buf.buf_len, 1);
@@ -1269,25 +1293,26 @@
audio->drv_status &= ~ADRV_STATUS_PAUSE;
break;
- case AUDIO_REGISTER_PMEM: {
- struct msm_audio_pmem_info info;
- MM_DBG("AUDIO_REGISTER_PMEM\n");
- if (copy_from_user(&info, (void *) arg, sizeof(info)))
+ case AUDIO_REGISTER_ION: {
+ struct msm_audio_ion_info info;
+ MM_DBG("AUDIO_REGISTER_ION\n");
+ if (copy_from_user(&info, (void *) arg, sizeof(info)))
rc = -EFAULT;
else
- rc = audlpa_pmem_add(audio, &info);
+ rc = audlpa_ion_add(audio, &info);
break;
}
- case AUDIO_DEREGISTER_PMEM: {
- struct msm_audio_pmem_info info;
- MM_DBG("AUDIO_DEREGISTER_PMEM\n");
- if (copy_from_user(&info, (void *) arg, sizeof(info)))
+ case AUDIO_DEREGISTER_ION: {
+ struct msm_audio_ion_info info;
+ MM_DBG("AUDIO_DEREGISTER_ION\n");
+ if (copy_from_user(&info, (void *) arg, sizeof(info)))
rc = -EFAULT;
else
- rc = audlpa_pmem_remove(audio, &info);
+ rc = audlpa_ion_remove(audio, &info);
break;
}
+
case AUDIO_ASYNC_WRITE:
if (audio->drv_status & ADRV_STATUS_FSYNC)
rc = -EBUSY;
@@ -1373,15 +1398,16 @@
return audlpa_async_fsync(audio);
}
-static void audlpa_reset_pmem_region(struct audio *audio)
+static void audpcm_reset_ion_region(struct audio *audio)
{
- struct audlpa_pmem_region *region;
+ struct audlpa_ion_region *region;
struct list_head *ptr, *next;
- list_for_each_safe(ptr, next, &audio->pmem_region_queue) {
- region = list_entry(ptr, struct audlpa_pmem_region, list);
+ list_for_each_safe(ptr, next, &audio->ion_region_queue) {
+ region = list_entry(ptr, struct audlpa_ion_region, list);
list_del(®ion->list);
- put_pmem_file(region->file);
+ ion_unmap_kernel(audio->client, region->handle);
+ ion_free(audio->client, region->handle);
kfree(region);
}
@@ -1399,7 +1425,7 @@
auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
audio_disable(audio);
audlpa_async_flush(audio);
- audlpa_reset_pmem_region(audio);
+ audpcm_reset_ion_region(audio);
msm_adsp_put(audio->audplay);
audpp_adec_free(audio->dec_id);
@@ -1410,13 +1436,12 @@
audio->event_abort = 1;
wake_up(&audio->event_wait);
audlpa_reset_event_queue(audio);
- iounmap(audio->data);
- free_contiguous_memory_by_paddr(audio->phys);
mutex_unlock(&audio->lock);
#ifdef CONFIG_DEBUG_FS
if (audio->dentry)
debugfs_remove(audio->dentry);
#endif
+ ion_client_destroy(audio->client);
kfree(audio);
return 0;
}
@@ -1589,7 +1614,7 @@
spin_lock_init(&audio->dsp_lock);
init_waitqueue_head(&audio->write_wait);
INIT_LIST_HEAD(&audio->out_queue);
- INIT_LIST_HEAD(&audio->pmem_region_queue);
+ INIT_LIST_HEAD(&audio->ion_region_queue);
INIT_LIST_HEAD(&audio->free_event_queue);
INIT_LIST_HEAD(&audio->event_queue);
init_waitqueue_head(&audio->wait);
@@ -1650,13 +1675,19 @@
break;
}
}
+
+ audio->client = msm_ion_client_create(UINT_MAX, "Audio_LPA_Client");
+ if (IS_ERR_OR_NULL(audio->client)) {
+ pr_err("Unable to create ION client\n");
+ goto err;
+ }
+ MM_DBG("Ion client created\n");
+
done:
return rc;
event_err:
msm_adsp_put(audio->audplay);
err:
- iounmap(audio->data);
- free_contiguous_memory_by_paddr(audio->phys);
audpp_adec_free(audio->dec_id);
MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
kfree(audio);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_out.c b/arch/arm/mach-msm/qdsp5v2/audio_out.c
index 147ac77..e5c59ba 100644
--- a/arch/arm/mach-msm/qdsp5v2/audio_out.c
+++ b/arch/arm/mach-msm/qdsp5v2/audio_out.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-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
@@ -43,7 +43,7 @@
#include <mach/qdsp5v2/audio_dev_ctl.h>
#include <mach/msm_memtypes.h>
#include <mach/cpuidle.h>
-
+#include <linux/msm_ion.h>
#include <mach/htc_pwrsink.h>
#include <mach/debug_mm.h>
@@ -98,6 +98,8 @@
struct pm_qos_request pm_qos_req;
struct audpp_cmd_cfg_object_params_volume vol_pan;
+ struct ion_client *client;
+ struct ion_handle *buff_handle;
};
static void audio_out_listener(u32 evt_id, union auddev_evt_data *evt_payload,
@@ -702,19 +704,53 @@
static int __init audio_init(void)
{
- the_audio.phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K);
- if (the_audio.phys) {
- the_audio.map_v_write = ioremap(the_audio.phys, DMASZ);
- if (IS_ERR(the_audio.map_v_write)) {
- MM_ERR("could not map physical buffers\n");
- free_contiguous_memory_by_paddr(the_audio.phys);
- return -ENOMEM;
- }
- the_audio.data = the_audio.map_v_write;
- } else {
- MM_ERR("could not allocate physical buffers\n");
- return -ENOMEM;
+ unsigned long ionflag = 0;
+ ion_phys_addr_t addr = 0;
+ int rc;
+ int len = 0;
+ struct ion_handle *handle = NULL;
+ struct ion_client *client = NULL;
+
+ client = msm_ion_client_create(UINT_MAX, "HostPCM");
+ if (IS_ERR_OR_NULL(client)) {
+ MM_ERR("Unable to create ION client\n");
+ rc = -ENOMEM;
+ goto client_create_error;
}
+ the_audio.client = client;
+
+ handle = ion_alloc(client, DMASZ, SZ_4K,
+ ION_HEAP(ION_AUDIO_HEAP_ID), 0);
+ if (IS_ERR_OR_NULL(handle)) {
+ MM_ERR("Unable to create allocate O/P buffers\n");
+ rc = -ENOMEM;
+ goto buff_alloc_error;
+ }
+ the_audio.buff_handle = handle;
+
+ rc = ion_phys(client, handle, &addr, &len);
+ if (rc) {
+ MM_ERR("O/P buffers:Invalid phy: %x sz: %x\n",
+ (unsigned int) addr, (unsigned int) len);
+ goto buff_get_phys_error;
+ } else
+ MM_INFO("O/P buffers:valid phy: %x sz: %x\n",
+ (unsigned int) addr, (unsigned int) len);
+ the_audio.phys = (int32_t)addr;
+
+ rc = ion_handle_get_flags(client, handle, &ionflag);
+ if (rc) {
+ MM_ERR("could not get flags for the handle\n");
+ goto buff_get_flags_error;
+ }
+
+ the_audio.map_v_write = ion_map_kernel(client, handle);
+ if (IS_ERR(the_audio.map_v_write)) {
+ MM_ERR("could not map write buffers\n");
+ rc = -ENOMEM;
+ goto buff_map_error;
+ }
+ the_audio.data = (char *)the_audio.map_v_write;
MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\
(int) the_audio.data, (int) the_audio.phys);
mutex_init(&the_audio.lock);
@@ -725,6 +761,15 @@
pm_qos_add_request(&the_audio.pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
PM_QOS_DEFAULT_VALUE);
return misc_register(&audio_misc);
+buff_map_error:
+buff_get_phys_error:
+buff_get_flags_error:
+ ion_free(client, the_audio.buff_handle);
+buff_alloc_error:
+ ion_client_destroy(client);
+client_create_error:
+ return rc;
+
}
late_initcall(audio_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c b/arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c
index ce67ebb..ff3a696 100644
--- a/arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c
+++ b/arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-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
@@ -27,7 +27,7 @@
#include <linux/wait.h>
#include <linux/dma-mapping.h>
#include <linux/msm_audio.h>
-#include <linux/android_pmem.h>
+#include <linux/msm_ion.h>
#include <linux/memory_alloc.h>
#include <mach/msm_memtypes.h>
@@ -121,6 +121,8 @@
int abort; /* set when error, like sample rate mismatch */
int dual_mic_config;
char *build_id;
+ struct ion_client *client;
+ struct ion_handle *output_buff_handle;
};
static struct audio_in the_audio_in;
@@ -842,10 +844,11 @@
audio->audrec = NULL;
audio->opened = 0;
if (audio->data) {
- iounmap(audio->map_v_read);
- free_contiguous_memory_by_paddr(audio->phys);
+ ion_unmap_kernel(audio->client, audio->output_buff_handle);
+ ion_free(audio->client, audio->output_buff_handle);
audio->data = NULL;
}
+ ion_client_destroy(audio->client);
mutex_unlock(&audio->lock);
return 0;
}
@@ -855,27 +858,68 @@
struct audio_in *audio = &the_audio_in;
int rc;
int encid;
+ int len = 0;
+ unsigned long ionflag = 0;
+ ion_phys_addr_t addr = 0;
+ struct ion_handle *handle = NULL;
+ struct ion_client *client = NULL;
mutex_lock(&audio->lock);
if (audio->opened) {
rc = -EBUSY;
goto done;
}
- audio->phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K);
- if (audio->phys) {
- audio->map_v_read = ioremap(audio->phys, DMASZ);
- if (IS_ERR(audio->map_v_read)) {
- MM_ERR("could not map read phys buffers\n");
+
+ client = msm_ion_client_create(UINT_MAX, "Audio_PCM_in_client");
+ if (IS_ERR_OR_NULL(client)) {
+ MM_ERR("Unable to create ION client\n");
rc = -ENOMEM;
- free_contiguous_memory_by_paddr(audio->phys);
- goto done;
- }
- audio->data = audio->map_v_read;
- } else {
- MM_ERR("could not allocate read buffers\n");
- rc = -ENOMEM;
- goto done;
+ goto client_create_error;
}
+ audio->client = client;
+
+ MM_DBG("allocating mem sz = %d\n", DMASZ);
+ handle = ion_alloc(client, DMASZ, SZ_4K,
+ ION_HEAP(ION_AUDIO_HEAP_ID), 0);
+ if (IS_ERR_OR_NULL(handle)) {
+ MM_ERR("Unable to create allocate O/P buffers\n");
+ rc = -ENOMEM;
+ goto output_buff_alloc_error;
+ }
+
+ audio->output_buff_handle = handle;
+
+ rc = ion_phys(client , handle, &addr, &len);
+ if (rc) {
+ MM_ERR("O/P buffers:Invalid phy: %x sz: %x\n",
+ (unsigned int) addr, (unsigned int) len);
+ rc = -ENOMEM;
+ goto output_buff_get_phys_error;
+ } else {
+ MM_INFO("O/P buffers:valid phy: %x sz: %x\n",
+ (unsigned int) addr, (unsigned int) len);
+ }
+ audio->phys = (int32_t)addr;
+
+ rc = ion_handle_get_flags(client, handle, &ionflag);
+ if (rc) {
+ MM_ERR("could not get flags for the handle\n");
+ rc = -ENOMEM;
+ goto output_buff_get_flags_error;
+ }
+
+ audio->map_v_read = ion_map_kernel(client, handle);
+ if (IS_ERR(audio->data)) {
+ MM_ERR("could not map read buffers,freeing instance 0x%08x\n",
+ (int)audio);
+ rc = -ENOMEM;
+ goto output_buff_map_error;
+ }
+ MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n",
+ audio->phys, (int)audio->data);
+
+ audio->data = (char *)audio->map_v_read;
+
MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\
(int) audio->data, (int) audio->phys);
if ((file->f_mode & FMODE_WRITE) &&
@@ -941,6 +985,13 @@
mutex_unlock(&audio->lock);
return rc;
evt_error:
+output_buff_map_error:
+output_buff_get_phys_error:
+output_buff_get_flags_error:
+ ion_free(client, audio->output_buff_handle);
+output_buff_alloc_error:
+ ion_client_destroy(client);
+client_create_error:
msm_adsp_put(audio->audrec);
audpreproc_aenc_free(audio->enc_id);
mutex_unlock(&audio->lock);
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_lpa.c b/arch/arm/mach-msm/qdsp6v2/audio_lpa.c
index 176f364..edf8f77 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_lpa.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_lpa.c
@@ -707,15 +707,6 @@
case RESET_EVENTS:
reset_device();
break;
- case APR_BASIC_RSP_RESULT:
- switch (payload[0]) {
- case ASM_STREAM_CMD_CLOSE:
- audlpa_unmap_ion_region(audio);
- break;
- default:
- break;
- }
- break;
default:
break;
}
@@ -1136,6 +1127,7 @@
audlpa_async_flush(audio);
audio->wflush = 0;
audio_disable(audio);
+ audlpa_unmap_ion_region(audio);
msm_clear_session_id(audio->ac->session);
auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->ac->session);
q6asm_audio_client_free(audio->ac);
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
index 28bf5c6..d6abdda 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
@@ -403,7 +403,7 @@
return;
}
-void audio_aio_unmap_ion_region(struct q6audio_aio *audio)
+static void audio_aio_unmap_ion_region(struct q6audio_aio *audio)
{
struct audio_aio_ion_region *region;
struct list_head *ptr, *next;
@@ -436,6 +436,7 @@
audio->drv_ops.out_flush(audio);
audio->drv_ops.in_flush(audio);
audio_aio_disable(audio);
+ audio_aio_unmap_ion_region(audio);
audio_aio_reset_ion_region(audio);
ion_client_destroy(audio->client);
audio->event_abort = 1;
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h
index 2b936c5..b2829c3 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h
+++ b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h
@@ -210,7 +210,6 @@
int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync);
void audio_aio_async_out_flush(struct q6audio_aio *audio);
void audio_aio_async_in_flush(struct q6audio_aio *audio);
-void audio_aio_unmap_ion_region(struct q6audio_aio *audio);
#ifdef CONFIG_DEBUG_FS
ssize_t audio_aio_debug_open(struct inode *inode, struct file *file);
ssize_t audio_aio_debug_read(struct file *file, char __user *buf,
diff --git a/arch/arm/mach-msm/qdsp6v2/q6audio_v1_aio.c b/arch/arm/mach-msm/qdsp6v2/q6audio_v1_aio.c
index b46e0d3..96f823f 100644
--- a/arch/arm/mach-msm/qdsp6v2/q6audio_v1_aio.c
+++ b/arch/arm/mach-msm/qdsp6v2/q6audio_v1_aio.c
@@ -37,7 +37,6 @@
case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
case ASM_DATA_EVENT_ENC_SR_CM_NOTIFY:
- case APR_BASIC_RSP_RESULT:
audio_aio_cb(opcode, token, payload, audio);
break;
default:
@@ -107,15 +106,6 @@
e_payload.stream_info.sample_rate = audio->pcm_cfg.sample_rate;
audio_aio_post_event(audio, AUDIO_EVENT_STREAM_INFO, e_payload);
break;
- case APR_BASIC_RSP_RESULT:
- switch (payload[0]) {
- case ASM_STREAM_CMD_CLOSE:
- audio_aio_unmap_ion_region(audio);
- break;
- default:
- break;
- }
- break;
default:
break;
}
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.c
index 5400ccc..dce3812 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.c
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.c
@@ -515,6 +515,9 @@
case FORMAT_USRAW:
int_format = US_RAW_FORMAT;
break;
+ case FORMAT_USPROX:
+ int_format = US_PROX_FORMAT;
+ break;
default:
pr_err("%s: Invalid format[%d]\n", __func__, ext_format);
break;
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h b/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h
index 1338e86..1fe71bf 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h
@@ -20,6 +20,7 @@
#define FORMAT_USPS_EPOS 0x00000000
#define FORMAT_USRAW 0x00000001
+#define FORMAT_USPROX 0x00000002
#define INVALID_FORMAT 0xffffffff
#define IN 0x000
diff --git a/arch/arm/mach-msm/rpm-smd.c b/arch/arm/mach-msm/rpm-smd.c
index 7c31e76..f97eb9a 100644
--- a/arch/arm/mach-msm/rpm-smd.c
+++ b/arch/arm/mach-msm/rpm-smd.c
@@ -943,7 +943,7 @@
static bool msm_rpm_set_standalone(void)
{
- if (machine_is_msm8974()) {
+ if (machine_is_msm9625()) {
pr_warn("%s(): Running in standalone mode, requests "
"will not be sent to RPM\n", __func__);
standalone = true;
diff --git a/arch/arm/mach-msm/rpm_stats.h b/arch/arm/mach-msm/rpm_stats.h
index 918d4fb..a3beaa4 100644
--- a/arch/arm/mach-msm/rpm_stats.h
+++ b/arch/arm/mach-msm/rpm_stats.h
@@ -20,4 +20,19 @@
phys_addr_t phys_addr_base;
u32 phys_size;
};
+
+struct msm_rpm_master_stats_platform_data {
+ phys_addr_t phys_addr_base;
+ u32 phys_size;
+ char **masters;
+ /*
+ * RPM maintains PC stats for each master in MSG RAM,
+ * it allocates 256 bytes for this use.
+ * No of masters differs for different targets.
+ * Based on the number of masters, linux rpm stat
+ * driver reads (32 * nomasters) bytes to display
+ * master stats.
+ */
+ u32 nomasters;
+};
#endif
diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c
index 969af98..0beb952 100644
--- a/arch/arm/mach-msm/socinfo.c
+++ b/arch/arm/mach-msm/socinfo.c
@@ -35,6 +35,8 @@
HW_PLATFORM_LIQUID = 9,
/* Dragonboard platform id is assigned as 10 in CDT */
HW_PLATFORM_DRAGON = 10,
+ HW_PLATFORM_HRD = 13,
+ HW_PLATFORM_DTV = 14,
HW_PLATFORM_INVALID
};
@@ -47,7 +49,9 @@
[HW_PLATFORM_SVLTE_SURF] = "SLVTE_SURF",
[HW_PLATFORM_MTP] = "MTP",
[HW_PLATFORM_LIQUID] = "Liquid",
- [HW_PLATFORM_DRAGON] = "Dragon"
+ [HW_PLATFORM_DRAGON] = "Dragon",
+ [HW_PLATFORM_HRD] = "HRD",
+ [HW_PLATFORM_DTV] = "DTV",
};
enum {
diff --git a/arch/arm/mach-msm/tz_log.c b/arch/arm/mach-msm/tz_log.c
index db797cd..5a7ec69 100644
--- a/arch/arm/mach-msm/tz_log.c
+++ b/arch/arm/mach-msm/tz_log.c
@@ -496,17 +496,23 @@
*/
tzdiag_phy_iobase = readl_relaxed(virt_iobase);
- /*
- * Map the 4KB diagnostic information area
- */
- tzdbg.virt_iobase = devm_ioremap_nocache(&pdev->dev,
- tzdiag_phy_iobase, DEBUG_MAX_RW_BUF);
+ if (!pdev->dev.of_node) {
- if (!tzdbg.virt_iobase) {
- dev_err(&pdev->dev,
- "%s: ERROR could not ioremap: start=%p, len=%u\n",
- __func__, (void *) tzdiag_phy_iobase, DEBUG_MAX_RW_BUF);
- return -ENXIO;
+ /*
+ * Map the 4KB diagnostic information area
+ */
+ tzdbg.virt_iobase = devm_ioremap_nocache(&pdev->dev,
+ tzdiag_phy_iobase, DEBUG_MAX_RW_BUF);
+
+ if (!tzdbg.virt_iobase) {
+ dev_err(&pdev->dev,
+ "%s: ERROR could not ioremap: start=%p, len=%u\n",
+ __func__, (void *) tzdiag_phy_iobase,
+ DEBUG_MAX_RW_BUF);
+ return -ENXIO;
+ }
+ } else {
+ tzdbg.virt_iobase = virt_iobase;
}
ptr = kzalloc(DEBUG_MAX_RW_BUF, GFP_KERNEL);
diff --git a/arch/arm/mach-msm/wcnss-ssr-8974.c b/arch/arm/mach-msm/wcnss-ssr-8974.c
index b837efc..8841b52 100644
--- a/arch/arm/mach-msm/wcnss-ssr-8974.c
+++ b/arch/arm/mach-msm/wcnss-ssr-8974.c
@@ -14,7 +14,11 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/err.h>
-
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/wcnss_wlan.h>
+#include <linux/delay.h>
+#include <mach/peripheral-loader.h>
#include <mach/subsystem_restart.h>
#include <mach/msm_smsm.h>
@@ -24,6 +28,7 @@
static int ss_restart_inprogress;
static int wcnss_crash;
static struct subsys_device *wcnss_ssr_dev;
+static struct delayed_work cancel_vote_work;
#define WCNSS_APSS_WDOG_BITE_RESET_RDY_IRQ 181
@@ -98,14 +103,44 @@
return IRQ_HANDLED;
}
+static void wcnss_post_bootup(struct work_struct *work)
+{
+ struct platform_device *pdev = wcnss_get_platform_device();
+ struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
+
+ pr_debug(MODULE_NAME ": Cancel APPS vote for Iris & Pronto\n");
+
+ wcnss_wlan_power(&pdev->dev, pwlanconfig,
+ WCNSS_WLAN_SWITCH_OFF);
+}
static int wcnss_shutdown(const struct subsys_desc *subsys)
{
+ pil_force_shutdown("wcnss");
+ flush_delayed_work(&cancel_vote_work);
+ wcnss_flush_delayed_boot_votes();
+ disable_irq_nosync(WCNSS_APSS_WDOG_BITE_RESET_RDY_IRQ);
+
return 0;
}
static int wcnss_powerup(const struct subsys_desc *subsys)
{
+ struct platform_device *pdev = wcnss_get_platform_device();
+ struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
+ int ret = -1;
+
+ if (pdev && pwlanconfig)
+ ret = wcnss_wlan_power(&pdev->dev, pwlanconfig,
+ WCNSS_WLAN_SWITCH_ON);
+ if (!ret) {
+ msleep(1000);
+ pil_force_boot("wcnss");
+ }
+ ss_restart_inprogress = false;
+ enable_irq(WCNSS_APSS_WDOG_BITE_RESET_RDY_IRQ);
+ schedule_delayed_work(&cancel_vote_work, msecs_to_jiffies(5000));
+
return 0;
}
@@ -155,6 +190,8 @@
if (IS_ERR(wcnss_ssr_dev))
return PTR_ERR(wcnss_ssr_dev);
+ INIT_DELAYED_WORK(&cancel_vote_work, wcnss_post_bootup);
+
pr_info("%s: module initialized\n", MODULE_NAME);
out:
return ret;
diff --git a/arch/arm/mach-msm/wdog_debug.c b/arch/arm/mach-msm/wdog_debug.c
index 82800cf..08dd9ce 100644
--- a/arch/arm/mach-msm/wdog_debug.c
+++ b/arch/arm/mach-msm/wdog_debug.c
@@ -110,7 +110,6 @@
goto err;
wdog_data->dev = &pdev->dev;
platform_set_drvdata(pdev, wdog_data);
- msm_enable_wdog_debug();
return 0;
err:
kzfree(wdog_data);
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index cb245ee..e351eb0 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -880,6 +880,16 @@
This option enables optimisations for the PL310 cache
controller.
+config CACHE_PL310_ERP
+ tristate "PL310 CACHE Error Reporting"
+ depends on CACHE_PL310
+ help
+ Say 'Y' here to enable reporting of external L2 cache errors.
+ This feature can be used as a system debugging technique if cache
+ corruption is suspected.
+ Cache error statistics will also be reported in sysfs
+ /sys/devices/platform/pl310_erp/cache_erp
+
config CACHE_TAUROS2
bool "Enable the Tauros2 L2 cache controller"
depends on (ARCH_DOVE || ARCH_MMP || CPU_PJ4)
diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile
index 1c415af..6314e94 100644
--- a/arch/arm/mm/Makefile
+++ b/arch/arm/mm/Makefile
@@ -100,6 +100,7 @@
obj-$(CONFIG_CACHE_FEROCEON_L2) += cache-feroceon-l2.o
obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o
+obj-$(CONFIG_CACHE_PL310_ERP) += cache-pl310-erp.o
obj-$(CONFIG_CACHE_XSC3L2) += cache-xsc3l2.o
obj-$(CONFIG_CACHE_TAUROS2) += cache-tauros2.o
obj-$(CONFIG_VCM) += vcm.o vcm_alloc.o
diff --git a/arch/arm/mm/cache-pl310-erp.c b/arch/arm/mm/cache-pl310-erp.c
new file mode 100644
index 0000000..ad75143
--- /dev/null
+++ b/arch/arm/mm/cache-pl310-erp.c
@@ -0,0 +1,283 @@
+/* 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/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/cpu.h>
+#include <linux/io.h>
+#include <asm/cputype.h>
+#include <asm/hardware/cache-l2x0.h>
+
+#define MODULE_NAME "pl310_erp"
+
+struct pl310_drv_data {
+ unsigned int irq;
+ unsigned int ecntr;
+ unsigned int parrt;
+ unsigned int parrd;
+ unsigned int errwd;
+ unsigned int errwt;
+ unsigned int errrt;
+ unsigned int errrd;
+ unsigned int slverr;
+ unsigned int decerr;
+ void __iomem *base;
+};
+
+#define ECNTR BIT(0)
+#define PARRT BIT(1)
+#define PARRD BIT(2)
+#define ERRWT BIT(3)
+#define ERRWD BIT(4)
+#define ERRRT BIT(5)
+#define ERRRD BIT(6)
+#define SLVERR BIT(7)
+#define DECERR BIT(8)
+
+static irqreturn_t pl310_erp_irq(int irq, void *dev_id)
+{
+ struct pl310_drv_data *p = platform_get_drvdata(dev_id);
+ uint16_t mask_int_stat, int_clear = 0, error = 0;
+
+ mask_int_stat = readl_relaxed(p->base + L2X0_MASKED_INTR_STAT);
+
+ if (mask_int_stat & ECNTR) {
+ pr_alert("Event Counter1/0 Overflow Increment error\n");
+ p->ecntr++;
+ int_clear = mask_int_stat & ECNTR;
+ }
+
+ if (mask_int_stat & PARRT) {
+ pr_alert("Read parity error on L2 Tag RAM\n");
+ p->parrt++;
+ error = 1;
+ int_clear = mask_int_stat & PARRT;
+ }
+
+ if (mask_int_stat & PARRD) {
+ pr_alert("Read parity error on L2 Tag RAM\n");
+ p->parrd++;
+ error = 1;
+ int_clear = mask_int_stat & PARRD;
+ }
+
+ if (mask_int_stat & ERRWT) {
+ pr_alert("Write error on L2 Tag RAM\n");
+ p->errwt++;
+ int_clear = mask_int_stat & ERRWT;
+ }
+
+ if (mask_int_stat & ERRWD) {
+ pr_alert("Write error on L2 Data RAM\n");
+ p->errwd++;
+ int_clear = mask_int_stat & ERRWD;
+ }
+
+ if (mask_int_stat & ERRRT) {
+ pr_alert("Read error on L2 Tag RAM\n");
+ p->errrt++;
+ int_clear = mask_int_stat & ERRRT;
+ }
+
+ if (mask_int_stat & ERRRD) {
+ pr_alert("Read error on L2 Data RAM\n");
+ p->errrd++;
+ int_clear = mask_int_stat & ERRRD;
+ }
+
+ if (mask_int_stat & DECERR) {
+ pr_alert("L2 master port decode error\n");
+ p->decerr++;
+ int_clear = mask_int_stat & DECERR;
+ }
+
+ if (mask_int_stat & SLVERR) {
+ pr_alert("L2 slave port error\n");
+ p->slverr++;
+ int_clear = mask_int_stat & SLVERR;
+ }
+
+ writel_relaxed(int_clear, p->base + L2X0_INTR_CLEAR);
+
+ /* Make sure the interrupts are cleared */
+ mb();
+
+ /* WARNING will be thrown whenever we receive any L2 interrupt.
+ * Other than parity on tag/data ram, irrespective of the bits
+ * set we will throw a warning.
+ */
+ WARN_ON(!error);
+
+ /* Panic in case we encounter parity error in TAG/DATA Ram */
+ BUG_ON(error);
+
+ return IRQ_HANDLED;
+}
+
+static void pl310_mask_int(struct pl310_drv_data *p, bool enable)
+{
+ uint16_t mask;
+
+ if (enable)
+ mask = 0x1FF;
+ else
+ mask = 0x0;
+
+ writel_relaxed(mask, p->base + L2X0_INTR_MASK);
+
+ /* Make sure Mask is updated */
+ mb();
+
+ pr_debug("Mask interrupt %x\n",
+ readl_relaxed(p->base + L2X0_INTR_MASK));
+}
+
+static int pl310_erp_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct pl310_drv_data *p = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE,
+ "L2CC Interrupt Number:\t\t\t%d\n"\
+ "Event Counter1/0 Overflow Increment:\t%u\n"\
+ "Parity Error on L2 Tag RAM (Read):\t%u\n"\
+ "Parity Error on L2 Data RAM (Read):\t%u\n"\
+ "Error on L2 Tag RAM (Write):\t\t%u\n"\
+ "Error on L2 Data RAM (Write):\t\t%u\n"\
+ "Error on L2 Tag RAM (Read):\t\t%u\n"\
+ "Error on L2 Data RAM (Read):\t\t%u\n"\
+ "SLave Error from L3 Port:\t\t%u\n"\
+ "Decode Error from L3 Port:\t\t%u\n",
+ p->irq, p->ecntr, p->parrt, p->parrd, p->errwt, p->errwd,
+ p->errrt, p->errrd, p->slverr, p->decerr);
+}
+
+static DEVICE_ATTR(cache_erp, 0664, pl310_erp_show, NULL);
+
+static int __init pl310_create_sysfs(struct device *dev)
+{
+ /* create a sysfs entry at
+ * /sys/devices/platform/pl310_erp/cache_erp
+ */
+ return device_create_file(dev, &dev_attr_cache_erp);
+}
+
+static int __devinit pl310_cache_erp_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ struct pl310_drv_data *drv_data;
+ int ret;
+
+ drv_data = devm_kzalloc(&pdev->dev, sizeof(struct pl310_drv_data),
+ GFP_KERNEL);
+ if (drv_data == NULL) {
+ dev_err(&pdev->dev, "cannot allocate memory\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "No L2 base address\n");
+ ret = -ENODEV;
+ goto error;
+ }
+
+ if (!devm_request_mem_region(&pdev->dev, r->start, resource_size(r),
+ "erp")) {
+ ret = -EBUSY;
+ goto error;
+ }
+
+ drv_data->base = devm_ioremap_nocache(&pdev->dev, r->start,
+ resource_size(r));
+ if (!drv_data->base) {
+ dev_err(&pdev->dev, "errored to ioremap 0x%x\n", r->start);
+ ret = -ENOMEM;
+ goto error;
+ }
+ dev_dbg(&pdev->dev, "L2CC base 0x%p\n", drv_data->base);
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "l2_irq");
+ if (!r) {
+ dev_err(&pdev->dev, "No L2 IRQ resource\n");
+ ret = -ENODEV;
+ goto error;
+ }
+
+ drv_data->irq = r->start;
+
+ ret = devm_request_irq(&pdev->dev, drv_data->irq, pl310_erp_irq,
+ IRQF_TRIGGER_RISING, "l2cc_intr", pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "request irq for L2 interrupt failed\n");
+ goto error;
+ }
+
+ platform_set_drvdata(pdev, drv_data);
+
+ pl310_mask_int(drv_data, true);
+
+ ret = pl310_create_sysfs(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to create sysfs entry\n");
+ goto sysfs_err;
+ }
+
+ return 0;
+
+sysfs_err:
+ platform_set_drvdata(pdev, NULL);
+ pl310_mask_int(drv_data, false);
+error:
+ return ret;
+}
+
+static int __devexit pl310_cache_erp_remove(struct platform_device *pdev)
+{
+ struct pl310_drv_data *p = platform_get_drvdata(pdev);
+
+ pl310_mask_int(p, false);
+
+ device_remove_file(&pdev->dev, &dev_attr_cache_erp);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver pl310_cache_erp_driver = {
+ .probe = pl310_cache_erp_probe,
+ .remove = __devexit_p(pl310_cache_erp_remove),
+ .driver = {
+ .name = MODULE_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pl310_cache_erp_init(void)
+{
+ return platform_driver_register(&pl310_cache_erp_driver);
+}
+module_init(pl310_cache_erp_init);
+
+static void __exit pl310_cache_erp_exit(void)
+{
+ platform_driver_unregister(&pl310_cache_erp_driver);
+}
+module_exit(pl310_cache_erp_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PL310 cache error reporting driver");
diff --git a/drivers/cpufreq/cpufreq_gov_msm.c b/drivers/cpufreq/cpufreq_gov_msm.c
index 9ad1811..6ddbf4e 100644
--- a/drivers/cpufreq/cpufreq_gov_msm.c
+++ b/drivers/cpufreq/cpufreq_gov_msm.c
@@ -18,26 +18,118 @@
#include <linux/kobject.h>
#include <linux/cpufreq.h>
#include <linux/platform_device.h>
+#include <linux/cpu_pm.h>
+#include <linux/pm_qos.h>
+#include <linux/hrtimer.h>
+#include <linux/tick.h>
#include <mach/msm_dcvs.h>
+struct cpu_idle_info {
+ int enabled;
+ int dcvs_core_id;
+ struct pm_qos_request pm_qos_req;
+};
+
+static DEFINE_PER_CPU_SHARED_ALIGNED(struct cpu_idle_info, cpu_idle_info);
+static DEFINE_PER_CPU_SHARED_ALIGNED(u64, iowait_on_cpu);
+static uint32_t latency;
+
+static int msm_dcvs_idle_notifier(int core_num,
+ enum msm_core_control_event event)
+{
+ struct cpu_idle_info *info = &per_cpu(cpu_idle_info, core_num);
+
+ switch (event) {
+ case MSM_DCVS_ENABLE_IDLE_PULSE:
+ info->enabled = true;
+ break;
+
+ case MSM_DCVS_DISABLE_IDLE_PULSE:
+ info->enabled = false;
+ break;
+
+ case MSM_DCVS_ENABLE_HIGH_LATENCY_MODES:
+ pm_qos_update_request(&info->pm_qos_req, PM_QOS_DEFAULT_VALUE);
+ break;
+
+ case MSM_DCVS_DISABLE_HIGH_LATENCY_MODES:
+ pm_qos_update_request(&info->pm_qos_req, latency);
+ break;
+ }
+
+ return 0;
+}
+
+static int msm_cpuidle_notifier(struct notifier_block *self, unsigned long cmd,
+ void *v)
+{
+ struct cpu_idle_info *info =
+ &per_cpu(cpu_idle_info, smp_processor_id());
+ u64 io_wait_us = 0;
+ u64 prev_io_wait_us = 0;
+ u64 last_update_time = 0;
+ u64 val = 0;
+ uint32_t iowaited = 0;
+
+ if (!info->enabled)
+ return NOTIFY_OK;
+
+ switch (cmd) {
+ case CPU_PM_ENTER:
+ val = get_cpu_iowait_time_us(smp_processor_id(),
+ &last_update_time);
+ /* val could be -1 when NOHZ is not enabled */
+ if (val == (u64)-1)
+ val = 0;
+ per_cpu(iowait_on_cpu, smp_processor_id()) = val;
+ msm_dcvs_idle(info->dcvs_core_id, MSM_DCVS_IDLE_ENTER, 0);
+ break;
+
+ case CPU_PM_EXIT:
+ prev_io_wait_us = per_cpu(iowait_on_cpu, smp_processor_id());
+ val = get_cpu_iowait_time_us(smp_processor_id(),
+ &last_update_time);
+ if (val == (u64)-1)
+ val = 0;
+ io_wait_us = val;
+ iowaited = (io_wait_us - prev_io_wait_us);
+ msm_dcvs_idle(info->dcvs_core_id, MSM_DCVS_IDLE_EXIT, iowaited);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block idle_nb = {
+ .notifier_call = msm_cpuidle_notifier,
+};
+
+static void msm_gov_idle_source_init(int cpu, int dcvs_core_id)
+{
+ struct cpu_idle_info *info = NULL;
+
+ info = &per_cpu(cpu_idle_info, cpu);
+ info->dcvs_core_id = dcvs_core_id;
+
+ pm_qos_add_request(&info->pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
+ PM_QOS_DEFAULT_VALUE);
+}
+
struct msm_gov {
- int cpu;
- unsigned int cur_freq;
- unsigned int min_freq;
- unsigned int max_freq;
- struct msm_dcvs_freq gov_notifier;
- struct cpufreq_policy *policy;
+ int cpu;
+ unsigned int cur_freq;
+ unsigned int min_freq;
+ unsigned int max_freq;
+ struct cpufreq_policy *policy;
+ int dcvs_core_id;
};
static DEFINE_PER_CPU_SHARED_ALIGNED(struct mutex, gov_mutex);
static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_gov, msm_gov_info);
-static char core_name[NR_CPUS][10];
static void msm_gov_check_limits(struct cpufreq_policy *policy)
{
struct msm_gov *gov = &per_cpu(msm_gov_info, policy->cpu);
- struct msm_dcvs_freq *dcvs_notifier =
- &(per_cpu(msm_gov_info, policy->cpu).gov_notifier);
if (policy->max < gov->cur_freq)
__cpufreq_driver_target(policy, policy->max,
@@ -52,15 +144,14 @@
gov->cur_freq = policy->cur;
gov->min_freq = policy->min;
gov->max_freq = policy->max;
- msm_dcvs_update_limits(dcvs_notifier);
+ msm_dcvs_update_limits(gov->dcvs_core_id);
}
-static int msm_dcvs_freq_set(struct msm_dcvs_freq *self,
+static int msm_dcvs_freq_set(int core_num,
unsigned int freq)
{
int ret = -EINVAL;
- struct msm_gov *gov =
- container_of(self, struct msm_gov, gov_notifier);
+ struct msm_gov *gov = &per_cpu(msm_gov_info, core_num);
mutex_lock(&per_cpu(gov_mutex, gov->cpu));
@@ -84,11 +175,9 @@
return ret;
}
-static unsigned int msm_dcvs_freq_get(struct msm_dcvs_freq *self)
+static unsigned int msm_dcvs_freq_get(int core_num)
{
- struct msm_gov *gov =
- container_of(self, struct msm_gov, gov_notifier);
-
+ struct msm_gov *gov = &per_cpu(msm_gov_info, core_num);
/*
* the rw_sem in cpufreq is always held when this is called.
* The policy->cur won't be updated in this case - so it is safe to
@@ -104,8 +193,6 @@
int ret = 0;
int handle = 0;
struct msm_gov *gov = &per_cpu(msm_gov_info, policy->cpu);
- struct msm_dcvs_freq *dcvs_notifier =
- &(per_cpu(msm_gov_info, cpu).gov_notifier);
switch (event) {
case CPUFREQ_GOV_START:
@@ -115,15 +202,14 @@
mutex_lock(&per_cpu(gov_mutex, cpu));
per_cpu(msm_gov_info, cpu).cpu = cpu;
gov->policy = policy;
- dcvs_notifier->core_name = core_name[cpu];
- handle = msm_dcvs_freq_sink_start(dcvs_notifier);
+ handle = msm_dcvs_freq_sink_start(gov->dcvs_core_id);
BUG_ON(handle < 0);
msm_gov_check_limits(policy);
mutex_unlock(&per_cpu(gov_mutex, cpu));
break;
case CPUFREQ_GOV_STOP:
- msm_dcvs_freq_sink_stop(dcvs_notifier);
+ msm_dcvs_freq_sink_stop(gov->dcvs_core_id);
break;
case CPUFREQ_GOV_LIMITS:
@@ -144,26 +230,40 @@
static int __devinit msm_gov_probe(struct platform_device *pdev)
{
- int ret = 0;
int cpu;
struct msm_dcvs_core_info *core = NULL;
+ struct msm_dcvs_core_info *core_info = NULL;
+ struct msm_gov_platform_data *pdata = pdev->dev.platform_data;
int sensor = 0;
core = pdev->dev.platform_data;
+ core_info = pdata->info;
+ latency = pdata->latency;
for_each_possible_cpu(cpu) {
+ struct msm_gov *gov = &per_cpu(msm_gov_info, cpu);
+
mutex_init(&per_cpu(gov_mutex, cpu));
- snprintf(core_name[cpu], 10, "cpu%d", cpu);
if (cpu < core->num_cores)
- sensor = core->sensors[cpu];
- ret = msm_dcvs_register_core(core_name[cpu], core,
+ sensor = core_info->sensors[cpu];
+ gov->dcvs_core_id = msm_dcvs_register_core(
+ MSM_DCVS_CORE_TYPE_CPU,
+ cpu,
+ core_info,
msm_dcvs_freq_set,
msm_dcvs_freq_get,
+ msm_dcvs_idle_notifier,
sensor);
- if (ret)
+ if (gov->dcvs_core_id < 0) {
pr_err("Unable to register core for %d\n", cpu);
+ return -EINVAL;
+ }
+
+ msm_gov_idle_source_init(cpu, gov->dcvs_core_id);
}
+ cpu_pm_register_notifier(&idle_nb);
+
return cpufreq_register_governor(&cpufreq_gov_msm);
}
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index 785ba6c..78161b6 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -869,7 +869,11 @@
static void dbs_input_event(struct input_handle *handle, unsigned int type,
unsigned int code, int value)
{
- int i;
+ int i, j;
+ struct cpumask cpus_scheduled;
+ struct cpu_dbs_info_s *dbs_info;
+ cpumask_clear(&cpus_scheduled);
+
if ((dbs_tuners_ins.powersave_bias == POWERSAVE_BIAS_MAXLEVEL) ||
(dbs_tuners_ins.powersave_bias == POWERSAVE_BIAS_MINLEVEL)) {
@@ -878,7 +882,23 @@
}
for_each_online_cpu(i) {
+ dbs_info = &per_cpu(od_cpu_dbs_info, i);
+
+ if (!dbs_info->cur_policy) {
+ pr_err("Dbs policy is NULL\n");
+ continue;
+ }
+
+ for_each_cpu(j, &cpus_scheduled) {
+ if (cpumask_test_cpu(j, dbs_info->cur_policy->cpus))
+ goto skip_schedule;
+ }
+ cpumask_set_cpu(i, &cpus_scheduled);
queue_work_on(i, input_wq, &per_cpu(dbs_refresh_work, i));
+
+ /* This CPU is already running at new frequency */
+skip_schedule:
+ ;
}
}
diff --git a/drivers/gpu/msm/a3xx_reg.h b/drivers/gpu/msm/a3xx_reg.h
index 6a010a9..2681836 100644
--- a/drivers/gpu/msm/a3xx_reg.h
+++ b/drivers/gpu/msm/a3xx_reg.h
@@ -531,7 +531,11 @@
#define RBBM_BLOCK_ID_MARB_3 0x2b
/* RBBM_CLOCK_CTL default value */
-#define A3XX_RBBM_CLOCK_CTL_DEFAULT 0xBFFFFFFF
+#define A305_RBBM_CLOCK_CTL_DEFAULT 0xAAAAAAAA
+#define A320_RBBM_CLOCK_CTL_DEFAULT 0xBFFFFFFF
+#define A330_RBBM_CLOCK_CTL_DEFAULT 0xAAAAAAAE
+
+#define A330_RBBM_GPR0_CTL_DEFAULT 0x0AE2B8AE
/* COUNTABLE FOR SP PERFCOUNTER */
#define SP_FS_FULL_ALU_INSTRUCTIONS 0x0E
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 97711e2..4eca776 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -2263,12 +2263,15 @@
static uint io_cnt;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+ struct adreno_context *adreno_ctx = context->devctxt;
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;
memset(prev_reg_val, 0, sizeof(prev_reg_val));
@@ -2278,12 +2281,20 @@
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) {
- 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);
- status = -EINVAL;
- goto done;
+ if (!(adreno_ctx->flags & CTXT_FLAGS_USER_GENERATED_TS)) {
+ KGSL_DRV_ERR(device,
+ "Cannot wait for invalid ts <%d:0x%x>, "
+ "last issued ts <%d:0x%x>\n",
+ context_id, timestamp, context_id, ts_issued);
+ status = -EINVAL;
+ goto done;
+ } else
+ retry_ts_cmp = 1;
}
/*
@@ -2353,7 +2364,22 @@
time_elapsed += wait;
wait = KGSL_TIMEOUT_PART;
- retries++;
+ 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) {
+ 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);
+ status = -EINVAL;
+ goto done;
+ }
+ retry_ts_cmp = 0;
+ }
} while (!msecs || time_elapsed < msecs);
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 66402fe..fd9a0c3 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -33,7 +33,7 @@
/* Flags to control command packet settings */
#define KGSL_CMD_FLAGS_NONE 0x00000000
#define KGSL_CMD_FLAGS_PMODE 0x00000001
-#define KGSL_CMD_FLAGS_DUMMY_INTR_CMD 0x00000002
+#define KGSL_CMD_FLAGS_INTERNAL_ISSUE 0x00000002
/* Command identifiers */
#define KGSL_CONTEXT_TO_MEM_IDENTIFIER 0x2EADBEEF
@@ -179,6 +179,8 @@
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);
struct kgsl_memdesc *adreno_find_region(struct kgsl_device *device,
unsigned int pt_base,
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index 104baf8..4c7534c 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -433,6 +433,19 @@
tmp_ctx.cmd = cmd;
}
+unsigned int adreno_a3xx_rbbm_clock_ctl_default(struct adreno_device
+ *adreno_dev)
+{
+ if (adreno_is_a305(adreno_dev))
+ return A305_RBBM_CLOCK_CTL_DEFAULT;
+ else if (adreno_is_a320(adreno_dev))
+ return A320_RBBM_CLOCK_CTL_DEFAULT;
+ else if (adreno_is_a330(adreno_dev))
+ return A330_RBBM_CLOCK_CTL_DEFAULT;
+
+ BUG_ON(1);
+}
+
/* Copy GMEM contents to system memory shadow. */
static unsigned int *build_gmem2sys_cmds(struct adreno_device *adreno_dev,
struct adreno_context *drawctxt,
@@ -442,7 +455,7 @@
unsigned int *start = cmds;
*cmds++ = cp_type0_packet(A3XX_RBBM_CLOCK_CTL, 1);
- *cmds++ = A3XX_RBBM_CLOCK_CTL_DEFAULT;
+ *cmds++ = adreno_a3xx_rbbm_clock_ctl_default(adreno_dev);
*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
*cmds++ = CP_REG(A3XX_RB_MODE_CONTROL);
@@ -1238,7 +1251,7 @@
unsigned int *start = cmds;
*cmds++ = cp_type0_packet(A3XX_RBBM_CLOCK_CTL, 1);
- *cmds++ = A3XX_RBBM_CLOCK_CTL_DEFAULT;
+ *cmds++ = adreno_a3xx_rbbm_clock_ctl_default(adreno_dev);
*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5);
*cmds++ = CP_REG(A3XX_HLSQ_CONTROL_0_REG);
@@ -2826,7 +2839,11 @@
/* Enable Clock gating */
adreno_regwrite(device, A3XX_RBBM_CLOCK_CTL,
- A3XX_RBBM_CLOCK_CTL_DEFAULT);
+ adreno_a3xx_rbbm_clock_ctl_default(adreno_dev));
+
+ if (adreno_is_a330(adreno_dev))
+ adreno_regwrite(device, A3XX_RBBM_GPR0_CTL,
+ A330_RBBM_GPR0_CTL_DEFAULT);
/* Set the OCMEM base address for A330 */
if (adreno_is_a330(adreno_dev)) {
diff --git a/drivers/gpu/msm/adreno_a3xx_snapshot.c b/drivers/gpu/msm/adreno_a3xx_snapshot.c
index d49fc23..a410445 100644
--- a/drivers/gpu/msm/adreno_a3xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a3xx_snapshot.c
@@ -383,7 +383,7 @@
/* Enable Clock gating */
adreno_regwrite(device, A3XX_RBBM_CLOCK_CTL,
- A3XX_RBBM_CLOCK_CTL_DEFAULT);
+ adreno_a3xx_rbbm_clock_ctl_default(adreno_dev));
return snapshot;
}
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index 7cbc7a8..a107a27 100644
--- a/drivers/gpu/msm/adreno_drawctxt.c
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -169,6 +169,14 @@
if (flags & KGSL_CONTEXT_PER_CONTEXT_TS)
drawctxt->flags |= CTXT_FLAGS_PER_CONTEXT_TS;
+ if (flags & KGSL_CONTEXT_USER_GENERATED_TS) {
+ if (!(flags & KGSL_CONTEXT_PER_CONTEXT_TS)) {
+ ret = -EINVAL;
+ goto err;
+ }
+ drawctxt->flags |= CTXT_FLAGS_USER_GENERATED_TS;
+ }
+
ret = adreno_dev->gpudev->ctxt_create(adreno_dev, drawctxt);
if (ret)
goto err;
diff --git a/drivers/gpu/msm/adreno_drawctxt.h b/drivers/gpu/msm/adreno_drawctxt.h
index 034d6e9..58e4791 100644
--- a/drivers/gpu/msm/adreno_drawctxt.h
+++ b/drivers/gpu/msm/adreno_drawctxt.h
@@ -48,6 +48,8 @@
#define CTXT_FLAGS_GPU_HANG_RECOVERED BIT(12)
/* Context is being destroyed so dont save it */
#define CTXT_FLAGS_BEING_DESTROYED BIT(13)
+/* User mode generated timestamps enabled */
+#define CTXT_FLAGS_USER_GENERATED_TS BIT(14)
struct kgsl_device;
struct adreno_device;
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index 0dd140b..da9daf7 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -487,11 +487,10 @@
adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb,
struct adreno_context *context,
unsigned int flags, unsigned int *cmds,
- int sizedwords)
+ int sizedwords, uint32_t timestamp)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(rb->device);
unsigned int *ringcmds;
- unsigned int timestamp;
unsigned int total_sizedwords = sizedwords;
unsigned int i;
unsigned int rcmd_gpu;
@@ -506,19 +505,38 @@
if (context && context->flags & CTXT_FLAGS_PER_CONTEXT_TS)
context_id = context->id;
+ if ((context->flags & CTXT_FLAGS_USER_GENERATED_TS) &&
+ (!(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE))) {
+ if (timestamp_cmp(rb->timestamp[context_id],
+ timestamp) >= 0) {
+ KGSL_DRV_ERR(rb->device,
+ "Invalid user generated ts <%d:0x%x>, "
+ "less than last issued ts <%d:0x%x>\n",
+ context_id, timestamp, context_id,
+ rb->timestamp[context_id]);
+ return -ERANGE;
+ }
+ }
+
/* reserve space to temporarily turn off protected mode
* error checking if needed
*/
total_sizedwords += flags & KGSL_CMD_FLAGS_PMODE ? 4 : 0;
/* 2 dwords to store the start of command sequence */
total_sizedwords += 2;
- total_sizedwords += context ? 7 : 0;
+ /*
+ * Add CP_COND_EXEC commands to generate CP_INTERRUPT only
+ * for submissions from userspace.
+ */
+ total_sizedwords += (context &&
+ !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) ? 7 : 0;
if (adreno_is_a3xx(adreno_dev))
total_sizedwords += 7;
total_sizedwords += 2; /* scratchpad ts for recovery */
- if (context && context->flags & CTXT_FLAGS_PER_CONTEXT_TS) {
+ if (context && context->flags & CTXT_FLAGS_PER_CONTEXT_TS &&
+ !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) {
total_sizedwords += 3; /* sop timestamp */
total_sizedwords += 4; /* eop timestamp */
total_sizedwords += 3; /* global timestamp without cache
@@ -564,10 +582,13 @@
/* always increment the global timestamp. once. */
rb->timestamp[KGSL_MEMSTORE_GLOBAL]++;
- if (context && !(flags & KGSL_CMD_FLAGS_DUMMY_INTR_CMD)) {
+ /* Do not update context's timestamp for internal submissions */
+ if (context && !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) {
if (context_id == KGSL_MEMSTORE_GLOBAL)
rb->timestamp[context->id] =
rb->timestamp[KGSL_MEMSTORE_GLOBAL];
+ else if (context->flags & CTXT_FLAGS_USER_GENERATED_TS)
+ rb->timestamp[context_id] = timestamp;
else
rb->timestamp[context_id]++;
}
@@ -591,7 +612,8 @@
GSL_RB_WRITE(ringcmds, rcmd_gpu, 0x00);
}
- if (context && context->flags & CTXT_FLAGS_PER_CONTEXT_TS) {
+ if (context && context->flags & CTXT_FLAGS_PER_CONTEXT_TS
+ && !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) {
/* start-of-pipeline timestamp */
GSL_RB_WRITE(ringcmds, rcmd_gpu,
cp_type3_packet(CP_MEM_WRITE, 2));
@@ -619,11 +641,13 @@
cp_type3_packet(CP_EVENT_WRITE, 3));
GSL_RB_WRITE(ringcmds, rcmd_gpu, CACHE_FLUSH_TS);
GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr +
- KGSL_MEMSTORE_OFFSET(context_id, eoptimestamp)));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, rb->timestamp[context_id]);
+ KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
+ eoptimestamp)));
+ GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ rb->timestamp[KGSL_MEMSTORE_GLOBAL]);
}
- if (context) {
+ if (context && !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) {
/* Conditional execution based on memory values */
GSL_RB_WRITE(ringcmds, rcmd_gpu,
cp_type3_packet(CP_COND_EXEC, 4));
@@ -675,8 +699,8 @@
device->state & KGSL_STATE_HUNG)
return;
- adreno_ringbuffer_addcmds(rb, a_ctxt, KGSL_CMD_FLAGS_DUMMY_INTR_CMD,
- cmds, sizedwords);
+ adreno_ringbuffer_addcmds(rb, a_ctxt, KGSL_CMD_FLAGS_INTERNAL_ISSUE,
+ cmds, sizedwords, 0);
}
unsigned int
@@ -692,7 +716,11 @@
if (device->state & KGSL_STATE_HUNG)
return kgsl_readtimestamp(device, KGSL_MEMSTORE_GLOBAL,
KGSL_TIMESTAMP_RETIRED);
- return adreno_ringbuffer_addcmds(rb, drawctxt, flags, cmds, sizedwords);
+
+ flags |= KGSL_CMD_FLAGS_INTERNAL_ISSUE;
+
+ return adreno_ringbuffer_addcmds(rb, drawctxt, flags, cmds,
+ sizedwords, 0);
}
static bool _parse_ibs(struct kgsl_device_private *dev_priv, uint gpuaddr,
@@ -973,8 +1001,9 @@
adreno_drawctxt_switch(adreno_dev, drawctxt, flags);
*timestamp = adreno_ringbuffer_addcmds(&adreno_dev->ringbuffer,
- drawctxt, 0,
- &link[0], (cmds - link));
+ drawctxt,
+ 0,
+ &link[0], (cmds - link), *timestamp);
KGSL_CMD_INFO(device, "<%d:0x%x> g %08x numibs %d\n",
context->id, *timestamp, (unsigned int)ibdesc, numibs);
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 5ba844a..afe384b 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -722,6 +722,7 @@
list_add(&private->list, &kgsl_driver.process_list);
kgsl_process_init_sysfs(private);
+ kgsl_process_init_debugfs(private);
out:
mutex_unlock(&kgsl_driver.process_mutex);
@@ -744,6 +745,7 @@
goto unlock;
kgsl_process_uninit_sysfs(private);
+ debugfs_remove_recursive(private->debug_root);
list_del(&private->list);
@@ -1080,6 +1082,7 @@
unsigned int cmd, void *data)
{
int result = 0;
+ int i = 0;
struct kgsl_ringbuffer_issueibcmds *param = data;
struct kgsl_ibdesc *ibdesc;
struct kgsl_context *context;
@@ -1141,6 +1144,16 @@
param->numibs = 1;
}
+ for (i = 0; i < param->numibs; i++) {
+ if (!kgsl_mmu_gpuaddr_in_range(ibdesc[i].gpuaddr)) {
+ result = -ERANGE;
+ KGSL_DRV_ERR(dev_priv->device,
+ "invalid ib base GPU virtual addr %x\n",
+ ibdesc[i].gpuaddr);
+ goto free_ibdesc;
+ }
+ }
+
result = dev_priv->device->ftbl->issueibcmds(dev_priv,
context,
ibdesc,
@@ -1468,6 +1481,8 @@
return -ENOMEM;
memdesc->sglen = sglen;
+ memdesc->sglen_alloc = sglen;
+
sg_init_table(memdesc->sg, sglen);
spin_lock(¤t->mm->page_table_lock);
@@ -1632,11 +1647,16 @@
#endif
static int kgsl_setup_ion(struct kgsl_mem_entry *entry,
- struct kgsl_pagetable *pagetable, int fd)
+ struct kgsl_pagetable *pagetable, void *data)
{
struct ion_handle *handle;
struct scatterlist *s;
struct sg_table *sg_table;
+ struct kgsl_map_user_mem *param = data;
+ int fd = param->fd;
+
+ if (!param->len)
+ return -EINVAL;
if (IS_ERR_OR_NULL(kgsl_ion_client))
return -ENODEV;
@@ -1741,8 +1761,7 @@
entry->memtype = KGSL_MEM_ENTRY_ASHMEM;
break;
case KGSL_USER_MEM_TYPE_ION:
- result = kgsl_setup_ion(entry, private->pagetable,
- param->fd);
+ result = kgsl_setup_ion(entry, private->pagetable, data);
break;
default:
KGSL_CORE_ERR("Invalid memory type: %x\n", memtype);
@@ -1754,6 +1773,11 @@
entry->memdesc.priv |= param->flags & KGSL_MEMTYPE_MASK;
+ if (entry->memdesc.size >= SZ_1M)
+ entry->memdesc.priv |= ilog2(SZ_1M) << KGSL_MEMALIGN_SHIFT;
+ else if (entry->memdesc.size >= SZ_64K)
+ entry->memdesc.priv |= ilog2(SZ_64K) << KGSL_MEMALIGN_SHIFT;
+
result = kgsl_mmu_map(private->pagetable,
&entry->memdesc,
GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index 416eda9..2861117 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -135,7 +135,8 @@
unsigned int size;
unsigned int priv;
struct scatterlist *sg;
- unsigned int sglen;
+ unsigned int sglen; /* Active entries in the sglist */
+ unsigned int sglen_alloc; /* Allocated entries in the sglist */
struct kgsl_memdesc_ops *ops;
int flags;
};
@@ -182,6 +183,8 @@
struct kgsl_process_private *private, unsigned int gpuaddr,
size_t size);
+void kgsl_get_memory_usage(char *str, size_t len, unsigned int memflags);
+
int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts,
void (*cb)(struct kgsl_device *, void *, u32, u32), void *priv,
void *owner);
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index 545d2b3..b49c260 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -16,6 +16,7 @@
#include "kgsl.h"
#include "kgsl_device.h"
+#include "kgsl_sharedmem.h"
/*default log levels is error for everything*/
#define KGSL_LOG_LEVEL_DEFAULT 3
@@ -23,6 +24,7 @@
struct dentry *kgsl_debugfs_dir;
static struct dentry *pm_d_debugfs;
+struct dentry *proc_d_debugfs;
static int pm_dump_set(void *data, u64 val)
{
@@ -146,9 +148,90 @@
}
+static const char * const memtype_strings[] = {
+ "gpumem",
+ "pmem",
+ "ashmem",
+ "usermap",
+ "ion",
+};
+
+static const char *memtype_str(int memtype)
+{
+ if (memtype < ARRAY_SIZE(memtype_strings))
+ return memtype_strings[memtype];
+ return "unknown";
+}
+
+static int process_mem_print(struct seq_file *s, void *unused)
+{
+ struct kgsl_mem_entry *entry;
+ struct rb_node *node;
+ struct kgsl_process_private *private = s->private;
+ char flags[4];
+ char usage[16];
+ unsigned int align;
+
+ spin_lock(&private->mem_lock);
+ seq_printf(s, "%8s %8s %5s %10s %16s %5s\n",
+ "gpuaddr", "size", "flags", "type", "usage", "sglen");
+ for (node = rb_first(&private->mem_rb); node; node = rb_next(node)) {
+ struct kgsl_memdesc *m;
+
+ entry = rb_entry(node, struct kgsl_mem_entry, node);
+ m = &entry->memdesc;
+
+ flags[0] = m->priv & KGSL_MEMFLAGS_GLOBAL ? 'g' : '-';
+ flags[1] = m->priv & KGSL_MEMFLAGS_GPUREADONLY ? 'r' : '-';
+
+ align = (m->priv & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT;
+ if (align >= ilog2(SZ_1M))
+ flags[2] = 'L';
+ else if (align >= ilog2(SZ_64K))
+ flags[2] = 'l';
+ else
+ flags[2] = '-';
+
+ flags[3] = '\0';
+
+ kgsl_get_memory_usage(usage, sizeof(usage), m->priv);
+
+ seq_printf(s, "%08x %8d %5s %10s %16s %5d\n",
+ m->gpuaddr, m->size, flags,
+ memtype_str(entry->memtype), usage, m->sglen);
+ }
+ spin_unlock(&private->mem_lock);
+ return 0;
+}
+
+static int process_mem_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, process_mem_print, inode->i_private);
+}
+
+static const struct file_operations process_mem_fops = {
+ .open = process_mem_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+void
+kgsl_process_init_debugfs(struct kgsl_process_private *private)
+{
+ unsigned char name[16];
+
+ snprintf(name, sizeof(name), "%d", private->pid);
+
+ private->debug_root = debugfs_create_dir(name, proc_d_debugfs);
+ debugfs_create_file("mem", 0400, private->debug_root, private,
+ &process_mem_fops);
+}
+
void kgsl_core_debugfs_init(void)
{
kgsl_debugfs_dir = debugfs_create_dir("kgsl", 0);
+ proc_d_debugfs = debugfs_create_dir("proc", kgsl_debugfs_dir);
}
void kgsl_core_debugfs_close(void)
diff --git a/drivers/gpu/msm/kgsl_debugfs.h b/drivers/gpu/msm/kgsl_debugfs.h
index 5e10988..898c4e9 100644
--- a/drivers/gpu/msm/kgsl_debugfs.h
+++ b/drivers/gpu/msm/kgsl_debugfs.h
@@ -15,6 +15,7 @@
#define _KGSL_DEBUGFS_H
struct kgsl_device;
+struct kgsl_process_private;
#ifdef CONFIG_DEBUG_FS
void kgsl_core_debugfs_init(void);
@@ -28,11 +29,16 @@
return kgsl_debugfs_dir;
}
+int kgsl_process_init_debugfs(struct kgsl_process_private *);
#else
static inline void kgsl_core_debugfs_init(void) { }
static inline void kgsl_device_debugfs_init(struct kgsl_device *device) { }
static inline void kgsl_core_debugfs_close(void) { }
static inline struct dentry *kgsl_get_debugfs_dir(void) { return NULL; }
+static inline int kgsl_process_init_debugfs(struct kgsl_process_private *)
+{
+ return 0;
+}
#endif
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index dc597f5..4394118 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -251,6 +251,7 @@
struct kgsl_pagetable *pagetable;
struct list_head list;
struct kobject kobj;
+ struct dentry *debug_root;
struct {
unsigned int cur;
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index 54ba5ad..dbb88ee 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -606,6 +606,7 @@
int ret;
struct gen_pool *pool;
int size;
+ int page_align = ilog2(PAGE_SIZE);
if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE) {
if (memdesc->sglen == 1) {
@@ -630,7 +631,17 @@
/* Allocate from kgsl pool if it exists for global mappings */
pool = _get_pool(pagetable, memdesc->priv);
- memdesc->gpuaddr = gen_pool_alloc(pool, size);
+ /* Allocate aligned virtual addresses for iommu. This allows
+ * more efficient pagetable entries if the physical memory
+ * is also aligned. Don't do this for GPUMMU, because
+ * the address space is so small.
+ */
+ if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype() &&
+ (memdesc->priv & KGSL_MEMALIGN_MASK)) {
+ page_align = (memdesc->priv & KGSL_MEMALIGN_MASK)
+ >> KGSL_MEMALIGN_SHIFT;
+ }
+ memdesc->gpuaddr = gen_pool_alloc_aligned(pool, size, page_align);
if (memdesc->gpuaddr == 0) {
KGSL_CORE_ERR("gen_pool_alloc(%d) failed from pool: %s\n",
size,
diff --git a/drivers/gpu/msm/kgsl_pwrscale_msm.c b/drivers/gpu/msm/kgsl_pwrscale_msm.c
index 3f9b3d7..b302bee 100644
--- a/drivers/gpu/msm/kgsl_pwrscale_msm.c
+++ b/drivers/gpu/msm/kgsl_pwrscale_msm.c
@@ -20,21 +20,21 @@
#include "kgsl_trace.h"
struct msm_priv {
- struct kgsl_device *device;
- int enabled;
- int handle;
- unsigned int cur_freq;
- struct msm_dcvs_idle idle_source;
- struct msm_dcvs_freq freq_sink;
- struct msm_dcvs_core_info *core_info;
- int gpu_busy;
+ struct kgsl_device *device;
+ int enabled;
+ unsigned int cur_freq;
+ struct msm_dcvs_core_info *core_info;
+ int gpu_busy;
+ int dcvs_core_id;
};
-static int msm_idle_enable(struct msm_dcvs_idle *self,
- enum msm_core_control_event event)
+/* reference to be used in idle and freq callbacks */
+static struct msm_priv *the_msm_priv;
+
+static int msm_idle_enable(int type_core_num,
+ enum msm_core_control_event event)
{
- struct msm_priv *priv = container_of(self, struct msm_priv,
- idle_source);
+ struct msm_priv *priv = the_msm_priv;
switch (event) {
case MSM_DCVS_ENABLE_IDLE_PULSE:
@@ -53,12 +53,10 @@
/* Set the requested frequency if it is within 5MHz (delta) of a
* supported frequency.
*/
-static int msm_set_freq(struct msm_dcvs_freq *self,
- unsigned int freq)
+static int msm_set_freq(int core_num, unsigned int freq)
{
int i, delta = 5000000;
- struct msm_priv *priv = container_of(self, struct msm_priv,
- freq_sink);
+ struct msm_priv *priv = the_msm_priv;
struct kgsl_device *device = priv->device;
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
@@ -79,10 +77,10 @@
return priv->cur_freq / 1000;
}
-static unsigned int msm_get_freq(struct msm_dcvs_freq *self)
+static unsigned int msm_get_freq(int core_num)
{
- struct msm_priv *priv = container_of(self, struct msm_priv,
- freq_sink);
+ struct msm_priv *priv = the_msm_priv;
+
/* return current frequency in kHz */
return priv->cur_freq / 1000;
}
@@ -92,7 +90,7 @@
{
struct msm_priv *priv = pwrscale->priv;
if (priv->enabled && !priv->gpu_busy) {
- msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_EXIT, 0);
+ msm_dcvs_idle(priv->dcvs_core_id, MSM_DCVS_IDLE_EXIT, 0);
trace_kgsl_mpdcvs(device, 1);
priv->gpu_busy = 1;
}
@@ -106,7 +104,8 @@
if (priv->enabled && priv->gpu_busy)
if (device->ftbl->isidle(device)) {
- msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_ENTER, 0);
+ msm_dcvs_idle(priv->dcvs_core_id,
+ MSM_DCVS_IDLE_ENTER, 0);
trace_kgsl_mpdcvs(device, 0);
priv->gpu_busy = 0;
}
@@ -119,7 +118,7 @@
struct msm_priv *priv = pwrscale->priv;
if (priv->enabled && priv->gpu_busy) {
- msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_ENTER, 0);
+ msm_dcvs_idle(priv->dcvs_core_id, MSM_DCVS_IDLE_ENTER, 0);
trace_kgsl_mpdcvs(device, 0);
priv->gpu_busy = 0;
}
@@ -127,13 +126,14 @@
return;
}
-static void msm_remove_io_fraction(struct kgsl_device *device)
+static void msm_set_io_fraction(struct kgsl_device *device,
+ unsigned int value)
{
int i;
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
for (i = 0; i < pwr->num_pwrlevels; i++)
- pwr->pwrlevels[i].io_fraction = 100;
+ pwr->pwrlevels[i].io_fraction = value;
}
@@ -154,59 +154,58 @@
{
struct msm_priv *priv;
struct msm_dcvs_freq_entry *tbl;
- int i, ret, low_level;
+ int i, ret = -EINVAL, low_level;
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
struct platform_device *pdev =
container_of(device->parentdev, struct platform_device, dev);
struct kgsl_device_platform_data *pdata = pdev->dev.platform_data;
- priv = pwrscale->priv = kzalloc(sizeof(struct msm_priv),
- GFP_KERNEL);
- if (pwrscale->priv == NULL)
- return -ENOMEM;
+ if (the_msm_priv) {
+ priv = pwrscale->priv = the_msm_priv;
+ } else {
+ priv = pwrscale->priv = kzalloc(sizeof(struct msm_priv),
+ GFP_KERNEL);
+ if (pwrscale->priv == NULL)
+ return -ENOMEM;
- priv->core_info = pdata->core_info;
- tbl = priv->core_info->freq_tbl;
- /* Fill in frequency table from low to high, reversing order. */
- low_level = pwr->num_pwrlevels - KGSL_PWRLEVEL_LAST_OFFSET;
- for (i = 0; i <= low_level; i++)
- tbl[i].freq = pwr->pwrlevels[low_level - i].gpu_freq / 1000;
- ret = msm_dcvs_register_core(device->name, priv->core_info,
- msm_set_freq, msm_get_freq,
- priv->core_info->sensors[0]);
- if (ret) {
- KGSL_PWR_ERR(device, "msm_dcvs_register_core failed");
- goto err;
+ priv->core_info = pdata->core_info;
+ tbl = priv->core_info->freq_tbl;
+ /* Fill in frequency table from low to high, reversing order. */
+ low_level = pwr->num_pwrlevels - KGSL_PWRLEVEL_LAST_OFFSET;
+ for (i = 0; i <= low_level; i++)
+ tbl[i].freq =
+ pwr->pwrlevels[low_level - i].gpu_freq / 1000;
+ priv->dcvs_core_id =
+ msm_dcvs_register_core(MSM_DCVS_CORE_TYPE_GPU,
+ 0,
+ priv->core_info,
+ msm_set_freq, msm_get_freq, msm_idle_enable,
+ priv->core_info->sensors[0]);
+ if (priv->dcvs_core_id < 0) {
+ KGSL_PWR_ERR(device, "msm_dcvs_register_core failed");
+ goto err;
+ }
+ the_msm_priv = priv;
}
-
priv->device = device;
- priv->idle_source.enable = msm_idle_enable;
- priv->idle_source.core_name = device->name;
- priv->handle = msm_dcvs_idle_source_register(&priv->idle_source);
- if (priv->handle < 0) {
- ret = priv->handle;
- KGSL_PWR_ERR(device, "msm_dcvs_idle_source_register failed\n");
- goto err;
- }
-
- priv->freq_sink.core_name = device->name;
- ret = msm_dcvs_freq_sink_start(&priv->freq_sink);
+ ret = msm_dcvs_freq_sink_start(priv->dcvs_core_id);
if (ret >= 0) {
if (device->ftbl->isidle(device)) {
priv->gpu_busy = 0;
- msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_ENTER, 0);
+ msm_dcvs_idle(priv->dcvs_core_id,
+ MSM_DCVS_IDLE_ENTER, 0);
} else {
priv->gpu_busy = 1;
}
- msm_remove_io_fraction(device);
+ msm_set_io_fraction(device, 0);
return 0;
}
KGSL_PWR_ERR(device, "msm_dcvs_freq_sink_register failed\n");
- msm_dcvs_idle_source_unregister(&priv->idle_source);
err:
- kfree(pwrscale->priv);
+ if (!the_msm_priv)
+ kfree(pwrscale->priv);
pwrscale->priv = NULL;
return ret;
@@ -219,9 +218,7 @@
if (pwrscale->priv == NULL)
return;
- msm_dcvs_idle_source_unregister(&priv->idle_source);
- msm_dcvs_freq_sink_stop(&priv->freq_sink);
- kfree(pwrscale->priv);
+ msm_dcvs_freq_sink_stop(priv->dcvs_core_id);
pwrscale->priv = NULL;
msm_restore_io_fraction(device);
}
diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c
index bdc5686..77617ba 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.c
+++ b/drivers/gpu/msm/kgsl_sharedmem.c
@@ -317,21 +317,42 @@
struct vm_area_struct *vma,
struct vm_fault *vmf)
{
- unsigned long offset;
- struct page *page;
- int i;
+ int i, pgoff;
+ struct scatterlist *s = memdesc->sg;
+ unsigned int offset;
- offset = (unsigned long) vmf->virtual_address - vma->vm_start;
+ offset = ((unsigned long) vmf->virtual_address - vma->vm_start);
- i = offset >> PAGE_SHIFT;
- page = sg_page(&memdesc->sg[i]);
- if (page == NULL)
+ if (offset >= memdesc->size)
return VM_FAULT_SIGBUS;
- get_page(page);
+ pgoff = offset >> PAGE_SHIFT;
- vmf->page = page;
- return 0;
+ /*
+ * The sglist might be comprised of mixed blocks of memory depending
+ * on how many 64K pages were allocated. This means we have to do math
+ * to find the actual 4K page to map in user space
+ */
+
+ for (i = 0; i < memdesc->sglen; i++) {
+ int npages = s->length >> PAGE_SHIFT;
+
+ if (pgoff < npages) {
+ struct page *page = sg_page(s);
+
+ page = nth_page(page, pgoff);
+
+ get_page(page);
+ vmf->page = page;
+
+ return 0;
+ }
+
+ pgoff -= npages;
+ s = sg_next(s);
+ }
+
+ return VM_FAULT_SIGBUS;
}
static int kgsl_page_alloc_vmflags(struct kgsl_memdesc *memdesc)
@@ -357,7 +378,7 @@
}
if (memdesc->sg)
for_each_sg(memdesc->sg, sg, sglen, i)
- __free_page(sg_page(sg));
+ __free_pages(sg_page(sg), get_order(sg->length));
}
static int kgsl_contiguous_vmflags(struct kgsl_memdesc *memdesc)
@@ -379,23 +400,32 @@
pgprot_t page_prot = pgprot_writecombine(PAGE_KERNEL);
struct page **pages = NULL;
struct scatterlist *sg;
+ int npages = PAGE_ALIGN(memdesc->size) >> PAGE_SHIFT;
int sglen = memdesc->sglen;
- int i;
+ int i, count = 0;
/* Don't map the guard page if it exists */
if (memdesc->flags & KGSL_MEMDESC_GUARD_PAGE)
sglen--;
/* create a list of pages to call vmap */
- pages = vmalloc(sglen * sizeof(struct page *));
+ pages = vmalloc(npages * sizeof(struct page *));
if (!pages) {
KGSL_CORE_ERR("vmalloc(%d) failed\n",
- sglen * sizeof(struct page *));
+ npages * sizeof(struct page *));
return -ENOMEM;
}
- for_each_sg(memdesc->sg, sg, sglen, i)
- pages[i] = sg_page(sg);
- memdesc->hostptr = vmap(pages, sglen,
+
+ for_each_sg(memdesc->sg, sg, sglen, i) {
+ struct page *page = sg_page(sg);
+ int j;
+
+ for (j = 0; j < sg->length >> PAGE_SHIFT; j++)
+ pages[count++] = page++;
+ }
+
+
+ memdesc->hostptr = vmap(pages, count,
VM_IOREMAP, page_prot);
KGSL_STATS_ADD(memdesc->size, kgsl_driver.stats.vmalloc,
kgsl_driver.stats.vmalloc_max);
@@ -503,14 +533,15 @@
static int
_kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc,
struct kgsl_pagetable *pagetable,
- size_t size, unsigned int protflags)
+ size_t size, unsigned int flags, unsigned int protflags)
{
- int i, order, ret = 0;
- int sglen = PAGE_ALIGN(size) / PAGE_SIZE;
+ int pcount = 0, order, ret = 0;
+ int j, len, page_size, sglen_alloc, sglen = 0;
struct page **pages = NULL;
pgprot_t page_prot = pgprot_writecombine(PAGE_KERNEL);
void *ptr;
struct sysinfo si;
+ unsigned int align;
/*
* Get the current memory information to be used in deciding if we
@@ -530,23 +561,36 @@
if (size >= ((si.freeram << PAGE_SHIFT) - SZ_32M))
return -ENOMEM;
+ align = (flags & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT;
+
+ page_size = (align >= ilog2(SZ_64K) && size >= SZ_64K)
+ ? SZ_64K : PAGE_SIZE;
+
+ /*
+ * There needs to be enough room in the sg structure to be able to
+ * service the allocation entirely with PAGE_SIZE sized chunks
+ */
+
+ sglen_alloc = PAGE_ALIGN(size) >> PAGE_SHIFT;
+
/*
* Add guard page to the end of the allocation when the
* IOMMU is in use.
*/
if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU)
- sglen++;
+ sglen_alloc++;
memdesc->size = size;
memdesc->pagetable = pagetable;
+ memdesc->priv |= (flags & KGSL_MEMALIGN_MASK);
memdesc->ops = &kgsl_page_alloc_ops;
- memdesc->sg = kgsl_sg_alloc(sglen);
+ memdesc->sg = kgsl_sg_alloc(sglen_alloc);
if (memdesc->sg == NULL) {
KGSL_CORE_ERR("vmalloc(%d) failed\n",
- sglen * sizeof(struct scatterlist));
+ sglen_alloc * sizeof(struct scatterlist));
ret = -ENOMEM;
goto done;
}
@@ -558,38 +602,52 @@
* two pages; well within the acceptable limits for using kmalloc.
*/
- pages = kmalloc(sglen * sizeof(struct page *), GFP_KERNEL);
+ pages = kmalloc(sglen_alloc * sizeof(struct page *), GFP_KERNEL);
if (pages == NULL) {
KGSL_CORE_ERR("kmalloc (%d) failed\n",
- sglen * sizeof(struct page *));
+ sglen_alloc * sizeof(struct page *));
ret = -ENOMEM;
goto done;
}
kmemleak_not_leak(memdesc->sg);
- memdesc->sglen = sglen;
- sg_init_table(memdesc->sg, sglen);
+ memdesc->sglen_alloc = sglen_alloc;
+ sg_init_table(memdesc->sg, sglen_alloc);
- for (i = 0; i < PAGE_ALIGN(size) / PAGE_SIZE; i++) {
+ len = size;
- /*
- * Don't use GFP_ZERO here because it is faster to memset the
- * range ourselves (see below)
- */
+ while (len > 0) {
+ struct page *page;
+ unsigned int gfp_mask = GFP_KERNEL | __GFP_HIGHMEM |
+ __GFP_NOWARN;
+ int j;
- pages[i] = alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
- if (pages[i] == NULL) {
- ret = -ENOMEM;
- memdesc->sglen = i;
- goto done;
+ /* don't waste space at the end of the allocation*/
+ if (len < page_size)
+ page_size = PAGE_SIZE;
+
+ if (page_size != PAGE_SIZE)
+ gfp_mask |= __GFP_COMP;
+
+ page = alloc_pages(gfp_mask, get_order(page_size));
+
+ if (page == NULL) {
+ if (page_size != PAGE_SIZE) {
+ page_size = PAGE_SIZE;
+ continue;
+ }
}
- sg_set_page(&memdesc->sg[i], pages[i], PAGE_SIZE, 0);
+ for (j = 0; j < page_size >> PAGE_SHIFT; j++)
+ pages[pcount++] = nth_page(page, j);
+
+ sg_set_page(&memdesc->sg[sglen++], page, page_size, 0);
+ len -= page_size;
}
- /* ADd the guard page to the end of the sglist */
+ /* Add the guard page to the end of the sglist */
if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU) {
/*
@@ -603,13 +661,14 @@
__GFP_HIGHMEM);
if (kgsl_guard_page != NULL) {
- sg_set_page(&memdesc->sg[sglen - 1], kgsl_guard_page,
+ sg_set_page(&memdesc->sg[sglen++], kgsl_guard_page,
PAGE_SIZE, 0);
memdesc->flags |= KGSL_MEMDESC_GUARD_PAGE;
- } else
- memdesc->sglen--;
+ }
}
+ memdesc->sglen = sglen;
+
/*
* All memory that goes to the user has to be zeroed out before it gets
* exposed to userspace. This means that the memory has to be mapped in
@@ -629,18 +688,16 @@
* path
*/
- ptr = vmap(pages, i, VM_IOREMAP, page_prot);
+ ptr = vmap(pages, pcount, VM_IOREMAP, page_prot);
if (ptr != NULL) {
memset(ptr, 0, memdesc->size);
dmac_flush_range(ptr, ptr + memdesc->size);
vunmap(ptr);
} else {
- int j;
-
/* Very, very, very slow path */
- for (j = 0; j < i; j++) {
+ for (j = 0; j < pcount; j++) {
ptr = kmap_atomic(pages[j]);
memset(ptr, 0, PAGE_SIZE);
dmac_flush_range(ptr, ptr + PAGE_SIZE);
@@ -683,7 +740,7 @@
size = ALIGN(size, PAGE_SIZE * 2);
ret = _kgsl_sharedmem_page_alloc(memdesc, pagetable, size,
- GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
+ 0, GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
if (!ret)
ret = kgsl_page_alloc_map_kernel(memdesc);
if (ret)
@@ -707,7 +764,7 @@
protflags |= GSL_PT_PAGE_WV;
return _kgsl_sharedmem_page_alloc(memdesc, pagetable, size,
- protflags);
+ flags, protflags);
}
EXPORT_SYMBOL(kgsl_sharedmem_page_alloc_user);
@@ -757,7 +814,7 @@
if (memdesc->ops && memdesc->ops->free)
memdesc->ops->free(memdesc);
- kgsl_sg_free(memdesc->sg, memdesc->sglen);
+ kgsl_sg_free(memdesc->sg, memdesc->sglen_alloc);
memset(memdesc, 0, sizeof(*memdesc));
}
@@ -919,3 +976,42 @@
return 0;
}
EXPORT_SYMBOL(kgsl_sharedmem_map_vma);
+
+static const char * const memtype_str[] = {
+ [KGSL_MEMTYPE_OBJECTANY] = "any(0)",
+ [KGSL_MEMTYPE_FRAMEBUFFER] = "framebuffer",
+ [KGSL_MEMTYPE_RENDERBUFFER] = "renderbuffer",
+ [KGSL_MEMTYPE_ARRAYBUFFER] = "arraybuffer",
+ [KGSL_MEMTYPE_ELEMENTARRAYBUFFER] = "elementarraybuffer",
+ [KGSL_MEMTYPE_VERTEXARRAYBUFFER] = "vertexarraybuffer",
+ [KGSL_MEMTYPE_TEXTURE] = "texture",
+ [KGSL_MEMTYPE_SURFACE] = "surface",
+ [KGSL_MEMTYPE_EGL_SURFACE] = "egl_surface",
+ [KGSL_MEMTYPE_GL] = "gl",
+ [KGSL_MEMTYPE_CL] = "cl",
+ [KGSL_MEMTYPE_CL_BUFFER_MAP] = "cl_buffer_map",
+ [KGSL_MEMTYPE_CL_BUFFER_NOMAP] = "cl_buffer_nomap",
+ [KGSL_MEMTYPE_CL_IMAGE_MAP] = "cl_image_map",
+ [KGSL_MEMTYPE_CL_IMAGE_NOMAP] = "cl_image_nomap",
+ [KGSL_MEMTYPE_CL_KERNEL_STACK] = "cl_kernel_stack",
+ [KGSL_MEMTYPE_COMMAND] = "command",
+ [KGSL_MEMTYPE_2D] = "2d",
+ [KGSL_MEMTYPE_EGL_IMAGE] = "egl_image",
+ [KGSL_MEMTYPE_EGL_SHADOW] = "egl_shadow",
+ [KGSL_MEMTYPE_MULTISAMPLE] = "egl_multisample",
+ /* KGSL_MEMTYPE_KERNEL handled below, to avoid huge array */
+};
+
+void kgsl_get_memory_usage(char *name, size_t name_size, unsigned int memflags)
+{
+ unsigned char type;
+
+ type = (memflags & KGSL_MEMTYPE_MASK) >> KGSL_MEMTYPE_SHIFT;
+ if (type == KGSL_MEMTYPE_KERNEL)
+ strlcpy(name, "kernel", name_size);
+ else if (type < ARRAY_SIZE(memtype_str) && memtype_str[type] != NULL)
+ strlcpy(name, memtype_str[type], name_size);
+ else
+ snprintf(name, name_size, "unknown(%3d)", type);
+}
+EXPORT_SYMBOL(kgsl_get_memory_usage);
diff --git a/drivers/gpu/msm/kgsl_trace.h b/drivers/gpu/msm/kgsl_trace.h
index bba06bc..81cb34f 100644
--- a/drivers/gpu/msm/kgsl_trace.h
+++ b/drivers/gpu/msm/kgsl_trace.h
@@ -316,17 +316,21 @@
__field(unsigned int, gpuaddr)
__field(unsigned int, size)
__field(unsigned int, tgid)
+ __array(char, usage, 16)
),
TP_fast_assign(
__entry->gpuaddr = mem_entry->memdesc.gpuaddr;
__entry->size = mem_entry->memdesc.size;
__entry->tgid = mem_entry->priv->pid;
+ kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
+ mem_entry->memdesc.priv);
),
TP_printk(
- "gpuaddr=0x%08x size=%d tgid=%d",
- __entry->gpuaddr, __entry->size, __entry->tgid
+ "gpuaddr=0x%08x size=%d tgid=%d usage=%s",
+ __entry->gpuaddr, __entry->size, __entry->tgid,
+ __entry->usage
)
);
@@ -342,6 +346,7 @@
__field(int, fd)
__field(int, type)
__field(unsigned int, tgid)
+ __array(char, usage, 16)
),
TP_fast_assign(
@@ -350,12 +355,15 @@
__entry->fd = fd;
__entry->type = mem_entry->memtype;
__entry->tgid = mem_entry->priv->pid;
+ kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
+ mem_entry->memdesc.priv);
),
TP_printk(
- "gpuaddr=0x%08x size=%d type=%d fd=%d tgid=%d",
+ "gpuaddr=0x%08x size=%d type=%d fd=%d tgid=%d usage %s",
__entry->gpuaddr, __entry->size,
- __entry->type, __entry->fd, __entry->tgid
+ __entry->type, __entry->fd, __entry->tgid,
+ __entry->usage
)
);
@@ -371,6 +379,7 @@
__field(int, type)
__field(int, fd)
__field(unsigned int, tgid)
+ __array(char, usage, 16)
),
TP_fast_assign(
@@ -378,12 +387,14 @@
__entry->size = mem_entry->memdesc.size;
__entry->type = mem_entry->memtype;
__entry->tgid = mem_entry->priv->pid;
+ kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
+ mem_entry->memdesc.priv);
),
TP_printk(
- "gpuaddr=0x%08x size=%d type=%d tgid=%d",
+ "gpuaddr=0x%08x size=%d type=%d tgid=%d usage=%s",
__entry->gpuaddr, __entry->size, __entry->type,
- __entry->tgid
+ __entry->tgid, __entry->usage
)
);
@@ -399,6 +410,7 @@
__field(unsigned int, gpuaddr)
__field(unsigned int, size)
__field(int, type)
+ __array(char, usage, 16)
__field(unsigned int, drawctxt_id)
__field(unsigned int, curr_ts)
__field(unsigned int, free_ts)
@@ -408,6 +420,8 @@
__assign_str(device_name, device->name);
__entry->gpuaddr = mem_entry->memdesc.gpuaddr;
__entry->size = mem_entry->memdesc.size;
+ kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
+ mem_entry->memdesc.priv);
__entry->drawctxt_id = id;
__entry->type = mem_entry->memtype;
__entry->curr_ts = curr_ts;
@@ -415,12 +429,13 @@
),
TP_printk(
- "d_name=%s gpuaddr=0x%08x size=%d type=%d ctx=%u"
+ "d_name=%s gpuaddr=0x%08x size=%d type=%d usage=%s ctx=%u"
" curr_ts=0x%x free_ts=0x%x",
__get_str(device_name),
__entry->gpuaddr,
__entry->size,
__entry->type,
+ __entry->usage,
__entry->drawctxt_id,
__entry->curr_ts,
__entry->free_ts
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index 62bfce4..ce1a18e 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -557,6 +557,7 @@
memset(pdata, 0, sizeof *pdata);
pdata->rep = !!of_get_property(node, "autorepeat", NULL);
+ pdata->name = of_get_property(node, "input-name", NULL);
/* First count the subnodes */
pdata->nbuttons = 0;
diff --git a/drivers/iommu/msm_iommu-v2.c b/drivers/iommu/msm_iommu-v2.c
index f49d009..9d88fdd 100644
--- a/drivers/iommu/msm_iommu-v2.c
+++ b/drivers/iommu/msm_iommu-v2.c
@@ -148,9 +148,9 @@
return ret;
}
-static void __reset_iommu(void __iomem *base, int smt_size)
+static void __reset_iommu(void __iomem *base)
{
- int i;
+ int i, smt_size;
SET_ACR(base, 0);
SET_NSACR(base, 0);
@@ -162,6 +162,7 @@
SET_PMCR(base, 0);
SET_SCR1(base, 0);
SET_SSDR_N(base, 0, 0);
+ smt_size = GET_IDR0_NUMSMRG(base);
for (i = 0; i < smt_size; i++)
SET_SMR_VALID(base, i, 0);
@@ -169,11 +170,11 @@
mb();
}
-static void __program_iommu(void __iomem *base, int smt_size,
+static void __program_iommu(void __iomem *base,
struct msm_iommu_bfb_settings *bfb_settings)
{
int i;
- __reset_iommu(base, smt_size);
+ __reset_iommu(base);
SET_CR0_SMCFCFG(base, 1);
SET_CR0_USFCFG(base, 1);
@@ -208,9 +209,10 @@
mb();
}
-static void __release_smg(void __iomem *base, int ctx, int smt_size)
+static void __release_smg(void __iomem *base, int ctx)
{
- int i;
+ int i, smt_size;
+ smt_size = GET_IDR0_NUMSMRG(base);
/* Invalidate any SMGs associated with this context */
for (i = 0; i < smt_size; i++)
@@ -221,14 +223,14 @@
static void __program_context(void __iomem *base, int ctx, int ncb,
phys_addr_t pgtable, int redirect,
- u32 *sids, int len, int smt_size)
+ u32 *sids, int len)
{
unsigned int prrr, nmrr;
unsigned int pn;
- int i, j, found, num = 0;
+ int i, j, found, num = 0, smt_size;
__reset_context(base, ctx);
-
+ smt_size = GET_IDR0_NUMSMRG(base);
pn = pgtable >> CB_TTBR0_ADDR_SHIFT;
SET_TTBCR(base, ctx, 0);
SET_CB_TTBR0_ADDR(base, ctx, pn);
@@ -435,13 +437,12 @@
}
if (!msm_iommu_ctx_attached(dev->parent))
- __program_iommu(iommu_drvdata->base, iommu_drvdata->nsmr,
+ __program_iommu(iommu_drvdata->base,
iommu_drvdata->bfb_settings);
__program_context(iommu_drvdata->base, ctx_drvdata->num,
iommu_drvdata->ncb, __pa(priv->pt.fl_table),
- priv->pt.redirect, ctx_drvdata->sids, ctx_drvdata->nsid,
- iommu_drvdata->nsmr);
+ priv->pt.redirect, ctx_drvdata->sids, ctx_drvdata->nsid);
__disable_clocks(iommu_drvdata);
list_add(&(ctx_drvdata->attached_elm), &priv->list_attached);
@@ -478,8 +479,7 @@
GET_CB_CONTEXTIDR_ASID(iommu_drvdata->base, ctx_drvdata->num));
__reset_context(iommu_drvdata->base, ctx_drvdata->num);
- __release_smg(iommu_drvdata->base, ctx_drvdata->num,
- iommu_drvdata->nsmr);
+ __release_smg(iommu_drvdata->base, ctx_drvdata->num);
__disable_clocks(iommu_drvdata);
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index e17e1f8..df66a3a 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -467,6 +467,92 @@
return pgprot;
}
+static unsigned long *make_second_level(struct msm_priv *priv,
+ unsigned long *fl_pte)
+{
+ unsigned long *sl;
+ sl = (unsigned long *) __get_free_pages(GFP_KERNEL,
+ get_order(SZ_4K));
+
+ if (!sl) {
+ pr_debug("Could not allocate second level table\n");
+ goto fail;
+ }
+ memset(sl, 0, SZ_4K);
+ clean_pte(sl, sl + NUM_SL_PTE, priv->redirect);
+
+ *fl_pte = ((((int)__pa(sl)) & FL_BASE_MASK) | \
+ FL_TYPE_TABLE);
+
+ clean_pte(fl_pte, fl_pte + 1, priv->redirect);
+fail:
+ return sl;
+}
+
+static int sl_4k(unsigned long *sl_pte, phys_addr_t pa, unsigned int pgprot)
+{
+ int ret = 0;
+
+ if (*sl_pte) {
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ *sl_pte = (pa & SL_BASE_MASK_SMALL) | SL_NG | SL_SHARED
+ | SL_TYPE_SMALL | pgprot;
+fail:
+ return ret;
+}
+
+static int sl_64k(unsigned long *sl_pte, phys_addr_t pa, unsigned int pgprot)
+{
+ int ret = 0;
+
+ int i;
+
+ for (i = 0; i < 16; i++)
+ if (*(sl_pte+i)) {
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ for (i = 0; i < 16; i++)
+ *(sl_pte+i) = (pa & SL_BASE_MASK_LARGE) | SL_NG
+ | SL_SHARED | SL_TYPE_LARGE | pgprot;
+
+fail:
+ return ret;
+}
+
+
+static inline int fl_1m(unsigned long *fl_pte, phys_addr_t pa, int pgprot)
+{
+ if (*fl_pte)
+ return -EBUSY;
+
+ *fl_pte = (pa & 0xFFF00000) | FL_NG | FL_TYPE_SECT | FL_SHARED
+ | pgprot;
+
+ return 0;
+}
+
+
+static inline int fl_16m(unsigned long *fl_pte, phys_addr_t pa, int pgprot)
+{
+ int i;
+ int ret = 0;
+ for (i = 0; i < 16; i++)
+ if (*(fl_pte+i)) {
+ ret = -EBUSY;
+ goto fail;
+ }
+ for (i = 0; i < 16; i++)
+ *(fl_pte+i) = (pa & 0xFF000000) | FL_SUPERSECTION
+ | FL_TYPE_SECT | FL_SHARED | FL_NG | pgprot;
+fail:
+ return ret;
+}
+
static int msm_iommu_map(struct iommu_domain *domain, unsigned long va,
phys_addr_t pa, size_t len, int prot)
{
@@ -514,28 +600,16 @@
fl_pte = fl_table + fl_offset; /* int pointers, 4 bytes */
if (len == SZ_16M) {
- int i = 0;
-
- for (i = 0; i < 16; i++)
- if (*(fl_pte+i)) {
- ret = -EBUSY;
- goto fail;
- }
-
- for (i = 0; i < 16; i++)
- *(fl_pte+i) = (pa & 0xFF000000) | FL_SUPERSECTION
- | FL_TYPE_SECT | FL_SHARED | FL_NG | pgprot;
+ ret = fl_16m(fl_pte, pa, pgprot);
+ if (ret)
+ goto fail;
clean_pte(fl_pte, fl_pte + 16, priv->redirect);
}
if (len == SZ_1M) {
- if (*fl_pte) {
- ret = -EBUSY;
+ ret = fl_1m(fl_pte, pa, pgprot);
+ if (ret)
goto fail;
- }
-
- *fl_pte = (pa & 0xFFF00000) | FL_NG | FL_TYPE_SECT | FL_SHARED
- | pgprot;
clean_pte(fl_pte, fl_pte + 1, priv->redirect);
}
@@ -543,22 +617,10 @@
if (len == SZ_4K || len == SZ_64K) {
if (*fl_pte == 0) {
- unsigned long *sl;
- sl = (unsigned long *) __get_free_pages(GFP_KERNEL,
- get_order(SZ_4K));
-
- if (!sl) {
- pr_debug("Could not allocate second level table\n");
+ if (make_second_level(priv, fl_pte) == NULL) {
ret = -ENOMEM;
goto fail;
}
- memset(sl, 0, SZ_4K);
- clean_pte(sl, sl + NUM_SL_PTE, priv->redirect);
-
- *fl_pte = ((((int)__pa(sl)) & FL_BASE_MASK) | \
- FL_TYPE_TABLE);
-
- clean_pte(fl_pte, fl_pte + 1, priv->redirect);
}
if (!(*fl_pte & FL_TYPE_TABLE)) {
@@ -572,29 +634,17 @@
sl_pte = sl_table + sl_offset;
if (len == SZ_4K) {
- if (*sl_pte) {
- ret = -EBUSY;
+ ret = sl_4k(sl_pte, pa, pgprot);
+ if (ret)
goto fail;
- }
- *sl_pte = (pa & SL_BASE_MASK_SMALL) | SL_NG | SL_SHARED
- | SL_TYPE_SMALL | pgprot;
clean_pte(sl_pte, sl_pte + 1, priv->redirect);
}
if (len == SZ_64K) {
- int i;
-
- for (i = 0; i < 16; i++)
- if (*(sl_pte+i)) {
- ret = -EBUSY;
- goto fail;
- }
-
- for (i = 0; i < 16; i++)
- *(sl_pte+i) = (pa & SL_BASE_MASK_LARGE) | SL_NG
- | SL_SHARED | SL_TYPE_LARGE | pgprot;
-
+ ret = sl_64k(sl_pte, pa, pgprot);
+ if (ret)
+ goto fail;
clean_pte(sl_pte, sl_pte + 16, priv->redirect);
}
@@ -712,22 +762,28 @@
return pa;
}
+static inline int is_fully_aligned(unsigned int va, phys_addr_t pa, size_t len,
+ int align)
+{
+ return IS_ALIGNED(va, align) && IS_ALIGNED(pa, align)
+ && (len >= align);
+}
+
static int msm_iommu_map_range(struct iommu_domain *domain, unsigned int va,
struct scatterlist *sg, unsigned int len,
int prot)
{
unsigned int pa;
unsigned int offset = 0;
- unsigned int pgprot;
unsigned long *fl_table;
unsigned long *fl_pte;
unsigned long fl_offset;
- unsigned long *sl_table;
+ unsigned long *sl_table = NULL;
unsigned long sl_offset, sl_start;
- unsigned int chunk_offset = 0;
- unsigned int chunk_pa;
+ unsigned int chunk_size, chunk_offset = 0;
int ret = 0;
struct msm_priv *priv;
+ unsigned int pgprot4k, pgprot64k, pgprot1m, pgprot16m;
mutex_lock(&msm_iommu_lock);
@@ -736,49 +792,78 @@
priv = domain->priv;
fl_table = priv->pgtable;
- pgprot = __get_pgprot(prot, SZ_4K);
+ pgprot4k = __get_pgprot(prot, SZ_4K);
+ pgprot64k = __get_pgprot(prot, SZ_64K);
+ pgprot1m = __get_pgprot(prot, SZ_1M);
+ pgprot16m = __get_pgprot(prot, SZ_16M);
- if (!pgprot) {
+ if (!pgprot4k || !pgprot64k || !pgprot1m || !pgprot16m) {
ret = -EINVAL;
goto fail;
}
fl_offset = FL_OFFSET(va); /* Upper 12 bits */
fl_pte = fl_table + fl_offset; /* int pointers, 4 bytes */
-
- sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
- sl_offset = SL_OFFSET(va);
-
- chunk_pa = get_phys_addr(sg);
- if (chunk_pa == 0) {
- pr_debug("No dma address for sg %p\n", sg);
- ret = -EINVAL;
- goto fail;
- }
+ pa = get_phys_addr(sg);
while (offset < len) {
- /* Set up a 2nd level page table if one doesn't exist */
- if (*fl_pte == 0) {
- sl_table = (unsigned long *)
- __get_free_pages(GFP_KERNEL, get_order(SZ_4K));
+ chunk_size = SZ_4K;
- if (!sl_table) {
- pr_debug("Could not allocate second level table\n");
+ if (is_fully_aligned(va, pa, sg->length - chunk_offset,
+ SZ_16M))
+ chunk_size = SZ_16M;
+ else if (is_fully_aligned(va, pa, sg->length - chunk_offset,
+ SZ_1M))
+ chunk_size = SZ_1M;
+ /* 64k or 4k determined later */
+
+ /* for 1M and 16M, only first level entries are required */
+ if (chunk_size >= SZ_1M) {
+ if (chunk_size == SZ_16M) {
+ ret = fl_16m(fl_pte, pa, pgprot16m);
+ if (ret)
+ goto fail;
+ clean_pte(fl_pte, fl_pte + 16, priv->redirect);
+ fl_pte += 16;
+ } else if (chunk_size == SZ_1M) {
+ ret = fl_1m(fl_pte, pa, pgprot1m);
+ if (ret)
+ goto fail;
+ clean_pte(fl_pte, fl_pte + 1, priv->redirect);
+ fl_pte++;
+ }
+
+ offset += chunk_size;
+ chunk_offset += chunk_size;
+ va += chunk_size;
+ pa += chunk_size;
+
+ if (chunk_offset >= sg->length && offset < len) {
+ chunk_offset = 0;
+ sg = sg_next(sg);
+ pa = get_phys_addr(sg);
+ if (pa == 0) {
+ pr_debug("No dma address for sg %p\n",
+ sg);
+ ret = -EINVAL;
+ goto fail;
+ }
+ }
+ continue;
+ }
+ /* for 4K or 64K, make sure there is a second level table */
+ if (*fl_pte == 0) {
+ if (!make_second_level(priv, fl_pte)) {
ret = -ENOMEM;
goto fail;
}
-
- memset(sl_table, 0, SZ_4K);
- clean_pte(sl_table, sl_table + NUM_SL_PTE,
- priv->redirect);
-
- *fl_pte = ((((int)__pa(sl_table)) & FL_BASE_MASK) |
- FL_TYPE_TABLE);
- clean_pte(fl_pte, fl_pte + 1, priv->redirect);
- } else
- sl_table = (unsigned long *)
- __va(((*fl_pte) & FL_BASE_MASK));
-
+ }
+ if (!(*fl_pte & FL_TYPE_TABLE)) {
+ ret = -EBUSY;
+ goto fail;
+ }
+ sl_table = __va(((*fl_pte) & FL_BASE_MASK));
+ sl_offset = SL_OFFSET(va);
/* Keep track of initial position so we
* don't clean more than we have to
*/
@@ -786,21 +871,39 @@
/* Build the 2nd level page table */
while (offset < len && sl_offset < NUM_SL_PTE) {
- pa = chunk_pa + chunk_offset;
- sl_table[sl_offset] = (pa & SL_BASE_MASK_SMALL) |
- pgprot | SL_NG | SL_SHARED | SL_TYPE_SMALL;
- sl_offset++;
- offset += SZ_4K;
- chunk_offset += SZ_4K;
+ /* Map a large 64K page if the chunk is large enough and
+ * the pa and va are aligned
+ */
+
+ if (is_fully_aligned(va, pa, sg->length - chunk_offset,
+ SZ_64K))
+ chunk_size = SZ_64K;
+ else
+ chunk_size = SZ_4K;
+
+ if (chunk_size == SZ_4K) {
+ sl_4k(&sl_table[sl_offset], pa, pgprot4k);
+ sl_offset++;
+ } else {
+ BUG_ON(sl_offset + 16 > NUM_SL_PTE);
+ sl_64k(&sl_table[sl_offset], pa, pgprot64k);
+ sl_offset += 16;
+ }
+
+
+ offset += chunk_size;
+ chunk_offset += chunk_size;
+ va += chunk_size;
+ pa += chunk_size;
if (chunk_offset >= sg->length && offset < len) {
chunk_offset = 0;
sg = sg_next(sg);
- chunk_pa = get_phys_addr(sg);
- if (chunk_pa == 0) {
+ pa = get_phys_addr(sg);
+ if (pa == 0) {
pr_debug("No dma address for sg %p\n",
- sg);
+ sg);
ret = -EINVAL;
goto fail;
}
@@ -808,7 +911,7 @@
}
clean_pte(sl_table + sl_start, sl_table + sl_offset,
- priv->redirect);
+ priv->redirect);
fl_pte++;
sl_offset = 0;
@@ -842,45 +945,53 @@
fl_offset = FL_OFFSET(va); /* Upper 12 bits */
fl_pte = fl_table + fl_offset; /* int pointers, 4 bytes */
- sl_start = SL_OFFSET(va);
-
while (offset < len) {
- sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
- sl_end = ((len - offset) / SZ_4K) + sl_start;
+ if (*fl_pte & FL_TYPE_TABLE) {
+ sl_start = SL_OFFSET(va);
+ sl_table = __va(((*fl_pte) & FL_BASE_MASK));
+ sl_end = ((len - offset) / SZ_4K) + sl_start;
- if (sl_end > NUM_SL_PTE)
- sl_end = NUM_SL_PTE;
+ if (sl_end > NUM_SL_PTE)
+ sl_end = NUM_SL_PTE;
- memset(sl_table + sl_start, 0, (sl_end - sl_start) * 4);
- clean_pte(sl_table + sl_start, sl_table + sl_end,
- priv->redirect);
+ memset(sl_table + sl_start, 0, (sl_end - sl_start) * 4);
+ clean_pte(sl_table + sl_start, sl_table + sl_end,
+ priv->redirect);
- offset += (sl_end - sl_start) * SZ_4K;
+ offset += (sl_end - sl_start) * SZ_4K;
+ va += (sl_end - sl_start) * SZ_4K;
- /* Unmap and free the 2nd level table if all mappings in it
- * were removed. This saves memory, but the table will need
- * to be re-allocated the next time someone tries to map these
- * VAs.
- */
- used = 0;
+ /* Unmap and free the 2nd level table if all mappings
+ * in it were removed. This saves memory, but the table
+ * will need to be re-allocated the next time someone
+ * tries to map these VAs.
+ */
+ used = 0;
- /* If we just unmapped the whole table, don't bother
- * seeing if there are still used entries left.
- */
- if (sl_end - sl_start != NUM_SL_PTE)
- for (i = 0; i < NUM_SL_PTE; i++)
- if (sl_table[i]) {
- used = 1;
- break;
- }
- if (!used) {
- free_page((unsigned long)sl_table);
+ /* If we just unmapped the whole table, don't bother
+ * seeing if there are still used entries left.
+ */
+ if (sl_end - sl_start != NUM_SL_PTE)
+ for (i = 0; i < NUM_SL_PTE; i++)
+ if (sl_table[i]) {
+ used = 1;
+ break;
+ }
+ if (!used) {
+ free_page((unsigned long)sl_table);
+ *fl_pte = 0;
+
+ clean_pte(fl_pte, fl_pte + 1, priv->redirect);
+ }
+
+ sl_start = 0;
+ } else {
*fl_pte = 0;
-
clean_pte(fl_pte, fl_pte + 1, priv->redirect);
+ va += SZ_1M;
+ offset += SZ_1M;
+ sl_start = 0;
}
-
- sl_start = 0;
fl_pte++;
}
diff --git a/drivers/iommu/msm_iommu_dev-v2.c b/drivers/iommu/msm_iommu_dev-v2.c
index 237d601..68612ba 100644
--- a/drivers/iommu/msm_iommu_dev-v2.c
+++ b/drivers/iommu/msm_iommu_dev-v2.c
@@ -94,7 +94,6 @@
{
struct device_node *child;
int ret = 0;
- u32 nsmr;
ret = device_move(&pdev->dev, &msm_iommu_root_dev->dev, DPM_ORDER_NONE);
if (ret)
@@ -104,18 +103,6 @@
if (ret)
goto fail;
- ret = of_property_read_u32(pdev->dev.of_node, "qcom,iommu-smt-size",
- &nsmr);
- if (ret)
- goto fail;
-
- if (nsmr > MAX_NUM_SMR) {
- pr_err("Invalid SMT size: %d\n", nsmr);
- ret = -EINVAL;
- goto fail;
- }
-
- drvdata->nsmr = nsmr;
for_each_child_of_node(pdev->dev.of_node, child) {
drvdata->ncb++;
if (!of_platform_device_create(child, NULL, &pdev->dev))
diff --git a/drivers/media/video/msm/Kconfig b/drivers/media/video/msm/Kconfig
index e9b4e2b..be9c43c 100644
--- a/drivers/media/video/msm/Kconfig
+++ b/drivers/media/video/msm/Kconfig
@@ -208,6 +208,15 @@
two mipi lanes, required for msm8625 platform.
Say Y here if this is msm8625 variant platform.
+config IMX135
+ bool "Sensor imx135 (Sony 13MP)"
+ depends on MSM_CAMERA
+ ---help---
+ Support for IMX135 sensor driver.
+ This is a Sony 13MP Bayer Sensor with autofocus and video HDR
+ support.
+ Say Y if the platform uses IMX135 sensor.
+
config VB6801
bool "Sensor vb6801"
depends on MSM_CAMERA && !ARCH_MSM8X60 && !MSM_CAMERA_V4L2
diff --git a/drivers/media/video/msm/msm_mctl_pp.c b/drivers/media/video/msm/msm_mctl_pp.c
index 105426e..7155d4c 100644
--- a/drivers/media/video/msm/msm_mctl_pp.c
+++ b/drivers/media/video/msm/msm_mctl_pp.c
@@ -681,6 +681,7 @@
ret_frame.dirty = 0;
ret_frame.node_type = frame.node_type;
ret_frame.timestamp = frame.timestamp;
+ ret_frame.frame_id = frame.frame_id;
D("%s Frame done id: %d\n", __func__, frame.frame_id);
rc = msm_mctl_buf_done_pp(p_mctl, &buf_handle, &buf, &ret_frame);
return rc;
diff --git a/drivers/media/video/msm/sensors/Makefile b/drivers/media/video/msm/sensors/Makefile
index cd228a1..a70a632 100644
--- a/drivers/media/video/msm/sensors/Makefile
+++ b/drivers/media/video/msm/sensors/Makefile
@@ -8,6 +8,7 @@
obj-$(CONFIG_OV8825) += ov8825_v4l2.o
obj-$(CONFIG_IMX074) += imx074_v4l2.o
obj-$(CONFIG_S5K3L1YX) += s5k3l1yx.o
+obj-$(CONFIG_IMX135) += imx135_v4l2.o
obj-$(CONFIG_OV2720) += ov2720.o
obj-$(CONFIG_MT9M114) += mt9m114_v4l2.o
obj-$(CONFIG_S5K4E1) += s5k4e1_v4l2.o
diff --git a/drivers/media/video/msm/sensors/imx135_v4l2.c b/drivers/media/video/msm/sensors/imx135_v4l2.c
new file mode 100644
index 0000000..f480923
--- /dev/null
+++ b/drivers/media/video/msm/sensors/imx135_v4l2.c
@@ -0,0 +1,553 @@
+/* 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 "msm_sensor.h"
+#define SENSOR_NAME "imx135"
+#define PLATFORM_DRIVER_NAME "msm_camera_imx135"
+#define imx135_obj imx135_##obj
+
+DEFINE_MUTEX(imx135_mut);
+static struct msm_sensor_ctrl_t imx135_s_ctrl;
+
+static struct msm_camera_i2c_reg_conf imx135_start_settings[] = {
+ {0x0100, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf imx135_stop_settings[] = {
+ {0x0100, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf imx135_groupon_settings[] = {
+ {0x104, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf imx135_groupoff_settings[] = {
+ {0x104, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf imx135_recommend_settings[] = {
+/* Recommended global settings */
+ {0x0220, 0x01},
+ {0x3008, 0xB0},
+ {0x320A, 0x01},
+ {0x320D, 0x10},
+ {0x3216, 0x2E},
+ {0x3230, 0x0A},
+ {0x3228, 0x05},
+ {0x3229, 0x02},
+ {0x322C, 0x02},
+ {0x3302, 0x10},
+ {0x3390, 0x45},
+ {0x3409, 0x0C},
+ {0x340B, 0xF5},
+ {0x340C, 0x2D},
+ {0x3412, 0x41},
+ {0x3413, 0xAD},
+ {0x3414, 0x1E},
+ {0x3427, 0x04},
+ {0x3480, 0x1E},
+ {0x3484, 0x1E},
+ {0x3488, 0x1E},
+ {0x348C, 0x1E},
+ {0x3490, 0x1E},
+ {0x3494, 0x1E},
+ {0x349C, 0x38},
+ {0x34A3, 0x38},
+ {0x3511, 0x8F},
+ {0x3518, 0x00},
+ {0x3519, 0x94},
+ {0x3833, 0x20},
+ {0x3893, 0x01},
+ {0x38C2, 0x08},
+ {0x3C09, 0x01},
+ {0x4300, 0x00},
+ {0x4316, 0x12},
+ {0x4317, 0x22},
+ {0x431A, 0x00},
+ {0x4324, 0x03},
+ {0x4325, 0x20},
+ {0x4326, 0x03},
+ {0x4327, 0x84},
+ {0x4328, 0x03},
+ {0x4329, 0x20},
+ {0x432A, 0x03},
+ {0x432B, 0x84},
+ {0x4401, 0x3F},
+ {0x4412, 0x3F},
+ {0x4413, 0xFF},
+ {0x4446, 0x3F},
+ {0x4447, 0xFF},
+ {0x4452, 0x00},
+ {0x4453, 0xA0},
+ {0x4454, 0x08},
+ {0x4455, 0x00},
+ {0x4458, 0x18},
+ {0x4459, 0x18},
+ {0x445A, 0x3F},
+ {0x445B, 0x3A},
+ {0x4463, 0x00},
+ {0x4465, 0x00},
+ {0x446E, 0x01},
+/* Image Quality Settings */
+/* Bypass Settings */
+ {0x4203, 0x48},
+/* Defect Correction Recommended Setting */
+ {0x4100, 0xE0},
+ {0x4102, 0x0B},
+/* RGB Filter Recommended Setting */
+ {0x4281, 0x22},
+ {0x4282, 0x82},
+ {0x4284, 0x00},
+ {0x4287, 0x18},
+ {0x4288, 0x00},
+ {0x428B, 0x1E},
+ {0x428C, 0x00},
+ {0x428F, 0x08},
+/* DLC/ADP Recommended Setting */
+ {0x4207, 0x00},
+ {0x4218, 0x02},
+ {0x421B, 0x00},
+ {0x4222, 0x04},
+ {0x4223, 0x44},
+ {0x4224, 0x46},
+ {0x4225, 0xFF},
+ {0x4226, 0x14},
+ {0x4227, 0xF2},
+ {0x4228, 0xFC},
+ {0x4229, 0x60},
+ {0x422A, 0xFA},
+ {0x422B, 0xFE},
+ {0x422C, 0xFE},
+/* Color Artifact Recommended Setting */
+ {0x4243, 0xAA}
+};
+
+/* IMX135 mode 1/2 HV at 24MHz */
+static struct msm_camera_i2c_reg_conf imx135_prev_settings[] = {
+/* Clock Setting */
+ {0x011E, 0x18},
+ {0x011F, 0x00},
+ {0x0301, 0x05},
+ {0x0303, 0x01},
+ {0x0304, 0x0B},
+ {0x0305, 0x03},
+ {0x0306, 0x01},
+ {0x0307, 0x5E},
+ {0x0309, 0x05},
+ {0x030B, 0x02},
+ {0x030C, 0x00},
+ {0x030D, 0x71},
+ {0x030E, 0x01},
+ {0x3A06, 0x12},
+/* Mode setting */
+ {0x0101, 0x00},
+ {0x0105, 0x00},
+ {0x0108, 0x03},
+ {0x0109, 0x30},
+ {0x010B, 0x32},
+ {0x0112, 0x0A},
+ {0x0113, 0x0A},
+ {0x0381, 0x01},
+ {0x0383, 0x01},
+ {0x0385, 0x01},
+ {0x0387, 0x01},
+ {0x0390, 0x01}, /* binning_en = 1 */
+ {0x0391, 0x22}, /* binning_type */
+ {0x0392, 0x00}, /* binning_mode = 0 (average) */
+ {0x0401, 0x00},
+ {0x0404, 0x00},
+ {0x0405, 0x10},
+ {0x4083, 0x01},
+/* Size setting*/
+ {0x0340, 0x0A}, /* frame_length_lines = 2680*/
+ {0x0341, 0x78},
+ {0x034C, 0x08},
+ {0x034D, 0x38},
+ {0x034E, 0x06},
+ {0x034F, 0x18},
+ {0x0354, 0x08},
+ {0x0355, 0x38},
+ {0x0356, 0x06},
+ {0x0357, 0x18},
+ {0x3310, 0x08},
+ {0x3311, 0x38},
+ {0x3312, 0x06},
+ {0x3313, 0x18},
+ {0x331C, 0x02},
+ {0x331D, 0xC0},
+ {0x33B0, 0x04},
+ {0x33B1, 0x00},
+ {0x33B3, 0x00},
+ {0x7006, 0x04},
+/* Global Timing Setting */
+ {0x0830, 0x67},
+ {0x0831, 0x27},
+ {0x0832, 0x47},
+ {0x0833, 0x27},
+ {0x0834, 0x27},
+ {0x0835, 0x1F},
+ {0x0836, 0x87},
+ {0x0837, 0x2F},
+ {0x0839, 0x1F},
+ {0x083A, 0x17},
+ {0x083B, 0x02},
+/* Integration Time Setting */
+ {0x0254, 0x00},
+/* Gain Setting */
+ {0x0205, 0x33}
+};
+
+/* IMX135 Mode Fullsize at 24MHz */
+static struct msm_camera_i2c_reg_conf imx135_snap_settings[] = {
+/* Clock Setting */
+ {0x011E, 0x18},
+ {0x011F, 0x00},
+ {0x0301, 0x05},
+ {0x0303, 0x01},
+ {0x0304, 0x0B},
+ {0x0305, 0x03},
+ {0x0306, 0x01},
+ {0x0307, 0x5E},
+ {0x0309, 0x05},
+ {0x030B, 0x01},
+ {0x030C, 0x00},
+ {0x030D, 0x60}, /* pll_multiplier = 96 */
+ {0x030E, 0x01},
+ {0x3A06, 0x11},
+/* Mode setting */
+ {0x0101, 0x00},
+ {0x0105, 0x00},
+ {0x0108, 0x03},
+ {0x0109, 0x30},
+ {0x010B, 0x32},
+ {0x0112, 0x0A},
+ {0x0113, 0x0A},
+ {0x0381, 0x01},
+ {0x0383, 0x01},
+ {0x0385, 0x01},
+ {0x0387, 0x01},
+ {0x0390, 0x00},
+ {0x0391, 0x11},
+ {0x0392, 0x00},
+ {0x0401, 0x00},
+ {0x0404, 0x00},
+ {0x0405, 0x10},
+ {0x4083, 0x01},
+/* Size setting */
+ {0x0340, 0x0C},
+ {0x0341, 0x46},
+ {0x034C, 0x10},
+ {0x034D, 0x70},
+ {0x034E, 0x0C},
+ {0x034F, 0x30},
+ {0x0354, 0x10},
+ {0x0355, 0x70},
+ {0x0356, 0x0C},
+ {0x0357, 0x30},
+ {0x3310, 0x10},
+ {0x3311, 0x70},
+ {0x3312, 0x0C},
+ {0x3313, 0x30},
+ {0x331C, 0x06},
+ {0x331D, 0x00},
+ {0x33B0, 0x04},
+ {0x33B1, 0x00},
+ {0x33B3, 0x00},
+ {0x7006, 0x04},
+/* Global Timing Setting */
+ {0x0830, 0x7F},
+ {0x0831, 0x37},
+ {0x0832, 0x5F},
+ {0x0833, 0x37},
+ {0x0834, 0x37},
+ {0x0835, 0x3F},
+ {0x0836, 0xC7},
+ {0x0837, 0x3F},
+ {0x0839, 0x1F},
+ {0x083A, 0x17},
+ {0x083B, 0x02},
+/* Integration Time Setting */
+ {0x0250, 0x0B},
+/* Gain Setting */
+ {0x0205, 0x33}
+};
+
+
+static struct msm_camera_i2c_reg_conf imx135_hdr_settings[] = {
+/* Clock Setting */
+ {0x011E, 0x18},
+ {0x011F, 0x00},
+ {0x0301, 0x05},
+ {0x0303, 0x01},
+ {0x0304, 0x0B},
+ {0x0305, 0x03},
+ {0x0306, 0x01},
+ {0x0307, 0x5E},
+ {0x0309, 0x05},
+ {0x030B, 0x02},
+ {0x030C, 0x00},
+ {0x030D, 0x71},
+ {0x030E, 0x01},
+ {0x3A06, 0x12},
+/* Mode setting */
+ {0x0101, 0x00},
+ {0x0105, 0x00},
+ {0x0108, 0x03},
+ {0x0109, 0x30},
+ {0x010B, 0x32},
+ {0x0112, 0x0E},
+ {0x0113, 0x0A},
+ {0x0381, 0x01},
+ {0x0383, 0x01},
+ {0x0385, 0x01},
+ {0x0387, 0x01},
+ {0x0390, 0x00},
+ {0x0391, 0x11},
+ {0x0392, 0x00},
+ {0x0401, 0x00},
+ {0x0404, 0x00},
+ {0x0405, 0x10},
+ {0x4083, 0x01},
+/* Size setting */
+ {0x0340, 0x0C},
+ {0x0341, 0x48},
+ {0x034C, 0x08},
+ {0x034D, 0x38},
+ {0x034E, 0x06},
+ {0x034F, 0x18},
+ {0x0354, 0x08},
+ {0x0355, 0x38},
+ {0x0356, 0x06},
+ {0x0357, 0x18},
+ {0x3310, 0x08},
+ {0x3311, 0x38},
+ {0x3312, 0x06},
+ {0x3313, 0x18},
+ {0x331C, 0x02},
+ {0x331D, 0xA0},
+ {0x33B0, 0x08},
+ {0x33B1, 0x38},
+ {0x33B3, 0x01},
+ {0x7006, 0x04},
+/* Global Timing Setting */
+ {0x0830, 0x67},
+ {0x0831, 0x27},
+ {0x0832, 0x47},
+ {0x0833, 0x27},
+ {0x0834, 0x27},
+ {0x0835, 0x1F},
+ {0x0836, 0x87},
+ {0x0837, 0x2F},
+ {0x0839, 0x1F},
+ {0x083A, 0x17},
+ {0x083B, 0x02},
+/* Integration Time Setting */
+ {0x0250, 0x0B},
+/* Gain Setting */
+ {0x0205, 0x33}
+};
+
+static struct v4l2_subdev_info imx135_subdev_info[] = {
+ {
+ .code = V4L2_MBUS_FMT_SBGGR10_1X10,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ },
+ /* more can be supported, to be added later */
+};
+
+static struct msm_camera_i2c_conf_array imx135_init_conf[] = {
+ {&imx135_recommend_settings[0],
+ ARRAY_SIZE(imx135_recommend_settings), 0, MSM_CAMERA_I2C_BYTE_DATA}
+};
+
+static struct msm_camera_i2c_conf_array imx135_confs[] = {
+ {&imx135_snap_settings[0],
+ ARRAY_SIZE(imx135_snap_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+ {&imx135_prev_settings[0],
+ ARRAY_SIZE(imx135_prev_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+ {&imx135_hdr_settings[0],
+ ARRAY_SIZE(imx135_hdr_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+};
+
+static struct msm_sensor_output_info_t imx135_dimensions[] = {
+ /* RES0 snapshot(FULL SIZE) */
+ {
+ .x_output = 4208,
+ .y_output = 3120,
+ .line_length_pclk = 4572,
+ .frame_length_lines = 3142,
+ .vt_pixel_clk = 307200000,
+ .op_pixel_clk = 307200000,
+ .binning_factor = 1,
+ },
+ /* RES1 4:3 preview(1/2HV QTR SIZE) */
+ {
+ .x_output = 2104,
+ .y_output = 1560,
+ .line_length_pclk = 4572,
+ .frame_length_lines = 2680,
+ .vt_pixel_clk = 361600000,
+ .op_pixel_clk = 180800000,
+ .binning_factor = 1,
+ },
+ /* RES2 4:3 HDR movie mode */
+ {
+ .x_output = 2104,
+ .y_output = 1560,
+ .line_length_pclk = 4572,
+ .frame_length_lines = 3144,
+ .vt_pixel_clk = 361600000,
+ .op_pixel_clk = 180800000,
+ .binning_factor = 1,
+ },
+};
+
+static struct msm_sensor_output_reg_addr_t imx135_reg_addr = {
+ .x_output = 0x34C,
+ .y_output = 0x34E,
+ .line_length_pclk = 0x342,
+ .frame_length_lines = 0x340,
+};
+
+static struct msm_sensor_id_info_t imx135_id_info = {
+ .sensor_id_reg_addr = 0x0000,
+ .sensor_id = 0x0087,
+};
+
+static struct msm_sensor_exp_gain_info_t imx135_exp_gain_info = {
+ .coarse_int_time_addr = 0x202,
+ .global_gain_addr = 0x205,
+ .vert_offset = 4,
+};
+
+static const struct i2c_device_id imx135_i2c_id[] = {
+ {SENSOR_NAME, (kernel_ulong_t)&imx135_s_ctrl},
+ { }
+};
+
+static struct i2c_driver imx135_i2c_driver = {
+ .id_table = imx135_i2c_id,
+ .probe = msm_sensor_i2c_probe,
+ .driver = {
+ .name = SENSOR_NAME,
+ },
+};
+
+static struct msm_camera_i2c_client imx135_sensor_i2c_client = {
+ .addr_type = MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+static int __init msm_sensor_init_module(void)
+{
+ return i2c_add_driver(&imx135_i2c_driver);
+}
+
+static struct v4l2_subdev_core_ops imx135_subdev_core_ops = {
+ .ioctl = msm_sensor_subdev_ioctl,
+ .s_power = msm_sensor_power,
+};
+
+static struct v4l2_subdev_video_ops imx135_subdev_video_ops = {
+ .enum_mbus_fmt = msm_sensor_v4l2_enum_fmt,
+};
+
+static struct v4l2_subdev_ops imx135_subdev_ops = {
+ .core = &imx135_subdev_core_ops,
+ .video = &imx135_subdev_video_ops,
+};
+
+int32_t imx135_write_exp_gain(struct msm_sensor_ctrl_t *s_ctrl,
+ uint16_t gain, uint32_t line)
+{
+ uint32_t fl_lines;
+ uint8_t offset;
+ fl_lines = s_ctrl->curr_frame_length_lines;
+ fl_lines = (fl_lines * s_ctrl->fps_divider) / Q10;
+ offset = s_ctrl->sensor_exp_gain_info->vert_offset;
+ if (line > (fl_lines - offset))
+ fl_lines = line + offset;
+
+ s_ctrl->func_tbl->sensor_group_hold_on(s_ctrl);
+ msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+ s_ctrl->sensor_output_reg_addr->frame_length_lines, fl_lines,
+ MSM_CAMERA_I2C_WORD_DATA);
+ msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+ s_ctrl->sensor_exp_gain_info->coarse_int_time_addr, line,
+ MSM_CAMERA_I2C_WORD_DATA);
+ msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+ s_ctrl->sensor_exp_gain_info->global_gain_addr, gain,
+ MSM_CAMERA_I2C_BYTE_DATA);
+ s_ctrl->func_tbl->sensor_group_hold_off(s_ctrl);
+ return 0;
+}
+
+static struct msm_sensor_fn_t imx135_func_tbl = {
+ .sensor_start_stream = msm_sensor_start_stream,
+ .sensor_stop_stream = msm_sensor_stop_stream,
+ .sensor_group_hold_on = msm_sensor_group_hold_on,
+ .sensor_group_hold_off = msm_sensor_group_hold_off,
+ .sensor_set_fps = msm_sensor_set_fps,
+ .sensor_write_exp_gain = imx135_write_exp_gain,
+ .sensor_write_snapshot_exp_gain = imx135_write_exp_gain,
+ .sensor_setting = msm_sensor_setting,
+ .sensor_csi_setting = msm_sensor_setting1,
+ .sensor_set_sensor_mode = msm_sensor_set_sensor_mode,
+ .sensor_mode_init = msm_sensor_mode_init,
+ .sensor_get_output_info = msm_sensor_get_output_info,
+ .sensor_config = msm_sensor_config,
+ .sensor_power_up = msm_sensor_power_up,
+ .sensor_power_down = msm_sensor_power_down,
+ .sensor_adjust_frame_lines = msm_sensor_adjust_frame_lines1,
+ .sensor_get_csi_params = msm_sensor_get_csi_params,
+};
+
+static struct msm_sensor_reg_t imx135_regs = {
+ .default_data_type = MSM_CAMERA_I2C_BYTE_DATA,
+ .start_stream_conf = imx135_start_settings,
+ .start_stream_conf_size = ARRAY_SIZE(imx135_start_settings),
+ .stop_stream_conf = imx135_stop_settings,
+ .stop_stream_conf_size = ARRAY_SIZE(imx135_stop_settings),
+ .group_hold_on_conf = imx135_groupon_settings,
+ .group_hold_on_conf_size = ARRAY_SIZE(imx135_groupon_settings),
+ .group_hold_off_conf = imx135_groupoff_settings,
+ .group_hold_off_conf_size =
+ ARRAY_SIZE(imx135_groupoff_settings),
+ .init_settings = &imx135_init_conf[0],
+ .init_size = ARRAY_SIZE(imx135_init_conf),
+ .mode_settings = &imx135_confs[0],
+ .output_settings = &imx135_dimensions[0],
+ .num_conf = ARRAY_SIZE(imx135_confs),
+};
+
+static struct msm_sensor_ctrl_t imx135_s_ctrl = {
+ .msm_sensor_reg = &imx135_regs,
+ .sensor_i2c_client = &imx135_sensor_i2c_client,
+ .sensor_i2c_addr = 0x20,
+ .sensor_output_reg_addr = &imx135_reg_addr,
+ .sensor_id_info = &imx135_id_info,
+ .sensor_exp_gain_info = &imx135_exp_gain_info,
+ .cam_mode = MSM_SENSOR_MODE_INVALID,
+ .msm_sensor_mutex = &imx135_mut,
+ .sensor_i2c_driver = &imx135_i2c_driver,
+ .sensor_v4l2_subdev_info = imx135_subdev_info,
+ .sensor_v4l2_subdev_info_size = ARRAY_SIZE(imx135_subdev_info),
+ .sensor_v4l2_subdev_ops = &imx135_subdev_ops,
+ .func_tbl = &imx135_func_tbl,
+ .clk_rate = MSM_SENSOR_MCLK_24HZ,
+};
+
+module_init(msm_sensor_init_module);
+MODULE_DESCRIPTION("Sony 13MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/vfe/msm_vfe40.c b/drivers/media/video/msm/vfe/msm_vfe40.c
index e958241..9ffa874 100644
--- a/drivers/media/video/msm/vfe/msm_vfe40.c
+++ b/drivers/media/video/msm/vfe/msm_vfe40.c
@@ -4615,19 +4615,19 @@
NOTIFY_VFE_IRQ,
(void *)VFE_IRQ_STATUS0_STATS_CS);
- if (qcmd->vfeInterruptStatus0 &
+ if (qcmd->vfeInterruptStatus1 &
VFE_IRQ_STATUS1_SYNC_TIMER0)
v4l2_subdev_notify(&vfe40_ctrl->subdev,
NOTIFY_VFE_IRQ,
(void *)VFE_IRQ_STATUS1_SYNC_TIMER0);
- if (qcmd->vfeInterruptStatus0 &
+ if (qcmd->vfeInterruptStatus1 &
VFE_IRQ_STATUS1_SYNC_TIMER1)
v4l2_subdev_notify(&vfe40_ctrl->subdev,
NOTIFY_VFE_IRQ,
(void *)VFE_IRQ_STATUS1_SYNC_TIMER1);
- if (qcmd->vfeInterruptStatus0 &
+ if (qcmd->vfeInterruptStatus1 &
VFE_IRQ_STATUS1_SYNC_TIMER2)
v4l2_subdev_notify(&vfe40_ctrl->subdev,
NOTIFY_VFE_IRQ,
diff --git a/drivers/media/video/msm_vidc/msm_vdec.c b/drivers/media/video/msm_vidc/msm_vdec.c
index d843d87..e7b5caf 100644
--- a/drivers/media/video/msm_vidc/msm_vdec.c
+++ b/drivers/media/video/msm_vidc/msm_vdec.c
@@ -709,7 +709,6 @@
static inline int start_streaming(struct msm_vidc_inst *inst)
{
int rc = 0;
- unsigned long flags;
struct vb2_buf_entry *temp;
struct list_head *ptr, *next;
inst->in_reconfig = false;
@@ -737,7 +736,7 @@
goto fail_start;
}
- spin_lock_irqsave(&inst->lock, flags);
+ mutex_lock(&inst->sync_lock);
if (!list_empty(&inst->pendingq)) {
list_for_each_safe(ptr, next, &inst->pendingq) {
temp = list_entry(ptr, struct vb2_buf_entry, list);
@@ -751,7 +750,7 @@
kfree(temp);
}
}
- spin_unlock_irqrestore(&inst->lock, flags);
+ mutex_unlock(&inst->sync_lock);
return rc;
fail_start:
return rc;
diff --git a/drivers/media/video/msm_vidc/msm_venc.c b/drivers/media/video/msm_vidc/msm_venc.c
index 948676a..4573018 100644
--- a/drivers/media/video/msm_vidc/msm_venc.c
+++ b/drivers/media/video/msm_vidc/msm_venc.c
@@ -574,7 +574,6 @@
{
int i, rc = 0;
struct msm_vidc_inst *inst;
- struct hal_frame_size frame_sz;
unsigned long flags;
if (!q || !q->drv_priv) {
dprintk(VIDC_ERR, "Invalid input, q = %p\n", q);
@@ -598,26 +597,6 @@
dprintk(VIDC_ERR, "Failed to open instance\n");
break;
}
- frame_sz.buffer_type = HAL_BUFFER_INPUT;
- frame_sz.width = inst->prop.width;
- frame_sz.height = inst->prop.height;
- dprintk(VIDC_DBG, "width = %d, height = %d\n",
- frame_sz.width, frame_sz.height);
- rc = vidc_hal_session_set_property((void *)inst->session,
- HAL_PARAM_FRAME_SIZE, &frame_sz);
- if (rc) {
- dprintk(VIDC_ERR,
- "Failed to set framesize for Output port\n");
- break;
- }
- frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
- rc = vidc_hal_session_set_property((void *)inst->session,
- HAL_PARAM_FRAME_SIZE, &frame_sz);
- if (rc) {
- dprintk(VIDC_ERR,
- "Failed to set hal property for framesize\n");
- break;
- }
rc = msm_comm_try_get_bufreqs(inst);
if (rc) {
dprintk(VIDC_ERR,
@@ -626,7 +605,9 @@
}
*num_planes = 1;
spin_lock_irqsave(&inst->lock, flags);
- *num_buffers = inst->buff_req.buffer[0].buffer_count_actual;
+ *num_buffers = inst->buff_req.buffer[0].buffer_count_actual =
+ max(*num_buffers, inst->buff_req.buffer[0].
+ buffer_count_actual);
spin_unlock_irqrestore(&inst->lock, flags);
dprintk(VIDC_DBG, "size = %d, alignment = %d, count = %d\n",
inst->buff_req.buffer[0].buffer_size,
@@ -648,7 +629,6 @@
static inline int start_streaming(struct msm_vidc_inst *inst)
{
int rc = 0;
- unsigned long flags;
struct vb2_buf_entry *temp;
struct list_head *ptr, *next;
@@ -679,7 +659,7 @@
"Failed to move inst: %p to start done state\n", inst);
goto fail_start;
}
- spin_lock_irqsave(&inst->lock, flags);
+ mutex_lock(&inst->sync_lock);
if (!list_empty(&inst->pendingq)) {
list_for_each_safe(ptr, next, &inst->pendingq) {
temp = list_entry(ptr, struct vb2_buf_entry, list);
@@ -693,7 +673,7 @@
kfree(temp);
}
}
- spin_unlock_irqrestore(&inst->lock, flags);
+ mutex_unlock(&inst->sync_lock);
return rc;
fail_start:
return rc;
@@ -1345,6 +1325,7 @@
int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
{
const struct msm_vidc_format *fmt = NULL;
+ struct hal_frame_size frame_sz;
int rc = 0;
int i;
if (!inst || !f) {
@@ -1366,6 +1347,26 @@
} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
inst->prop.width = f->fmt.pix_mp.width;
inst->prop.height = f->fmt.pix_mp.height;
+ frame_sz.buffer_type = HAL_BUFFER_INPUT;
+ frame_sz.width = inst->prop.width;
+ frame_sz.height = inst->prop.height;
+ dprintk(VIDC_DBG, "width = %d, height = %d\n",
+ frame_sz.width, frame_sz.height);
+ rc = vidc_hal_session_set_property((void *)inst->session,
+ HAL_PARAM_FRAME_SIZE, &frame_sz);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to set framesize for Output port\n");
+ goto exit;
+ }
+ frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
+ rc = vidc_hal_session_set_property((void *)inst->session,
+ HAL_PARAM_FRAME_SIZE, &frame_sz);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to set hal property for framesize\n");
+ goto exit;
+ }
fmt = msm_comm_get_pixel_fmt_fourcc(venc_formats,
ARRAY_SIZE(venc_formats), f->fmt.pix_mp.pixelformat,
OUTPUT_PORT);
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.c b/drivers/media/video/msm_vidc/msm_vidc_common.c
index 1cad40f..a6805af 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -1451,7 +1451,6 @@
int rc = 0;
struct vb2_queue *q;
struct msm_vidc_inst *inst;
- unsigned long flags;
struct vb2_buf_entry *entry;
struct vidc_frame_data frame_data;
q = vb->vb2_queue;
@@ -1468,10 +1467,9 @@
goto err_no_mem;
}
entry->vb = vb;
- dprintk(VIDC_DBG, "Queueing buffer in pendingq\n");
- spin_lock_irqsave(&inst->lock, flags);
+ mutex_lock(&inst->sync_lock);
list_add_tail(&entry->list, &inst->pendingq);
- spin_unlock_irqrestore(&inst->lock, flags);
+ mutex_unlock(&inst->sync_lock);
} else {
int64_t time_usec = timeval_to_ns(&vb->v4l2_buf.timestamp);
do_div(time_usec, NSEC_PER_USEC);
@@ -1777,18 +1775,46 @@
int rc = 0;
bool ip_flush = false;
bool op_flush = false;
+ struct list_head *ptr, *next;
+ struct vb2_buf_entry *temp;
+ struct mutex *lock;
ip_flush = flags & V4L2_QCOM_CMD_FLUSH_OUTPUT;
op_flush = flags & V4L2_QCOM_CMD_FLUSH_CAPTURE;
-
if (ip_flush && !op_flush) {
dprintk(VIDC_WARN, "Input only flush not supported\n");
return 0;
}
mutex_lock(&inst->sync_lock);
if (inst->in_reconfig && !ip_flush && op_flush) {
+ if (!list_empty(&inst->pendingq)) {
+ /*Execution can never reach here since port reconfig
+ * wont happen unless pendingq is emptied out
+ * (both pendingq and flush being secured with same
+ * lock). Printing a message here incase this breaks.*/
+ dprintk(VIDC_WARN,
+ "FLUSH BUG: Pending q not empty! It should be empty\n");
+ }
rc = vidc_hal_session_flush(inst->session,
HAL_FLUSH_OUTPUT);
} else {
+ if (!list_empty(&inst->pendingq)) {
+ /*If flush is called after queueing buffers but before
+ * streamon driver should flush the pending queue*/
+ list_for_each_safe(ptr, next, &inst->pendingq) {
+ temp =
+ list_entry(ptr, struct vb2_buf_entry, list);
+ if (temp->vb->v4l2_buf.type ==
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ lock = &inst->bufq[CAPTURE_PORT].lock;
+ else
+ lock = &inst->bufq[OUTPUT_PORT].lock;
+ mutex_lock(lock);
+ vb2_buffer_done(temp->vb, VB2_BUF_STATE_DONE);
+ mutex_unlock(lock);
+ list_del(&temp->list);
+ kfree(temp);
+ }
+ }
rc = vidc_hal_session_flush(inst->session,
HAL_FLUSH_ALL);
}
diff --git a/drivers/media/video/msm_vidc/vidc_hal.c b/drivers/media/video/msm_vidc/vidc_hal.c
index f0d0e73..8ac35d9 100644
--- a/drivers/media/video/msm_vidc/vidc_hal.c
+++ b/drivers/media/video/msm_vidc/vidc_hal.c
@@ -1437,6 +1437,7 @@
prop->multi_slice);
break;
}
+ hfi->slice_size = prop->slice_size;
pkt->size += sizeof(u32) + sizeof(struct
hfi_multi_slice_control);
break;
diff --git a/drivers/media/video/msm_wfd/wfd-ioctl.c b/drivers/media/video/msm_wfd/wfd-ioctl.c
index 04b787a..d8080dd 100644
--- a/drivers/media/video/msm_wfd/wfd-ioctl.c
+++ b/drivers/media/video/msm_wfd/wfd-ioctl.c
@@ -91,6 +91,7 @@
u32 out_buf_size;
struct list_head input_mem_list;
struct wfd_stats stats;
+ struct completion stop_mdp_thread;
};
struct wfd_vid_buffer {
@@ -522,7 +523,7 @@
static int mdp_output_thread(void *data)
{
- int rc = 0;
+ int rc = 0, no_sig_wait = 0;
struct file *filp = (struct file *)data;
struct wfd_inst *inst = filp->private_data;
struct wfd_device *wfd_dev =
@@ -531,6 +532,14 @@
struct mem_region *mregion;
struct vsg_buf_info ibuf_vsg;
while (!kthread_should_stop()) {
+ if (rc) {
+ WFD_MSG_DBG("%s() error in output thread\n", __func__);
+ if (!no_sig_wait) {
+ wait_for_completion(&inst->stop_mdp_thread);
+ no_sig_wait = 1;
+ }
+ continue;
+ }
WFD_MSG_DBG("waiting for mdp output\n");
rc = v4l2_subdev_call(&wfd_dev->mdp_sdev,
core, ioctl, MDP_DQ_BUFFER, (void *)&obuf_mdp);
@@ -540,7 +549,7 @@
WFD_MSG_ERR("MDP reported err %d\n", rc);
WFD_MSG_ERR("Streamoff called\n");
- break;
+ continue;
} else {
wfd_stats_update(&inst->stats,
WFD_STAT_EVENT_MDP_DEQUEUE);
@@ -550,7 +559,7 @@
if (!mregion) {
WFD_MSG_ERR("mdp cookie is null\n");
rc = -EINVAL;
- break;
+ continue;
}
ibuf_vsg.mdp_buf_info = obuf_mdp;
@@ -565,7 +574,7 @@
if (rc) {
WFD_MSG_ERR("Failed to queue frame to vsg\n");
- break;
+ continue;
} else {
wfd_stats_update(&inst->stats,
WFD_STAT_EVENT_VSG_QUEUE);
@@ -599,7 +608,7 @@
WFD_MSG_ERR("Failed to start vsg\n");
goto subdev_start_fail;
}
-
+ init_completion(&inst->stop_mdp_thread);
inst->mdp_task = kthread_run(mdp_output_thread, priv_data,
"mdp_output_thread");
if (IS_ERR(inst->mdp_task)) {
@@ -634,6 +643,7 @@
if (rc)
WFD_MSG_ERR("Failed to stop VSG\n");
+ complete(&inst->stop_mdp_thread);
kthread_stop(inst->mdp_task);
rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
ENCODE_FLUSH, (void *)inst->venc_inst);
diff --git a/drivers/media/video/vcap_v4l2.c b/drivers/media/video/vcap_v4l2.c
index 753171c..a160915 100644
--- a/drivers/media/video/vcap_v4l2.c
+++ b/drivers/media/video/vcap_v4l2.c
@@ -19,6 +19,8 @@
#include <linux/videodev2.h>
#include <linux/platform_device.h>
#include <linux/memory_alloc.h>
+#include <linux/ctype.h>
+#include <linux/debugfs.h>
#include <mach/board.h>
#include <mach/gpio.h>
@@ -51,13 +53,31 @@
static struct vcap_dev *vcap_ctrl;
-static unsigned debug;
-
-#define dprintk(level, fmt, arg...) \
- do { \
- if (debug >= level) \
- printk(KERN_DEBUG "VCAP: " fmt, ## arg); \
- } while (0)
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *vcap_debugfs_base;
+static struct reg_range debug_reg_range[] = {
+ {
+ VCAP_REG_RANGE_1_MIN,
+ VCAP_REG_RANGE_1_MAX,
+ },
+ {
+ VCAP_REG_RANGE_2_MIN,
+ VCAP_REG_RANGE_2_MAX,
+ },
+ {
+ VCAP_REG_RANGE_3_MIN,
+ VCAP_REG_RANGE_3_MAX,
+ },
+ {
+ VCAP_REG_RANGE_4_MIN,
+ VCAP_REG_RANGE_4_MAX,
+ },
+ {
+ VCAP_REG_RANGE_5_MIN,
+ VCAP_REG_RANGE_5_MAX,
+ },
+};
+#endif
int vcap_reg_powerup(struct vcap_dev *dev)
{
@@ -91,7 +111,7 @@
int num_gpios = pdata->num_gpios;
unsigned *gpios = pdata->gpios;
- dprintk(4, "GPIO config start\n");
+ pr_debug("GPIO config start\n");
if (on) {
for (i = 0; i < num_gpios; i++) {
ret = gpio_request(gpios[i], "vcap:vc");
@@ -112,7 +132,7 @@
for (i = 0; i < num_gpios; i++)
gpio_free(gpios[i]);
}
- dprintk(4, "GPIO config exit\n");
+ pr_debug("GPIO config exit\n");
return 0;
gpio_failed:
for (i--; i >= 0; i--)
@@ -151,6 +171,7 @@
pr_err("%s: Failed core set_rate %d\n", __func__, ret);
goto fail_vcap_clk;
}
+ dev->dbg_p.clk_rate = (uint32_t) rate;
dev->vcap_npl_clk = clk_get(ddev, "vcap_npl_clk");
if (IS_ERR(dev->vcap_npl_clk)) {
@@ -198,6 +219,7 @@
dev->vcap_npl_clk = NULL;
fail_vcap_clk:
+ dev->dbg_p.clk_rate = 0;
clk_disable(dev->vcap_clk);
fail_vcap_clk_unprep:
clk_unprepare(dev->vcap_clk);
@@ -228,6 +250,8 @@
clk_put(dev->vcap_clk);
dev->vcap_clk = NULL;
}
+
+ dev->dbg_p.clk_rate = 0;
}
int vcap_get_bus_client_handle(struct vcap_dev *dev)
@@ -244,6 +268,7 @@
unsigned long rate)
{
int rc;
+ pr_debug("Enter %s", __func__);
rc = vcap_reg_powerup(dev);
if (rc < 0)
@@ -259,6 +284,7 @@
goto gpio_failed;
writel_relaxed(0x00030003, VCAP_OFFSET(0xD78));
writel_relaxed(0x00030003, VCAP_OFFSET(0xD7C));
+ pr_debug("Success Exit %s", __func__);
return 0;
gpio_failed:
@@ -274,10 +300,12 @@
int vcap_disable(struct vcap_dev *dev)
{
+ pr_debug("Enter %s", __func__);
config_gpios(0, dev->vcap_pdata);
msm_bus_scale_unregister_client(dev->bus_client_handle);
dev->bus_client_handle = 0;
+ dev->dbg_p.bw_request = 0;
vcap_clk_powerdown(dev);
vcap_reg_powerdown(dev);
return 0;
@@ -311,34 +339,34 @@
struct vb2_buffer *vb;
if (q->fileio) {
- dprintk(1, "%s: file io in progress\n", __func__);
+ pr_debug("%s: file io in progress\n", __func__);
return -EBUSY;
}
if (b->type != q->type) {
- dprintk(1, "%s: invalid buffer type\n", __func__);
+ pr_debug("%s: invalid buffer type\n", __func__);
return -EINVAL;
}
if (b->index >= q->num_buffers) {
- dprintk(1, "%s: buffer index out of range\n", __func__);
+ pr_debug("%s: buffer index out of range\n", __func__);
return -EINVAL;
}
vb = q->bufs[b->index];
if (NULL == vb) {
- dprintk(1, "%s: buffer is NULL\n", __func__);
+ pr_debug("%s: buffer is NULL\n", __func__);
return -EINVAL;
}
if (b->memory != q->memory) {
- dprintk(1, "%s: invalid memory type\n", __func__);
+ pr_debug("%s: invalid memory type\n", __func__);
return -EINVAL;
}
if (vb->state != VB2_BUF_STATE_DEQUEUED &&
vb->state != VB2_BUF_STATE_PREPARED) {
- dprintk(1, "%s: buffer already in use\n", __func__);
+ pr_err("%s: buffer already in use\n", __func__);
return -EINVAL;
}
@@ -361,17 +389,17 @@
unsigned long flags;
if (q->fileio) {
- dprintk(1, "%s: file io in progress\n", __func__);
+ pr_debug("%s: file io in progress\n", __func__);
return -EBUSY;
}
if (b->type != q->type) {
- dprintk(1, "%s: invalid buffer type\n", __func__);
+ pr_debug("%s: invalid buffer type\n", __func__);
return -EINVAL;
}
if (!q->streaming) {
- dprintk(1, "Streaming off, will not wait for buffers\n");
+ pr_debug("Streaming off, will not wait for buffers\n");
return -EINVAL;
}
@@ -384,13 +412,13 @@
switch (vb->state) {
case VB2_BUF_STATE_DONE:
- dprintk(3, "%s: Returning done buffer\n", __func__);
+ pr_debug("%s: Returning done buffer\n", __func__);
break;
case VB2_BUF_STATE_ERROR:
- dprintk(3, "%s: Ret done buf with err\n", __func__);
+ pr_debug("%s: Ret done buf with err\n", __func__);
break;
default:
- dprintk(1, "%s: Invalid buffer state\n", __func__);
+ pr_debug("%s: Invalid buffer state\n", __func__);
return -EINVAL;
}
@@ -402,7 +430,7 @@
return 0;
}
- dprintk(1, "No buffers to dequeue\n");
+ pr_debug("%s: No buffers to dequeue\n", __func__);
return -EAGAIN;
}
@@ -415,28 +443,28 @@
int rc;
if (q->fileio) {
- dprintk(1, "%s: file io in progress\n", __func__);
+ pr_debug("%s: file io in progress\n", __func__);
return -EBUSY;
}
if (b->type != q->type) {
- dprintk(1, "%s: invalid buffer type\n", __func__);
+ pr_debug("%s: invalid buffer type\n", __func__);
return -EINVAL;
}
if (b->index >= q->num_buffers) {
- dprintk(1, "%s: buffer index out of range\n", __func__);
+ pr_debug("%s: buffer index out of range\n", __func__);
return -EINVAL;
}
vb = q->bufs[b->index];
if (NULL == vb) {
- dprintk(1, "%s: buffer is NULL\n", __func__);
+ pr_debug("%s: buffer is NULL\n", __func__);
return -EINVAL;
}
if (vb->state != VB2_BUF_STATE_DEQUEUED) {
- dprintk(1, "%s: buffer already in use\n", __func__);
+ pr_debug("%s: buffer already in use\n", __func__);
return -EINVAL;
}
@@ -468,7 +496,7 @@
buf = container_of(vb, struct vcap_buffer, vb);
if (buf->ion_handle == NULL) {
- dprintk(1, "%s: no ION handle to free\n", __func__);
+ pr_debug("%s: no ION handle to free\n", __func__);
return;
}
buf->paddr = 0;
@@ -546,7 +574,7 @@
static int capture_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct vcap_client_data *c_data = vb2_get_drv_priv(vq);
- dprintk(2, "VC start streaming\n");
+ pr_debug("VC start streaming\n");
return vc_start_capture(c_data);
}
@@ -643,7 +671,7 @@
static int vp_in_start_streaming(struct vb2_queue *vq, unsigned int count)
{
- dprintk(2, "VP IN start streaming\n");
+ pr_debug("VP IN start streaming\n");
return 0;
}
@@ -652,7 +680,7 @@
struct vcap_client_data *c_data = vb2_get_drv_priv(vq);
struct vb2_buffer *vb;
- dprintk(2, "VP stop streaming\n");
+ pr_debug("VP IN stop streaming\n");
while (!list_empty(&c_data->vp_action.in_active)) {
struct vcap_buffer *buf;
@@ -749,7 +777,7 @@
struct vcap_client_data *c_data = vb2_get_drv_priv(vq);
struct vb2_buffer *vb;
- dprintk(2, "VP out q stop streaming\n");
+ pr_debug("VP OUT q stop streaming\n");
vp_stop_capture(c_data);
while (!list_empty(&c_data->vp_action.out_active)) {
@@ -890,7 +918,7 @@
struct vcap_dev *dev = c_data->dev;
int rc;
- dprintk(3, "In Req Buf %08x\n", (unsigned int)rb->type);
+ pr_debug("VCAP: In Req Buf %08x\n", (unsigned int)rb->type);
c_data->op_mode = determine_mode(c_data);
if (c_data->op_mode == UNKNOWN_VCAP_OP) {
pr_err("VCAP Error: %s: VCAP in unknown mode\n", __func__);
@@ -968,25 +996,25 @@
struct vb2_queue *q;
int rc;
- dprintk(3, "In Q Buf %08x\n", (unsigned int)p->type);
+ pr_debug("VCAP In Q Buf %08x\n", (unsigned int)p->type);
switch (p->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (c_data->op_mode == VC_AND_VP_VCAP_OP) {
/* If buffer in vp_in_q it will be coming back */
q = &c_data->vp_in_vidq;
if (p->index >= q->num_buffers) {
- dprintk(1, "qbuf: buffer index out of range\n");
+ pr_debug("VCAP qbuf: buffer index out of range\n");
return -EINVAL;
}
vb = q->bufs[p->index];
if (NULL == vb) {
- dprintk(1, "qbuf: buffer is NULL\n");
+ pr_debug("VCAP qbuf: buffer is NULL\n");
return -EINVAL;
}
if (vb->state != VB2_BUF_STATE_DEQUEUED) {
- dprintk(1, "qbuf: buffer already in use\n");
+ pr_debug("VCAP qbuf: buffer already in use\n");
return -EINVAL;
}
rc = get_phys_addr(c_data->dev, &c_data->vc_vidq, p);
@@ -1035,7 +1063,7 @@
struct vcap_client_data *c_data = to_client_data(file->private_data);
int rc;
- dprintk(3, "In DQ Buf %08x\n", (unsigned int)p->type);
+ pr_debug("VCAP In DQ Buf %08x\n", (unsigned int)p->type);
switch (p->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (c_data->op_mode == VC_AND_VP_VCAP_OP)
@@ -1072,18 +1100,18 @@
int streamon_validate_q(struct vb2_queue *q)
{
if (q->fileio) {
- dprintk(1, "streamon: file io in progress\n");
+ pr_debug("%s: file io in progress\n", __func__);
return -EBUSY;
}
if (q->streaming) {
- dprintk(1, "streamon: already streaming\n");
+ pr_debug("%s: already streaming\n", __func__);
return -EBUSY;
}
if (V4L2_TYPE_IS_OUTPUT(q->type)) {
if (list_empty(&q->queued_list)) {
- dprintk(1, "streamon: no output buffers queued\n");
+ pr_debug("%s: no output buffers queued\n", __func__);
return -EINVAL;
}
}
@@ -1103,10 +1131,11 @@
idx++;
} while (idx < length);
if (idx == length) {
- pr_err("VCAP: Defaulting to highest BW request\n");
+ pr_info("VCAP: Defaulting to highest BW request\n");
idx--;
}
msm_bus_scale_client_update_request(dev->bus_client_handle, idx);
+ dev->dbg_p.bw_request = bus_vectors[idx].vectors[0].ab;
return 0;
}
@@ -1118,7 +1147,7 @@
unsigned long rate;
long rate_rc;
- dprintk(3, "In Stream ON\n");
+ pr_debug("VCAP: In Stream ON\n");
if (determine_mode(c_data) != c_data->op_mode) {
pr_err("VCAP Error: %s: s_fmt called after req_buf", __func__);
return -ENOTRECOVERABLE;
@@ -1160,6 +1189,8 @@
if (rc < 0)
goto free_res;
+ dev->dbg_p.clk_rate = (uint32_t) rate;
+
rate = (c_data->vc_format.hactive_end -
c_data->vc_format.hactive_start);
@@ -1204,6 +1235,8 @@
}
rate = (unsigned long)rate_rc;
rc = clk_set_rate(dev->vcap_clk, rate);
+
+ dev->dbg_p.clk_rate = (uint32_t) rate;
if (rc < 0)
goto free_res;
@@ -1279,6 +1312,8 @@
if (rc < 0)
goto free_res;
+ dev->dbg_p.clk_rate = (uint32_t) rate;
+
rate = (c_data->vc_format.hactive_end -
c_data->vc_format.hactive_start);
@@ -1376,12 +1411,12 @@
int streamoff_validate_q(struct vb2_queue *q)
{
if (q->fileio) {
- dprintk(1, "streamoff: file io in progress\n");
+ pr_debug("%s: file io in progress\n", __func__);
return -EBUSY;
}
if (!q->streaming) {
- dprintk(1, "streamoff: not streaming\n");
+ pr_debug("%s: not streaming\n", __func__);
return -EINVAL;
}
return 0;
@@ -1791,7 +1826,7 @@
struct vb2_queue *q;
unsigned int mask = 0;
- dprintk(1, "Enter slect/poll\n");
+ pr_debug("%s: Enter slect/poll\n", __func__);
switch (c_data->op_mode) {
case VC_VCAP_OP:
@@ -1866,13 +1901,276 @@
return vc_handler(vcap_ctrl);
}
+#ifdef CONFIG_DEBUG_FS
+/* Query VCAP resource usage */
+static ssize_t read_dump_info(struct file *file, char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct vcap_dev *dev = file->private_data;
+ char str_buf[512];
+ size_t tot_size = 0, size;
+
+ if (dev->vc_client) {
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "VCAP: VC\n");
+ tot_size += size;
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "vc_resourse = %d\n", dev->vc_resource);
+ tot_size += size;
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "vc_enabled = %d\n", atomic_read(&dev->vc_enabled));
+ tot_size += size;
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "vc_client id = %p\n", dev->vc_client);
+ tot_size += size;
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "vc_queue_count = %d\n",
+ atomic_read(&dev->vc_client->vc_vidq.queued_count));
+ tot_size += size;
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "vc_total_buffers = %d\n",
+ dev->vc_client->vc_action.tot_buf);
+ tot_size += size;
+ } else {
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "VCAP: VC not in use\n");
+ tot_size += size;
+ }
+ if (dev->vp_client) {
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "VCAP: VP\n");
+ tot_size += size;
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "vp_resourse = %d\n", dev->vp_resource);
+ tot_size += size;
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "vp_enabled = %d\n", atomic_read(&dev->vp_enabled));
+ tot_size += size;
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "vp_client id = %p\n", dev->vp_client);
+ tot_size += size;
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "vp_in_queue_count = %d\n",
+ atomic_read(
+ &dev->vp_client->vp_in_vidq.queued_count));
+ tot_size += size;
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "vp_out_queue_count = %d\n",
+ atomic_read(
+ &dev->vp_client->vp_out_vidq.queued_count));
+ tot_size += size;
+ } else {
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "VCAP: VP not in use\n");
+ tot_size += size;
+ }
+
+ return simple_read_from_buffer(user_buf, len, ppos, str_buf, tot_size);
+}
+
+static const struct file_operations dump_info_fops = {
+ .read = read_dump_info,
+ .open = simple_open,
+ .llseek = default_llseek,
+};
+
+static int vcap_debug_clk_rate_get(void *data, u64 *val)
+{
+ struct vcap_dev *dev = data;
+ *val = (u64)dev->dbg_p.clk_rate;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(clk_rate_fops, vcap_debug_clk_rate_get,
+ NULL, "%llu\n");
+
+static int vcap_debug_bw_req_get(void *data, u64 *val)
+{
+ struct vcap_dev *dev = data;
+ *val = (u64)dev->dbg_p.bw_request;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(bw_req_fops, vcap_debug_bw_req_get,
+ NULL, "%llu\n");
+
+static int vcap_debug_drop_frames_get(void *data, u64 *val)
+{
+ struct vcap_dev *dev = data;
+ struct timeval tv;
+ int drop_count;
+
+ if (!dev->vc_resource)
+ return -EPERM;
+ drop_count = atomic_read(&dev->dbg_p.vc_drop_count);
+ atomic_set(&dev->dbg_p.vc_drop_count, 0);
+
+ do_gettimeofday(&tv);
+ dev->dbg_p.vc_timestamp = (uint32_t) (tv.tv_sec * VCAP_USEC +
+ tv.tv_usec);
+
+ *val = (u64)drop_count;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(tot_frame_drop_fops, vcap_debug_drop_frames_get,
+ NULL, "%llu\n");
+
+static int vcap_debug_drop_fps_get(void *data, u64 *val)
+{
+ struct vcap_dev *dev = data;
+ struct timeval tv;
+ int drop_count;
+ uint32_t new_ts;
+
+ if (!dev->vc_resource)
+ return -EPERM;
+ drop_count = atomic_read(&dev->dbg_p.vc_drop_count);
+ atomic_set(&dev->dbg_p.vc_drop_count, 0);
+
+ do_gettimeofday(&tv);
+ new_ts = (uint32_t) (tv.tv_sec * VCAP_USEC +
+ tv.tv_usec);
+
+ if ((new_ts - dev->dbg_p.vc_timestamp) / VCAP_USEC &&
+ new_ts > dev->dbg_p.vc_timestamp)
+ drop_count /= ((new_ts - dev->dbg_p.vc_timestamp) / VCAP_USEC);
+ else
+ drop_count = 0;
+
+ dev->dbg_p.vc_timestamp = new_ts;
+ *val = (u64)drop_count;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(drop_fps_fops, vcap_debug_drop_fps_get,
+ NULL, "%llu\n");
+
+static int vcap_debug_vp_lat_get(void *data, u64 *val)
+{
+ struct vcap_dev *dev = data;
+
+ if (!dev->vp_resource)
+ return -EPERM;
+ *val = (u64)dev->dbg_p.vp_ewma;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(vp_lat_fops, vcap_debug_vp_lat_get,
+ NULL, "%llu\n");
+
+/* Read/Write to VCAP Registers */
+static int vcap_debug_reg_set(void *data, u64 val)
+{
+ struct vcap_dev *dev = data;
+ int i;
+ for (i = 0; i < ARRAY_SIZE(debug_reg_range); i++) {
+ if (val >= debug_reg_range[i].min_val && val <=
+ debug_reg_range[i].max_val)
+ break;
+ }
+ if (i == ARRAY_SIZE(debug_reg_range))
+ return -EINVAL;
+ dev->dbg_p.reg_addr = (uint32_t) val;
+ return 0;
+}
+
+static int vcap_debug_reg_get(void *data, u64 *val)
+{
+ struct vcap_dev *dev = data;
+ *val = (u64)dev->dbg_p.reg_addr;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(vcap_reg_fops, vcap_debug_reg_get,
+ vcap_debug_reg_set, "0x%08llx\n")
+
+static int vcap_debug_reg_rdwr_set(void *data, u64 val)
+{
+ struct vcap_dev *dev = data;
+ u32 reg_val = (u32) val;
+
+ writel_iowmb(reg_val, VCAP_OFFSET(dev->dbg_p.reg_addr));
+ return 0;
+}
+
+static int vcap_debug_reg_rdwr_get(void *data, u64 *val)
+{
+ struct vcap_dev *dev = data;
+ *val = (u64)readl_relaxed(VCAP_OFFSET(dev->dbg_p.reg_addr));
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(vcap_reg_rdwr_fops, vcap_debug_reg_rdwr_get,
+ vcap_debug_reg_rdwr_set, "0x%08llx\n");
+
+static int vcap_debugfs_init(struct vcap_dev *dev)
+{
+ vcap_debugfs_base = debugfs_create_dir("vcap", NULL);
+ if (!vcap_debugfs_base)
+ return -ENOMEM;
+
+ if (!debugfs_create_file("dump_info", S_IRUGO,
+ vcap_debugfs_base, dev, &dump_info_fops))
+ goto error;
+
+ if (!debugfs_create_file("vcap_core_clk_rate", S_IRUGO,
+ vcap_debugfs_base, dev, &clk_rate_fops))
+ goto error;
+
+ if (!debugfs_create_file("vcap_bw_req", S_IRUGO,
+ vcap_debugfs_base, dev, &bw_req_fops))
+ goto error;
+
+ if (!debugfs_create_file("vc_total_frames_drop", S_IRUGO,
+ vcap_debugfs_base, dev, &tot_frame_drop_fops))
+ goto error;
+
+ if (!debugfs_create_file("vc_drop_fps", S_IRUGO,
+ vcap_debugfs_base, dev, &drop_fps_fops))
+ goto error;
+
+ if (!debugfs_create_file("vp_avg_completion_t", S_IRUGO,
+ vcap_debugfs_base, dev, &vp_lat_fops))
+ goto error;
+
+ if (!debugfs_create_file("vcap_reg_addr", S_IRUGO | S_IWUSR,
+ vcap_debugfs_base, dev, &vcap_reg_fops))
+ goto error;
+
+ if (!debugfs_create_file("vcap_reg_val", S_IRUGO | S_IWUSR,
+ vcap_debugfs_base, dev, &vcap_reg_rdwr_fops))
+ goto error;
+ return 0;
+
+error:
+ debugfs_remove_recursive(vcap_debugfs_base);
+ vcap_debugfs_base = NULL;
+ return -ENOMEM;
+}
+
+static void vcap_debugfs_remove(void)
+{
+ if (vcap_debugfs_base) {
+ debugfs_remove_recursive(vcap_debugfs_base);
+ vcap_debugfs_base = NULL;
+ }
+}
+#else
+
+static int vcap_debugfs_init(struct vcap_dev *dev)
+{
+ return 0;
+}
+static void vcap_debugfs_remove(void) {}
+#endif
+
static int __devinit vcap_probe(struct platform_device *pdev)
{
struct vcap_dev *dev;
struct video_device *vfd;
int ret;
- dprintk(1, "Probe started\n");
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
@@ -1949,6 +2247,7 @@
if (ret)
goto unreg_dev;
msm_bus_scale_client_update_request(dev->bus_client_handle, 0);
+ dev->dbg_p.bw_request = 0;
ret = detect_vc(dev);
@@ -1987,6 +2286,11 @@
goto rel_vcap_wq;
}
+ atomic_set(&dev->dbg_p.vc_drop_count, 0);
+ ret = vcap_debugfs_init(dev);
+ if (ret < 0)
+ pr_err("VCAP debugfs failed to load");
+
dev->vc_tot_buf = 2;
atomic_set(&dev->vc_enabled, 0);
atomic_set(&dev->vp_enabled, 0);
@@ -1996,7 +2300,6 @@
init_waitqueue_head(&dev->vp_dummy_waitq);
vcap_disable(dev);
- dprintk(1, "Exit probe succesfully");
return 0;
rel_vcap_wq:
destroy_workqueue(dev->vcap_wq);
@@ -2020,6 +2323,7 @@
static int __devexit vcap_remove(struct platform_device *pdev)
{
struct vcap_dev *dev = vcap_ctrl;
+ vcap_debugfs_remove();
ion_client_destroy(dev->ion_client);
flush_workqueue(dev->vcap_wq);
destroy_workqueue(dev->vcap_wq);
diff --git a/drivers/media/video/vcap_vc.c b/drivers/media/video/vcap_vc.c
index 572c272..3d81161 100644
--- a/drivers/media/video/vcap_vc.c
+++ b/drivers/media/video/vcap_vc.c
@@ -27,14 +27,6 @@
#include <media/vcap_fmt.h>
#include "vcap_vc.h"
-static unsigned debug;
-
-#define dprintk(level, fmt, arg...) \
- do { \
- if (debug >= level) \
- printk(KERN_DEBUG "VC: " fmt, ## arg); \
- } while (0)
-
void config_buffer(struct vcap_client_data *c_data,
struct vcap_buffer *buf,
void __iomem *y_addr,
@@ -73,7 +65,7 @@
vb_vc = vp_work->cd->vc_vidq.bufs[p.index];
if (NULL == vb_vc) {
- dprintk(1, "%s: buffer is NULL\n", __func__);
+ pr_debug("%s: buffer is NULL\n", __func__);
vcvp_qbuf(&vp_work->cd->vc_vidq, &p);
return;
}
@@ -81,7 +73,7 @@
vb_vp = vp_work->cd->vp_in_vidq.bufs[p.index];
if (NULL == vb_vp) {
- dprintk(1, "%s: buffer is NULL\n", __func__);
+ pr_debug("%s: buffer is NULL\n", __func__);
vcvp_qbuf(&vp_work->cd->vc_vidq, &p);
return;
}
@@ -145,7 +137,7 @@
irq = readl_relaxed(VCAP_VC_INT_STATUS);
- dprintk(1, "%s: irq=0x%08x\n", __func__, irq);
+ pr_debug("%s: irq=0x%08x\n", __func__, irq);
c_data = dev->vc_client;
if (!c_data->streaming) {
@@ -254,6 +246,8 @@
v4l2_event_queue(dev->vfd, &v4l2_evt);
c_data->vc_action.top_field =
!c_data->vc_action.top_field;
+
+ atomic_inc(&dev->dbg_p.vc_drop_count);
continue;
}
buf = list_entry(c_data->vc_action.active.next,
@@ -296,12 +290,13 @@
{
struct vc_action *vc_action = &c_data->vc_action;
struct vcap_dev *dev;
+ struct timeval tv;
unsigned long flags = 0;
int rc, i, counter = 0;
struct vcap_buffer *buf;
dev = c_data->dev;
- dprintk(2, "Start Kickoff\n");
+ pr_debug("Start Kickoff\n");
if (dev->vc_client == NULL) {
pr_err("No active vc client\n");
@@ -344,6 +339,11 @@
c_data->vc_action.vc_ts.tv_usec =
c_data->vc_action.last_ts % VCAP_USEC;
+ atomic_set(&dev->dbg_p.vc_drop_count, 0);
+ do_gettimeofday(&tv);
+ dev->dbg_p.vc_timestamp = (uint32_t) (tv.tv_sec * VCAP_USEC +
+ tv.tv_usec);
+
rc = 0;
for (i = 0; i < c_data->vc_action.tot_buf; i++)
rc = rc << 1 | 0x2;
@@ -416,7 +416,7 @@
rc = readl_relaxed(VCAP_VC_NPL_CTRL);
writel_iowmb(0x00000002, VCAP_VC_NPL_CTRL);
- dprintk(2, "%s: Starting VC configuration\n", __func__);
+ pr_debug("%s: Starting VC configuration\n", __func__);
writel_iowmb(0x00000002, VCAP_VC_NPL_CTRL);
writel_iowmb(0x00000004 | vc_format->color_space << 1 |
vc_format->mode << 3 |
@@ -464,7 +464,7 @@
writel_relaxed(0x00006b38, VCAP_VC_IN_CTRL5);
writel_iowmb(0x00000001 , VCAP_OFFSET(0x0d00));
- dprintk(2, "%s: Done VC configuration\n", __func__);
+ pr_debug("%s: Done VC configuration\n", __func__);
return 0;
}
@@ -473,7 +473,7 @@
{
int result;
result = readl_relaxed(VCAP_HARDWARE_VERSION_REG);
- dprintk(1, "Hardware version: %08x\n", result);
+ pr_debug("Hardware version: %08x\n", result);
if (result != VCAP_HARDWARE_VERSION)
return -ENODEV;
INIT_WORK(&dev->vc_to_vp_work.work, mov_buf_to_vp);
diff --git a/drivers/media/video/vcap_vp.c b/drivers/media/video/vcap_vp.c
index a017cf2..c7de465 100644
--- a/drivers/media/video/vcap_vp.c
+++ b/drivers/media/video/vcap_vp.c
@@ -25,14 +25,6 @@
#include <media/vcap_fmt.h>
#include "vcap_vp.h"
-static unsigned debug;
-
-#define dprintk(level, fmt, arg...) \
- do { \
- if (debug >= level) \
- printk(KERN_DEBUG "VP: " fmt, ## arg); \
- } while (0)
-
void config_nr_buffer(struct vcap_client_data *c_data,
struct vcap_buffer *buf)
{
@@ -72,10 +64,10 @@
if (!c_data->streaming)
return -ENOEXEC;
dev = c_data->dev;
- dprintk(2, "Start setup buffers\n");
+ pr_debug("VP: Start setup buffers\n");
if (dev->vp_shutdown) {
- dprintk(1, "%s: VP shutting down, no buf setup\n",
+ pr_debug("%s: VP shutting down, no buf setup\n",
__func__);
return -EPERM;
}
@@ -86,7 +78,7 @@
spin_lock_irqsave(&dev->vp_client->cap_slock, flags);
if (list_empty(&vp_act->in_active)) {
spin_unlock_irqrestore(&dev->vp_client->cap_slock, flags);
- dprintk(1, "%s: VP We have no more input buffers\n",
+ pr_debug("%s: VP We have no more input buffers\n",
__func__);
return -EAGAIN;
}
@@ -94,7 +86,7 @@
if (list_empty(&vp_act->out_active)) {
spin_unlock_irqrestore(&dev->vp_client->cap_slock,
flags);
- dprintk(1, "%s: VP We have no more output buffers\n",
+ pr_debug("%s: VP We have no more output buffers\n",
__func__);
return -EAGAIN;
}
@@ -136,7 +128,7 @@
vb_vc = vp_work->cd->vc_vidq.bufs[p.index];
if (NULL == vb_vc) {
- dprintk(1, "%s: buffer is NULL\n", __func__);
+ pr_debug("%s: buffer is NULL\n", __func__);
vcvp_qbuf(&vp_work->cd->vp_in_vidq, &p);
return;
}
@@ -144,7 +136,7 @@
vb_vp = vp_work->cd->vp_in_vidq.bufs[p.index];
if (NULL == vb_vp) {
- dprintk(1, "%s: buffer is NULL\n", __func__);
+ pr_debug("%s: buffer is NULL\n", __func__);
vcvp_qbuf(&vp_work->cd->vp_in_vidq, &p);
return;
}
@@ -158,7 +150,7 @@
/* This call should not fail */
rc = vcvp_qbuf(&vp_work->cd->vc_vidq, &p);
if (rc < 0) {
- dprintk(1, "%s: qbuf to vc failed\n", __func__);
+ pr_err("%s: qbuf to vc failed\n", __func__);
buf_vp->ion_handle = buf_vc->ion_handle;
buf_vp->paddr = buf_vc->paddr;
buf_vc->ion_handle = NULL;
@@ -197,6 +189,7 @@
struct vp_work_t *vp_work = container_of(work, struct vp_work_t, work);
struct vcap_dev *dev;
struct vp_action *vp_act;
+ struct timeval tv;
unsigned long flags = 0;
uint32_t irq;
int rc;
@@ -278,6 +271,11 @@
writel_iowmb(0x00000000 | top_field, VCAP_VP_CTRL);
writel_iowmb(0x00010000 | top_field, VCAP_VP_CTRL);
enable_irq(dev->vpirq->start);
+
+ do_gettimeofday(&tv);
+ dev->dbg_p.vp_timestamp = (uint32_t) (tv.tv_sec * VCAP_USEC +
+ tv.tv_usec);
+
writel_iowmb(irq, VCAP_VP_INT_CLEAR);
}
@@ -288,6 +286,8 @@
struct v4l2_event v4l2_evt;
uint32_t irq;
int rc;
+ struct timeval tv;
+ uint32_t new_ts;
irq = readl_relaxed(VCAP_VP_INT_STATUS);
if (dev->vp_dummy_event == true) {
@@ -318,7 +318,7 @@
v4l2_event_queue(dev->vfd, &v4l2_evt);
}
- dprintk(1, "%s: irq=0x%08x\n", __func__, irq);
+ pr_debug("%s: irq=0x%08x\n", __func__, irq);
if (!(irq & (VP_PIC_DONE | VP_MODE_CHANGE))) {
writel_relaxed(irq, VCAP_VP_INT_CLEAR);
pr_err("VP IRQ shows some error\n");
@@ -341,6 +341,17 @@
return -EAGAIN;
}
+ do_gettimeofday(&tv);
+ new_ts = (uint32_t) (tv.tv_sec * VCAP_USEC +
+ tv.tv_usec);
+ if (new_ts > dev->dbg_p.vp_timestamp) {
+ dev->dbg_p.vp_ewma = ((new_ts - dev->dbg_p.vp_timestamp) /
+ 10 + (dev->dbg_p.vp_ewma / 10 * 9));
+ }
+
+ dev->dbg_p.vp_timestamp = (uint32_t) (tv.tv_sec * VCAP_USEC +
+ tv.tv_usec);
+
INIT_WORK(&dev->vp_work.work, vp_wq_fnc);
dev->vp_work.cd = c_data;
rc = queue_work(dev->vcap_wq, &dev->vp_work.work);
@@ -663,7 +674,7 @@
uint32_t reg;
int rc = 0;
- dprintk(2, "%s: Start VP dummy event\n", __func__);
+ pr_debug("%s: Start VP dummy event\n", __func__);
handle = ion_alloc(dev->ion_client, 0x1200, SZ_4K,
ION_HEAP(ION_CP_MM_HEAP_ID), 0);
if (IS_ERR_OR_NULL(handle)) {
@@ -723,7 +734,7 @@
c_data->vp_out_fmt.height = height;
ion_free(dev->ion_client, handle);
- dprintk(2, "%s: Exit VP dummy event\n", __func__);
+ pr_debug("%s: Exit VP dummy event\n", __func__);
return rc;
}
@@ -731,6 +742,7 @@
{
struct vcap_dev *dev;
struct vp_action *vp_act;
+ struct timeval tv;
unsigned long flags = 0;
unsigned int chroma_fmt = 0;
int size;
@@ -740,7 +752,7 @@
return -ENOEXEC;
dev = c_data->dev;
- dprintk(2, "Start Kickoff\n");
+ pr_debug("Start VP Kickoff\n");
if (dev->vp_client == NULL) {
pr_err("No active vp client\n");
@@ -815,6 +827,11 @@
writel_iowmb(0x00010000 | top_field, VCAP_VP_CTRL);
atomic_set(&c_data->dev->vp_enabled, 1);
enable_irq(dev->vpirq->start);
+
+ do_gettimeofday(&tv);
+ dev->dbg_p.vp_timestamp = (uint32_t) (tv.tv_sec * VCAP_USEC +
+ tv.tv_usec);
+
return 0;
}
@@ -822,10 +839,11 @@
{
struct vcap_dev *dev;
struct vp_action *vp_act;
+ struct timeval tv;
int rc;
bool top_field = 0;
- dprintk(2, "Start Continue\n");
+ pr_debug("Start VP Continue\n");
dev = c_data->dev;
if (dev->vp_client == NULL) {
@@ -854,5 +872,10 @@
atomic_set(&c_data->dev->vp_enabled, 1);
enable_irq(dev->vpirq->start);
+
+ do_gettimeofday(&tv);
+ dev->dbg_p.vp_timestamp = (uint32_t) (tv.tv_sec * VCAP_USEC +
+ tv.tv_usec);
+
return 0;
}
diff --git a/drivers/mfd/pm8038-core.c b/drivers/mfd/pm8038-core.c
index 9815f6e..48bc92d 100644
--- a/drivers/mfd/pm8038-core.c
+++ b/drivers/mfd/pm8038-core.c
@@ -33,6 +33,11 @@
#define REG_RTC_BASE 0x11D
#define REG_IRQ_BASE 0x1BB
+#define REG_BATT_ALARM_THRESH 0x023
+#define REG_BATT_ALARM_CTRL1 0x024
+#define REG_BATT_ALARM_CTRL2 0x021
+#define REG_BATT_ALARM_PWM_CTRL 0x020
+
#define REG_SPK_BASE 0x253
#define REG_SPK_REGISTERS 6
@@ -336,6 +341,27 @@
.pdata_size = sizeof(struct pm8xxx_tm_core_data),
};
+static const struct resource batt_alarm_cell_resources[] __devinitconst = {
+ SINGLE_IRQ_RESOURCE("pm8921_batt_alarm_irq", PM8038_BATT_ALARM_IRQ),
+};
+
+static struct pm8xxx_batt_alarm_core_data batt_alarm_cdata = {
+ .irq_name = "pm8921_batt_alarm_irq",
+ .reg_addr_threshold = REG_BATT_ALARM_THRESH,
+ .reg_addr_ctrl1 = REG_BATT_ALARM_CTRL1,
+ .reg_addr_ctrl2 = REG_BATT_ALARM_CTRL2,
+ .reg_addr_pwm_ctrl = REG_BATT_ALARM_PWM_CTRL,
+};
+
+static struct mfd_cell batt_alarm_cell __devinitdata = {
+ .name = PM8XXX_BATT_ALARM_DEV_NAME,
+ .id = -1,
+ .resources = batt_alarm_cell_resources,
+ .num_resources = ARRAY_SIZE(batt_alarm_cell_resources),
+ .platform_data = &batt_alarm_cdata,
+ .pdata_size = sizeof(struct pm8xxx_batt_alarm_core_data),
+};
+
static const struct resource ccadc_cell_resources[] __devinitconst = {
SINGLE_IRQ_RESOURCE("PM8921_BMS_CCADC_EOC", PM8921_BMS_CCADC_EOC),
};
@@ -661,6 +687,13 @@
goto bail;
}
+ ret = mfd_add_devices(pmic->dev, 0, &batt_alarm_cell, 1, NULL,
+ irq_base);
+ if (ret) {
+ pr_err("Failed to add battery alarm subdevice ret=%d\n", ret);
+ goto bail;
+ }
+
if (pdata->ccadc_pdata) {
ccadc_cell.platform_data = pdata->ccadc_pdata;
ccadc_cell.pdata_size =
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
index e7e11d0..65537e4 100644
--- a/drivers/mfd/wcd9xxx-core.c
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/mfd/core.h>
#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
@@ -257,13 +258,20 @@
u8 byte[4];
struct mfd_cell *dev;
int size;
+ int num_irqs;
} wcd9xxx_codecs[] = {
- {{0x2, 0x0, 0x0, 0x1}, tabla_devs, ARRAY_SIZE(tabla_devs)},
- {{0x1, 0x0, 0x0, 0x1}, tabla1x_devs, ARRAY_SIZE(tabla1x_devs)},
- {{0x0, 0x0, 0x2, 0x1}, taiko_devs, ARRAY_SIZE(taiko_devs)},
- {{0x0, 0x0, 0x0, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs)},
- {{0x1, 0x0, 0x1, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs)},
- {{0x2, 0x0, 0x1, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs)},
+ {{0x2, 0x0, 0x0, 0x1}, tabla_devs, ARRAY_SIZE(tabla_devs),
+ TABLA_NUM_IRQS},
+ {{0x1, 0x0, 0x0, 0x1}, tabla1x_devs, ARRAY_SIZE(tabla1x_devs),
+ TABLA_NUM_IRQS},
+ {{0x0, 0x0, 0x2, 0x1}, taiko_devs, ARRAY_SIZE(taiko_devs),
+ TAIKO_NUM_IRQS},
+ {{0x0, 0x0, 0x0, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs),
+ SITAR_NUM_IRQS},
+ {{0x1, 0x0, 0x1, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs),
+ SITAR_NUM_IRQS},
+ {{0x2, 0x0, 0x1, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs),
+ SITAR_NUM_IRQS},
};
static void wcd9xxx_bring_up(struct wcd9xxx *wcd9xxx)
@@ -312,8 +320,9 @@
}
}
static int wcd9xxx_check_codec_type(struct wcd9xxx *wcd9xxx,
- struct mfd_cell **wcd9xxx_dev,
- int *wcd9xxx_dev_size)
+ struct mfd_cell **wcd9xxx_dev,
+ int *wcd9xxx_dev_size,
+ int *wcd9xxx_dev_num_irqs)
{
int i;
int ret;
@@ -343,6 +352,7 @@
wcd9xxx_codecs[i].dev->name);
*wcd9xxx_dev = wcd9xxx_codecs[i].dev;
*wcd9xxx_dev_size = wcd9xxx_codecs[i].size;
+ *wcd9xxx_dev_num_irqs = wcd9xxx_codecs[i].num_irqs;
break;
}
i++;
@@ -359,7 +369,7 @@
return ret;
}
-static int wcd9xxx_device_init(struct wcd9xxx *wcd9xxx, int irq)
+static int wcd9xxx_device_init(struct wcd9xxx *wcd9xxx)
{
int ret;
struct mfd_cell *wcd9xxx_dev = NULL;
@@ -379,6 +389,11 @@
wcd9xxx_bring_up(wcd9xxx);
+ ret = wcd9xxx_check_codec_type(wcd9xxx, &wcd9xxx_dev, &wcd9xxx_dev_size,
+ &wcd9xxx->num_irqs);
+ if (ret < 0)
+ goto err_irq;
+
if (wcd9xxx->irq != -1) {
ret = wcd9xxx_irq_init(wcd9xxx);
if (ret) {
@@ -386,11 +401,7 @@
goto err;
}
}
- ret = wcd9xxx_check_codec_type(wcd9xxx, &wcd9xxx_dev,
- &wcd9xxx_dev_size);
- if (ret < 0)
- goto err_irq;
ret = mfd_add_devices(wcd9xxx->dev, -1, wcd9xxx_dev, wcd9xxx_dev_size,
NULL, 0);
if (ret != 0) {
@@ -795,10 +806,12 @@
wcd9xxx->read_dev = wcd9xxx_i2c_read;
wcd9xxx->write_dev = wcd9xxx_i2c_write;
- wcd9xxx->irq = pdata->irq;
- wcd9xxx->irq_base = pdata->irq_base;
+ if (!wcd9xxx->dev->of_node) {
+ wcd9xxx->irq = pdata->irq;
+ wcd9xxx->irq_base = pdata->irq_base;
+ }
- ret = wcd9xxx_device_init(wcd9xxx, wcd9xxx->irq);
+ ret = wcd9xxx_device_init(wcd9xxx);
if (ret) {
pr_err("%s: error, initializing device failed\n", __func__);
goto err_device_init;
@@ -1072,7 +1085,6 @@
pdata->reset_gpio);
goto err;
}
- pdata->irq = -1;
ret = wcd9xxx_dt_parse_slim_interface_dev_info(dev,
&pdata->slimbus_slave_device);
@@ -1163,14 +1175,12 @@
}
wcd9xxx->read_dev = wcd9xxx_slim_read_device;
wcd9xxx->write_dev = wcd9xxx_slim_write_device;
- wcd9xxx->irq = pdata->irq;
- wcd9xxx->irq_base = pdata->irq_base;
wcd9xxx_pgd_la = wcd9xxx->slim->laddr;
-
- if (pdata->num_irqs < TABLA_NUM_IRQS)
- pr_warn("%s: Not enough interrupt lines allocated\n", __func__);
-
wcd9xxx->slim_slave = &pdata->slimbus_slave_device;
+ if (!wcd9xxx->dev->of_node) {
+ wcd9xxx->irq = pdata->irq;
+ wcd9xxx->irq_base = pdata->irq_base;
+ }
ret = slim_add_device(slim->ctrl, wcd9xxx->slim_slave);
if (ret) {
@@ -1190,7 +1200,7 @@
wcd9xxx_inf_la = wcd9xxx->slim_slave->laddr;
wcd9xxx_intf = WCD9XXX_INTERFACE_TYPE_SLIMBUS;
- ret = wcd9xxx_device_init(wcd9xxx, wcd9xxx->irq);
+ ret = wcd9xxx_device_init(wcd9xxx);
if (ret) {
pr_err("%s: error, initializing device failed\n", __func__);
goto err_slim_add;
diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c
index e9b2ef3..103c1a3 100644
--- a/drivers/mfd/wcd9xxx-irq.c
+++ b/drivers/mfd/wcd9xxx-irq.c
@@ -18,8 +18,13 @@
#include <linux/mfd/wcd9xxx/core.h>
#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
#include <linux/mfd/wcd9xxx/wcd9310_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+#include <linux/delay.h>
+#include <linux/irqdomain.h>
#include <linux/interrupt.h>
-
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
#include <mach/cpuidle.h>
#define BYTE_BIT_MASK(nr) (1UL << ((nr) % BITS_PER_BYTE))
@@ -27,19 +32,18 @@
#define WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS 100
-struct wcd9xxx_irq {
- bool level;
+#ifdef CONFIG_OF
+struct wcd9xxx_irq_drv_data {
+ struct irq_domain *domain;
+ int irq;
};
+#endif
-static struct wcd9xxx_irq wcd9xxx_irqs[TABLA_NUM_IRQS] = {
- [0] = { .level = 1},
-/* All other wcd9xxx interrupts are edge triggered */
-};
-
-static inline int irq_to_wcd9xxx_irq(struct wcd9xxx *wcd9xxx, int irq)
-{
- return irq - wcd9xxx->irq_base;
-}
+static int virq_to_phyirq(struct wcd9xxx *wcd9xxx, int virq);
+static int phyirq_to_virq(struct wcd9xxx *wcd9xxx, int irq);
+static unsigned int wcd9xxx_irq_get_upstream_irq(struct wcd9xxx *wcd9xxx);
+static void wcd9xxx_irq_put_upstream_irq(struct wcd9xxx *wcd9xxx);
+static int wcd9xxx_map_irq(struct wcd9xxx *wcd9xxx, int irq);
static void wcd9xxx_irq_lock(struct irq_data *data)
{
@@ -58,8 +62,9 @@
*/
if (wcd9xxx->irq_masks_cur[i] != wcd9xxx->irq_masks_cache[i]) {
wcd9xxx->irq_masks_cache[i] = wcd9xxx->irq_masks_cur[i];
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MASK0+i,
- wcd9xxx->irq_masks_cur[i]);
+ wcd9xxx_reg_write(wcd9xxx,
+ WCD9XXX_A_INTR_MASK0 + i,
+ wcd9xxx->irq_masks_cur[i]);
}
}
@@ -69,7 +74,7 @@
static void wcd9xxx_irq_enable(struct irq_data *data)
{
struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
- int wcd9xxx_irq = irq_to_wcd9xxx_irq(wcd9xxx, data->irq);
+ int wcd9xxx_irq = virq_to_phyirq(wcd9xxx, data->irq);
wcd9xxx->irq_masks_cur[BIT_BYTE(wcd9xxx_irq)] &=
~(BYTE_BIT_MASK(wcd9xxx_irq));
}
@@ -77,9 +82,14 @@
static void wcd9xxx_irq_disable(struct irq_data *data)
{
struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
- int wcd9xxx_irq = irq_to_wcd9xxx_irq(wcd9xxx, data->irq);
+ int wcd9xxx_irq = virq_to_phyirq(wcd9xxx, data->irq);
wcd9xxx->irq_masks_cur[BIT_BYTE(wcd9xxx_irq)]
- |= BYTE_BIT_MASK(wcd9xxx_irq);
+ |= BYTE_BIT_MASK(wcd9xxx_irq);
+}
+
+static void wcd9xxx_irq_mask(struct irq_data *d)
+{
+ /* do nothing but required as linux calls irq_mask without NULL check */
}
static struct irq_chip wcd9xxx_irq_chip = {
@@ -88,6 +98,7 @@
.irq_bus_sync_unlock = wcd9xxx_irq_sync_unlock,
.irq_disable = wcd9xxx_irq_disable,
.irq_enable = wcd9xxx_irq_enable,
+ .irq_mask = wcd9xxx_irq_mask,
};
enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg(struct wcd9xxx *wcd9xxx,
@@ -167,62 +178,72 @@
static void wcd9xxx_irq_dispatch(struct wcd9xxx *wcd9xxx, int irqbit)
{
- if ((irqbit <= TABLA_IRQ_MBHC_INSERTION) &&
- (irqbit >= TABLA_IRQ_MBHC_REMOVAL)) {
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_CLEAR0 +
- BIT_BYTE(irqbit), BYTE_BIT_MASK(irqbit));
+ if ((irqbit <= WCD9XXX_IRQ_MBHC_INSERTION) &&
+ (irqbit >= WCD9XXX_IRQ_MBHC_REMOVAL)) {
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_CLEAR0 +
+ BIT_BYTE(irqbit),
+ BYTE_BIT_MASK(irqbit));
if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MODE, 0x02);
- handle_nested_irq(wcd9xxx->irq_base + irqbit);
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MODE, 0x02);
+ handle_nested_irq(phyirq_to_virq(wcd9xxx, irqbit));
} else {
- handle_nested_irq(wcd9xxx->irq_base + irqbit);
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_CLEAR0 +
- BIT_BYTE(irqbit), BYTE_BIT_MASK(irqbit));
+ handle_nested_irq(phyirq_to_virq(wcd9xxx, irqbit));
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_CLEAR0 +
+ BIT_BYTE(irqbit),
+ BYTE_BIT_MASK(irqbit));
if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MODE, 0x02);
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MODE, 0x02);
}
}
+static int wcd9xxx_num_irq_regs(const struct wcd9xxx *wcd9xxx)
+{
+ return (wcd9xxx->num_irqs / 8) + ((wcd9xxx->num_irqs % 8) ? 1 : 0);
+}
+
static irqreturn_t wcd9xxx_irq_thread(int irq, void *data)
{
int ret;
- struct wcd9xxx *wcd9xxx = data;
- u8 status[WCD9XXX_NUM_IRQ_REGS];
int i;
+ struct wcd9xxx *wcd9xxx = data;
+ int num_irq_regs = wcd9xxx_num_irq_regs(wcd9xxx);
+ u8 status[num_irq_regs];
if (unlikely(wcd9xxx_lock_sleep(wcd9xxx) == false)) {
dev_err(wcd9xxx->dev, "Failed to hold suspend\n");
return IRQ_NONE;
}
- ret = wcd9xxx_bulk_read(wcd9xxx, TABLA_A_INTR_STATUS0,
- WCD9XXX_NUM_IRQ_REGS, status);
+ ret = wcd9xxx_bulk_read(wcd9xxx, WCD9XXX_A_INTR_STATUS0,
+ num_irq_regs, status);
if (ret < 0) {
dev_err(wcd9xxx->dev, "Failed to read interrupt status: %d\n",
ret);
wcd9xxx_unlock_sleep(wcd9xxx);
return IRQ_NONE;
}
+
/* Apply masking */
- for (i = 0; i < WCD9XXX_NUM_IRQ_REGS; i++)
+ for (i = 0; i < num_irq_regs; i++)
status[i] &= ~wcd9xxx->irq_masks_cur[i];
/* Find out which interrupt was triggered and call that interrupt's
* handler function
*/
- if (status[BIT_BYTE(TABLA_IRQ_SLIMBUS)] &
- BYTE_BIT_MASK(TABLA_IRQ_SLIMBUS))
- wcd9xxx_irq_dispatch(wcd9xxx, TABLA_IRQ_SLIMBUS);
+ if (status[BIT_BYTE(WCD9XXX_IRQ_SLIMBUS)] &
+ BYTE_BIT_MASK(WCD9XXX_IRQ_SLIMBUS))
+ wcd9xxx_irq_dispatch(wcd9xxx, WCD9XXX_IRQ_SLIMBUS);
/* Since codec has only one hardware irq line which is shared by
* codec's different internal interrupts, so it's possible master irq
* handler dispatches multiple nested irq handlers after breaking
* order. Dispatch MBHC interrupts order to follow MBHC state
* machine's order */
- for (i = TABLA_IRQ_MBHC_INSERTION; i >= TABLA_IRQ_MBHC_REMOVAL; i--) {
+ for (i = WCD9XXX_IRQ_MBHC_INSERTION;
+ i >= WCD9XXX_IRQ_MBHC_REMOVAL; i--) {
if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i))
wcd9xxx_irq_dispatch(wcd9xxx, i);
}
- for (i = TABLA_IRQ_BG_PRECHARGE; i < TABLA_NUM_IRQS; i++) {
+ for (i = WCD9XXX_IRQ_BG_PRECHARGE; i < wcd9xxx->num_irqs; i++) {
if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i))
wcd9xxx_irq_dispatch(wcd9xxx, i);
}
@@ -231,59 +252,105 @@
return IRQ_HANDLED;
}
+void wcd9xxx_free_irq(struct wcd9xxx *wcd9xxx, int irq, void *data)
+{
+ free_irq(phyirq_to_virq(wcd9xxx, irq), data);
+}
+
+void wcd9xxx_enable_irq(struct wcd9xxx *wcd9xxx, int irq)
+{
+ enable_irq(phyirq_to_virq(wcd9xxx, irq));
+}
+
+void wcd9xxx_disable_irq(struct wcd9xxx *wcd9xxx, int irq)
+{
+ disable_irq_nosync(phyirq_to_virq(wcd9xxx, irq));
+}
+
+void wcd9xxx_disable_irq_sync(struct wcd9xxx *wcd9xxx, int irq)
+{
+ disable_irq(phyirq_to_virq(wcd9xxx, irq));
+}
+
+static int wcd9xxx_irq_setup_downstream_irq(struct wcd9xxx *wcd9xxx)
+{
+ int irq, virq, ret;
+
+ pr_debug("%s: enter\n", __func__);
+
+ for (irq = 0; irq < wcd9xxx->num_irqs; irq++) {
+ /* Map OF irq */
+ virq = wcd9xxx_map_irq(wcd9xxx, irq);
+ pr_debug("%s: irq %d -> %d\n", __func__, irq, virq);
+ if (virq == NO_IRQ) {
+ pr_err("%s, No interrupt specifier for irq %d\n",
+ __func__, irq);
+ return NO_IRQ;
+ }
+
+ ret = irq_set_chip_data(virq, wcd9xxx);
+ if (ret) {
+ pr_err("%s: Failed to configure irq %d (%d)\n",
+ __func__, irq, ret);
+ return ret;
+ }
+
+ if (wcd9xxx->irq_level_high[irq])
+ irq_set_chip_and_handler(virq, &wcd9xxx_irq_chip,
+ handle_level_irq);
+ else
+ irq_set_chip_and_handler(virq, &wcd9xxx_irq_chip,
+ handle_edge_irq);
+
+ irq_set_nested_thread(virq, 1);
+ }
+
+ pr_debug("%s: leave\n", __func__);
+
+ return 0;
+}
+
int wcd9xxx_irq_init(struct wcd9xxx *wcd9xxx)
{
- int ret;
- unsigned int i, cur_irq;
+ int i, ret;
+ u8 irq_level[wcd9xxx_num_irq_regs(wcd9xxx)];
mutex_init(&wcd9xxx->irq_lock);
+ wcd9xxx->irq = wcd9xxx_irq_get_upstream_irq(wcd9xxx);
if (!wcd9xxx->irq) {
- dev_warn(wcd9xxx->dev,
- "No interrupt specified, no interrupts\n");
- wcd9xxx->irq_base = 0;
- return 0;
+ pr_warn("%s: irq driver is not yet initialized\n", __func__);
+ mutex_destroy(&wcd9xxx->irq_lock);
+ return -EPROBE_DEFER;
+ }
+ pr_debug("%s: probed irq %d\n", __func__, wcd9xxx->irq);
+
+ /* Setup downstream IRQs */
+ ret = wcd9xxx_irq_setup_downstream_irq(wcd9xxx);
+ if (ret) {
+ pr_err("%s: Failed to setup downstream IRQ\n", __func__);
+ wcd9xxx_irq_put_upstream_irq(wcd9xxx);
+ return ret;
}
- if (!wcd9xxx->irq_base) {
- dev_err(wcd9xxx->dev,
- "No interrupt base specified, no interrupts\n");
- return 0;
- }
- /* Mask the individual interrupt sources */
- for (i = 0, cur_irq = wcd9xxx->irq_base; i < TABLA_NUM_IRQS; i++,
- cur_irq++) {
+ /* All other wcd9xxx interrupts are edge triggered */
+ wcd9xxx->irq_level_high[0] = true;
- irq_set_chip_data(cur_irq, wcd9xxx);
-
- if (wcd9xxx_irqs[i].level)
- irq_set_chip_and_handler(cur_irq, &wcd9xxx_irq_chip,
- handle_level_irq);
- else
- irq_set_chip_and_handler(cur_irq, &wcd9xxx_irq_chip,
- handle_edge_irq);
-
- irq_set_nested_thread(cur_irq, 1);
-
- /* ARM needs us to explicitly flag the IRQ as valid
- * and will set them noprobe when we do so. */
-#ifdef CONFIG_ARM
- set_irq_flags(cur_irq, IRQF_VALID);
-#else
- set_irq_noprobe(cur_irq);
-#endif
-
+ /* mask all the interrupts */
+ memset(irq_level, 0, wcd9xxx_num_irq_regs(wcd9xxx));
+ for (i = 0; i < wcd9xxx->num_irqs; i++) {
wcd9xxx->irq_masks_cur[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
wcd9xxx->irq_masks_cache[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
- wcd9xxx->irq_level[BIT_BYTE(i)] |= wcd9xxx_irqs[i].level <<
- (i % BITS_PER_BYTE);
+ irq_level[BIT_BYTE(i)] |=
+ wcd9xxx->irq_level_high[i] << (i % BITS_PER_BYTE);
}
- for (i = 0; i < WCD9XXX_NUM_IRQ_REGS; i++) {
+
+ for (i = 0; i < wcd9xxx_num_irq_regs(wcd9xxx); i++) {
/* Initialize interrupt mask and level registers */
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_LEVEL0 + i,
- wcd9xxx->irq_level[i]);
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MASK0 + i,
- wcd9xxx->irq_masks_cur[i]);
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_LEVEL0 + i,
+ irq_level[i]);
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MASK0 + i,
+ wcd9xxx->irq_masks_cur[i]);
}
ret = request_threaded_irq(wcd9xxx->irq, NULL, wcd9xxx_irq_thread,
@@ -308,18 +375,226 @@
free_irq(wcd9xxx->irq, wcd9xxx);
}
- if (ret)
+ if (ret) {
+ pr_err("%s: Failed to init wcd9xxx irq\n", __func__);
+ wcd9xxx_irq_put_upstream_irq(wcd9xxx);
mutex_destroy(&wcd9xxx->irq_lock);
+ }
return ret;
}
+int wcd9xxx_request_irq(struct wcd9xxx *wcd9xxx, int irq, irq_handler_t handler,
+ const char *name, void *data)
+{
+ int virq;
+
+ virq = phyirq_to_virq(wcd9xxx, irq);
+
+ /*
+ * ARM needs us to explicitly flag the IRQ as valid
+ * and will set them noprobe when we do so.
+ */
+#ifdef CONFIG_ARM
+ set_irq_flags(virq, IRQF_VALID);
+#else
+ set_irq_noprobe(virq);
+#endif
+
+ return request_threaded_irq(virq, NULL, handler, IRQF_TRIGGER_RISING,
+ name, data);
+}
+
void wcd9xxx_irq_exit(struct wcd9xxx *wcd9xxx)
{
if (wcd9xxx->irq) {
disable_irq_wake(wcd9xxx->irq);
free_irq(wcd9xxx->irq, wcd9xxx);
+ /* Release parent's of node */
+ wcd9xxx_irq_put_upstream_irq(wcd9xxx);
device_init_wakeup(wcd9xxx->dev, 0);
}
mutex_destroy(&wcd9xxx->irq_lock);
}
+
+#ifndef CONFIG_OF
+static int phyirq_to_virq(struct wcd9xxx *wcd9xxx, int offset)
+{
+ return wcd9xxx->irq_base + offset;
+}
+
+static int virq_to_phyirq(struct wcd9xxx *wcd9xxx, int virq)
+{
+ return virq - wcd9xxx->irq_base;
+}
+
+static unsigned int wcd9xxx_irq_get_upstream_irq(struct wcd9xxx *wcd9xxx)
+{
+ return wcd9xxx->irq;
+}
+
+static void wcd9xxx_irq_put_upstream_irq(struct wcd9xxx *wcd9xxx)
+{
+ /* Do nothing */
+}
+
+static int wcd9xxx_map_irq(struct wcd9xxx *wcd9xxx, int irq)
+{
+ return phyirq_to_virq(wcd9xxx, irq);
+}
+#else
+int __init wcd9xxx_irq_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ struct wcd9xxx_irq_drv_data *data;
+
+ pr_debug("%s: node %s, node parent %s\n", __func__,
+ node->name, node->parent->name);
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /*
+ * wcd9xxx_intc interrupt controller supports N to N irq mapping with
+ * single cell binding with irq numbers(offsets) only.
+ * Use irq_domain_simple_ops that has irq_domain_simple_map and
+ * irq_domain_xlate_onetwocell.
+ */
+ data->domain = irq_domain_add_linear(node, WCD9XXX_MAX_NUM_IRQS,
+ &irq_domain_simple_ops, data);
+ if (!data->domain) {
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static struct wcd9xxx_irq_drv_data *
+wcd9xxx_get_irq_drv_d(const struct wcd9xxx *wcd9xxx)
+{
+ struct device_node *pnode;
+ struct irq_domain *domain;
+
+ pnode = of_irq_find_parent(wcd9xxx->dev->of_node);
+ /* Shouldn't happen */
+ if (unlikely(!pnode))
+ return NULL;
+
+ domain = irq_find_host(pnode);
+ return (struct wcd9xxx_irq_drv_data *)domain->host_data;
+}
+
+static int phyirq_to_virq(struct wcd9xxx *wcd9xxx, int offset)
+{
+ struct wcd9xxx_irq_drv_data *data;
+
+ data = wcd9xxx_get_irq_drv_d(wcd9xxx);
+ if (!data) {
+ pr_warn("%s: not registered to interrupt controller\n",
+ __func__);
+ return -EINVAL;
+ }
+ return irq_linear_revmap(data->domain, offset);
+}
+
+static int virq_to_phyirq(struct wcd9xxx *wcd9xxx, int virq)
+{
+ struct irq_data *irq_data = irq_get_irq_data(virq);
+ return irq_data->hwirq;
+}
+
+static unsigned int wcd9xxx_irq_get_upstream_irq(struct wcd9xxx *wcd9xxx)
+{
+ struct wcd9xxx_irq_drv_data *data;
+
+ /* Hold parent's of node */
+ if (!of_node_get(of_irq_find_parent(wcd9xxx->dev->of_node)))
+ return -EINVAL;
+
+ data = wcd9xxx_get_irq_drv_d(wcd9xxx);
+ if (!data) {
+ pr_err("%s: interrupt controller is not registerd\n", __func__);
+ return 0;
+ }
+
+ rmb();
+ return data->irq;
+}
+
+static void wcd9xxx_irq_put_upstream_irq(struct wcd9xxx *wcd9xxx)
+{
+ /* Hold parent's of node */
+ of_node_put(of_irq_find_parent(wcd9xxx->dev->of_node));
+}
+
+static int wcd9xxx_map_irq(struct wcd9xxx *wcd9xxx, int irq)
+{
+ return of_irq_to_resource(wcd9xxx->dev->of_node, irq, NULL);
+}
+
+static int __devinit wcd9xxx_irq_probe(struct platform_device *pdev)
+{
+ int irq;
+ struct irq_domain *domain;
+ struct wcd9xxx_irq_drv_data *data;
+ int ret = -EINVAL;
+
+ irq = platform_get_irq_byname(pdev, "cdc-int");
+ if (irq < 0) {
+ dev_err(&pdev->dev, "%s: Couldn't find cdc-int node(%d)\n",
+ __func__, irq);
+ return -EINVAL;
+ } else {
+ dev_dbg(&pdev->dev, "%s: virq = %d\n", __func__, irq);
+ domain = irq_find_host(pdev->dev.of_node);
+ data = (struct wcd9xxx_irq_drv_data *)domain->host_data;
+ data->irq = irq;
+ wmb();
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int wcd9xxx_irq_remove(struct platform_device *pdev)
+{
+ struct irq_domain *domain;
+ struct wcd9xxx_irq_drv_data *data;
+
+ domain = irq_find_host(pdev->dev.of_node);
+ data = (struct wcd9xxx_irq_drv_data *)domain->host_data;
+ data->irq = 0;
+ wmb();
+
+ return 0;
+}
+
+static const struct of_device_id of_match[] = {
+ { .compatible = "qcom,wcd9xxx-irq" },
+ { }
+};
+
+static struct platform_driver wcd9xxx_irq_driver = {
+ .probe = wcd9xxx_irq_probe,
+ .remove = wcd9xxx_irq_remove,
+ .driver = {
+ .name = "wcd9xxx_intc",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(of_match),
+ },
+};
+
+static int wcd9xxx_irq_drv_init(void)
+{
+ return platform_driver_register(&wcd9xxx_irq_driver);
+}
+subsys_initcall(wcd9xxx_irq_drv_init);
+
+static void wcd9xxx_irq_drv_exit(void)
+{
+ platform_driver_unregister(&wcd9xxx_irq_driver);
+}
+module_exit(wcd9xxx_irq_drv_exit);
+#endif /* CONFIG_OF */
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index bc8eccf..12f896e 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -38,6 +38,7 @@
#include <mach/msm_bus_board.h>
#include <mach/scm.h>
#include <mach/peripheral-loader.h>
+#include <mach/socinfo.h>
#include "qseecom_legacy.h"
#define QSEECOM_DEV "qseecom"
@@ -179,6 +180,7 @@
int send_resp_flag;
uint32_t qseos_version;
+ struct device *pdev;
};
struct qseecom_client_handle {
@@ -208,9 +210,16 @@
atomic_t ioctl_count;
};
+struct clk *ce_core_clk;
+struct clk *ce_clk;
+struct clk *ce_core_src_clk;
+struct clk *ce_bus_clk;
+
/* Function proto types */
static int qsee_vote_for_clock(int32_t);
static void qsee_disable_clock_vote(int32_t);
+static int __qseecom_init_clk(void);
+static void __qseecom_disable_clk(void);
static int __qseecom_is_svc_unique(struct qseecom_dev_handle *data,
struct qseecom_register_listener_req *svc)
@@ -1735,9 +1744,131 @@
.release = qseecom_release
};
+static int __qseecom_init_clk()
+{
+ int rc = 0;
+ struct device *pdev;
+
+ pdev = qseecom.pdev;
+ /* Get CE3 src core clk. */
+ ce_core_src_clk = clk_get(pdev, "core_clk_src");
+ if (!IS_ERR(ce_core_src_clk)) {
+ ce_core_src_clk = ce_core_src_clk;
+
+ /* Set the core src clk @100Mhz */
+ rc = clk_set_rate(ce_core_src_clk, 100000000);
+ if (rc) {
+ clk_put(ce_core_src_clk);
+ pr_err("Unable to set the core src clk @100Mhz.\n");
+ goto err_clk;
+ }
+ } else {
+ pr_warn("Unable to get CE core src clk, set to NULL\n");
+ ce_core_src_clk = NULL;
+ }
+
+ /* Get CE core clk */
+ ce_core_clk = clk_get(pdev, "core_clk");
+ if (IS_ERR(ce_core_clk)) {
+ rc = PTR_ERR(ce_core_clk);
+ pr_err("Unable to get CE core clk\n");
+ if (ce_core_src_clk != NULL)
+ clk_put(ce_core_src_clk);
+ goto err_clk;
+ }
+
+ /* Get CE Interface clk */
+ ce_clk = clk_get(pdev, "iface_clk");
+ if (IS_ERR(ce_clk)) {
+ rc = PTR_ERR(ce_clk);
+ pr_err("Unable to get CE interface clk\n");
+ if (ce_core_src_clk != NULL)
+ clk_put(ce_core_src_clk);
+ clk_put(ce_core_clk);
+ goto err_clk;
+ }
+
+ /* Get CE AXI clk */
+ ce_bus_clk = clk_get(pdev, "bus_clk");
+ if (IS_ERR(ce_bus_clk)) {
+ rc = PTR_ERR(ce_bus_clk);
+ pr_err("Unable to get CE BUS interface clk\n");
+ if (ce_core_src_clk != NULL)
+ clk_put(ce_core_src_clk);
+ clk_put(ce_core_clk);
+ clk_put(ce_clk);
+ goto err_clk;
+ }
+
+ /* Enable CE core clk */
+ rc = clk_prepare_enable(ce_core_clk);
+ if (rc) {
+ pr_err("Unable to enable/prepare CE core clk\n");
+ if (ce_core_src_clk != NULL)
+ clk_put(ce_core_src_clk);
+ clk_put(ce_core_clk);
+ clk_put(ce_clk);
+ goto err_clk;
+ } else {
+ /* Enable CE clk */
+ rc = clk_prepare_enable(ce_clk);
+ if (rc) {
+ pr_err("Unable to enable/prepare CE iface clk\n");
+ clk_disable_unprepare(ce_core_clk);
+ if (ce_core_src_clk != NULL)
+ clk_put(ce_core_src_clk);
+ clk_put(ce_core_clk);
+ clk_put(ce_clk);
+ goto err_clk;
+ } else {
+ /* Enable AXI clk */
+ rc = clk_prepare_enable(ce_bus_clk);
+ if (rc) {
+ pr_err("Unable to enable/prepare CE iface clk\n");
+ clk_disable_unprepare(ce_core_clk);
+ clk_disable_unprepare(ce_clk);
+ if (ce_core_src_clk != NULL)
+ clk_put(ce_core_src_clk);
+ clk_put(ce_core_clk);
+ clk_put(ce_clk);
+ goto err_clk;
+ }
+ }
+ }
+ return rc;
+
+err_clk:
+ if (rc)
+ pr_err("Unable to init CE clks, rc = %d\n", rc);
+ clk_disable_unprepare(ce_clk);
+ clk_disable_unprepare(ce_core_clk);
+ clk_disable_unprepare(ce_bus_clk);
+ if (ce_core_src_clk != NULL)
+ clk_put(ce_core_src_clk);
+ clk_put(ce_clk);
+ clk_put(ce_core_clk);
+ clk_put(ce_bus_clk);
+ return rc;
+}
+
+
+
+static void __qseecom_disable_clk()
+{
+ clk_disable_unprepare(ce_clk);
+ clk_disable_unprepare(ce_core_clk);
+ clk_disable_unprepare(ce_bus_clk);
+ if (ce_core_src_clk != NULL)
+ clk_put(ce_core_src_clk);
+ clk_put(ce_clk);
+ clk_put(ce_core_clk);
+ clk_put(ce_bus_clk);
+}
+
static int __devinit qseecom_probe(struct platform_device *pdev)
{
int rc;
+ int ret;
struct device *class_dev;
char qsee_not_legacy = 0;
struct msm_bus_scale_pdata *qseecom_platform_support;
@@ -1796,6 +1927,8 @@
pil = NULL;
pil_ref_cnt = 0;
}
+
+ qseecom.pdev = class_dev;
/* Create ION msm client */
qseecom.ion_clnt = msm_ion_client_create(0x03, "qseecom-kernel");
if (qseecom.ion_clnt == NULL) {
@@ -1805,17 +1938,23 @@
}
/* register client for bus scaling */
- if (!pdev->dev.of_node) {
+ if (pdev->dev.of_node) {
+ ret = __qseecom_init_clk();
+ if (ret)
+ goto err;
+ qseecom_platform_support = (struct msm_bus_scale_pdata *)
+ msm_bus_cl_get_pdata(pdev);
+ } else {
qseecom_platform_support = (struct msm_bus_scale_pdata *)
pdev->dev.platform_data;
- qsee_perf_client = msm_bus_scale_register_client(
- qseecom_platform_support);
-
- if (!qsee_perf_client)
- pr_err("Unable to register bus client\n");
}
- return 0;
+ qsee_perf_client = msm_bus_scale_register_client(
+ qseecom_platform_support);
+
+ if (!qsee_perf_client)
+ pr_err("Unable to register bus client\n");
+ return 0;
err:
device_destroy(driver_class, qseecom_device_no);
class_destroy:
@@ -1856,6 +1995,9 @@
static void __devexit qseecom_exit(void)
{
+
+ __qseecom_disable_clk();
+
device_destroy(driver_class, qseecom_device_no);
class_destroy(driver_class);
unregister_chrdev_region(qseecom_device_no, 1);
diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig
index ebb4afe..33f0600 100644
--- a/drivers/mmc/card/Kconfig
+++ b/drivers/mmc/card/Kconfig
@@ -76,3 +76,13 @@
This driver is only of interest to those developing or
testing a host driver. Most people should say N here.
+
+config MMC_BLOCK_TEST
+ tristate "MMC block test"
+ depends on MMC_BLOCK && IOSCHED_TEST
+ help
+ MMC block test can be used with test iosched to test the MMC block
+ device.
+ Currently used to test eMMC 4.5 features (packed commands, sanitize,
+ BKOPs).
+
diff --git a/drivers/mmc/card/Makefile b/drivers/mmc/card/Makefile
index c73b406..d55107f 100644
--- a/drivers/mmc/card/Makefile
+++ b/drivers/mmc/card/Makefile
@@ -8,3 +8,4 @@
obj-$(CONFIG_SDIO_UART) += sdio_uart.o
+obj-$(CONFIG_MMC_BLOCK_TEST) += mmc_block_test.o
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index a9f1b53..7387d9a 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -65,6 +65,11 @@
(rq_data_dir(req) == WRITE))
#define PACKED_CMD_VER 0x01
#define PACKED_CMD_WR 0x02
+#define MMC_BLK_UPDATE_STOP_REASON(stats, reason) \
+ do { \
+ if (stats->enabled) \
+ stats->pack_stop_reason[reason]++; \
+ } while (0)
static DEFINE_MUTEX(block_mutex);
@@ -115,22 +120,13 @@
unsigned int part_curr;
struct device_attribute force_ro;
struct device_attribute power_ro_lock;
+ struct device_attribute num_wr_reqs_to_start_packing;
+ struct device_attribute min_sectors_to_check_bkops_status;
int area_type;
};
static DEFINE_MUTEX(open_lock);
-enum mmc_blk_status {
- MMC_BLK_SUCCESS = 0,
- MMC_BLK_PARTIAL,
- MMC_BLK_CMD_ERR,
- MMC_BLK_RETRY,
- MMC_BLK_ABORT,
- MMC_BLK_DATA_ERR,
- MMC_BLK_ECC_ERR,
- MMC_BLK_NOMEDIUM,
-};
-
enum {
MMC_PACKED_N_IDX = -1,
MMC_PACKED_N_ZERO,
@@ -279,6 +275,80 @@
return ret;
}
+static ssize_t
+num_wr_reqs_to_start_packing_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+ int num_wr_reqs_to_start_packing;
+ int ret;
+
+ num_wr_reqs_to_start_packing = md->queue.num_wr_reqs_to_start_packing;
+
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", num_wr_reqs_to_start_packing);
+
+ mmc_blk_put(md);
+ return ret;
+}
+
+static ssize_t
+num_wr_reqs_to_start_packing_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int value;
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+
+ sscanf(buf, "%d", &value);
+ if (value >= 0)
+ md->queue.num_wr_reqs_to_start_packing = value;
+
+ mmc_blk_put(md);
+ return count;
+}
+
+static ssize_t
+min_sectors_to_check_bkops_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+ unsigned int min_sectors_to_check_bkops_status;
+ struct mmc_card *card = md->queue.card;
+ int ret;
+
+ if (!card)
+ return -EINVAL;
+
+ min_sectors_to_check_bkops_status =
+ card->bkops_info.min_sectors_to_queue_delayed_work;
+
+ ret = snprintf(buf, PAGE_SIZE, "%d\n",
+ min_sectors_to_check_bkops_status);
+
+ mmc_blk_put(md);
+ return ret;
+}
+
+static ssize_t
+min_sectors_to_check_bkops_status_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int value;
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+ struct mmc_card *card = md->queue.card;
+
+ if (!card)
+ return -EINVAL;
+
+ sscanf(buf, "%d", &value);
+ if (value >= 0)
+ card->bkops_info.min_sectors_to_queue_delayed_work = value;
+
+ mmc_blk_put(md);
+ return count;
+}
+
static int mmc_blk_open(struct block_device *bdev, fmode_t mode)
{
struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk);
@@ -846,6 +916,9 @@
from = blk_rq_pos(req);
nr = blk_rq_sectors(req);
+ if (card->ext_csd.bkops_en)
+ card->bkops_info.sectors_changed += blk_rq_sectors(req);
+
if (mmc_can_discard(card))
arg = MMC_DISCARD_ARG;
else if (mmc_can_trim(card))
@@ -1324,6 +1397,136 @@
mmc_queue_bounce_pre(mqrq);
}
+static void mmc_blk_write_packing_control(struct mmc_queue *mq,
+ struct request *req)
+{
+ struct mmc_host *host = mq->card->host;
+ int data_dir;
+
+ if (!(host->caps2 & MMC_CAP2_PACKED_WR))
+ return;
+
+ /*
+ * In case the packing control is not supported by the host, it should
+ * not have an effect on the write packing. Therefore we have to enable
+ * the write packing
+ */
+ if (!(host->caps2 & MMC_CAP2_PACKED_WR_CONTROL)) {
+ mq->wr_packing_enabled = true;
+ return;
+ }
+
+ if (!req || (req && (req->cmd_flags & REQ_FLUSH))) {
+ if (mq->num_of_potential_packed_wr_reqs >
+ mq->num_wr_reqs_to_start_packing)
+ mq->wr_packing_enabled = true;
+ mq->num_of_potential_packed_wr_reqs = 0;
+ return;
+ }
+
+ data_dir = rq_data_dir(req);
+
+ if (data_dir == READ) {
+ mq->num_of_potential_packed_wr_reqs = 0;
+ mq->wr_packing_enabled = false;
+ return;
+ } else if (data_dir == WRITE) {
+ mq->num_of_potential_packed_wr_reqs++;
+ }
+
+ if (mq->num_of_potential_packed_wr_reqs >
+ mq->num_wr_reqs_to_start_packing)
+ mq->wr_packing_enabled = true;
+
+}
+
+struct mmc_wr_pack_stats *mmc_blk_get_packed_statistics(struct mmc_card *card)
+{
+ if (!card)
+ return NULL;
+
+ return &card->wr_pack_stats;
+}
+EXPORT_SYMBOL(mmc_blk_get_packed_statistics);
+
+void mmc_blk_init_packed_statistics(struct mmc_card *card)
+{
+ int max_num_of_packed_reqs = 0;
+
+ if (!card || !card->wr_pack_stats.packing_events)
+ return;
+
+ max_num_of_packed_reqs = card->ext_csd.max_packed_writes;
+
+ spin_lock(&card->wr_pack_stats.lock);
+ memset(card->wr_pack_stats.packing_events, 0,
+ (max_num_of_packed_reqs + 1) *
+ sizeof(*card->wr_pack_stats.packing_events));
+ memset(&card->wr_pack_stats.pack_stop_reason, 0,
+ sizeof(card->wr_pack_stats.pack_stop_reason));
+ card->wr_pack_stats.enabled = true;
+ spin_unlock(&card->wr_pack_stats.lock);
+}
+EXPORT_SYMBOL(mmc_blk_init_packed_statistics);
+
+void print_mmc_packing_stats(struct mmc_card *card)
+{
+ int i;
+ int max_num_of_packed_reqs = 0;
+
+ if ((!card) || (!card->wr_pack_stats.packing_events))
+ return;
+
+ max_num_of_packed_reqs = card->ext_csd.max_packed_writes;
+
+ spin_lock(&card->wr_pack_stats.lock);
+
+ pr_info("%s: write packing statistics:\n",
+ mmc_hostname(card->host));
+
+ for (i = 1 ; i <= max_num_of_packed_reqs ; ++i) {
+ if (card->wr_pack_stats.packing_events[i] != 0)
+ pr_info("%s: Packed %d reqs - %d times\n",
+ mmc_hostname(card->host), i,
+ card->wr_pack_stats.packing_events[i]);
+ }
+
+ pr_info("%s: stopped packing due to the following reasons:\n",
+ mmc_hostname(card->host));
+
+ if (card->wr_pack_stats.pack_stop_reason[EXCEEDS_SEGMENTS])
+ pr_info("%s: %d times: exceedmax num of segments\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[EXCEEDS_SEGMENTS]);
+ if (card->wr_pack_stats.pack_stop_reason[EXCEEDS_SECTORS])
+ pr_info("%s: %d times: exceeding the max num of sectors\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[EXCEEDS_SECTORS]);
+ if (card->wr_pack_stats.pack_stop_reason[WRONG_DATA_DIR])
+ pr_info("%s: %d times: wrong data direction\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[WRONG_DATA_DIR]);
+ if (card->wr_pack_stats.pack_stop_reason[FLUSH_OR_DISCARD])
+ pr_info("%s: %d times: flush or discard\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[FLUSH_OR_DISCARD]);
+ if (card->wr_pack_stats.pack_stop_reason[EMPTY_QUEUE])
+ pr_info("%s: %d times: empty queue\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[EMPTY_QUEUE]);
+ if (card->wr_pack_stats.pack_stop_reason[REL_WRITE])
+ pr_info("%s: %d times: rel write\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[REL_WRITE]);
+ if (card->wr_pack_stats.pack_stop_reason[THRESHOLD])
+ pr_info("%s: %d times: Threshold\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[THRESHOLD]);
+
+ spin_unlock(&card->wr_pack_stats.lock);
+}
+EXPORT_SYMBOL(print_mmc_packing_stats);
+
static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
{
struct request_queue *q = mq->queue;
@@ -1336,6 +1539,7 @@
u8 put_back = 0;
u8 max_packed_rw = 0;
u8 reqs = 0;
+ struct mmc_wr_pack_stats *stats = &card->wr_pack_stats;
mmc_blk_clear_packed(mq->mqrq_cur);
@@ -1343,6 +1547,9 @@
!card->ext_csd.packed_event_en)
goto no_packed;
+ if (!mq->wr_packing_enabled)
+ goto no_packed;
+
if ((rq_data_dir(cur) == WRITE) &&
(card->host->caps2 & MMC_CAP2_PACKED_WR))
max_packed_rw = card->ext_csd.max_packed_writes;
@@ -1373,26 +1580,33 @@
phys_segments++;
}
+ spin_lock(&stats->lock);
+
while (reqs < max_packed_rw - 1) {
spin_lock_irq(q->queue_lock);
next = blk_fetch_request(q);
spin_unlock_irq(q->queue_lock);
- if (!next)
+ if (!next) {
+ MMC_BLK_UPDATE_STOP_REASON(stats, EMPTY_QUEUE);
break;
+ }
if (mmc_large_sec(card) &&
!IS_ALIGNED(blk_rq_sectors(next), 8)) {
+ MMC_BLK_UPDATE_STOP_REASON(stats, LARGE_SEC_ALIGN);
put_back = 1;
break;
}
if (next->cmd_flags & REQ_DISCARD ||
next->cmd_flags & REQ_FLUSH) {
+ MMC_BLK_UPDATE_STOP_REASON(stats, FLUSH_OR_DISCARD);
put_back = 1;
break;
}
if (rq_data_dir(cur) != rq_data_dir(next)) {
+ MMC_BLK_UPDATE_STOP_REASON(stats, WRONG_DATA_DIR);
put_back = 1;
break;
}
@@ -1400,22 +1614,32 @@
if (mmc_req_rel_wr(next) &&
(md->flags & MMC_BLK_REL_WR) &&
!en_rel_wr) {
+ MMC_BLK_UPDATE_STOP_REASON(stats, REL_WRITE);
put_back = 1;
break;
}
req_sectors += blk_rq_sectors(next);
if (req_sectors > max_blk_count) {
+ if (stats->enabled)
+ stats->pack_stop_reason[EXCEEDS_SECTORS]++;
put_back = 1;
break;
}
phys_segments += next->nr_phys_segments;
if (phys_segments > max_phys_segs) {
+ MMC_BLK_UPDATE_STOP_REASON(stats, EXCEEDS_SEGMENTS);
put_back = 1;
break;
}
+ if (rq_data_dir(next) == WRITE) {
+ mq->num_of_potential_packed_wr_reqs++;
+ if (card->ext_csd.bkops_en)
+ card->bkops_info.sectors_changed +=
+ blk_rq_sectors(next);
+ }
list_add_tail(&next->queuelist, &mq->mqrq_cur->packed_list);
cur = next;
reqs++;
@@ -1427,6 +1651,15 @@
spin_unlock_irq(q->queue_lock);
}
+ if (stats->enabled) {
+ if (reqs + 1 <= card->ext_csd.max_packed_writes)
+ stats->packing_events[reqs + 1]++;
+ if (reqs + 1 == max_packed_rw)
+ MMC_BLK_UPDATE_STOP_REASON(stats, THRESHOLD);
+ }
+
+ spin_unlock(&stats->lock);
+
if (reqs > 0) {
list_add(&req->queuelist, &mq->mqrq_cur->packed_list);
mq->mqrq_cur->packed_num = ++reqs;
@@ -1512,7 +1745,18 @@
brq->data.sg_len = mmc_queue_map_sg(mq, mqrq);
mqrq->mmc_active.mrq = &brq->mrq;
- mqrq->mmc_active.err_check = mmc_blk_packed_err_check;
+
+ /*
+ * This is intended for packed commands tests usage - in case these
+ * functions are not in use the respective pointers are NULL
+ */
+ if (mq->err_check_fn)
+ mqrq->mmc_active.err_check = mq->err_check_fn;
+ else
+ mqrq->mmc_active.err_check = mmc_blk_packed_err_check;
+
+ if (mq->packed_test_fn)
+ mq->packed_test_fn(mq->queue, mqrq);
mmc_queue_bounce_pre(mqrq);
}
@@ -1625,8 +1869,11 @@
if (!rqc && !mq->mqrq_prev->req)
return 0;
- if (rqc)
+ if (rqc) {
+ if ((card->ext_csd.bkops_en) && (rq_data_dir(rqc) == WRITE))
+ card->bkops_info.sectors_changed += blk_rq_sectors(rqc);
reqs = mmc_blk_prep_packed_list(mq, rqc);
+ }
do {
if (rqc) {
@@ -1797,6 +2044,8 @@
goto out;
}
+ mmc_blk_write_packing_control(mq, req);
+
if (req && req->cmd_flags & REQ_SANITIZE) {
/* complete ongoing async transfer before issuing sanitize */
if (card->host && card->host->areq)
@@ -2028,6 +2277,8 @@
if (md) {
card = md->queue.card;
+ device_remove_file(disk_to_dev(md->disk),
+ &md->num_wr_reqs_to_start_packing);
if (md->disk->flags & GENHD_FL_UP) {
device_remove_file(disk_to_dev(md->disk), &md->force_ro);
if ((md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
@@ -2095,6 +2346,29 @@
goto power_ro_lock_fail;
}
+ md->num_wr_reqs_to_start_packing.show =
+ num_wr_reqs_to_start_packing_show;
+ md->num_wr_reqs_to_start_packing.store =
+ num_wr_reqs_to_start_packing_store;
+ sysfs_attr_init(&md->num_wr_reqs_to_start_packing.attr);
+ md->num_wr_reqs_to_start_packing.attr.name =
+ "num_wr_reqs_to_start_packing";
+ md->num_wr_reqs_to_start_packing.attr.mode = S_IRUGO | S_IWUSR;
+ ret = device_create_file(disk_to_dev(md->disk),
+ &md->num_wr_reqs_to_start_packing);
+ if (ret)
+ goto power_ro_lock_fail;
+
+ md->min_sectors_to_check_bkops_status.show =
+ min_sectors_to_check_bkops_status_show;
+ md->min_sectors_to_check_bkops_status.store =
+ min_sectors_to_check_bkops_status_store;
+ sysfs_attr_init(&md->min_sectors_to_check_bkops_status.attr);
+ md->min_sectors_to_check_bkops_status.attr.name =
+ "min_sectors_to_check_bkops_status";
+ md->min_sectors_to_check_bkops_status.attr.mode = S_IRUGO | S_IWUSR;
+ ret = device_create_file(disk_to_dev(md->disk),
+ &md->min_sectors_to_check_bkops_status);
if (ret)
goto power_ro_lock_fail;
diff --git a/drivers/mmc/card/mmc_block_test.c b/drivers/mmc/card/mmc_block_test.c
new file mode 100644
index 0000000..bdda8d5
--- /dev/null
+++ b/drivers/mmc/card/mmc_block_test.c
@@ -0,0 +1,1945 @@
+/* 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.
+ *
+ */
+
+/* MMC block test */
+
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/debugfs.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/delay.h>
+#include <linux/test-iosched.h>
+#include "queue.h"
+
+#define MODULE_NAME "mmc_block_test"
+#define TEST_MAX_SECTOR_RANGE (600*1024*1024) /* 600 MB */
+#define TEST_MAX_BIOS_PER_REQ 120
+#define CMD23_PACKED_BIT (1 << 30)
+#define LARGE_PRIME_1 1103515367
+#define LARGE_PRIME_2 35757
+#define PACKED_HDR_VER_MASK 0x000000FF
+#define PACKED_HDR_RW_MASK 0x0000FF00
+#define PACKED_HDR_NUM_REQS_MASK 0x00FF0000
+#define PACKED_HDR_BITS_16_TO_29_SET 0x3FFF0000
+
+#define test_pr_debug(fmt, args...) pr_debug("%s: "fmt"\n", MODULE_NAME, args)
+#define test_pr_info(fmt, args...) pr_info("%s: "fmt"\n", MODULE_NAME, args)
+#define test_pr_err(fmt, args...) pr_err("%s: "fmt"\n", MODULE_NAME, args)
+
+enum is_random {
+ NON_RANDOM_TEST,
+ RANDOM_TEST,
+};
+
+enum mmc_block_test_testcases {
+ /* Start of send write packing test group */
+ SEND_WRITE_PACKING_MIN_TESTCASE,
+ TEST_STOP_DUE_TO_READ = SEND_WRITE_PACKING_MIN_TESTCASE,
+ TEST_STOP_DUE_TO_READ_AFTER_MAX_REQS,
+ TEST_STOP_DUE_TO_FLUSH,
+ TEST_STOP_DUE_TO_FLUSH_AFTER_MAX_REQS,
+ TEST_STOP_DUE_TO_EMPTY_QUEUE,
+ TEST_STOP_DUE_TO_MAX_REQ_NUM,
+ TEST_STOP_DUE_TO_THRESHOLD,
+ SEND_WRITE_PACKING_MAX_TESTCASE = TEST_STOP_DUE_TO_THRESHOLD,
+
+ /* Start of err check test group */
+ ERR_CHECK_MIN_TESTCASE,
+ TEST_RET_ABORT = ERR_CHECK_MIN_TESTCASE,
+ TEST_RET_PARTIAL_FOLLOWED_BY_SUCCESS,
+ TEST_RET_PARTIAL_FOLLOWED_BY_ABORT,
+ TEST_RET_PARTIAL_MULTIPLE_UNTIL_SUCCESS,
+ TEST_RET_PARTIAL_MAX_FAIL_IDX,
+ TEST_RET_RETRY,
+ TEST_RET_CMD_ERR,
+ TEST_RET_DATA_ERR,
+ ERR_CHECK_MAX_TESTCASE = TEST_RET_DATA_ERR,
+
+ /* Start of send invalid test group */
+ INVALID_CMD_MIN_TESTCASE,
+ TEST_HDR_INVALID_VERSION = INVALID_CMD_MIN_TESTCASE,
+ TEST_HDR_WRONG_WRITE_CODE,
+ TEST_HDR_INVALID_RW_CODE,
+ TEST_HDR_DIFFERENT_ADDRESSES,
+ TEST_HDR_REQ_NUM_SMALLER_THAN_ACTUAL,
+ TEST_HDR_REQ_NUM_LARGER_THAN_ACTUAL,
+ TEST_HDR_CMD23_PACKED_BIT_SET,
+ TEST_CMD23_MAX_PACKED_WRITES,
+ TEST_CMD23_ZERO_PACKED_WRITES,
+ TEST_CMD23_PACKED_BIT_UNSET,
+ TEST_CMD23_REL_WR_BIT_SET,
+ TEST_CMD23_BITS_16TO29_SET,
+ TEST_CMD23_HDR_BLK_NOT_IN_COUNT,
+ INVALID_CMD_MAX_TESTCASE = TEST_CMD23_HDR_BLK_NOT_IN_COUNT,
+
+ /*
+ * Start of packing control test group.
+ * in these next testcases the abbreviation FB = followed by
+ */
+ PACKING_CONTROL_MIN_TESTCASE,
+ TEST_PACKING_EXP_ONE_OVER_TRIGGER_FB_READ =
+ PACKING_CONTROL_MIN_TESTCASE,
+ TEST_PACKING_EXP_N_OVER_TRIGGER,
+ TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ,
+ TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N,
+ TEST_PACKING_EXP_THRESHOLD_OVER_TRIGGER,
+ TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS,
+ TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS,
+ TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER,
+ TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER,
+ TEST_PACK_MIX_PACKED_NO_PACKED_PACKED,
+ TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED,
+ PACKING_CONTROL_MAX_TESTCASE = TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED,
+};
+
+enum mmc_block_test_group {
+ TEST_NO_GROUP,
+ TEST_GENERAL_GROUP,
+ TEST_SEND_WRITE_PACKING_GROUP,
+ TEST_ERR_CHECK_GROUP,
+ TEST_SEND_INVALID_GROUP,
+ TEST_PACKING_CONTROL_GROUP,
+};
+
+struct mmc_block_test_debug {
+ struct dentry *send_write_packing_test;
+ struct dentry *err_check_test;
+ struct dentry *send_invalid_packed_test;
+ struct dentry *random_test_seed;
+ struct dentry *packing_control_test;
+};
+
+struct mmc_block_test_data {
+ /* The number of write requests that the test will issue */
+ int num_requests;
+ /* The expected write packing statistics for the current test */
+ struct mmc_wr_pack_stats exp_packed_stats;
+ /*
+ * A user-defined seed for random choices of number of bios written in
+ * a request, and of number of requests issued in a test
+ * This field is randomly updated after each use
+ */
+ unsigned int random_test_seed;
+ /* A retry counter used in err_check tests */
+ int err_check_counter;
+ /* Can be one of the values of enum test_group */
+ enum mmc_block_test_group test_group;
+ /*
+ * Indicates if the current testcase is running with random values of
+ * num_requests and num_bios (in each request)
+ */
+ int is_random;
+ /* Data structure for debugfs dentrys */
+ struct mmc_block_test_debug debug;
+ /*
+ * Data structure containing individual test information, including
+ * self-defined specific data
+ */
+ struct test_info test_info;
+ /* mmc block device test */
+ struct blk_dev_test_type bdt;
+};
+
+static struct mmc_block_test_data *mbtd;
+
+/*
+ * A callback assigned to the packed_test_fn field.
+ * Called from block layer in mmc_blk_packed_hdr_wrq_prep.
+ * Here we alter the packed header or CMD23 in order to send an invalid
+ * packed command to the card.
+ */
+static void test_invalid_packed_cmd(struct request_queue *q,
+ struct mmc_queue_req *mqrq)
+{
+ struct mmc_queue *mq = q->queuedata;
+ u32 *packed_cmd_hdr = mqrq->packed_cmd_hdr;
+ struct request *req = mqrq->req;
+ struct request *second_rq;
+ struct test_request *test_rq;
+ struct mmc_blk_request *brq = &mqrq->brq;
+ int num_requests;
+ int max_packed_reqs;
+
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return;
+ }
+
+ test_rq = (struct test_request *)req->elv.priv[0];
+ if (!test_rq) {
+ test_pr_err("%s: NULL test_rq", __func__);
+ return;
+ }
+ max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+
+ switch (mbtd->test_info.testcase) {
+ case TEST_HDR_INVALID_VERSION:
+ test_pr_info("%s: set invalid header version", __func__);
+ /* Put 0 in header version field (1 byte, offset 0 in header) */
+ packed_cmd_hdr[0] = packed_cmd_hdr[0] & ~PACKED_HDR_VER_MASK;
+ break;
+ case TEST_HDR_WRONG_WRITE_CODE:
+ test_pr_info("%s: wrong write code", __func__);
+ /* Set R/W field with R value (1 byte, offset 1 in header) */
+ packed_cmd_hdr[0] = packed_cmd_hdr[0] & ~PACKED_HDR_RW_MASK;
+ packed_cmd_hdr[0] = packed_cmd_hdr[0] | 0x00000100;
+ break;
+ case TEST_HDR_INVALID_RW_CODE:
+ test_pr_info("%s: invalid r/w code", __func__);
+ /* Set R/W field with invalid value */
+ packed_cmd_hdr[0] = packed_cmd_hdr[0] & ~PACKED_HDR_RW_MASK;
+ packed_cmd_hdr[0] = packed_cmd_hdr[0] | 0x00000400;
+ break;
+ case TEST_HDR_DIFFERENT_ADDRESSES:
+ test_pr_info("%s: different addresses", __func__);
+ second_rq = list_entry(req->queuelist.next, struct request,
+ queuelist);
+ test_pr_info("%s: test_rq->sector=%ld, second_rq->sector=%ld",
+ __func__, (long)req->__sector,
+ (long)second_rq->__sector);
+ /*
+ * Put start sector of second write request in the first write
+ * request's cmd25 argument in the packed header
+ */
+ packed_cmd_hdr[3] = second_rq->__sector;
+ break;
+ case TEST_HDR_REQ_NUM_SMALLER_THAN_ACTUAL:
+ test_pr_info("%s: request num smaller than actual" , __func__);
+ num_requests = (packed_cmd_hdr[0] & PACKED_HDR_NUM_REQS_MASK)
+ >> 16;
+ /* num of entries is decremented by 1 */
+ num_requests = (num_requests - 1) << 16;
+ /*
+ * Set number of requests field in packed write header to be
+ * smaller than the actual number (1 byte, offset 2 in header)
+ */
+ packed_cmd_hdr[0] = (packed_cmd_hdr[0] &
+ ~PACKED_HDR_NUM_REQS_MASK) + num_requests;
+ break;
+ case TEST_HDR_REQ_NUM_LARGER_THAN_ACTUAL:
+ test_pr_info("%s: request num larger than actual" , __func__);
+ num_requests = (packed_cmd_hdr[0] & PACKED_HDR_NUM_REQS_MASK)
+ >> 16;
+ /* num of entries is incremented by 1 */
+ num_requests = (num_requests + 1) << 16;
+ /*
+ * Set number of requests field in packed write header to be
+ * larger than the actual number (1 byte, offset 2 in header).
+ */
+ packed_cmd_hdr[0] = (packed_cmd_hdr[0] &
+ ~PACKED_HDR_NUM_REQS_MASK) + num_requests;
+ break;
+ case TEST_HDR_CMD23_PACKED_BIT_SET:
+ test_pr_info("%s: header CMD23 packed bit set" , __func__);
+ /*
+ * Set packed bit (bit 30) in cmd23 argument of first and second
+ * write requests in packed write header.
+ * These are located at bytes 2 and 4 in packed write header
+ */
+ packed_cmd_hdr[2] = packed_cmd_hdr[2] | CMD23_PACKED_BIT;
+ packed_cmd_hdr[4] = packed_cmd_hdr[4] | CMD23_PACKED_BIT;
+ break;
+ case TEST_CMD23_MAX_PACKED_WRITES:
+ test_pr_info("%s: CMD23 request num > max_packed_reqs",
+ __func__);
+ /*
+ * Set the individual packed cmd23 request num to
+ * max_packed_reqs + 1
+ */
+ brq->sbc.arg = MMC_CMD23_ARG_PACKED | (max_packed_reqs + 1);
+ break;
+ case TEST_CMD23_ZERO_PACKED_WRITES:
+ test_pr_info("%s: CMD23 request num = 0", __func__);
+ /* Set the individual packed cmd23 request num to zero */
+ brq->sbc.arg = MMC_CMD23_ARG_PACKED;
+ break;
+ case TEST_CMD23_PACKED_BIT_UNSET:
+ test_pr_info("%s: CMD23 packed bit unset", __func__);
+ /*
+ * Set the individual packed cmd23 packed bit to 0,
+ * although there is a packed write request
+ */
+ brq->sbc.arg &= ~CMD23_PACKED_BIT;
+ break;
+ case TEST_CMD23_REL_WR_BIT_SET:
+ test_pr_info("%s: CMD23 REL WR bit set", __func__);
+ /* Set the individual packed cmd23 reliable write bit */
+ brq->sbc.arg = MMC_CMD23_ARG_PACKED | MMC_CMD23_ARG_REL_WR;
+ break;
+ case TEST_CMD23_BITS_16TO29_SET:
+ test_pr_info("%s: CMD23 bits [16-29] set", __func__);
+ brq->sbc.arg = MMC_CMD23_ARG_PACKED |
+ PACKED_HDR_BITS_16_TO_29_SET;
+ break;
+ case TEST_CMD23_HDR_BLK_NOT_IN_COUNT:
+ test_pr_info("%s: CMD23 hdr not in block count", __func__);
+ brq->sbc.arg = MMC_CMD23_ARG_PACKED |
+ ((rq_data_dir(req) == READ) ? 0 : mqrq->packed_blocks);
+ break;
+ default:
+ test_pr_err("%s: unexpected testcase %d",
+ __func__, mbtd->test_info.testcase);
+ break;
+ }
+}
+
+/*
+ * A callback assigned to the err_check_fn field of the mmc_request by the
+ * MMC/card/block layer.
+ * Called upon request completion by the MMC/core layer.
+ * Here we emulate an error return value from the card.
+ */
+static int test_err_check(struct mmc_card *card, struct mmc_async_req *areq)
+{
+ struct mmc_queue_req *mq_rq = container_of(areq, struct mmc_queue_req,
+ mmc_active);
+ struct request_queue *req_q = test_iosched_get_req_queue();
+ struct mmc_queue *mq;
+ int max_packed_reqs;
+ int ret = 0;
+
+ if (req_q)
+ mq = req_q->queuedata;
+ else {
+ test_pr_err("%s: NULL request_queue", __func__);
+ return 0;
+ }
+
+ if (!mq) {
+ test_pr_err("%s: %s: NULL mq", __func__,
+ mmc_hostname(card->host));
+ return 0;
+ }
+
+ max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+
+ if (!mq_rq) {
+ test_pr_err("%s: %s: NULL mq_rq", __func__,
+ mmc_hostname(card->host));
+ return 0;
+ }
+
+ switch (mbtd->test_info.testcase) {
+ case TEST_RET_ABORT:
+ test_pr_info("%s: return abort", __func__);
+ ret = MMC_BLK_ABORT;
+ break;
+ case TEST_RET_PARTIAL_FOLLOWED_BY_SUCCESS:
+ test_pr_info("%s: return partial followed by success",
+ __func__);
+ /*
+ * Since in this testcase num_requests is always >= 2,
+ * we can be sure that packed_fail_idx is always >= 1
+ */
+ mq_rq->packed_fail_idx = (mbtd->num_requests / 2);
+ test_pr_info("%s: packed_fail_idx = %d"
+ , __func__, mq_rq->packed_fail_idx);
+ mq->err_check_fn = NULL;
+ ret = MMC_BLK_PARTIAL;
+ break;
+ case TEST_RET_PARTIAL_FOLLOWED_BY_ABORT:
+ if (!mbtd->err_check_counter) {
+ test_pr_info("%s: return partial followed by abort",
+ __func__);
+ mbtd->err_check_counter++;
+ /*
+ * Since in this testcase num_requests is always >= 3,
+ * we have that packed_fail_idx is always >= 1
+ */
+ mq_rq->packed_fail_idx = (mbtd->num_requests / 2);
+ test_pr_info("%s: packed_fail_idx = %d"
+ , __func__, mq_rq->packed_fail_idx);
+ ret = MMC_BLK_PARTIAL;
+ break;
+ }
+ mbtd->err_check_counter = 0;
+ mq->err_check_fn = NULL;
+ ret = MMC_BLK_ABORT;
+ break;
+ case TEST_RET_PARTIAL_MULTIPLE_UNTIL_SUCCESS:
+ test_pr_info("%s: return partial multiple until success",
+ __func__);
+ if (++mbtd->err_check_counter >= (mbtd->num_requests)) {
+ mq->err_check_fn = NULL;
+ mbtd->err_check_counter = 0;
+ ret = MMC_BLK_PARTIAL;
+ break;
+ }
+ mq_rq->packed_fail_idx = 1;
+ ret = MMC_BLK_PARTIAL;
+ break;
+ case TEST_RET_PARTIAL_MAX_FAIL_IDX:
+ test_pr_info("%s: return partial max fail_idx", __func__);
+ mq_rq->packed_fail_idx = max_packed_reqs - 1;
+ mq->err_check_fn = NULL;
+ ret = MMC_BLK_PARTIAL;
+ break;
+ case TEST_RET_RETRY:
+ test_pr_info("%s: return retry", __func__);
+ ret = MMC_BLK_RETRY;
+ break;
+ case TEST_RET_CMD_ERR:
+ test_pr_info("%s: return cmd err", __func__);
+ ret = MMC_BLK_CMD_ERR;
+ break;
+ case TEST_RET_DATA_ERR:
+ test_pr_info("%s: return data err", __func__);
+ ret = MMC_BLK_DATA_ERR;
+ break;
+ default:
+ test_pr_err("%s: unexpected testcase %d",
+ __func__, mbtd->test_info.testcase);
+ }
+
+ return ret;
+}
+
+/*
+ * This is a specific implementation for the get_test_case_str_fn function
+ * pointer in the test_info data structure. Given a valid test_data instance,
+ * the function returns a string resembling the test name, based on the testcase
+ */
+static char *get_test_case_str(struct test_data *td)
+{
+ if (!td) {
+ test_pr_err("%s: NULL td", __func__);
+ return NULL;
+ }
+
+ switch (td->test_info.testcase) {
+ case TEST_STOP_DUE_TO_FLUSH:
+ return "Test stop due to flush";
+ case TEST_STOP_DUE_TO_FLUSH_AFTER_MAX_REQS:
+ return "Test stop due to flush after max-1 reqs";
+ case TEST_STOP_DUE_TO_READ:
+ return "Test stop due to read";
+ case TEST_STOP_DUE_TO_READ_AFTER_MAX_REQS:
+ return "Test stop due to read after max-1 reqs";
+ case TEST_STOP_DUE_TO_EMPTY_QUEUE:
+ return "Test stop due to empty queue";
+ case TEST_STOP_DUE_TO_MAX_REQ_NUM:
+ return "Test stop due to max req num";
+ case TEST_STOP_DUE_TO_THRESHOLD:
+ return "Test stop due to exceeding threshold";
+ case TEST_RET_ABORT:
+ return "Test err_check return abort";
+ case TEST_RET_PARTIAL_FOLLOWED_BY_SUCCESS:
+ return "Test err_check return partial followed by success";
+ case TEST_RET_PARTIAL_FOLLOWED_BY_ABORT:
+ return "Test err_check return partial followed by abort";
+ case TEST_RET_PARTIAL_MULTIPLE_UNTIL_SUCCESS:
+ return "Test err_check return partial multiple until success";
+ case TEST_RET_PARTIAL_MAX_FAIL_IDX:
+ return "Test err_check return partial max fail index";
+ case TEST_RET_RETRY:
+ return "Test err_check return retry";
+ case TEST_RET_CMD_ERR:
+ return "Test err_check return cmd error";
+ case TEST_RET_DATA_ERR:
+ return "Test err_check return data error";
+ case TEST_HDR_INVALID_VERSION:
+ return "Test invalid - wrong header version";
+ case TEST_HDR_WRONG_WRITE_CODE:
+ return "Test invalid - wrong write code";
+ case TEST_HDR_INVALID_RW_CODE:
+ return "Test invalid - wrong R/W code";
+ case TEST_HDR_DIFFERENT_ADDRESSES:
+ return "Test invalid - header different addresses";
+ case TEST_HDR_REQ_NUM_SMALLER_THAN_ACTUAL:
+ return "Test invalid - header req num smaller than actual";
+ case TEST_HDR_REQ_NUM_LARGER_THAN_ACTUAL:
+ return "Test invalid - header req num larger than actual";
+ case TEST_HDR_CMD23_PACKED_BIT_SET:
+ return "Test invalid - header cmd23 packed bit set";
+ case TEST_CMD23_MAX_PACKED_WRITES:
+ return "Test invalid - cmd23 max packed writes";
+ case TEST_CMD23_ZERO_PACKED_WRITES:
+ return "Test invalid - cmd23 zero packed writes";
+ case TEST_CMD23_PACKED_BIT_UNSET:
+ return "Test invalid - cmd23 packed bit unset";
+ case TEST_CMD23_REL_WR_BIT_SET:
+ return "Test invalid - cmd23 rel wr bit set";
+ case TEST_CMD23_BITS_16TO29_SET:
+ return "Test invalid - cmd23 bits [16-29] set";
+ case TEST_CMD23_HDR_BLK_NOT_IN_COUNT:
+ return "Test invalid - cmd23 header block not in count";
+ case TEST_PACKING_EXP_N_OVER_TRIGGER:
+ return "\nTest packing control - pack n";
+ case TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ:
+ return "\nTest packing control - pack n followed by read";
+ case TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N:
+ return "\nTest packing control - pack n followed by flush";
+ case TEST_PACKING_EXP_ONE_OVER_TRIGGER_FB_READ:
+ return "\nTest packing control - pack one followed by read";
+ case TEST_PACKING_EXP_THRESHOLD_OVER_TRIGGER:
+ return "\nTest packing control - pack threshold";
+ case TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS:
+ return "\nTest packing control - no packing";
+ case TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS:
+ return "\nTest packing control - no packing, trigger requests";
+ case TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER:
+ return "\nTest packing control - no pack, trigger-read-trigger";
+ case TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER:
+ return "\nTest packing control- no pack, trigger-flush-trigger";
+ case TEST_PACK_MIX_PACKED_NO_PACKED_PACKED:
+ return "\nTest packing control - mix: pack -> no pack -> pack";
+ case TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED:
+ return "\nTest packing control - mix: no pack->pack->no pack";
+ default:
+ return "Unknown testcase";
+ }
+
+ return NULL;
+}
+
+/*
+ * Compare individual testcase's statistics to the expected statistics:
+ * Compare stop reason and number of packing events
+ */
+static int check_wr_packing_statistics(struct test_data *td)
+{
+ struct mmc_wr_pack_stats *mmc_packed_stats;
+ struct mmc_queue *mq = td->req_q->queuedata;
+ int max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+ int i;
+ struct mmc_card *card = mq->card;
+ struct mmc_wr_pack_stats expected_stats;
+ int *stop_reason;
+ int ret = 0;
+
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ expected_stats = mbtd->exp_packed_stats;
+
+ mmc_packed_stats = mmc_blk_get_packed_statistics(card);
+ if (!mmc_packed_stats) {
+ test_pr_err("%s: NULL mmc_packed_stats", __func__);
+ return -EINVAL;
+ }
+
+ if (!mmc_packed_stats->packing_events) {
+ test_pr_err("%s: NULL packing_events", __func__);
+ return -EINVAL;
+ }
+
+ spin_lock(&mmc_packed_stats->lock);
+
+ if (!mmc_packed_stats->enabled) {
+ test_pr_err("%s write packing statistics are not enabled",
+ __func__);
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+ stop_reason = mmc_packed_stats->pack_stop_reason;
+
+ for (i = 1; i <= max_packed_reqs; ++i) {
+ if (mmc_packed_stats->packing_events[i] !=
+ expected_stats.packing_events[i]) {
+ test_pr_err(
+ "%s: Wrong pack stats in index %d, got %d, expected %d",
+ __func__, i, mmc_packed_stats->packing_events[i],
+ expected_stats.packing_events[i]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+ }
+
+ if (mmc_packed_stats->pack_stop_reason[EXCEEDS_SEGMENTS] !=
+ expected_stats.pack_stop_reason[EXCEEDS_SEGMENTS]) {
+ test_pr_err(
+ "%s: Wrong pack stop reason EXCEEDS_SEGMENTS %d, expected %d",
+ __func__, stop_reason[EXCEEDS_SEGMENTS],
+ expected_stats.pack_stop_reason[EXCEEDS_SEGMENTS]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+ if (mmc_packed_stats->pack_stop_reason[EXCEEDS_SECTORS] !=
+ expected_stats.pack_stop_reason[EXCEEDS_SECTORS]) {
+ test_pr_err(
+ "%s: Wrong pack stop reason EXCEEDS_SECTORS %d, expected %d",
+ __func__, stop_reason[EXCEEDS_SECTORS],
+ expected_stats.pack_stop_reason[EXCEEDS_SECTORS]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+ if (mmc_packed_stats->pack_stop_reason[WRONG_DATA_DIR] !=
+ expected_stats.pack_stop_reason[WRONG_DATA_DIR]) {
+ test_pr_err(
+ "%s: Wrong pack stop reason WRONG_DATA_DIR %d, expected %d",
+ __func__, stop_reason[WRONG_DATA_DIR],
+ expected_stats.pack_stop_reason[WRONG_DATA_DIR]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+ if (mmc_packed_stats->pack_stop_reason[FLUSH_OR_DISCARD] !=
+ expected_stats.pack_stop_reason[FLUSH_OR_DISCARD]) {
+ test_pr_err(
+ "%s: Wrong pack stop reason FLUSH_OR_DISCARD %d, expected %d",
+ __func__, stop_reason[FLUSH_OR_DISCARD],
+ expected_stats.pack_stop_reason[FLUSH_OR_DISCARD]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+ if (mmc_packed_stats->pack_stop_reason[EMPTY_QUEUE] !=
+ expected_stats.pack_stop_reason[EMPTY_QUEUE]) {
+ test_pr_err(
+ "%s: Wrong pack stop reason EMPTY_QUEUE %d, expected %d",
+ __func__, stop_reason[EMPTY_QUEUE],
+ expected_stats.pack_stop_reason[EMPTY_QUEUE]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+ if (mmc_packed_stats->pack_stop_reason[REL_WRITE] !=
+ expected_stats.pack_stop_reason[REL_WRITE]) {
+ test_pr_err(
+ "%s: Wrong pack stop reason REL_WRITE %d, expected %d",
+ __func__, stop_reason[REL_WRITE],
+ expected_stats.pack_stop_reason[REL_WRITE]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+exit_err:
+ spin_unlock(&mmc_packed_stats->lock);
+ if (ret && mmc_packed_stats->enabled)
+ print_mmc_packing_stats(card);
+ return ret;
+cancel_round:
+ spin_unlock(&mmc_packed_stats->lock);
+ test_iosched_set_ignore_round(true);
+ return 0;
+}
+
+/*
+ * Pseudo-randomly choose a seed based on the last seed, and update it in
+ * seed_number. then return seed_number (mod max_val), or min_val.
+ */
+static unsigned int pseudo_random_seed(unsigned int *seed_number,
+ unsigned int min_val,
+ unsigned int max_val)
+{
+ int ret = 0;
+
+ if (!seed_number)
+ return 0;
+
+ *seed_number = ((unsigned int)(((unsigned long)*seed_number *
+ (unsigned long)LARGE_PRIME_1) + LARGE_PRIME_2));
+ ret = (unsigned int)((*seed_number) % max_val);
+
+ return (ret > min_val ? ret : min_val);
+}
+
+/*
+ * Given a pseudo-random seed, find a pseudo-random num_of_bios.
+ * Make sure that num_of_bios is not larger than TEST_MAX_SECTOR_RANGE
+ */
+static void pseudo_rnd_num_of_bios(unsigned int *num_bios_seed,
+ unsigned int *num_of_bios)
+{
+ do {
+ *num_of_bios = pseudo_random_seed(num_bios_seed, 1,
+ TEST_MAX_BIOS_PER_REQ);
+ if (!(*num_of_bios))
+ *num_of_bios = 1;
+ } while ((*num_of_bios) * BIO_U32_SIZE * 4 > TEST_MAX_SECTOR_RANGE);
+}
+
+/* Add a single read request to the given td's request queue */
+static int prepare_request_add_read(struct test_data *td)
+{
+ int ret;
+ int start_sec;
+
+ if (td)
+ start_sec = td->start_sector;
+ else {
+ test_pr_err("%s: NULL td", __func__);
+ return 0;
+ }
+
+ test_pr_info("%s: Adding a read request, first req_id=%d", __func__,
+ td->wr_rd_next_req_id);
+
+ ret = test_iosched_add_wr_rd_test_req(0, READ, start_sec, 2,
+ TEST_PATTERN_5A, NULL);
+ if (ret) {
+ test_pr_err("%s: failed to add a read request", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Add a single flush request to the given td's request queue */
+static int prepare_request_add_flush(struct test_data *td)
+{
+ int ret;
+
+ if (!td) {
+ test_pr_err("%s: NULL td", __func__);
+ return 0;
+ }
+
+ test_pr_info("%s: Adding a flush request, first req_id=%d", __func__,
+ td->unique_next_req_id);
+ ret = test_iosched_add_unique_test_req(0, REQ_UNIQUE_FLUSH,
+ 0, 0, NULL);
+ if (ret) {
+ test_pr_err("%s: failed to add a flush request", __func__);
+ return ret;
+ }
+
+ return ret;
+}
+
+/*
+ * Add num_requets amount of write requests to the given td's request queue.
+ * If random test mode is chosen we pseudo-randomly choose the number of bios
+ * for each write request, otherwise add between 1 to 5 bio per request.
+ */
+static int prepare_request_add_write_reqs(struct test_data *td,
+ int num_requests, int is_err_expected,
+ int is_random)
+{
+ int i;
+ unsigned int start_sec;
+ int num_bios;
+ int ret = 0;
+ unsigned int *bio_seed = &mbtd->random_test_seed;
+
+ if (td)
+ start_sec = td->start_sector;
+ else {
+ test_pr_err("%s: NULL td", __func__);
+ return ret;
+ }
+
+ test_pr_info("%s: Adding %d write requests, first req_id=%d", __func__,
+ num_requests, td->wr_rd_next_req_id);
+
+ for (i = 1; i <= num_requests; i++) {
+ start_sec = td->start_sector + 4096 * td->num_of_write_bios;
+ if (is_random)
+ pseudo_rnd_num_of_bios(bio_seed, &num_bios);
+ else
+ /*
+ * For the non-random case, give num_bios a value
+ * between 1 and 5, to keep a small number of BIOs
+ */
+ num_bios = (i%5)+1;
+
+ ret = test_iosched_add_wr_rd_test_req(is_err_expected, WRITE,
+ start_sec, num_bios, TEST_PATTERN_5A, NULL);
+
+ if (ret) {
+ test_pr_err("%s: failed to add a write request",
+ __func__);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Prepare the write, read and flush requests for a generic packed commands
+ * testcase
+ */
+static int prepare_packed_requests(struct test_data *td, int is_err_expected,
+ int num_requests, int is_random)
+{
+ int ret = 0;
+ struct mmc_queue *mq;
+ int max_packed_reqs;
+ struct request_queue *req_q;
+
+ if (!td) {
+ pr_err("%s: NULL td", __func__);
+ return -EINVAL;
+ }
+
+ req_q = td->req_q;
+
+ if (!req_q) {
+ pr_err("%s: NULL request queue", __func__);
+ return -EINVAL;
+ }
+
+ mq = req_q->queuedata;
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+
+ if (mbtd->random_test_seed <= 0) {
+ mbtd->random_test_seed =
+ (unsigned int)(get_jiffies_64() & 0xFFFF);
+ test_pr_info("%s: got seed from jiffies %d",
+ __func__, mbtd->random_test_seed);
+ }
+
+ mmc_blk_init_packed_statistics(mq->card);
+
+ ret = prepare_request_add_write_reqs(td, num_requests, is_err_expected,
+ is_random);
+ if (ret)
+ return ret;
+
+ /* Avoid memory corruption in upcoming stats set */
+ if (td->test_info.testcase == TEST_STOP_DUE_TO_THRESHOLD)
+ num_requests--;
+
+ memset((void *)mbtd->exp_packed_stats.pack_stop_reason, 0,
+ sizeof(mbtd->exp_packed_stats.pack_stop_reason));
+ memset(mbtd->exp_packed_stats.packing_events, 0,
+ (max_packed_reqs + 1) * sizeof(u32));
+ if (num_requests <= max_packed_reqs)
+ mbtd->exp_packed_stats.packing_events[num_requests] = 1;
+
+ switch (td->test_info.testcase) {
+ case TEST_STOP_DUE_TO_FLUSH:
+ case TEST_STOP_DUE_TO_FLUSH_AFTER_MAX_REQS:
+ ret = prepare_request_add_flush(td);
+ if (ret)
+ return ret;
+
+ mbtd->exp_packed_stats.pack_stop_reason[FLUSH_OR_DISCARD] = 1;
+ break;
+ case TEST_STOP_DUE_TO_READ:
+ case TEST_STOP_DUE_TO_READ_AFTER_MAX_REQS:
+ ret = prepare_request_add_read(td);
+ if (ret)
+ return ret;
+
+ mbtd->exp_packed_stats.pack_stop_reason[WRONG_DATA_DIR] = 1;
+ break;
+ case TEST_STOP_DUE_TO_THRESHOLD:
+ mbtd->exp_packed_stats.packing_events[num_requests] = 1;
+ mbtd->exp_packed_stats.packing_events[1] = 1;
+ mbtd->exp_packed_stats.pack_stop_reason[THRESHOLD] = 1;
+ mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+ break;
+ case TEST_STOP_DUE_TO_MAX_REQ_NUM:
+ case TEST_RET_PARTIAL_MAX_FAIL_IDX:
+ mbtd->exp_packed_stats.pack_stop_reason[THRESHOLD] = 1;
+ break;
+ default:
+ mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+ }
+ mbtd->num_requests = num_requests;
+
+ return 0;
+}
+
+/*
+ * Prepare the write, read and flush requests for the packing control
+ * testcases
+ */
+static int prepare_packed_control_tests_requests(struct test_data *td,
+ int is_err_expected, int num_requests, int is_random)
+{
+ int ret = 0;
+ struct mmc_queue *mq;
+ int max_packed_reqs;
+ int temp_num_req = num_requests;
+ struct request_queue *req_q;
+ int test_packed_trigger;
+ int num_packed_reqs;
+
+ if (!td) {
+ test_pr_err("%s: NULL td\n", __func__);
+ return -EINVAL;
+ }
+
+ req_q = td->req_q;
+
+ if (!req_q) {
+ test_pr_err("%s: NULL request queue\n", __func__);
+ return -EINVAL;
+ }
+
+ mq = req_q->queuedata;
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+ test_packed_trigger = mq->num_wr_reqs_to_start_packing;
+ num_packed_reqs = num_requests - test_packed_trigger;
+
+ if (mbtd->random_test_seed == 0) {
+ mbtd->random_test_seed =
+ (unsigned int)(get_jiffies_64() & 0xFFFF);
+ test_pr_info("%s: got seed from jiffies %d",
+ __func__, mbtd->random_test_seed);
+ }
+
+ mmc_blk_init_packed_statistics(mq->card);
+
+ if (td->test_info.testcase ==
+ TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED) {
+ temp_num_req = num_requests;
+ num_requests = test_packed_trigger - 1;
+ }
+
+ /* Verify that the packing is disabled before starting the test */
+ mq->wr_packing_enabled = false;
+ mq->num_of_potential_packed_wr_reqs = 0;
+
+ if (td->test_info.testcase == TEST_PACK_MIX_PACKED_NO_PACKED_PACKED) {
+ mq->num_of_potential_packed_wr_reqs = test_packed_trigger + 1;
+ mq->wr_packing_enabled = true;
+ num_requests = test_packed_trigger + 2;
+ }
+
+ ret = prepare_request_add_write_reqs(td, num_requests, is_err_expected,
+ is_random);
+ if (ret)
+ goto exit;
+
+ if (td->test_info.testcase == TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED)
+ num_requests = temp_num_req;
+
+ memset((void *)mbtd->exp_packed_stats.pack_stop_reason, 0,
+ sizeof(mbtd->exp_packed_stats.pack_stop_reason));
+ memset(mbtd->exp_packed_stats.packing_events, 0,
+ (max_packed_reqs + 1) * sizeof(u32));
+
+ switch (td->test_info.testcase) {
+ case TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ:
+ case TEST_PACKING_EXP_ONE_OVER_TRIGGER_FB_READ:
+ ret = prepare_request_add_read(td);
+ if (ret)
+ goto exit;
+
+ mbtd->exp_packed_stats.pack_stop_reason[WRONG_DATA_DIR] = 1;
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+ break;
+ case TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N:
+ ret = prepare_request_add_flush(td);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, num_packed_reqs,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+ mbtd->exp_packed_stats.pack_stop_reason[FLUSH_OR_DISCARD] = 1;
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 2;
+ break;
+ case TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER:
+ ret = prepare_request_add_read(td);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, test_packed_trigger,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+ break;
+ case TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER:
+ ret = prepare_request_add_flush(td);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, test_packed_trigger,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+ break;
+ case TEST_PACK_MIX_PACKED_NO_PACKED_PACKED:
+ ret = prepare_request_add_read(td);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, test_packed_trigger-1,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, num_requests,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ mbtd->exp_packed_stats.packing_events[num_requests] = 1;
+ mbtd->exp_packed_stats.packing_events[num_requests-1] = 1;
+ mbtd->exp_packed_stats.pack_stop_reason[WRONG_DATA_DIR] = 1;
+ mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+ break;
+ case TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED:
+ ret = prepare_request_add_read(td);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, num_requests,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_read(td);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, test_packed_trigger-1,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ mbtd->exp_packed_stats.pack_stop_reason[WRONG_DATA_DIR] = 1;
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+ break;
+ case TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS:
+ case TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS:
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+ break;
+ default:
+ mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+ }
+ mbtd->num_requests = num_requests;
+
+exit:
+ return ret;
+}
+
+/*
+ * Prepare requests for the TEST_RET_PARTIAL_FOLLOWED_BY_ABORT testcase.
+ * In this testcase we have mixed error expectations from different
+ * write requests, hence the special prepare function.
+ */
+static int prepare_partial_followed_by_abort(struct test_data *td,
+ int num_requests)
+{
+ int i, start_address;
+ int is_err_expected = 0;
+ int ret = 0;
+ struct mmc_queue *mq = test_iosched_get_req_queue()->queuedata;
+ int max_packed_reqs;
+
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+
+ mmc_blk_init_packed_statistics(mq->card);
+
+ for (i = 1; i <= num_requests; i++) {
+ if (i > (num_requests / 2))
+ is_err_expected = 1;
+
+ start_address = td->start_sector + 4096 * td->num_of_write_bios;
+ ret = test_iosched_add_wr_rd_test_req(is_err_expected, WRITE,
+ start_address, (i % 5) + 1, TEST_PATTERN_5A,
+ NULL);
+ if (ret) {
+ test_pr_err("%s: failed to add a write request",
+ __func__);
+ return ret;
+ }
+ }
+
+ memset((void *)&mbtd->exp_packed_stats.pack_stop_reason, 0,
+ sizeof(mbtd->exp_packed_stats.pack_stop_reason));
+ memset(mbtd->exp_packed_stats.packing_events, 0,
+ (max_packed_reqs + 1) * sizeof(u32));
+ mbtd->exp_packed_stats.packing_events[num_requests] = 1;
+ mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+
+ mbtd->num_requests = num_requests;
+
+ return ret;
+}
+
+/*
+ * Get number of write requests for current testcase. If random test mode was
+ * chosen, pseudo-randomly choose the number of requests, otherwise set to
+ * two less than the packing threshold.
+ */
+static int get_num_requests(struct test_data *td)
+{
+ int *seed = &mbtd->random_test_seed;
+ struct request_queue *req_q;
+ struct mmc_queue *mq;
+ int max_num_requests;
+ int num_requests;
+ int min_num_requests = 2;
+ int is_random = mbtd->is_random;
+ int max_for_double;
+ int test_packed_trigger;
+
+ req_q = test_iosched_get_req_queue();
+ if (req_q)
+ mq = req_q->queuedata;
+ else {
+ test_pr_err("%s: NULL request queue", __func__);
+ return 0;
+ }
+
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ max_num_requests = mq->card->ext_csd.max_packed_writes;
+ num_requests = max_num_requests - 2;
+ test_packed_trigger = mq->num_wr_reqs_to_start_packing;
+
+ /*
+ * Here max_for_double is intended for packed control testcases
+ * in which we issue many write requests. It's purpose is to prevent
+ * exceeding max number of req_queue requests.
+ */
+ max_for_double = max_num_requests - 10;
+
+ if (td->test_info.testcase ==
+ TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS)
+ /* Don't expect packing, so issue up to trigger-1 reqs */
+ num_requests = test_packed_trigger - 1;
+
+ if (is_random) {
+ if (td->test_info.testcase ==
+ TEST_RET_PARTIAL_FOLLOWED_BY_ABORT)
+ /*
+ * Here we don't want num_requests to be less than 1
+ * as a consequence of division by 2.
+ */
+ min_num_requests = 3;
+
+ if (td->test_info.testcase ==
+ TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS)
+ /* Don't expect packing, so issue up to trigger reqs */
+ max_num_requests = test_packed_trigger;
+
+ num_requests = pseudo_random_seed(seed, min_num_requests,
+ max_num_requests - 1);
+ }
+
+ if (td->test_info.testcase ==
+ TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS)
+ num_requests -= test_packed_trigger;
+
+ if (td->test_info.testcase == TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N)
+ num_requests =
+ num_requests > max_for_double ? max_for_double : num_requests;
+
+ if (mbtd->test_group == TEST_PACKING_CONTROL_GROUP)
+ num_requests += test_packed_trigger;
+
+ if (td->test_info.testcase == TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS)
+ num_requests = test_packed_trigger;
+
+ return num_requests;
+}
+
+/*
+ * An implementation for the prepare_test_fn pointer in the test_info
+ * data structure. According to the testcase we add the right number of requests
+ * and decide if an error is expected or not.
+ */
+static int prepare_test(struct test_data *td)
+{
+ struct mmc_queue *mq = test_iosched_get_req_queue()->queuedata;
+ int max_num_requests;
+ int num_requests = 0;
+ int ret = 0;
+ int is_random = mbtd->is_random;
+ int test_packed_trigger = mq->num_wr_reqs_to_start_packing;
+
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ max_num_requests = mq->card->ext_csd.max_packed_writes;
+
+ if (is_random && mbtd->random_test_seed == 0) {
+ mbtd->random_test_seed =
+ (unsigned int)(get_jiffies_64() & 0xFFFF);
+ test_pr_info("%s: got seed from jiffies %d",
+ __func__, mbtd->random_test_seed);
+ }
+
+ num_requests = get_num_requests(td);
+
+ if (mbtd->test_group == TEST_SEND_INVALID_GROUP)
+ mq->packed_test_fn =
+ test_invalid_packed_cmd;
+
+ if (mbtd->test_group == TEST_ERR_CHECK_GROUP)
+ mq->err_check_fn = test_err_check;
+
+ switch (td->test_info.testcase) {
+ case TEST_STOP_DUE_TO_FLUSH:
+ case TEST_STOP_DUE_TO_READ:
+ case TEST_RET_PARTIAL_FOLLOWED_BY_SUCCESS:
+ case TEST_RET_PARTIAL_MULTIPLE_UNTIL_SUCCESS:
+ case TEST_STOP_DUE_TO_EMPTY_QUEUE:
+ case TEST_CMD23_PACKED_BIT_UNSET:
+ ret = prepare_packed_requests(td, 0, num_requests, is_random);
+ break;
+ case TEST_STOP_DUE_TO_FLUSH_AFTER_MAX_REQS:
+ case TEST_STOP_DUE_TO_READ_AFTER_MAX_REQS:
+ ret = prepare_packed_requests(td, 0, max_num_requests - 1,
+ is_random);
+ break;
+ case TEST_RET_PARTIAL_FOLLOWED_BY_ABORT:
+ ret = prepare_partial_followed_by_abort(td, num_requests);
+ break;
+ case TEST_STOP_DUE_TO_MAX_REQ_NUM:
+ case TEST_RET_PARTIAL_MAX_FAIL_IDX:
+ ret = prepare_packed_requests(td, 0, max_num_requests,
+ is_random);
+ break;
+ case TEST_STOP_DUE_TO_THRESHOLD:
+ ret = prepare_packed_requests(td, 0, max_num_requests + 1,
+ is_random);
+ break;
+ case TEST_RET_ABORT:
+ case TEST_RET_RETRY:
+ case TEST_RET_CMD_ERR:
+ case TEST_RET_DATA_ERR:
+ case TEST_HDR_INVALID_VERSION:
+ case TEST_HDR_WRONG_WRITE_CODE:
+ case TEST_HDR_INVALID_RW_CODE:
+ case TEST_HDR_DIFFERENT_ADDRESSES:
+ case TEST_HDR_REQ_NUM_SMALLER_THAN_ACTUAL:
+ case TEST_HDR_REQ_NUM_LARGER_THAN_ACTUAL:
+ case TEST_CMD23_MAX_PACKED_WRITES:
+ case TEST_CMD23_ZERO_PACKED_WRITES:
+ case TEST_CMD23_REL_WR_BIT_SET:
+ case TEST_CMD23_BITS_16TO29_SET:
+ case TEST_CMD23_HDR_BLK_NOT_IN_COUNT:
+ case TEST_HDR_CMD23_PACKED_BIT_SET:
+ ret = prepare_packed_requests(td, 1, num_requests, is_random);
+ break;
+ case TEST_PACKING_EXP_N_OVER_TRIGGER:
+ case TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ:
+ case TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS:
+ case TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS:
+ case TEST_PACK_MIX_PACKED_NO_PACKED_PACKED:
+ case TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED:
+ ret = prepare_packed_control_tests_requests(td, 0, num_requests,
+ is_random);
+ break;
+ case TEST_PACKING_EXP_THRESHOLD_OVER_TRIGGER:
+ ret = prepare_packed_control_tests_requests(td, 0,
+ max_num_requests, is_random);
+ break;
+ case TEST_PACKING_EXP_ONE_OVER_TRIGGER_FB_READ:
+ ret = prepare_packed_control_tests_requests(td, 0,
+ test_packed_trigger + 1,
+ is_random);
+ break;
+ case TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N:
+ ret = prepare_packed_control_tests_requests(td, 0, num_requests,
+ is_random);
+ break;
+ case TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER:
+ case TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER:
+ ret = prepare_packed_control_tests_requests(td, 0,
+ test_packed_trigger, is_random);
+ break;
+ default:
+ test_pr_info("%s: Invalid test case...", __func__);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+/*
+ * An implementation for the post_test_fn in the test_info data structure.
+ * In our case we just reset the function pointers in the mmc_queue in order for
+ * the FS to be able to dispatch it's requests correctly after the test is
+ * finished.
+ */
+static int post_test(struct test_data *td)
+{
+ struct mmc_queue *mq;
+
+ if (!td)
+ return -EINVAL;
+
+ mq = td->req_q->queuedata;
+
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ mq->packed_test_fn = NULL;
+ mq->err_check_fn = NULL;
+
+ return 0;
+}
+
+/*
+ * This function checks, based on the current test's test_group, that the
+ * packed commands capability and control are set right. In addition, we check
+ * if the card supports the packed command feature.
+ */
+static int validate_packed_commands_settings(void)
+{
+ struct request_queue *req_q;
+ struct mmc_queue *mq;
+ int max_num_requests;
+ struct mmc_host *host;
+
+ req_q = test_iosched_get_req_queue();
+ if (!req_q) {
+ test_pr_err("%s: test_iosched_get_req_queue failed", __func__);
+ test_iosched_set_test_result(TEST_FAILED);
+ return -EINVAL;
+ }
+
+ mq = req_q->queuedata;
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ max_num_requests = mq->card->ext_csd.max_packed_writes;
+ host = mq->card->host;
+
+ if (!(host->caps2 && MMC_CAP2_PACKED_WR)) {
+ test_pr_err("%s: Packed Write capability disabled, exit test",
+ __func__);
+ test_iosched_set_test_result(TEST_NOT_SUPPORTED);
+ return -EINVAL;
+ }
+
+ if (max_num_requests == 0) {
+ test_pr_err(
+ "%s: no write packing support, ext_csd.max_packed_writes=%d",
+ __func__, mq->card->ext_csd.max_packed_writes);
+ test_iosched_set_test_result(TEST_NOT_SUPPORTED);
+ return -EINVAL;
+ }
+
+ test_pr_info("%s: max number of packed requests supported is %d ",
+ __func__, max_num_requests);
+
+ switch (mbtd->test_group) {
+ case TEST_SEND_WRITE_PACKING_GROUP:
+ case TEST_ERR_CHECK_GROUP:
+ case TEST_SEND_INVALID_GROUP:
+ /* disable the packing control */
+ host->caps2 &= ~MMC_CAP2_PACKED_WR_CONTROL;
+ break;
+ case TEST_PACKING_CONTROL_GROUP:
+ host->caps2 |= MMC_CAP2_PACKED_WR_CONTROL;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static bool message_repeat;
+static int test_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ message_repeat = 1;
+ return 0;
+}
+
+/* send_packing TEST */
+static ssize_t send_write_packing_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+ int j = 0;
+
+ test_pr_info("%s: -- send_write_packing TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+
+ mbtd->test_group = TEST_SEND_WRITE_PACKING_GROUP;
+
+ if (validate_packed_commands_settings())
+ return count;
+
+ if (mbtd->random_test_seed > 0)
+ test_pr_info("%s: Test seed: %d", __func__,
+ mbtd->random_test_seed);
+
+ memset(&mbtd->test_info, 0, sizeof(struct test_info));
+
+ mbtd->test_info.data = mbtd;
+ mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+ mbtd->test_info.post_test_fn = post_test;
+
+ for (i = 0; i < number; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ====================", __func__);
+
+ for (j = SEND_WRITE_PACKING_MIN_TESTCASE;
+ j <= SEND_WRITE_PACKING_MAX_TESTCASE; j++) {
+
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ break;
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = NON_RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ break;
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ }
+ }
+
+ test_pr_info("%s: Completed all the test cases.", __func__);
+
+ return count;
+}
+
+static ssize_t send_write_packing_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nsend_write_packing_test\n"
+ "=========\n"
+ "Description:\n"
+ "This test checks the following scenarios\n"
+ "- Pack due to FLUSH message\n"
+ "- Pack due to FLUSH after threshold writes\n"
+ "- Pack due to READ message\n"
+ "- Pack due to READ after threshold writes\n"
+ "- Pack due to empty queue\n"
+ "- Pack due to threshold writes\n"
+ "- Pack due to one over threshold writes\n");
+
+ if (message_repeat == 1) {
+ message_repeat = 0;
+ return strnlen(buffer, count);
+ } else {
+ return 0;
+ }
+}
+
+const struct file_operations send_write_packing_test_ops = {
+ .open = test_open,
+ .write = send_write_packing_test_write,
+ .read = send_write_packing_test_read,
+};
+
+/* err_check TEST */
+static ssize_t err_check_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+ int j = 0;
+
+ test_pr_info("%s: -- err_check TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+ mbtd->test_group = TEST_ERR_CHECK_GROUP;
+
+ if (validate_packed_commands_settings())
+ return count;
+
+ if (mbtd->random_test_seed > 0)
+ test_pr_info("%s: Test seed: %d", __func__,
+ mbtd->random_test_seed);
+
+ memset(&mbtd->test_info, 0, sizeof(struct test_info));
+
+ mbtd->test_info.data = mbtd;
+ mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+ mbtd->test_info.post_test_fn = post_test;
+
+ for (i = 0; i < number; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ====================", __func__);
+
+ for (j = ERR_CHECK_MIN_TESTCASE;
+ j <= ERR_CHECK_MAX_TESTCASE ; j++) {
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ break;
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = NON_RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ break;
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ }
+ }
+
+ test_pr_info("%s: Completed all the test cases.", __func__);
+
+ return count;
+}
+
+static ssize_t err_check_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nerr_check_TEST\n"
+ "=========\n"
+ "Description:\n"
+ "This test checks the following scenarios\n"
+ "- Return ABORT\n"
+ "- Return PARTIAL followed by success\n"
+ "- Return PARTIAL followed by abort\n"
+ "- Return PARTIAL multiple times until success\n"
+ "- Return PARTIAL with fail index = threshold\n"
+ "- Return RETRY\n"
+ "- Return CMD_ERR\n"
+ "- Return DATA_ERR\n");
+
+ if (message_repeat == 1) {
+ message_repeat = 0;
+ return strnlen(buffer, count);
+ } else {
+ return 0;
+ }
+}
+
+const struct file_operations err_check_test_ops = {
+ .open = test_open,
+ .write = err_check_test_write,
+ .read = err_check_test_read,
+};
+
+/* send_invalid_packed TEST */
+static ssize_t send_invalid_packed_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+ int j = 0;
+ int num_of_failures = 0;
+
+ test_pr_info("%s: -- send_invalid_packed TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+ mbtd->test_group = TEST_SEND_INVALID_GROUP;
+
+ if (validate_packed_commands_settings())
+ return count;
+
+ if (mbtd->random_test_seed > 0)
+ test_pr_info("%s: Test seed: %d", __func__,
+ mbtd->random_test_seed);
+
+ memset(&mbtd->test_info, 0, sizeof(struct test_info));
+
+ mbtd->test_info.data = mbtd;
+ mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+ mbtd->test_info.post_test_fn = post_test;
+
+ for (i = 0; i < number; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ====================", __func__);
+
+ for (j = INVALID_CMD_MIN_TESTCASE;
+ j <= INVALID_CMD_MAX_TESTCASE ; j++) {
+
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ num_of_failures++;
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = NON_RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ num_of_failures++;
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ }
+ }
+
+ test_pr_info("%s: Completed all the test cases.", __func__);
+
+ if (num_of_failures > 0) {
+ test_iosched_set_test_result(TEST_FAILED);
+ test_pr_err(
+ "There were %d failures during the test, TEST FAILED",
+ num_of_failures);
+ }
+ return count;
+}
+
+static ssize_t send_invalid_packed_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nsend_invalid_packed_TEST\n"
+ "=========\n"
+ "Description:\n"
+ "This test checks the following scenarios\n"
+ "- Send an invalid header version\n"
+ "- Send the wrong write code\n"
+ "- Send an invalid R/W code\n"
+ "- Send wrong start address in header\n"
+ "- Send header with block_count smaller than actual\n"
+ "- Send header with block_count larger than actual\n"
+ "- Send header CMD23 packed bit set\n"
+ "- Send CMD23 with block count over threshold\n"
+ "- Send CMD23 with block_count equals zero\n"
+ "- Send CMD23 packed bit unset\n"
+ "- Send CMD23 reliable write bit set\n"
+ "- Send CMD23 bits [16-29] set\n"
+ "- Send CMD23 header block not in block_count\n");
+
+ if (message_repeat == 1) {
+ message_repeat = 0;
+ return strnlen(buffer, count);
+ } else {
+ return 0;
+ }
+}
+
+const struct file_operations send_invalid_packed_test_ops = {
+ .open = test_open,
+ .write = send_invalid_packed_test_write,
+ .read = send_invalid_packed_test_read,
+};
+
+/* packing_control TEST */
+static ssize_t write_packing_control_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+ int j = 0;
+ struct mmc_queue *mq = test_iosched_get_req_queue()->queuedata;
+ int max_num_requests = mq->card->ext_csd.max_packed_writes;
+ int test_successful = 1;
+
+ test_pr_info("%s: -- write_packing_control TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+ test_pr_info("%s: max_num_requests = %d ", __func__,
+ max_num_requests);
+
+ memset(&mbtd->test_info, 0, sizeof(struct test_info));
+ mbtd->test_group = TEST_PACKING_CONTROL_GROUP;
+
+ if (validate_packed_commands_settings())
+ return count;
+
+ mbtd->test_info.data = mbtd;
+ mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+
+ for (i = 0; i < number; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ====================", __func__);
+
+ for (j = PACKING_CONTROL_MIN_TESTCASE;
+ j <= PACKING_CONTROL_MAX_TESTCASE; j++) {
+
+ test_successful = 1;
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret) {
+ test_successful = 0;
+ break;
+ }
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = NON_RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret) {
+ test_successful = 0;
+ break;
+ }
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ }
+
+ if (!test_successful)
+ break;
+ }
+
+ test_pr_info("%s: Completed all the test cases.", __func__);
+
+ return count;
+}
+
+static ssize_t write_packing_control_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nwrite_packing_control_test\n"
+ "=========\n"
+ "Description:\n"
+ "This test checks the following scenarios\n"
+ "- Packing expected - one over trigger\n"
+ "- Packing expected - N over trigger\n"
+ "- Packing expected - N over trigger followed by read\n"
+ "- Packing expected - N over trigger followed by flush\n"
+ "- Packing expected - threshold over trigger FB by flush\n"
+ "- Packing not expected - less than trigger\n"
+ "- Packing not expected - trigger requests\n"
+ "- Packing not expected - trigger, read, trigger\n"
+ "- Mixed state - packing -> no packing -> packing\n"
+ "- Mixed state - no packing -> packing -> no packing\n");
+
+ if (message_repeat == 1) {
+ message_repeat = 0;
+ return strnlen(buffer, count);
+ } else {
+ return 0;
+ }
+}
+
+const struct file_operations write_packing_control_test_ops = {
+ .open = test_open,
+ .write = write_packing_control_test_write,
+ .read = write_packing_control_test_read,
+};
+
+static void mmc_block_test_debugfs_cleanup(void)
+{
+ debugfs_remove(mbtd->debug.random_test_seed);
+ debugfs_remove(mbtd->debug.send_write_packing_test);
+ debugfs_remove(mbtd->debug.err_check_test);
+ debugfs_remove(mbtd->debug.send_invalid_packed_test);
+ debugfs_remove(mbtd->debug.packing_control_test);
+}
+
+static int mmc_block_test_debugfs_init(void)
+{
+ struct dentry *utils_root, *tests_root;
+
+ utils_root = test_iosched_get_debugfs_utils_root();
+ tests_root = test_iosched_get_debugfs_tests_root();
+
+ if (!utils_root || !tests_root)
+ return -EINVAL;
+
+ mbtd->debug.random_test_seed = debugfs_create_u32(
+ "random_test_seed",
+ S_IRUGO | S_IWUGO,
+ utils_root,
+ &mbtd->random_test_seed);
+
+ if (!mbtd->debug.random_test_seed)
+ goto err_nomem;
+
+ mbtd->debug.send_write_packing_test =
+ debugfs_create_file("send_write_packing_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &send_write_packing_test_ops);
+
+ if (!mbtd->debug.send_write_packing_test)
+ goto err_nomem;
+
+ mbtd->debug.err_check_test =
+ debugfs_create_file("err_check_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &err_check_test_ops);
+
+ if (!mbtd->debug.err_check_test)
+ goto err_nomem;
+
+ mbtd->debug.send_invalid_packed_test =
+ debugfs_create_file("send_invalid_packed_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &send_invalid_packed_test_ops);
+
+ if (!mbtd->debug.send_invalid_packed_test)
+ goto err_nomem;
+
+ mbtd->debug.packing_control_test = debugfs_create_file(
+ "packing_control_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &write_packing_control_test_ops);
+
+ if (!mbtd->debug.packing_control_test)
+ goto err_nomem;
+
+ return 0;
+
+err_nomem:
+ mmc_block_test_debugfs_cleanup();
+ return -ENOMEM;
+}
+
+static void mmc_block_test_probe(void)
+{
+ struct request_queue *q = test_iosched_get_req_queue();
+ struct mmc_queue *mq;
+ int max_packed_reqs;
+
+ if (!q) {
+ test_pr_err("%s: NULL request queue", __func__);
+ return;
+ }
+
+ mq = q->queuedata;
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return;
+ }
+
+ max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+ mbtd->exp_packed_stats.packing_events =
+ kzalloc((max_packed_reqs + 1) *
+ sizeof(*mbtd->exp_packed_stats.packing_events),
+ GFP_KERNEL);
+
+ mmc_block_test_debugfs_init();
+}
+
+static void mmc_block_test_remove(void)
+{
+ mmc_block_test_debugfs_cleanup();
+}
+
+static int __init mmc_block_test_init(void)
+{
+ mbtd = kzalloc(sizeof(struct mmc_block_test_data), GFP_KERNEL);
+ if (!mbtd) {
+ test_pr_err("%s: failed to allocate mmc_block_test_data",
+ __func__);
+ return -ENODEV;
+ }
+
+ mbtd->bdt.init_fn = mmc_block_test_probe;
+ mbtd->bdt.exit_fn = mmc_block_test_remove;
+ INIT_LIST_HEAD(&mbtd->bdt.list);
+ test_iosched_register(&mbtd->bdt);
+
+ return 0;
+}
+
+static void __exit mmc_block_test_exit(void)
+{
+ test_iosched_unregister(&mbtd->bdt);
+ kfree(mbtd);
+}
+
+module_init(mmc_block_test_init);
+module_exit(mmc_block_test_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MMC block test");
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index d818fc4..cc91646 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -25,6 +25,13 @@
#define MMC_QUEUE_SUSPENDED (1 << 0)
/*
+ * Based on benchmark tests the default num of requests to trigger the write
+ * packing was determined, to keep the read latency as low as possible and
+ * manage to keep the high write throughput.
+ */
+#define DEFAULT_NUM_REQS_TO_START_PACK 17
+
+/*
* Prepare a MMC request. This just filters out odd stuff.
*/
static int mmc_prep_request(struct request_queue *q, struct request *req)
@@ -52,6 +59,7 @@
struct mmc_queue *mq = d;
struct request_queue *q = mq->queue;
struct request *req;
+ struct mmc_card *card = mq->card;
current->flags |= PF_MEMALLOC;
@@ -67,6 +75,17 @@
spin_unlock_irq(q->queue_lock);
if (req || mq->mqrq_prev->req) {
+ /*
+ * If this is the first request, BKOPs might be in
+ * progress and needs to be stopped before issuing the
+ * request
+ */
+ if (card->ext_csd.bkops_en &&
+ card->bkops_info.started_delayed_bkops) {
+ card->bkops_info.started_delayed_bkops = false;
+ mmc_stop_bkops(card);
+ }
+
set_current_state(TASK_RUNNING);
mq->issue_fn(mq, req);
} else {
@@ -74,6 +93,7 @@
set_current_state(TASK_RUNNING);
break;
}
+ mmc_start_delayed_bkops(card);
up(&mq->thread_sem);
schedule();
down(&mq->thread_sem);
@@ -189,6 +209,7 @@
mq->mqrq_cur = mqrq_cur;
mq->mqrq_prev = mqrq_prev;
mq->queue->queuedata = mq;
+ mq->num_wr_reqs_to_start_packing = DEFAULT_NUM_REQS_TO_START_PACK;
blk_queue_prep_rq(mq->queue, mmc_prep_request);
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index 5e04938..a8c104e 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -12,6 +12,17 @@
struct mmc_data data;
};
+enum mmc_blk_status {
+ MMC_BLK_SUCCESS = 0,
+ MMC_BLK_PARTIAL,
+ MMC_BLK_CMD_ERR,
+ MMC_BLK_RETRY,
+ MMC_BLK_ABORT,
+ MMC_BLK_DATA_ERR,
+ MMC_BLK_ECC_ERR,
+ MMC_BLK_NOMEDIUM,
+};
+
enum mmc_packed_cmd {
MMC_PACKED_NONE = 0,
MMC_PACKED_WRITE,
@@ -45,6 +56,11 @@
struct mmc_queue_req mqrq[2];
struct mmc_queue_req *mqrq_cur;
struct mmc_queue_req *mqrq_prev;
+ bool wr_packing_enabled;
+ int num_of_potential_packed_wr_reqs;
+ int num_wr_reqs_to_start_packing;
+ int (*err_check_fn) (struct mmc_card *, struct mmc_async_req *);
+ void (*packed_test_fn) (struct request_queue *, struct mmc_queue_req *);
};
extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,
@@ -58,4 +74,6 @@
extern void mmc_queue_bounce_pre(struct mmc_queue_req *);
extern void mmc_queue_bounce_post(struct mmc_queue_req *);
+extern void print_mmc_packing_stats(struct mmc_card *card);
+
#endif
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 33f7d29..0592f9d 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -254,6 +254,8 @@
card->dev.release = mmc_release_card;
card->dev.type = type;
+ spin_lock_init(&card->wr_pack_stats.lock);
+
return card;
}
@@ -356,6 +358,8 @@
device_del(&card->dev);
}
+ kfree(card->wr_pack_stats.packing_events);
+
put_device(&card->dev);
}
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 9b316bb..7e03e5a 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -280,9 +280,42 @@
}
/**
+ * mmc_start_delayed_bkops() - Start a delayed work to check for
+ * the need of non urgent BKOPS
+ *
+ * @card: MMC card to start BKOPS on
+ */
+void mmc_start_delayed_bkops(struct mmc_card *card)
+{
+ if (!card || !card->ext_csd.bkops_en || mmc_card_doing_bkops(card))
+ return;
+
+ if (card->bkops_info.sectors_changed <
+ card->bkops_info.min_sectors_to_queue_delayed_work)
+ return;
+
+ pr_debug("%s: %s: queueing delayed_bkops_work\n",
+ mmc_hostname(card->host), __func__);
+
+ card->bkops_info.sectors_changed = 0;
+
+ /*
+ * cancel_delayed_bkops_work will prevent a race condition between
+ * fetching a request by the mmcqd and the delayed work, in case
+ * it was removed from the queue work but not started yet
+ */
+ card->bkops_info.cancel_delayed_work = false;
+ card->bkops_info.started_delayed_bkops = true;
+ queue_delayed_work(system_nrt_wq, &card->bkops_info.dw,
+ msecs_to_jiffies(
+ card->bkops_info.delay_ms));
+}
+EXPORT_SYMBOL(mmc_start_delayed_bkops);
+
+/**
* mmc_start_bkops - start BKOPS for supported cards
* @card: MMC card to start BKOPS
- * @form_exception: A flag to indicate if this function was
+ * @from_exception: A flag to indicate if this function was
* called due to an exception raised by the card
*
* Start background operations whenever requested.
@@ -296,25 +329,47 @@
bool use_busy_signal;
BUG_ON(!card);
-
- if (!card->ext_csd.bkops_en || mmc_card_doing_bkops(card))
+ if (!card->ext_csd.bkops_en)
return;
+ mmc_claim_host(card->host);
+
+ if ((card->bkops_info.cancel_delayed_work) && !from_exception) {
+ pr_debug("%s: %s: cancel_delayed_work was set, exit\n",
+ mmc_hostname(card->host), __func__);
+ card->bkops_info.cancel_delayed_work = false;
+ goto out;
+ }
+
+ if (mmc_card_doing_bkops(card)) {
+ pr_debug("%s: %s: already doing bkops, exit\n",
+ mmc_hostname(card->host), __func__);
+ goto out;
+ }
+
err = mmc_read_bkops_status(card);
if (err) {
pr_err("%s: Failed to read bkops status: %d\n",
mmc_hostname(card->host), err);
- return;
+ goto out;
}
if (!card->ext_csd.raw_bkops_status)
- return;
+ goto out;
+ pr_info("%s: %s: card->ext_csd.raw_bkops_status = 0x%x\n",
+ mmc_hostname(card->host), __func__,
+ card->ext_csd.raw_bkops_status);
+
+ /*
+ * If the function was called due to exception but there is no need
+ * for urgent BKOPS, BKOPs will be performed by the delayed BKOPs
+ * work, before going to suspend
+ */
if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2 &&
from_exception)
- return;
+ goto out;
- mmc_claim_host(card->host);
if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) {
timeout = MMC_BKOPS_MAX_TIMEOUT;
use_busy_signal = true;
@@ -336,13 +391,108 @@
* bkops executed synchronously, otherwise
* the operation is in progress
*/
- if (!use_busy_signal)
+ if (!use_busy_signal) {
mmc_card_set_doing_bkops(card);
+ pr_debug("%s: %s: starting the polling thread\n",
+ mmc_hostname(card->host), __func__);
+ queue_work(system_nrt_wq,
+ &card->bkops_info.poll_for_completion);
+ }
+
out:
mmc_release_host(card->host);
}
EXPORT_SYMBOL(mmc_start_bkops);
+/**
+ * mmc_bkops_completion_polling() - Poll on the card status to
+ * wait for the non-blocking BKOPS completion
+ * @work: The completion polling work
+ *
+ * The on-going reading of the card status will prevent the card
+ * from getting into suspend while it is in the middle of
+ * performing BKOPS.
+ * Since the non blocking BKOPS can be interrupted by a fetched
+ * request we also check IF mmc_card_doing_bkops in each
+ * iteration.
+ */
+void mmc_bkops_completion_polling(struct work_struct *work)
+{
+ struct mmc_card *card = container_of(work, struct mmc_card,
+ bkops_info.poll_for_completion);
+ unsigned long timeout_jiffies = jiffies +
+ msecs_to_jiffies(BKOPS_COMPLETION_POLLING_TIMEOUT_MS);
+ u32 status;
+ int err;
+
+ /*
+ * Wait for the BKOPs to complete. Keep reading the status to prevent
+ * the host from getting into suspend
+ */
+ do {
+ mmc_claim_host(card->host);
+
+ if (!mmc_card_doing_bkops(card))
+ goto out;
+
+ err = mmc_send_status(card, &status);
+ if (err) {
+ pr_err("%s: error %d requesting status\n",
+ mmc_hostname(card->host), err);
+ goto out;
+ }
+
+ /*
+ * Some cards mishandle the status bits, so make sure to check
+ * both the busy indication and the card state.
+ */
+ if ((status & R1_READY_FOR_DATA) &&
+ (R1_CURRENT_STATE(status) != R1_STATE_PRG)) {
+ pr_debug("%s: %s: completed BKOPs, exit polling\n",
+ mmc_hostname(card->host), __func__);
+ mmc_card_clr_doing_bkops(card);
+ card->bkops_info.started_delayed_bkops = false;
+ goto out;
+ }
+
+ mmc_release_host(card->host);
+
+ /*
+ * Sleep before checking the card status again to allow the
+ * card to complete the BKOPs operation
+ */
+ msleep(BKOPS_COMPLETION_POLLING_INTERVAL_MS);
+ } while (time_before(jiffies, timeout_jiffies));
+
+ pr_err("%s: %s: exit polling due to timeout\n",
+ mmc_hostname(card->host), __func__);
+
+ return;
+out:
+ mmc_release_host(card->host);
+}
+
+/**
+ * mmc_start_idle_time_bkops() - check if a non urgent BKOPS is
+ * needed
+ * @work: The idle time BKOPS work
+ */
+void mmc_start_idle_time_bkops(struct work_struct *work)
+{
+ struct mmc_card *card = container_of(work, struct mmc_card,
+ bkops_info.dw.work);
+
+ /*
+ * Prevent a race condition between mmc_stop_bkops and the delayed
+ * BKOPS work in case the delayed work is executed on another CPU
+ */
+ if (card->bkops_info.cancel_delayed_work)
+ return;
+
+ mmc_start_bkops(card, false);
+}
+EXPORT_SYMBOL(mmc_start_idle_time_bkops);
+
static void mmc_wait_done(struct mmc_request *mrq)
{
complete(&mrq->completion);
@@ -599,6 +749,19 @@
int err = 0;
BUG_ON(!card);
+
+ mmc_claim_host(card->host);
+
+ /*
+ * Notify the delayed work to be cancelled, in case it was already
+ * removed from the queue, but was not started yet
+ */
+ card->bkops_info.cancel_delayed_work = true;
+ if (delayed_work_pending(&card->bkops_info.dw))
+ cancel_delayed_work_sync(&card->bkops_info.dw);
+ if (!mmc_card_doing_bkops(card))
+ goto out;
+
err = mmc_interrupt_hpi(card);
/*
@@ -610,6 +773,8 @@
err = 0;
}
+out:
+ mmc_release_host(card->host);
return err;
}
EXPORT_SYMBOL(mmc_stop_bkops);
@@ -2615,15 +2780,13 @@
switch (mode) {
case PM_HIBERNATION_PREPARE:
case PM_SUSPEND_PREPARE:
- if (host->card && mmc_card_mmc(host->card) &&
- mmc_card_doing_bkops(host->card)) {
+ if (host->card && mmc_card_mmc(host->card)) {
err = mmc_stop_bkops(host->card);
if (err) {
pr_err("%s: didn't stop bkops\n",
mmc_hostname(host));
return err;
}
- mmc_card_clr_doing_bkops(host->card);
}
spin_lock_irqsave(&host->lock, flags);
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index 9ab5b17..4022ccc 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -318,6 +318,172 @@
.llseek = default_llseek,
};
+static int mmc_wr_pack_stats_open(struct inode *inode, struct file *filp)
+{
+ struct mmc_card *card = inode->i_private;
+
+ filp->private_data = card;
+ card->wr_pack_stats.print_in_read = 1;
+ return 0;
+}
+
+#define TEMP_BUF_SIZE 256
+static ssize_t mmc_wr_pack_stats_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct mmc_card *card = filp->private_data;
+ struct mmc_wr_pack_stats *pack_stats;
+ int i;
+ int max_num_of_packed_reqs = 0;
+ char *temp_buf;
+
+ if (!card)
+ return cnt;
+
+ if (!card->wr_pack_stats.print_in_read)
+ return 0;
+
+ if (!card->wr_pack_stats.enabled) {
+ pr_info("%s: write packing statistics are disabled\n",
+ mmc_hostname(card->host));
+ goto exit;
+ }
+
+ pack_stats = &card->wr_pack_stats;
+
+ if (!pack_stats->packing_events) {
+ pr_info("%s: NULL packing_events\n", mmc_hostname(card->host));
+ goto exit;
+ }
+
+ max_num_of_packed_reqs = card->ext_csd.max_packed_writes;
+
+ temp_buf = kmalloc(TEMP_BUF_SIZE, GFP_KERNEL);
+ if (!temp_buf)
+ goto exit;
+
+ spin_lock(&pack_stats->lock);
+
+ snprintf(temp_buf, TEMP_BUF_SIZE, "%s: write packing statistics:\n",
+ mmc_hostname(card->host));
+ strlcat(ubuf, temp_buf, cnt);
+
+ for (i = 1 ; i <= max_num_of_packed_reqs ; ++i) {
+ if (pack_stats->packing_events[i]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: Packed %d reqs - %d times\n",
+ mmc_hostname(card->host), i,
+ pack_stats->packing_events[i]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ }
+
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: stopped packing due to the following reasons:\n",
+ mmc_hostname(card->host));
+ strlcat(ubuf, temp_buf, cnt);
+
+ if (pack_stats->pack_stop_reason[EXCEEDS_SEGMENTS]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: exceed max num of segments\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[EXCEEDS_SEGMENTS]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ if (pack_stats->pack_stop_reason[EXCEEDS_SECTORS]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: exceed max num of sectors\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[EXCEEDS_SECTORS]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ if (pack_stats->pack_stop_reason[WRONG_DATA_DIR]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: wrong data direction\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[WRONG_DATA_DIR]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ if (pack_stats->pack_stop_reason[FLUSH_OR_DISCARD]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: flush or discard\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[FLUSH_OR_DISCARD]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ if (pack_stats->pack_stop_reason[EMPTY_QUEUE]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: empty queue\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[EMPTY_QUEUE]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ if (pack_stats->pack_stop_reason[REL_WRITE]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: rel write\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[REL_WRITE]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ if (pack_stats->pack_stop_reason[THRESHOLD]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: Threshold\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[THRESHOLD]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+
+ if (pack_stats->pack_stop_reason[LARGE_SEC_ALIGN]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: Large sector alignment\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[LARGE_SEC_ALIGN]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+
+ spin_unlock(&pack_stats->lock);
+
+ kfree(temp_buf);
+
+ pr_info("%s", ubuf);
+
+exit:
+ if (card->wr_pack_stats.print_in_read == 1) {
+ card->wr_pack_stats.print_in_read = 0;
+ return strnlen(ubuf, cnt);
+ }
+
+ return 0;
+}
+
+static ssize_t mmc_wr_pack_stats_write(struct file *filp,
+ const char __user *ubuf, size_t cnt,
+ loff_t *ppos)
+{
+ struct mmc_card *card = filp->private_data;
+ int value;
+
+ if (!card)
+ return cnt;
+
+ sscanf(ubuf, "%d", &value);
+ if (value) {
+ mmc_blk_init_packed_statistics(card);
+ } else {
+ spin_lock(&card->wr_pack_stats.lock);
+ card->wr_pack_stats.enabled = false;
+ spin_unlock(&card->wr_pack_stats.lock);
+ }
+
+ return cnt;
+}
+
+static const struct file_operations mmc_dbg_wr_pack_stats_fops = {
+ .open = mmc_wr_pack_stats_open,
+ .read = mmc_wr_pack_stats_read,
+ .write = mmc_wr_pack_stats_write,
+};
+
void mmc_add_card_debugfs(struct mmc_card *card)
{
struct mmc_host *host = card->host;
@@ -350,6 +516,12 @@
&mmc_dbg_ext_csd_fops))
goto err;
+ if (mmc_card_mmc(card) && (card->ext_csd.rev >= 6) &&
+ (card->host->caps2 & MMC_CAP2_PACKED_WR))
+ if (!debugfs_create_file("wr_pack_stats", S_IRUSR, root, card,
+ &mmc_dbg_wr_pack_stats_fops))
+ goto err;
+
return;
err:
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index e2172c5..ca8b01c 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1297,6 +1297,48 @@
} else {
card->ext_csd.packed_event_en = 1;
}
+
+ }
+
+ if (!oldcard) {
+ if ((host->caps2 & MMC_CAP2_PACKED_CMD) &&
+ (card->ext_csd.max_packed_writes > 0)) {
+ /*
+ * We would like to keep the statistics in an index
+ * that equals the num of packed requests
+ * (1 to max_packed_writes)
+ */
+ card->wr_pack_stats.packing_events = kzalloc(
+ (card->ext_csd.max_packed_writes + 1) *
+ sizeof(*card->wr_pack_stats.packing_events),
+ GFP_KERNEL);
+ if (!card->wr_pack_stats.packing_events)
+ goto free_card;
+ }
+
+ if (card->ext_csd.bkops_en) {
+ INIT_DELAYED_WORK(&card->bkops_info.dw,
+ mmc_start_idle_time_bkops);
+ INIT_WORK(&card->bkops_info.poll_for_completion,
+ mmc_bkops_completion_polling);
+
+ /*
+ * Calculate the time to start the BKOPs checking.
+ * The idle time of the host controller should be taken
+ * into account in order to prevent a race condition
+ * before starting BKOPs and going into suspend.
+ * If the host controller didn't set its idle time,
+ * a default value is used.
+ */
+ card->bkops_info.delay_ms = MMC_IDLE_BKOPS_TIME_MS;
+ if (card->bkops_info.host_suspend_tout_ms)
+ card->bkops_info.delay_ms = min(
+ card->bkops_info.delay_ms,
+ card->bkops_info.host_suspend_tout_ms/2);
+
+ card->bkops_info.min_sectors_to_queue_delayed_work =
+ BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK;
+ }
}
if (!oldcard)
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 357d290..29413ab 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -3,7 +3,8 @@
*
* Copyright (C) 2007 Google Inc,
* Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
- * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+ *
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -1821,7 +1822,7 @@
*/
wake_lock(&host->sdio_wlock);
} else {
- if (!mmc->card || !mmc_card_sdio(mmc->card)) {
+ if (mmc->card && !mmc_card_sdio(mmc->card)) {
WARN(1, "%s: SDCC core interrupt received for non-SDIO cards when SDCC clocks are off\n",
mmc_hostname(mmc));
ret = 1;
@@ -1855,7 +1856,7 @@
#endif
if (status & MCI_SDIOINTROPE) {
- if (!mmc->card || mmc_card_sdio(mmc->card)) {
+ if (mmc->card && !mmc_card_sdio(mmc->card)) {
WARN(1, "%s: SDIO interrupt received for non-SDIO card\n",
mmc_hostname(mmc));
ret = 1;
@@ -4145,25 +4146,35 @@
.hw_reset = msmsdcc_hw_reset,
};
+static void msmsdcc_enable_status_gpio(struct msmsdcc_host *host)
+{
+ unsigned int gpio_no = host->plat->status_gpio;
+ int status;
+
+ if (!gpio_is_valid(gpio_no))
+ return;
+
+ status = gpio_request(gpio_no, "SD_HW_Detect");
+ if (status)
+ pr_err("%s: %s: gpio_request(%d) failed\n",
+ mmc_hostname(host->mmc), __func__, gpio_no);
+}
+
+static void msmsdcc_disable_status_gpio(struct msmsdcc_host *host)
+{
+ if (gpio_is_valid(host->plat->status_gpio))
+ gpio_free(host->plat->status_gpio);
+}
+
static unsigned int
msmsdcc_slot_status(struct msmsdcc_host *host)
{
int status;
- unsigned int gpio_no = host->plat->status_gpio;
- status = gpio_request(gpio_no, "SD_HW_Detect");
- if (status) {
- pr_err("%s: %s: Failed to request GPIO %d\n",
- mmc_hostname(host->mmc), __func__, gpio_no);
- } else {
- status = gpio_direction_input(gpio_no);
- if (!status) {
- status = gpio_get_value_cansleep(gpio_no);
- if (host->plat->is_status_gpio_active_low)
- status = !status;
- }
- gpio_free(gpio_no);
- }
+ status = gpio_get_value_cansleep(host->plat->status_gpio);
+ if (host->plat->is_status_gpio_active_low)
+ status = !status;
+
return status;
}
@@ -5440,7 +5451,7 @@
struct mmc_platform_data *pdata;
struct device_node *np = dev->of_node;
u32 bus_width = 0, current_limit = 0;
- u32 *clk_table, *sup_voltages;
+ u32 *clk_table = NULL, *sup_voltages = NULL;
int clk_table_len, sup_volt_len, len;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
@@ -5825,6 +5836,7 @@
MMC_CAP_SET_XPC_180);
mmc->caps2 |= MMC_CAP2_PACKED_WR;
+ mmc->caps2 |= MMC_CAP2_PACKED_WR_CONTROL;
mmc->caps2 |= (MMC_CAP2_BOOTPART_NOACC | MMC_CAP2_DETECT_ON_ERR);
mmc->caps2 |= MMC_CAP2_SANITIZE;
mmc->caps2 |= MMC_CAP2_CACHE_CTRL;
@@ -5911,11 +5923,12 @@
plat->wpswitch_gpio = -ENOENT;
if (plat->status || gpio_is_valid(plat->status_gpio)) {
- if (plat->status)
+ if (plat->status) {
host->oldstat = plat->status(mmc_dev(host->mmc));
- else
+ } else {
+ msmsdcc_enable_status_gpio(host);
host->oldstat = msmsdcc_slot_status(host);
-
+ }
host->eject = !host->oldstat;
}
@@ -6098,6 +6111,7 @@
if (plat->status_irq)
free_irq(plat->status_irq, host);
+ msmsdcc_disable_status_gpio(host);
sdiowakeup_irq_free:
wake_lock_destroy(&host->sdio_suspend_wlock);
if (plat->sdiowakeup_irq)
@@ -6194,6 +6208,7 @@
if (plat->status_irq)
free_irq(plat->status_irq, host);
+ msmsdcc_disable_status_gpio(host);
wake_lock_destroy(&host->sdio_suspend_wlock);
if (plat->sdiowakeup_irq) {
@@ -6522,8 +6537,10 @@
rc = 0;
goto out;
}
- if (host->plat->status_irq)
+ if (host->plat->status_irq) {
disable_irq(host->plat->status_irq);
+ msmsdcc_disable_status_gpio(host);
+ }
if (!pm_runtime_suspended(dev))
rc = msmsdcc_runtime_suspend(dev);
@@ -6579,6 +6596,7 @@
host->pending_resume = true;
if (host->plat->status_irq) {
+ msmsdcc_enable_status_gpio(host);
msmsdcc_check_status((unsigned long)host);
enable_irq(host->plat->status_irq);
}
diff --git a/drivers/platform/msm/qpnp-pwm.c b/drivers/platform/msm/qpnp-pwm.c
index 6f9af36..ebe3ad06 100644
--- a/drivers/platform/msm/qpnp-pwm.c
+++ b/drivers/platform/msm/qpnp-pwm.c
@@ -439,7 +439,7 @@
int i, pwm_size, rc = 0;
int burst_size = SPMI_MAX_BUF_LEN;
int list_len = lut->list_len << 1;
- int offset = lut->lo_index << 2;
+ int offset = lut->lo_index << 1;
pwm_size = QPNP_GET_PWM_SIZE(
chip->qpnp_lpg_registers[QPNP_LPG_PWM_SIZE_CLK]) &
@@ -856,7 +856,7 @@
return qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],
- lpg_config->base_addr, QPNP_RAMP_CONTROL, 1, chip);
+ lpg_config->base_addr, QPNP_ENABLE_CONTROL, 1, chip);
}
static int qpnp_disable_pwm(struct pwm_device *pwm)
@@ -873,7 +873,7 @@
return qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],
- lpg_config->base_addr, QPNP_RAMP_CONTROL, 1, chip);
+ lpg_config->base_addr, QPNP_ENABLE_CONTROL, 1, chip);
}
static int _pwm_config(struct pwm_device *pwm, int duty_us, int period_us)
diff --git a/drivers/platform/msm/sps/sps.c b/drivers/platform/msm/sps/sps.c
index 5bbcc84..25febff 100644
--- a/drivers/platform/msm/sps/sps.c
+++ b/drivers/platform/msm/sps/sps.c
@@ -1828,19 +1828,6 @@
if (virt_addr != NULL)
bam->props.virt_addr = virt_addr;
- if ((bam_props->manage & SPS_BAM_MGR_DEVICE_REMOTE) != 0 &&
- (bam_props->manage & SPS_BAM_MGR_MULTI_EE) != 0 &&
- bam_props->ee == 0) {
- /*
- * BAM global is owned by a remote processor, so force EE index
- * to a non-zero value to insure EE zero globals are not
- * modified.
- */
- SPS_DBG2("sps:Setting EE for BAM %x to non-zero",
- bam_props->phys_addr);
- bam->props.ee = 1;
- }
-
ok = sps_bam_device_init(bam);
mutex_unlock(&bam->lock);
if (ok) {
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index e6e2f30..23903df 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -640,6 +640,26 @@
raw->last_good_ocv_raw -= MBG_TRANSIENT_ERROR_RAW;
}
+#define SEL_ALT_OREG_BIT BIT(2)
+static int ocv_ir_compensation(struct pm8921_bms_chip *chip, int ocv)
+{
+ int compensated_ocv;
+ int ibatt_ua;
+ int rbatt_mohm = chip->default_rbatt_mohm + chip->rconn_mohm;
+
+ pm_bms_masked_write(chip, BMS_TEST1,
+ SEL_ALT_OREG_BIT, SEL_ALT_OREG_BIT);
+
+ /* since the SEL_ALT_OREG_BIT is set this will give us VSENSE_OCV */
+ pm8921_bms_get_battery_current(&ibatt_ua);
+ compensated_ocv = ocv + div_s64((s64)ibatt_ua * rbatt_mohm, 1000);
+ pr_debug("comp ocv = %d, ocv = %d, ibatt_ua = %d, rbatt_mohm = %d\n",
+ compensated_ocv, ocv, ibatt_ua, rbatt_mohm);
+
+ pm_bms_masked_write(chip, BMS_TEST1, SEL_ALT_OREG_BIT, 0);
+ return compensated_ocv;
+}
+
static int read_soc_params_raw(struct pm8921_bms_chip *chip,
struct pm8921_soc_params *raw)
{
@@ -662,6 +682,8 @@
adjust_pon_ocv_raw(chip, raw);
convert_vbatt_raw_to_uv(chip, usb_chg,
raw->last_good_ocv_raw, &raw->last_good_ocv_uv);
+ raw->last_good_ocv_uv = ocv_ir_compensation(chip,
+ raw->last_good_ocv_uv);
chip->last_ocv_uv = raw->last_good_ocv_uv;
pr_debug("PON_OCV_UV = %d\n", chip->last_ocv_uv);
} else if (chip->prev_last_good_ocv_raw != raw->last_good_ocv_raw) {
@@ -1012,7 +1034,7 @@
* samples with the the shutdown_iavg_ua
*/
if (firsttime && chip->shutdown_iavg_ua != 0) {
- pr_emerg("Using shutdown_iavg_ua = %d in all samples\n",
+ pr_debug("Using shutdown_iavg_ua = %d in all samples\n",
chip->shutdown_iavg_ua);
for (i = 0; i < IAVG_SAMPLES; i++)
iavg_samples[i] = chip->shutdown_iavg_ua;
@@ -2332,12 +2354,6 @@
return IRQ_HANDLED;
}
-static irqreturn_t pm8921_bms_vsense_avg_handler(int irq, void *data)
-{
- pr_debug("irq = %d triggered", irq);
- return IRQ_HANDLED;
-}
-
struct pm_bms_irq_init_data {
unsigned int irq_id;
char *name;
@@ -2366,8 +2382,6 @@
pm8921_bms_ocv_for_r_handler),
BMS_IRQ(PM8921_BMS_GOOD_OCV, IRQF_TRIGGER_RISING,
pm8921_bms_good_ocv_handler),
- BMS_IRQ(PM8921_BMS_VSENSE_AVG, IRQF_TRIGGER_RISING,
- pm8921_bms_vsense_avg_handler),
};
static void free_irqs(struct pm8921_bms_chip *chip)
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
index 19454ca..b4080df 100644
--- a/drivers/power/pm8921-charger.c
+++ b/drivers/power/pm8921-charger.c
@@ -2621,6 +2621,11 @@
pr_debug("USB charger active\n");
pm_chg_iusbmax_get(chip, &usb_ma);
+ if (usb_ma == 500 && !usb_target_ma) {
+ pr_debug("Stopping Unplug Check Worker USB == 500mA\n");
+ disable_input_voltage_regulation(chip);
+ return;
+ }
if (usb_ma <= 100) {
pr_debug(
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index a3f6e58..b2709d2 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1021,6 +1021,9 @@
dev_vdbg(dwc->dev, "queing request %p to %s length %d\n",
request, ep->name, request->length);
+ WARN(!dep->direction && (request->length % ep->desc->wMaxPacketSize),
+ "trying to queue unaligned request (%d)\n", request->length);
+
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep_queue(dep, req);
spin_unlock_irqrestore(&dwc->lock, flags);
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 817bfbb..0b1a16e 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -52,6 +52,7 @@
#include "f_rmnet_sdio.c"
#include "f_rmnet_smd_sdio.c"
#include "f_rmnet.c"
+#include "f_audio_source.c"
#include "f_mass_storage.c"
#include "u_serial.c"
#include "u_sdio.c"
@@ -1475,6 +1476,68 @@
.ctrlrequest = accessory_function_ctrlrequest,
};
+static int audio_source_function_init(struct android_usb_function *f,
+ struct usb_composite_dev *cdev)
+{
+ struct audio_source_config *config;
+
+ config = kzalloc(sizeof(struct audio_source_config), GFP_KERNEL);
+ if (!config)
+ return -ENOMEM;
+ config->card = -1;
+ config->device = -1;
+ f->config = config;
+ return 0;
+}
+
+static void audio_source_function_cleanup(struct android_usb_function *f)
+{
+ kfree(f->config);
+}
+
+static int audio_source_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ struct audio_source_config *config = f->config;
+
+ return audio_source_bind_config(c, config);
+}
+
+static void audio_source_function_unbind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ struct audio_source_config *config = f->config;
+
+ config->card = -1;
+ config->device = -1;
+}
+
+static ssize_t audio_source_pcm_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct audio_source_config *config = f->config;
+
+ /* print PCM card and device numbers */
+ return sprintf(buf, "%d %d\n", config->card, config->device);
+}
+
+static DEVICE_ATTR(pcm, S_IRUGO | S_IWUSR, audio_source_pcm_show, NULL);
+
+static struct device_attribute *audio_source_function_attributes[] = {
+ &dev_attr_pcm,
+ NULL
+};
+
+static struct android_usb_function audio_source_function = {
+ .name = "audio_source",
+ .init = audio_source_function_init,
+ .cleanup = audio_source_function_cleanup,
+ .bind_config = audio_source_function_bind_config,
+ .unbind_config = audio_source_function_unbind_config,
+ .attributes = audio_source_function_attributes,
+};
+
static int android_uasp_connect_cb(bool connect)
{
/*
@@ -1543,6 +1606,7 @@
&rndis_qc_function,
&mass_storage_function,
&accessory_function,
+ &audio_source_function,
&uasp_function,
NULL
};
@@ -2175,6 +2239,11 @@
unsigned long flags;
composite_disconnect(gadget);
+ /* accessory HID support can be active while the
+ accessory function is not actually enabled,
+ so we need to inform it when we are disconnected.
+ */
+ acc_disconnect();
spin_lock_irqsave(&cdev->lock, flags);
dev->connected = 0;
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 0ace679..b3cab7a 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -76,6 +76,8 @@
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
#define ATDTW_SET_DELAY 100 /* 100msec delay */
+#define EP_PRIME_CHECK_DELAY (jiffies + msecs_to_jiffies(1000))
+#define MAX_PRIME_CHECK_RETRY 3 /*Wait for 3sec for EP prime failure */
/* ctrl register bank access */
static DEFINE_SPINLOCK(udc_lock);
@@ -498,8 +500,6 @@
hw_cwrite(CAP_ENDPTPRIME, BIT(n), BIT(n));
- while (hw_cread(CAP_ENDPTPRIME, BIT(n)))
- cpu_relax();
if (is_ctrl && dir == RX && hw_cread(CAP_ENDPTSETUPSTAT, BIT(num)))
return -EAGAIN;
@@ -1006,6 +1006,44 @@
}
/**
+ * dbg_prime_fail: prints a PRIME FAIL event
+ * @addr: endpoint address
+ * @mEp: endpoint structure
+ */
+static void dbg_prime_fail(u8 addr, const char *name,
+ const struct ci13xxx_ep *mEp)
+{
+ char msg[DBG_DATA_MSG];
+ struct ci13xxx_req *req;
+ struct list_head *ptr = NULL;
+
+ if (mEp != NULL) {
+ scnprintf(msg, sizeof(msg),
+ "PRIME fail EP%d%s QH:%08X",
+ mEp->num, mEp->dir ? "IN" : "OUT", mEp->qh.ptr->cap);
+ dbg_print(addr, name, 0, msg);
+ scnprintf(msg, sizeof(msg),
+ "cap:%08X %08X %08X\n",
+ mEp->qh.ptr->curr, mEp->qh.ptr->td.next,
+ mEp->qh.ptr->td.token);
+ dbg_print(addr, "QHEAD", 0, msg);
+
+ list_for_each(ptr, &mEp->qh.queue) {
+ req = list_entry(ptr, struct ci13xxx_req, queue);
+ scnprintf(msg, sizeof(msg),
+ "%08X:%08X:%08X\n",
+ req->dma, req->ptr->next,
+ req->ptr->token);
+ dbg_print(addr, "REQ", 0, msg);
+ scnprintf(msg, sizeof(msg), "%08X:%d\n",
+ req->ptr->page[0],
+ req->req.status);
+ dbg_print(addr, "REQPAGE", 0, msg);
+ }
+ }
+}
+
+/**
* show_events: displays the event buffer
*
* Check "device.h" for details
@@ -1468,12 +1506,14 @@
n = hw_ep_bit(mEp->num, mEp->dir);
pr_info("%s: prime:%08x stat:%08x ep#%d dir:%s"
"dTD_update_fail_count: %lu "
- "mEp->dTD_update_fail_count: %lu\n", __func__,
+ "mEp->dTD_update_fail_count: %lu"
+ "mEp->prime_fail_count: %lu\n", __func__,
hw_cread(CAP_ENDPTPRIME, ~0),
hw_cread(CAP_ENDPTSTAT, ~0),
mEp->num, mEp->dir ? "IN" : "OUT",
udc->dTD_update_fail_count,
- mEp->dTD_update_fail_count);
+ mEp->dTD_update_fail_count,
+ mEp->prime_fail_count);
pr_info("QH: cap:%08x cur:%08x next:%08x token:%08x\n",
mEp->qh.ptr->cap, mEp->qh.ptr->curr,
@@ -1661,6 +1701,57 @@
return ((ep->dir == TX) ? USB_ENDPOINT_DIR_MASK : 0) | ep->num;
}
+static void ep_prime_timer_func(unsigned long data)
+{
+ struct ci13xxx_ep *mEp = (struct ci13xxx_ep *)data;
+ struct ci13xxx_req *req;
+ struct list_head *ptr = NULL;
+ int n = hw_ep_bit(mEp->num, mEp->dir);
+ unsigned long flags;
+
+
+ spin_lock_irqsave(mEp->lock, flags);
+ if (!hw_cread(CAP_ENDPTPRIME, BIT(n)))
+ goto out;
+
+ if (list_empty(&mEp->qh.queue))
+ goto out;
+
+ req = list_entry(mEp->qh.queue.next, struct ci13xxx_req, queue);
+
+ mb();
+ if (!(TD_STATUS_ACTIVE & req->ptr->token))
+ goto out;
+
+ mEp->prime_timer_count++;
+ if (mEp->prime_timer_count == MAX_PRIME_CHECK_RETRY) {
+ mEp->prime_timer_count = 0;
+ pr_info("ep%d dir:%s QH:cap:%08x cur:%08x next:%08x tkn:%08x\n",
+ mEp->num, mEp->dir ? "IN" : "OUT",
+ mEp->qh.ptr->cap, mEp->qh.ptr->curr,
+ mEp->qh.ptr->td.next, mEp->qh.ptr->td.token);
+ list_for_each(ptr, &mEp->qh.queue) {
+ req = list_entry(ptr, struct ci13xxx_req, queue);
+ pr_info("\treq:%08xnext:%08xtkn:%08xpage0:%08xsts:%d\n",
+ req->dma, req->ptr->next,
+ req->ptr->token, req->ptr->page[0],
+ req->req.status);
+ }
+ dbg_prime_fail(0xFF, "PRIMEF", mEp);
+ mEp->prime_fail_count++;
+ } else {
+ mod_timer(&mEp->prime_timer, EP_PRIME_CHECK_DELAY);
+ }
+
+ spin_unlock_irqrestore(mEp->lock, flags);
+ return;
+
+out:
+ mEp->prime_timer_count = 0;
+ spin_unlock_irqrestore(mEp->lock, flags);
+
+}
+
/**
* _hardware_queue: configures a request at hardware level
* @gadget: gadget
@@ -1852,6 +1943,8 @@
ret = hw_ep_prime(mEp->num, mEp->dir,
mEp->type == USB_ENDPOINT_XFER_CONTROL);
+ if (!ret)
+ mod_timer(&mEp->prime_timer, EP_PRIME_CHECK_DELAY);
done:
return ret;
}
@@ -2300,6 +2393,8 @@
if (list_empty(&mEp->qh.queue))
return 0;
+ del_timer(&mEp->prime_timer);
+ mEp->prime_timer_count = 0;
list_for_each_entry_safe(mReq, mReqTemp, &mEp->qh.queue,
queue) {
dequeue:
@@ -2574,6 +2669,7 @@
struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep);
int retval = 0;
unsigned long flags;
+ unsigned mult = 0;
trace("ep = %p, desc = %p", ep, desc);
@@ -2599,13 +2695,15 @@
mEp->qh.ptr->cap = 0;
- if (mEp->type == USB_ENDPOINT_XFER_CONTROL)
+ if (mEp->type == USB_ENDPOINT_XFER_CONTROL) {
mEp->qh.ptr->cap |= QH_IOS;
- else if (mEp->type == USB_ENDPOINT_XFER_ISOC) {
+ } else if (mEp->type == USB_ENDPOINT_XFER_ISOC) {
mEp->qh.ptr->cap &= ~QH_MULT;
- mEp->qh.ptr->cap |= BIT(30);
- } else
+ mult = ((mEp->ep.maxpacket >> QH_MULT_SHIFT) + 1) & 0x03;
+ mEp->qh.ptr->cap |= (mult << ffs_nr(QH_MULT));
+ } else {
mEp->qh.ptr->cap |= QH_ZLT;
+ }
mEp->qh.ptr->cap |=
(mEp->ep.maxpacket << ffs_nr(QH_MAX_PKT)) & QH_MAX_PKT;
@@ -2647,6 +2745,8 @@
/* only internal SW should disable ctrl endpts */
+ del_timer(&mEp->prime_timer);
+ mEp->prime_timer_count = 0;
direction = mEp->dir;
do {
dbg_event(_usb_addr(mEp), "DISABLE", 0);
@@ -2959,6 +3059,8 @@
spin_lock_irqsave(mEp->lock, flags);
+ del_timer(&mEp->prime_timer);
+ mEp->prime_timer_count = 0;
dbg_event(_usb_addr(mEp), "FFLUSH", 0);
hw_ep_flush(mEp->num, mEp->dir);
@@ -3431,6 +3533,8 @@
for (i = 0; i < hw_ep_max; i++) {
struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i];
INIT_LIST_HEAD(&mEp->ep.ep_list);
+ setup_timer(&mEp->prime_timer, ep_prime_timer_func,
+ (unsigned long) mEp);
}
if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) {
diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h
index 3162e15..6b3cad8 100644
--- a/drivers/usb/gadget/ci13xxx_udc.h
+++ b/drivers/usb/gadget/ci13xxx_udc.h
@@ -67,6 +67,7 @@
#define QH_MAX_PKT (0x07FFUL << 16)
#define QH_ZLT BIT(29)
#define QH_MULT (0x0003UL << 30)
+#define QH_MULT_SHIFT 11
/* 1 */
u32 curr;
/* 2 - 8 */
@@ -107,6 +108,9 @@
struct device *device;
struct dma_pool *td_pool;
unsigned long dTD_update_fail_count;
+ unsigned long prime_fail_count;
+ int prime_timer_count;
+ struct timer_list prime_timer;
};
struct ci13xxx;
diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c
index 108caf9..42a6c43 100644
--- a/drivers/usb/gadget/f_accessory.c
+++ b/drivers/usb/gadget/f_accessory.c
@@ -33,6 +33,8 @@
#include <linux/device.h>
#include <linux/miscdevice.h>
+#include <linux/hid.h>
+#include <linux/hiddev.h>
#include <linux/usb.h>
#include <linux/usb/ch9.h>
#include <linux/usb/f_accessory.h>
@@ -40,7 +42,7 @@
#define BULK_BUFFER_SIZE 16384
#define ACC_STRING_SIZE 256
-#define PROTOCOL_VERSION 1
+#define PROTOCOL_VERSION 2
/* String IDs */
#define INTERFACE_STRING_INDEX 0
@@ -49,6 +51,20 @@
#define TX_REQ_MAX 4
#define RX_REQ_MAX 2
+struct acc_hid_dev {
+ struct list_head list;
+ struct hid_device *hid;
+ struct acc_dev *dev;
+ /* accessory defined ID */
+ int id;
+ /* HID report descriptor */
+ u8 *report_desc;
+ /* length of HID report descriptor */
+ int report_desc_len;
+ /* number of bytes of report_desc we have received so far */
+ int report_desc_offset;
+};
+
struct acc_dev {
struct usb_function function;
struct usb_composite_dev *cdev;
@@ -78,6 +94,8 @@
/* set to 1 if we have a pending start request */
int start_requested;
+ int audio_mode;
+
/* synchronize access to our device file */
atomic_t open_excl;
@@ -87,7 +105,21 @@
wait_queue_head_t write_wq;
struct usb_request *rx_req[RX_REQ_MAX];
int rx_done;
- struct delayed_work work;
+
+ /* delayed work for handling ACCESSORY_START */
+ struct delayed_work start_work;
+
+ /* worker for registering and unregistering hid devices */
+ struct work_struct hid_work;
+
+ /* list of active HID devices */
+ struct list_head hid_list;
+
+ /* list of new HID devices to register */
+ struct list_head new_hid_list;
+
+ /* list of dead HID devices to unregister */
+ struct list_head dead_hid_list;
};
static struct usb_interface_descriptor acc_interface_desc = {
@@ -296,6 +328,160 @@
}
}
+static void acc_complete_set_hid_report_desc(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ struct acc_hid_dev *hid = req->context;
+ struct acc_dev *dev = hid->dev;
+ int length = req->actual;
+
+ if (req->status != 0) {
+ pr_err("acc_complete_set_hid_report_desc, err %d\n",
+ req->status);
+ return;
+ }
+
+ memcpy(hid->report_desc + hid->report_desc_offset, req->buf, length);
+ hid->report_desc_offset += length;
+ if (hid->report_desc_offset == hid->report_desc_len) {
+ /* After we have received the entire report descriptor
+ * we schedule work to initialize the HID device
+ */
+ schedule_work(&dev->hid_work);
+ }
+}
+
+static void acc_complete_send_hid_event(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ struct acc_hid_dev *hid = req->context;
+ int length = req->actual;
+
+ if (req->status != 0) {
+ pr_err("acc_complete_send_hid_event, err %d\n", req->status);
+ return;
+ }
+
+ hid_report_raw_event(hid->hid, HID_INPUT_REPORT, req->buf, length, 1);
+}
+
+static int acc_hid_parse(struct hid_device *hid)
+{
+ struct acc_hid_dev *hdev = hid->driver_data;
+
+ hid_parse_report(hid, hdev->report_desc, hdev->report_desc_len);
+ return 0;
+}
+
+static int acc_hid_start(struct hid_device *hid)
+{
+ return 0;
+}
+
+static void acc_hid_stop(struct hid_device *hid)
+{
+}
+
+static int acc_hid_open(struct hid_device *hid)
+{
+ return 0;
+}
+
+static void acc_hid_close(struct hid_device *hid)
+{
+}
+
+static struct hid_ll_driver acc_hid_ll_driver = {
+ .parse = acc_hid_parse,
+ .start = acc_hid_start,
+ .stop = acc_hid_stop,
+ .open = acc_hid_open,
+ .close = acc_hid_close,
+};
+
+static struct acc_hid_dev *acc_hid_new(struct acc_dev *dev,
+ int id, int desc_len)
+{
+ struct acc_hid_dev *hdev;
+
+ hdev = kzalloc(sizeof(*hdev), GFP_ATOMIC);
+ if (!hdev)
+ return NULL;
+ hdev->report_desc = kzalloc(desc_len, GFP_ATOMIC);
+ if (!hdev->report_desc) {
+ kfree(hdev);
+ return NULL;
+ }
+ hdev->dev = dev;
+ hdev->id = id;
+ hdev->report_desc_len = desc_len;
+
+ return hdev;
+}
+
+static struct acc_hid_dev *acc_hid_get(struct list_head *list, int id)
+{
+ struct acc_hid_dev *hid;
+
+ list_for_each_entry(hid, list, list) {
+ if (hid->id == id)
+ return hid;
+ }
+ return NULL;
+}
+
+static int acc_register_hid(struct acc_dev *dev, int id, int desc_length)
+{
+ struct acc_hid_dev *hid;
+ unsigned long flags;
+
+ /* report descriptor length must be > 0 */
+ if (desc_length <= 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ /* replace HID if one already exists with this ID */
+ hid = acc_hid_get(&dev->hid_list, id);
+ if (!hid)
+ hid = acc_hid_get(&dev->new_hid_list, id);
+ if (hid)
+ list_move(&hid->list, &dev->dead_hid_list);
+
+ hid = acc_hid_new(dev, id, desc_length);
+ if (!hid) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -ENOMEM;
+ }
+
+ list_add(&hid->list, &dev->new_hid_list);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* schedule work to register the HID device */
+ schedule_work(&dev->hid_work);
+ return 0;
+}
+
+static int acc_unregister_hid(struct acc_dev *dev, int id)
+{
+ struct acc_hid_dev *hid;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ hid = acc_hid_get(&dev->hid_list, id);
+ if (!hid)
+ hid = acc_hid_get(&dev->new_hid_list, id);
+ if (!hid) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -EINVAL;
+ }
+
+ list_move(&hid->list, &dev->dead_hid_list);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ schedule_work(&dev->hid_work);
+ return 0;
+}
+
static int create_bulk_endpoints(struct acc_dev *dev,
struct usb_endpoint_descriptor *in_desc,
struct usb_endpoint_descriptor *out_desc)
@@ -353,7 +539,7 @@
return 0;
fail:
- printk(KERN_ERR "acc_bind() could not allocate requests\n");
+ pr_err("acc_bind() could not allocate requests\n");
while ((req = req_get(dev, &dev->tx_idle)))
acc_request_free(req, dev->ep_in);
for (i = 0; i < RX_REQ_MAX; i++)
@@ -510,6 +696,8 @@
break;
case ACCESSORY_IS_START_REQUESTED:
return dev->start_requested;
+ case ACCESSORY_GET_AUDIO_MODE:
+ return dev->audio_mode;
}
if (!src)
return -EINVAL;
@@ -540,7 +728,7 @@
return 0;
}
-/* file operations for /dev/acc_usb */
+/* file operations for /dev/usb_accessory */
static const struct file_operations acc_fops = {
.owner = THIS_MODULE,
.read = acc_read,
@@ -550,23 +738,47 @@
.release = acc_release,
};
+static int acc_hid_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ int ret;
+
+ ret = hid_parse(hdev);
+ if (ret)
+ return ret;
+ return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+}
+
static struct miscdevice acc_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "usb_accessory",
.fops = &acc_fops,
};
+static const struct hid_device_id acc_hid_table[] = {
+ { HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) },
+ { }
+};
+
+static struct hid_driver acc_hid_driver = {
+ .name = "USB accessory",
+ .id_table = acc_hid_table,
+ .probe = acc_hid_probe,
+};
static int acc_ctrlrequest(struct usb_composite_dev *cdev,
const struct usb_ctrlrequest *ctrl)
{
struct acc_dev *dev = _acc_dev;
int value = -EOPNOTSUPP;
+ struct acc_hid_dev *hid;
+ int offset;
u8 b_requestType = ctrl->bRequestType;
u8 b_request = ctrl->bRequest;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
+ unsigned long flags;
/*
printk(KERN_INFO "acc_ctrlrequest "
@@ -579,20 +791,56 @@
if (b_request == ACCESSORY_START) {
dev->start_requested = 1;
schedule_delayed_work(
- &dev->work, msecs_to_jiffies(10));
+ &dev->start_work, msecs_to_jiffies(10));
value = 0;
} else if (b_request == ACCESSORY_SEND_STRING) {
dev->string_index = w_index;
cdev->gadget->ep0->driver_data = dev;
cdev->req->complete = acc_complete_set_string;
value = w_length;
+ } else if (b_request == ACCESSORY_SET_AUDIO_MODE &&
+ w_index == 0 && w_length == 0) {
+ dev->audio_mode = w_value;
+ value = 0;
+ } else if (b_request == ACCESSORY_REGISTER_HID) {
+ value = acc_register_hid(dev, w_value, w_index);
+ } else if (b_request == ACCESSORY_UNREGISTER_HID) {
+ value = acc_unregister_hid(dev, w_value);
+ } else if (b_request == ACCESSORY_SET_HID_REPORT_DESC) {
+ spin_lock_irqsave(&dev->lock, flags);
+ hid = acc_hid_get(&dev->new_hid_list, w_value);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ if (!hid) {
+ value = -EINVAL;
+ goto err;
+ }
+ offset = w_index;
+ if (offset != hid->report_desc_offset
+ || offset + w_length > hid->report_desc_len) {
+ value = -EINVAL;
+ goto err;
+ }
+ cdev->req->context = hid;
+ cdev->req->complete = acc_complete_set_hid_report_desc;
+ value = w_length;
+ } else if (b_request == ACCESSORY_SEND_HID_EVENT) {
+ spin_lock_irqsave(&dev->lock, flags);
+ hid = acc_hid_get(&dev->hid_list, w_value);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ if (!hid) {
+ value = -EINVAL;
+ goto err;
+ }
+ cdev->req->context = hid;
+ cdev->req->complete = acc_complete_send_hid_event;
+ value = w_length;
}
} else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) {
if (b_request == ACCESSORY_GET_PROTOCOL) {
*((u16 *)cdev->req->buf) = PROTOCOL_VERSION;
value = sizeof(u16);
- /* clear any string left over from a previous session */
+ /* clear strings left over from a previous session */
memset(dev->manufacturer, 0, sizeof(dev->manufacturer));
memset(dev->model, 0, sizeof(dev->model));
memset(dev->description, 0, sizeof(dev->description));
@@ -600,6 +848,7 @@
memset(dev->uri, 0, sizeof(dev->uri));
memset(dev->serial, 0, sizeof(dev->serial));
dev->start_requested = 0;
+ dev->audio_mode = 0;
}
}
@@ -612,6 +861,7 @@
__func__);
}
+err:
if (value == -EOPNOTSUPP)
VDBG(cdev,
"unknown class-specific control req "
@@ -631,6 +881,10 @@
DBG(cdev, "acc_function_bind dev: %p\n", dev);
+ ret = hid_register_driver(&acc_hid_driver);
+ if (ret)
+ return ret;
+
dev->start_requested = 0;
/* allocate interface ID(s) */
@@ -660,6 +914,36 @@
}
static void
+kill_all_hid_devices(struct acc_dev *dev)
+{
+ struct acc_hid_dev *hid;
+ struct list_head *entry, *temp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ list_for_each_safe(entry, temp, &dev->hid_list) {
+ hid = list_entry(entry, struct acc_hid_dev, list);
+ list_del(&hid->list);
+ list_add(&hid->list, &dev->dead_hid_list);
+ }
+ list_for_each_safe(entry, temp, &dev->new_hid_list) {
+ hid = list_entry(entry, struct acc_hid_dev, list);
+ list_del(&hid->list);
+ list_add(&hid->list, &dev->dead_hid_list);
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ schedule_work(&dev->hid_work);
+}
+
+static void
+acc_hid_unbind(struct acc_dev *dev)
+{
+ hid_unregister_driver(&acc_hid_driver);
+ kill_all_hid_devices(dev);
+}
+
+static void
acc_function_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct acc_dev *dev = func_to_dev(f);
@@ -670,14 +954,104 @@
acc_request_free(req, dev->ep_in);
for (i = 0; i < RX_REQ_MAX; i++)
acc_request_free(dev->rx_req[i], dev->ep_out);
+
+ acc_hid_unbind(dev);
}
-static void acc_work(struct work_struct *data)
+static void acc_start_work(struct work_struct *data)
{
char *envp[2] = { "ACCESSORY=START", NULL };
kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp);
}
+static int acc_hid_init(struct acc_hid_dev *hdev)
+{
+ struct hid_device *hid;
+ int ret;
+
+ hid = hid_allocate_device();
+ if (IS_ERR(hid))
+ return PTR_ERR(hid);
+
+ hid->ll_driver = &acc_hid_ll_driver;
+ hid->dev.parent = acc_device.this_device;
+
+ hid->bus = BUS_USB;
+ hid->vendor = HID_ANY_ID;
+ hid->product = HID_ANY_ID;
+ hid->driver_data = hdev;
+ ret = hid_add_device(hid);
+ if (ret) {
+ pr_err("can't add hid device: %d\n", ret);
+ hid_destroy_device(hid);
+ return ret;
+ }
+
+ hdev->hid = hid;
+ return 0;
+}
+
+static void acc_hid_delete(struct acc_hid_dev *hid)
+{
+ kfree(hid->report_desc);
+ kfree(hid);
+}
+
+static void acc_hid_work(struct work_struct *data)
+{
+ struct acc_dev *dev = _acc_dev;
+ struct list_head *entry, *temp;
+ struct acc_hid_dev *hid;
+ struct list_head new_list, dead_list;
+ unsigned long flags;
+
+ INIT_LIST_HEAD(&new_list);
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ /* copy hids that are ready for initialization to new_list */
+ list_for_each_safe(entry, temp, &dev->new_hid_list) {
+ hid = list_entry(entry, struct acc_hid_dev, list);
+ if (hid->report_desc_offset == hid->report_desc_len)
+ list_move(&hid->list, &new_list);
+ }
+
+ if (list_empty(&dev->dead_hid_list)) {
+ INIT_LIST_HEAD(&dead_list);
+ } else {
+ /* move all of dev->dead_hid_list to dead_list */
+ dead_list.prev = dev->dead_hid_list.prev;
+ dead_list.next = dev->dead_hid_list.next;
+ dead_list.next->prev = &dead_list;
+ dead_list.prev->next = &dead_list;
+ INIT_LIST_HEAD(&dev->dead_hid_list);
+ }
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* register new HID devices */
+ list_for_each_safe(entry, temp, &new_list) {
+ hid = list_entry(entry, struct acc_hid_dev, list);
+ if (acc_hid_init(hid)) {
+ pr_err("can't add HID device %p\n", hid);
+ acc_hid_delete(hid);
+ } else {
+ spin_lock_irqsave(&dev->lock, flags);
+ list_move(&hid->list, &dev->hid_list);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ }
+ }
+
+ /* remove dead HID devices */
+ list_for_each_safe(entry, temp, &dead_list) {
+ hid = list_entry(entry, struct acc_hid_dev, list);
+ list_del(&hid->list);
+ if (hid->hid)
+ hid_destroy_device(hid->hid);
+ acc_hid_delete(hid);
+ }
+}
+
static int acc_function_set_alt(struct usb_function *f,
unsigned intf, unsigned alt)
{
@@ -783,7 +1157,11 @@
init_waitqueue_head(&dev->write_wq);
atomic_set(&dev->open_excl, 0);
INIT_LIST_HEAD(&dev->tx_idle);
- INIT_DELAYED_WORK(&dev->work, acc_work);
+ INIT_LIST_HEAD(&dev->hid_list);
+ INIT_LIST_HEAD(&dev->new_hid_list);
+ INIT_LIST_HEAD(&dev->dead_hid_list);
+ INIT_DELAYED_WORK(&dev->start_work, acc_start_work);
+ INIT_WORK(&dev->hid_work, acc_hid_work);
/* _acc_dev must be set before calling usb_gadget_register_driver */
_acc_dev = dev;
@@ -796,10 +1174,16 @@
err:
kfree(dev);
- printk(KERN_ERR "USB accessory gadget driver failed to initialize\n");
+ pr_err("USB accessory gadget driver failed to initialize\n");
return ret;
}
+static void acc_disconnect(void)
+{
+ /* unregister all HID devices if USB is disconnected */
+ kill_all_hid_devices(_acc_dev);
+}
+
static void acc_cleanup(void)
{
misc_deregister(&acc_device);
diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c
new file mode 100644
index 0000000..aae941e
--- /dev/null
+++ b/drivers/usb/gadget/f_audio_source.c
@@ -0,0 +1,832 @@
+/*
+ * Gadget Function Driver for USB audio source device
+ *
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/usb/audio.h>
+#include <linux/wait.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+
+#define SAMPLE_RATE 44100
+#define FRAMES_PER_MSEC (SAMPLE_RATE / 1000)
+
+#define IN_EP_MAX_PACKET_SIZE 256
+
+/* Number of requests to allocate */
+#define IN_EP_REQ_COUNT 4
+
+#define AUDIO_AC_INTERFACE 0
+#define AUDIO_AS_INTERFACE 1
+#define AUDIO_NUM_INTERFACES 2
+
+/* B.3.1 Standard AC Interface Descriptor */
+static struct usb_interface_descriptor audio_source_ac_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+};
+
+
+#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(AUDIO_NUM_INTERFACES)
+/* 1 input terminal, 1 output terminal and 1 feature unit */
+#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \
+ + UAC_DT_INPUT_TERMINAL_SIZE + UAC_DT_OUTPUT_TERMINAL_SIZE \
+ + UAC_DT_FEATURE_UNIT_SIZE(0))
+/* B.3.2 Class-Specific AC Interface Descriptor */
+static struct uac1_ac_header_descriptor_2 audio_source_ac_header_desc = {
+ .bLength = UAC_DT_AC_HEADER_LENGTH,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_HEADER,
+ .bcdADC = __constant_cpu_to_le16(0x0100),
+ .wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH),
+ .bInCollection = AUDIO_NUM_INTERFACES,
+ .baInterfaceNr = {
+ [0] = AUDIO_AC_INTERFACE,
+ [1] = AUDIO_AS_INTERFACE,
+ }
+};
+
+#define INPUT_TERMINAL_ID 1
+static struct uac_input_terminal_descriptor input_terminal_desc = {
+ .bLength = UAC_DT_INPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_INPUT_TERMINAL,
+ .bTerminalID = INPUT_TERMINAL_ID,
+ .wTerminalType = UAC_INPUT_TERMINAL_MICROPHONE,
+ .bAssocTerminal = 0,
+ .wChannelConfig = 0x3,
+};
+
+DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0);
+
+#define FEATURE_UNIT_ID 2
+static struct uac_feature_unit_descriptor_0 feature_unit_desc = {
+ .bLength = UAC_DT_FEATURE_UNIT_SIZE(0),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_FEATURE_UNIT,
+ .bUnitID = FEATURE_UNIT_ID,
+ .bSourceID = INPUT_TERMINAL_ID,
+ .bControlSize = 2,
+};
+
+#define OUTPUT_TERMINAL_ID 3
+static struct uac1_output_terminal_descriptor output_terminal_desc = {
+ .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
+ .bTerminalID = OUTPUT_TERMINAL_ID,
+ .wTerminalType = UAC_TERMINAL_STREAMING,
+ .bAssocTerminal = FEATURE_UNIT_ID,
+ .bSourceID = FEATURE_UNIT_ID,
+};
+
+/* B.4.1 Standard AS Interface Descriptor */
+static struct usb_interface_descriptor as_interface_alt_0_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+static struct usb_interface_descriptor as_interface_alt_1_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bAlternateSetting = 1,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+/* B.4.2 Class-Specific AS Interface Descriptor */
+static struct uac1_as_header_descriptor as_header_desc = {
+ .bLength = UAC_DT_AS_HEADER_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_AS_GENERAL,
+ .bTerminalLink = INPUT_TERMINAL_ID,
+ .bDelay = 1,
+ .wFormatTag = UAC_FORMAT_TYPE_I_PCM,
+};
+
+static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = {
+ .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_FORMAT_TYPE,
+ .bFormatType = UAC_FORMAT_TYPE_I,
+ .bSubframeSize = 2,
+ .bBitResolution = 16,
+ .bSamFreqType = 1,
+};
+
+/* Standard ISO IN Endpoint Descriptor for highspeed */
+static struct usb_endpoint_descriptor hs_as_in_ep_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_SYNC_SYNC
+ | USB_ENDPOINT_XFER_ISOC,
+ .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE),
+ .bInterval = 4, /* poll 1 per millisecond */
+};
+
+/* Standard ISO IN Endpoint Descriptor for highspeed */
+static struct usb_endpoint_descriptor fs_as_in_ep_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_SYNC_SYNC
+ | USB_ENDPOINT_XFER_ISOC,
+ .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE),
+ .bInterval = 1, /* poll 1 per millisecond */
+};
+
+/* Class-specific AS ISO OUT Endpoint Descriptor */
+static struct uac_iso_endpoint_descriptor as_iso_in_desc = {
+ .bLength = UAC_ISO_ENDPOINT_DESC_SIZE,
+ .bDescriptorType = USB_DT_CS_ENDPOINT,
+ .bDescriptorSubtype = UAC_EP_GENERAL,
+ .bmAttributes = 1,
+ .bLockDelayUnits = 1,
+ .wLockDelay = __constant_cpu_to_le16(1),
+};
+
+static struct usb_descriptor_header *hs_audio_desc[] = {
+ (struct usb_descriptor_header *)&audio_source_ac_interface_desc,
+ (struct usb_descriptor_header *)&audio_source_ac_header_desc,
+
+ (struct usb_descriptor_header *)&input_terminal_desc,
+ (struct usb_descriptor_header *)&output_terminal_desc,
+ (struct usb_descriptor_header *)&feature_unit_desc,
+
+ (struct usb_descriptor_header *)&as_interface_alt_0_desc,
+ (struct usb_descriptor_header *)&as_interface_alt_1_desc,
+ (struct usb_descriptor_header *)&as_header_desc,
+
+ (struct usb_descriptor_header *)&as_type_i_desc,
+
+ (struct usb_descriptor_header *)&hs_as_in_ep_desc,
+ (struct usb_descriptor_header *)&as_iso_in_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *fs_audio_desc[] = {
+ (struct usb_descriptor_header *)&audio_source_ac_interface_desc,
+ (struct usb_descriptor_header *)&audio_source_ac_header_desc,
+
+ (struct usb_descriptor_header *)&input_terminal_desc,
+ (struct usb_descriptor_header *)&output_terminal_desc,
+ (struct usb_descriptor_header *)&feature_unit_desc,
+
+ (struct usb_descriptor_header *)&as_interface_alt_0_desc,
+ (struct usb_descriptor_header *)&as_interface_alt_1_desc,
+ (struct usb_descriptor_header *)&as_header_desc,
+
+ (struct usb_descriptor_header *)&as_type_i_desc,
+
+ (struct usb_descriptor_header *)&fs_as_in_ep_desc,
+ (struct usb_descriptor_header *)&as_iso_in_desc,
+ NULL,
+};
+
+static struct snd_pcm_hardware audio_hw_info = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = SAMPLE_RATE,
+ .rate_max = SAMPLE_RATE,
+
+ .buffer_bytes_max = 1024 * 1024,
+ .period_bytes_min = 64,
+ .period_bytes_max = 512 * 1024,
+ .periods_min = 2,
+ .periods_max = 1024,
+};
+
+/*-------------------------------------------------------------------------*/
+
+struct audio_source_config {
+ int card;
+ int device;
+};
+
+struct audio_dev {
+ struct usb_function func;
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ struct snd_pcm_substream *substream;
+
+ struct list_head idle_reqs;
+ struct usb_ep *in_ep;
+
+ spinlock_t lock;
+
+ /* beginning, end and current position in our buffer */
+ void *buffer_start;
+ void *buffer_end;
+ void *buffer_pos;
+
+ /* byte size of a "period" */
+ unsigned int period;
+ /* bytes sent since last call to snd_pcm_period_elapsed */
+ unsigned int period_offset;
+ /* time we started playing */
+ ktime_t start_time;
+ /* number of frames sent since start_time */
+ s64 frames_sent;
+};
+
+static inline struct audio_dev *func_to_audio_source(struct usb_function *f)
+{
+ return container_of(f, struct audio_dev, func);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size)
+{
+ struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!req)
+ return NULL;
+
+ req->buf = kmalloc(buffer_size, GFP_KERNEL);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ return NULL;
+ }
+ req->length = buffer_size;
+ return req;
+}
+
+static void audio_request_free(struct usb_request *req, struct usb_ep *ep)
+{
+ if (req) {
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+ }
+}
+
+static void audio_req_put(struct audio_dev *audio, struct usb_request *req)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->lock, flags);
+ list_add_tail(&req->list, &audio->idle_reqs);
+ spin_unlock_irqrestore(&audio->lock, flags);
+}
+
+static struct usb_request *audio_req_get(struct audio_dev *audio)
+{
+ unsigned long flags;
+ struct usb_request *req;
+
+ spin_lock_irqsave(&audio->lock, flags);
+ if (list_empty(&audio->idle_reqs)) {
+ req = 0;
+ } else {
+ req = list_first_entry(&audio->idle_reqs, struct usb_request,
+ list);
+ list_del(&req->list);
+ }
+ spin_unlock_irqrestore(&audio->lock, flags);
+ return req;
+}
+
+/* send the appropriate number of packets to match our bitrate */
+static void audio_send(struct audio_dev *audio)
+{
+ struct snd_pcm_runtime *runtime;
+ struct usb_request *req;
+ int length, length1, length2, ret;
+ s64 msecs;
+ s64 frames;
+ ktime_t now;
+
+ /* audio->substream will be null if we have been closed */
+ if (!audio->substream)
+ return;
+ /* audio->buffer_pos will be null if we have been stopped */
+ if (!audio->buffer_pos)
+ return;
+
+ runtime = audio->substream->runtime;
+
+ /* compute number of frames to send */
+ now = ktime_get();
+ msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time);
+ do_div(msecs, 1000000);
+ frames = msecs * SAMPLE_RATE;
+ do_div(frames, 1000);
+
+ /* Readjust our frames_sent if we fall too far behind.
+ * If we get too far behind it is better to drop some frames than
+ * to keep sending data too fast in an attempt to catch up.
+ */
+ if (frames - audio->frames_sent > 10 * FRAMES_PER_MSEC)
+ audio->frames_sent = frames - FRAMES_PER_MSEC;
+
+ frames -= audio->frames_sent;
+
+ /* We need to send something to keep the pipeline going */
+ if (frames <= 0)
+ frames = FRAMES_PER_MSEC;
+
+ while (frames > 0) {
+ req = audio_req_get(audio);
+ if (!req)
+ break;
+
+ length = frames_to_bytes(runtime, frames);
+ if (length > IN_EP_MAX_PACKET_SIZE)
+ length = IN_EP_MAX_PACKET_SIZE;
+
+ if (audio->buffer_pos + length > audio->buffer_end)
+ length1 = audio->buffer_end - audio->buffer_pos;
+ else
+ length1 = length;
+ memcpy(req->buf, audio->buffer_pos, length1);
+ if (length1 < length) {
+ /* Wrap around and copy remaining length
+ * at beginning of buffer.
+ */
+ length2 = length - length1;
+ memcpy(req->buf + length1, audio->buffer_start,
+ length2);
+ audio->buffer_pos = audio->buffer_start + length2;
+ } else {
+ audio->buffer_pos += length1;
+ if (audio->buffer_pos >= audio->buffer_end)
+ audio->buffer_pos = audio->buffer_start;
+ }
+
+ req->length = length;
+ ret = usb_ep_queue(audio->in_ep, req, GFP_ATOMIC);
+ if (ret < 0) {
+ pr_err("usb_ep_queue failed ret: %d\n", ret);
+ audio_req_put(audio, req);
+ break;
+ }
+
+ frames -= bytes_to_frames(runtime, length);
+ audio->frames_sent += bytes_to_frames(runtime, length);
+ }
+}
+
+static void audio_control_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ /* nothing to do here */
+}
+
+static void audio_data_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct audio_dev *audio = req->context;
+
+ pr_debug("audio_data_complete req->status %d req->actual %d\n",
+ req->status, req->actual);
+
+ audio_req_put(audio, req);
+
+ if (!audio->buffer_start || req->status)
+ return;
+
+ audio->period_offset += req->actual;
+ if (audio->period_offset >= audio->period) {
+ snd_pcm_period_elapsed(audio->substream);
+ audio->period_offset = 0;
+ }
+ audio_send(audio);
+}
+
+static int audio_source_set_endpoint_req(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ int value = -EOPNOTSUPP;
+ u16 ep = le16_to_cpu(ctrl->wIndex);
+ u16 len = le16_to_cpu(ctrl->wLength);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+
+ pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+ ctrl->bRequest, w_value, len, ep);
+
+ switch (ctrl->bRequest) {
+ case UAC_SET_CUR:
+ case UAC_SET_MIN:
+ case UAC_SET_MAX:
+ case UAC_SET_RES:
+ value = len;
+ break;
+ default:
+ break;
+ }
+
+ return value;
+}
+
+static int audio_source_get_endpoint_req(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_composite_dev *cdev = f->config->cdev;
+ int value = -EOPNOTSUPP;
+ u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+ u16 len = le16_to_cpu(ctrl->wLength);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u8 *buf = cdev->req->buf;
+
+ pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+ ctrl->bRequest, w_value, len, ep);
+
+ if (w_value == UAC_EP_CS_ATTR_SAMPLE_RATE << 8) {
+ switch (ctrl->bRequest) {
+ case UAC_GET_CUR:
+ case UAC_GET_MIN:
+ case UAC_GET_MAX:
+ case UAC_GET_RES:
+ /* return our sample rate */
+ buf[0] = (u8)SAMPLE_RATE;
+ buf[1] = (u8)(SAMPLE_RATE >> 8);
+ buf[2] = (u8)(SAMPLE_RATE >> 16);
+ value = 3;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return value;
+}
+
+static int
+audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = cdev->req;
+ int value = -EOPNOTSUPP;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+ /* composite driver infrastructure handles everything; interface
+ * activation uses set_alt().
+ */
+ switch (ctrl->bRequestType) {
+ case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
+ value = audio_source_set_endpoint_req(f, ctrl);
+ break;
+
+ case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
+ value = audio_source_get_endpoint_req(f, ctrl);
+ break;
+ }
+
+ /* respond with data transfer or status phase? */
+ if (value >= 0) {
+ pr_debug("audio req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ req->zero = 0;
+ req->length = value;
+ req->complete = audio_control_complete;
+ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0)
+ pr_err("audio response on err %d\n", value);
+ }
+
+ /* device either stalls (value < 0) or reports success */
+ return value;
+}
+
+static int audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct audio_dev *audio = func_to_audio_source(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ int ret;
+
+ pr_debug("audio_set_alt intf %d, alt %d\n", intf, alt);
+
+ ret = config_ep_by_speed(cdev->gadget, f, audio->in_ep);
+ if (ret) {
+ audio->in_ep->desc = NULL;
+ ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+ audio->in_ep->name, ret);
+ return ret;
+ }
+ ret = usb_ep_enable(audio->in_ep);
+ if (ret) {
+ ERROR(cdev, "failed to enable ep %s, result %d\n",
+ audio->in_ep->name, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static void audio_disable(struct usb_function *f)
+{
+ struct audio_dev *audio = func_to_audio_source(f);
+
+ pr_debug("audio_disable\n");
+ usb_ep_disable(audio->in_ep);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void audio_build_desc(struct audio_dev *audio)
+{
+ u8 *sam_freq;
+ int rate;
+
+ /* Set channel numbers */
+ input_terminal_desc.bNrChannels = 2;
+ as_type_i_desc.bNrChannels = 2;
+
+ /* Set sample rates */
+ rate = SAMPLE_RATE;
+ sam_freq = as_type_i_desc.tSamFreq[0];
+ memcpy(sam_freq, &rate, 3);
+}
+
+/* audio function driver setup/binding */
+static int
+audio_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct audio_dev *audio = func_to_audio_source(f);
+ int status;
+ struct usb_ep *ep;
+ struct usb_request *req;
+ int i;
+
+ audio_build_desc(audio);
+
+ /* allocate instance-specific interface IDs, and patch descriptors */
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ audio_source_ac_interface_desc.bInterfaceNumber = status;
+
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ as_interface_alt_0_desc.bInterfaceNumber = status;
+ as_interface_alt_1_desc.bInterfaceNumber = status;
+
+ status = -ENODEV;
+
+ /* allocate our endpoint */
+ ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_ep_desc);
+ if (!ep)
+ goto fail;
+ audio->in_ep = ep;
+ ep->driver_data = audio; /* claim */
+
+ if (gadget_is_dualspeed(c->cdev->gadget))
+ hs_as_in_ep_desc.bEndpointAddress =
+ fs_as_in_ep_desc.bEndpointAddress;
+
+ for (i = 0, status = 0; i < IN_EP_REQ_COUNT && status == 0; i++) {
+ req = audio_request_new(ep, IN_EP_MAX_PACKET_SIZE);
+ if (req) {
+ req->context = audio;
+ req->complete = audio_data_complete;
+ audio_req_put(audio, req);
+ } else
+ status = -ENOMEM;
+ }
+
+fail:
+ return status;
+}
+
+static void
+audio_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct audio_dev *audio = func_to_audio_source(f);
+ struct usb_request *req;
+
+ while ((req = audio_req_get(audio)))
+ audio_request_free(req, audio->in_ep);
+
+ snd_card_free_when_closed(audio->card);
+ audio->card = NULL;
+ audio->pcm = NULL;
+ audio->substream = NULL;
+ audio->in_ep = NULL;
+}
+
+static void audio_pcm_playback_start(struct audio_dev *audio)
+{
+ audio->start_time = ktime_get();
+ audio->frames_sent = 0;
+ audio_send(audio);
+}
+
+static void audio_pcm_playback_stop(struct audio_dev *audio)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->lock, flags);
+ audio->buffer_start = 0;
+ audio->buffer_end = 0;
+ audio->buffer_pos = 0;
+ spin_unlock_irqrestore(&audio->lock, flags);
+}
+
+static int audio_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_dev *audio = substream->private_data;
+
+ runtime->private_data = audio;
+ runtime->hw = audio_hw_info;
+ snd_pcm_limit_hw_rates(runtime);
+ runtime->hw.channels_max = 2;
+
+ audio->substream = substream;
+ return 0;
+}
+
+static int audio_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct audio_dev *audio = substream->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->lock, flags);
+ audio->substream = NULL;
+ spin_unlock_irqrestore(&audio->lock, flags);
+
+ return 0;
+}
+
+static int audio_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int channels = params_channels(params);
+ unsigned int rate = params_rate(params);
+
+ if (rate != SAMPLE_RATE)
+ return -EINVAL;
+ if (channels != 2)
+ return -EINVAL;
+
+ return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(params));
+}
+
+static int audio_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int audio_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_dev *audio = runtime->private_data;
+
+ audio->period = snd_pcm_lib_period_bytes(substream);
+ audio->period_offset = 0;
+ audio->buffer_start = runtime->dma_area;
+ audio->buffer_end = audio->buffer_start
+ + snd_pcm_lib_buffer_bytes(substream);
+ audio->buffer_pos = audio->buffer_start;
+
+ return 0;
+}
+
+static snd_pcm_uframes_t audio_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_dev *audio = runtime->private_data;
+ ssize_t bytes = audio->buffer_pos - audio->buffer_start;
+
+ /* return offset of next frame to fill in our buffer */
+ return bytes_to_frames(runtime, bytes);
+}
+
+static int audio_pcm_playback_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct audio_dev *audio = substream->runtime->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ audio_pcm_playback_start(audio);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ audio_pcm_playback_stop(audio);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static struct audio_dev _audio_dev = {
+ .func = {
+ .name = "audio_source",
+ .bind = audio_bind,
+ .unbind = audio_unbind,
+ .set_alt = audio_set_alt,
+ .setup = audio_setup,
+ .disable = audio_disable,
+ .descriptors = fs_audio_desc,
+ .hs_descriptors = hs_audio_desc,
+ },
+ .lock = __SPIN_LOCK_UNLOCKED(_audio_dev.lock),
+ .idle_reqs = LIST_HEAD_INIT(_audio_dev.idle_reqs),
+};
+
+static struct snd_pcm_ops audio_playback_ops = {
+ .open = audio_pcm_open,
+ .close = audio_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = audio_pcm_hw_params,
+ .hw_free = audio_pcm_hw_free,
+ .prepare = audio_pcm_prepare,
+ .trigger = audio_pcm_playback_trigger,
+ .pointer = audio_pcm_pointer,
+};
+
+int audio_source_bind_config(struct usb_configuration *c,
+ struct audio_source_config *config)
+{
+ struct audio_dev *audio;
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ int err;
+
+ config->card = -1;
+ config->device = -1;
+
+ audio = &_audio_dev;
+
+ err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, 0, &card);
+ if (err)
+ return err;
+
+ snd_card_set_dev(card, &c->cdev->gadget->dev);
+
+ err = snd_pcm_new(card, "USB audio source", 0, 1, 0, &pcm);
+ if (err)
+ goto pcm_fail;
+ pcm->private_data = audio;
+ pcm->info_flags = 0;
+ audio->pcm = pcm;
+
+ strlcpy(pcm->name, "USB gadget audio", sizeof(pcm->name));
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &audio_playback_ops);
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ NULL, 0, 64 * 1024);
+
+ strlcpy(card->driver, "audio_source", sizeof(card->driver));
+ strlcpy(card->shortname, card->driver, sizeof(card->shortname));
+ strlcpy(card->longname, "USB accessory audio source",
+ sizeof(card->longname));
+
+ err = snd_card_register(card);
+ if (err)
+ goto register_fail;
+
+ err = usb_add_function(c, &audio->func);
+ if (err)
+ goto add_fail;
+
+ config->card = pcm->card->number;
+ config->device = pcm->device;
+ audio->card = card;
+ return 0;
+
+add_fail:
+register_fail:
+pcm_fail:
+ snd_card_free(audio->card);
+ return err;
+}
diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c
index 96790c5..ccbc330 100644
--- a/drivers/usb/gadget/f_mtp.c
+++ b/drivers/usb/gadget/f_mtp.c
@@ -463,6 +463,10 @@
if (count > MTP_BULK_BUFFER_SIZE)
return -EINVAL;
+ if (!IS_ALIGNED(count, dev->ep_out->maxpacket))
+ DBG(cdev, "%s - count(%d) not multiple of mtu(%d)\n", __func__,
+ count, dev->ep_out->maxpacket);
+
/* we will block until we're online */
DBG(cdev, "mtp_read: waiting for online state\n");
ret = wait_event_interruptible(dev->read_wq,
@@ -484,7 +488,7 @@
requeue_req:
/* queue a request */
req = dev->rx_req[0];
- req->length = count;
+ req->length = MTP_BULK_BUFFER_SIZE;
dev->rx_done = 0;
ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL);
if (ret < 0) {
@@ -751,6 +755,9 @@
count = dev->xfer_file_length;
DBG(cdev, "receive_file_work(%lld)\n", count);
+ if (!IS_ALIGNED(count, dev->ep_out->maxpacket))
+ DBG(cdev, "%s- count(%lld) not multiple of mtu(%d)\n", __func__,
+ count, dev->ep_out->maxpacket);
while (count > 0 || write_req) {
if (count > 0) {
@@ -758,8 +765,9 @@
read_req = dev->rx_req[cur_buf];
cur_buf = (cur_buf + 1) % RX_REQ_MAX;
- read_req->length = (count > MTP_BULK_BUFFER_SIZE
- ? MTP_BULK_BUFFER_SIZE : count);
+ /* some h/w expects size to be aligned to ep's MTU */
+ read_req->length = MTP_BULK_BUFFER_SIZE;
+
dev->rx_done = 0;
ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL);
if (ret < 0) {
@@ -795,6 +803,10 @@
usb_ep_dequeue(dev->ep_out, read_req);
break;
}
+ /* Check if we aligned the size due to MTU constraint */
+ if (count < read_req->length)
+ read_req->actual = (read_req->actual > count ?
+ count : read_req->actual);
/* if xfer_file_length is 0xFFFFFFFF, then we read until
* we get a zero length packet
*/
diff --git a/drivers/usb/gadget/f_uac1.c b/drivers/usb/gadget/f_uac1.c
index b385592..8c74381 100644
--- a/drivers/usb/gadget/f_uac1.c
+++ b/drivers/usb/gadget/f_uac1.c
@@ -62,8 +62,6 @@
static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value);
static int generic_get_cmd(struct usb_audio_control *con, u8 cmd);
-DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
-DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
#define SPEAKER_INPUT_TERMINAL_ID 3
#define SPEAKER_OUTPUT_TERMINAL_ID 4
diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c
index 3f4e428..b408bfd 100644
--- a/drivers/usb/gadget/msm72k_udc.c
+++ b/drivers/usb/gadget/msm72k_udc.c
@@ -516,6 +516,15 @@
{
struct usb_info *ui = ept->ui;
unsigned cfg = CONFIG_MAX_PKT(ept->ep.maxpacket) | CONFIG_ZLT;
+ const struct usb_endpoint_descriptor *desc = ept->ep.desc;
+ unsigned mult = 0;
+
+ if (desc && ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_ISOC)) {
+ cfg &= ~(CONFIG_MULT);
+ mult = ((ept->ep.maxpacket >> CONFIG_MULT_SHIFT) + 1) & 0x03;
+ cfg |= (mult << (ffs(CONFIG_MULT) - 1));
+ }
/* ep0 out needs interrupt-on-setup */
if (ept->bit == 0)
diff --git a/drivers/usb/misc/ks_bridge.c b/drivers/usb/misc/ks_bridge.c
index 656e379..61e6891 100644
--- a/drivers/usb/misc/ks_bridge.c
+++ b/drivers/usb/misc/ks_bridge.c
@@ -48,6 +48,7 @@
#define BOOT_BRIDGE_INDEX 0
#define EFS_BRIDGE_INDEX 1
#define MAX_DATA_PKT_SIZE 16384
+#define PENDING_URB_TIMEOUT 10
struct ks_bridge {
char *name;
@@ -58,7 +59,10 @@
struct list_head to_mdm_list;
struct list_head to_ks_list;
wait_queue_head_t ks_wait_q;
+ wait_queue_head_t pending_urb_wait;
struct miscdevice *fs_dev;
+ atomic_t tx_pending_cnt;
+ atomic_t rx_pending_cnt;
/* usb specific */
struct usb_device *udev;
@@ -207,13 +211,16 @@
dbg_log_event(ksb, "C TX_URB", urb->status, 0);
pr_debug("status:%d", urb->status);
- if (ksb->ifc)
+ if (test_bit(USB_DEV_CONNECTED, &ksb->flags))
usb_autopm_put_interface_async(ksb->ifc);
if (urb->status < 0)
pr_err_ratelimited("urb failed with err:%d", urb->status);
ksb_free_data_pkt(pkt);
+
+ atomic_dec(&ksb->tx_pending_cnt);
+ wake_up(&ksb->pending_urb_wait);
}
static void ksb_tomdm_work(struct work_struct *w)
@@ -252,6 +259,7 @@
dbg_log_event(ksb, "S TX_URB", pkt->len, 0);
+ atomic_inc(&ksb->tx_pending_cnt);
ret = usb_submit_urb(urb, GFP_KERNEL);
if (ret) {
pr_err("out urb submission failed");
@@ -259,6 +267,8 @@
usb_free_urb(urb);
ksb_free_data_pkt(pkt);
usb_autopm_put_interface(ksb->ifc);
+ atomic_dec(&ksb->tx_pending_cnt);
+ wake_up(&ksb->pending_urb_wait);
return;
}
@@ -420,8 +430,15 @@
ksb_rx_cb, pkt);
usb_anchor_urb(urb, &ksb->submitted);
- dbg_log_event(ksb, "S RX_URB", pkt->len, 0);
+ if (!test_bit(USB_DEV_CONNECTED, &ksb->flags)) {
+ usb_unanchor_urb(urb);
+ usb_free_urb(urb);
+ ksb_free_data_pkt(pkt);
+ ksb->alloced_read_pkts--;
+ return;
+ }
+ atomic_inc(&ksb->rx_pending_cnt);
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret) {
pr_err("in urb submission failed");
@@ -429,9 +446,13 @@
usb_free_urb(urb);
ksb_free_data_pkt(pkt);
ksb->alloced_read_pkts--;
+ atomic_dec(&ksb->rx_pending_cnt);
+ wake_up(&ksb->pending_urb_wait);
return;
}
+ dbg_log_event(ksb, "S RX_URB", pkt->len, 0);
+
usb_free_urb(urb);
}
static void ksb_rx_cb(struct urb *urb)
@@ -454,7 +475,7 @@
urb->status);
ksb_free_data_pkt(pkt);
ksb->alloced_read_pkts--;
- return;
+ goto done;
}
if (urb->actual_length == 0) {
@@ -474,7 +495,9 @@
resubmit_urb:
submit_one_urb(ksb);
-
+done:
+ atomic_dec(&ksb->rx_pending_cnt);
+ wake_up(&ksb->pending_urb_wait);
}
static void ksb_start_rx_work(struct work_struct *w)
@@ -487,6 +510,10 @@
int ret;
for (i = 0; i < NO_RX_REQS; i++) {
+
+ if (!test_bit(USB_DEV_CONNECTED, &ksb->flags))
+ return;
+
pkt = ksb_alloc_data_pkt(MAX_DATA_PKT_SIZE, GFP_KERNEL, ksb);
if (IS_ERR(pkt)) {
pr_err("unable to allocate data pkt");
@@ -516,6 +543,7 @@
dbg_log_event(ksb, "S RX_URB", pkt->len, 0);
+ atomic_inc(&ksb->rx_pending_cnt);
ret = usb_submit_urb(urb, GFP_KERNEL);
if (ret) {
pr_err("in urb submission failed");
@@ -524,6 +552,8 @@
ksb_free_data_pkt(pkt);
ksb->alloced_read_pkts--;
usb_autopm_put_interface(ksb->ifc);
+ atomic_dec(&ksb->rx_pending_cnt);
+ wake_up(&ksb->pending_urb_wait);
return;
}
@@ -590,6 +620,8 @@
usb_set_intfdata(ifc, ksb);
set_bit(USB_DEV_CONNECTED, &ksb->flags);
+ atomic_set(&ksb->tx_pending_cnt, 0);
+ atomic_set(&ksb->rx_pending_cnt, 0);
dbg_log_event(ksb, "PID-ATT", id->idProduct, 0);
@@ -640,9 +672,16 @@
clear_bit(USB_DEV_CONNECTED, &ksb->flags);
wake_up(&ksb->ks_wait_q);
cancel_work_sync(&ksb->to_mdm_work);
+ cancel_work_sync(&ksb->start_rx_work);
usb_kill_anchored_urbs(&ksb->submitted);
+ wait_event_interruptible_timeout(
+ ksb->pending_urb_wait,
+ !atomic_read(&ksb->tx_pending_cnt) &&
+ !atomic_read(&ksb->rx_pending_cnt),
+ msecs_to_jiffies(PENDING_URB_TIMEOUT));
+
spin_lock_irqsave(&ksb->lock, flags);
while (!list_empty(&ksb->to_ks_list)) {
pkt = list_first_entry(&ksb->to_ks_list,
@@ -741,6 +780,7 @@
INIT_LIST_HEAD(&ksb->to_mdm_list);
INIT_LIST_HEAD(&ksb->to_ks_list);
init_waitqueue_head(&ksb->ks_wait_q);
+ init_waitqueue_head(&ksb->pending_urb_wait);
ksb->wq = create_singlethread_workqueue(ksb->name);
if (!ksb->wq) {
pr_err("unable to allocate workqueue");
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 813fc94..f27e0eb 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -2041,6 +2041,8 @@
udelay(20);
break;
case SNPS_28NM_INTEGRATED_PHY:
+ /* disable DP and DM pull down resistors */
+ ulpi_write(phy, 0x6, 0xC);
/* Clear charger detecting control bits */
ulpi_write(phy, 0x1F, 0x86);
/* Clear alt interrupt latch and enable bits */
@@ -2120,8 +2122,7 @@
switch (motg->chg_state) {
case USB_CHG_STATE_UNDEFINED:
msm_chg_block_on(motg);
- if (motg->pdata->enable_dcd)
- msm_chg_enable_dcd(motg);
+ msm_chg_enable_dcd(motg);
msm_chg_enable_aca_det(motg);
motg->chg_state = USB_CHG_STATE_WAIT_FOR_DCD;
motg->dcd_retries = 0;
@@ -2148,12 +2149,10 @@
break;
}
}
- if (motg->pdata->enable_dcd)
- is_dcd = msm_chg_check_dcd(motg);
+ is_dcd = msm_chg_check_dcd(motg);
tmout = ++motg->dcd_retries == MSM_CHG_DCD_MAX_RETRIES;
if (is_dcd || tmout) {
- if (motg->pdata->enable_dcd)
- msm_chg_disable_dcd(motg);
+ msm_chg_disable_dcd(motg);
msm_chg_enable_primary_det(motg);
delay = MSM_CHG_PRIMARY_DET_TIME;
motg->chg_state = USB_CHG_STATE_DCD_DONE;
diff --git a/drivers/video/msm/mddi_quickvx.c b/drivers/video/msm/mddi_quickvx.c
index 95e7d41..37c147d 100644
--- a/drivers/video/msm/mddi_quickvx.c
+++ b/drivers/video/msm/mddi_quickvx.c
@@ -263,22 +263,10 @@
int ql_mddi_write(uint32 address, uint32 value)
{
- uint32 regval = 0;
int ret = 0;
ret = mddi_queue_register_write(address, value, TRUE, 0);
- if (!ret) {
- ret = mddi_queue_register_read(address, ®val, TRUE, 0);
- if (regval != value) {
- MDDI_MSG_DEBUG("\nMismatch: ql_mddi_write[0x%x]->0x%x "
- "r0x%x\n", address, value, regval);
- } else {
- MDDI_MSG_DEBUG("\nMatch: ql_mddi_write[0x%x]->0x%x "
- "r0x%x\n", address, value, regval);
- }
- }
-
return ret;
}
@@ -294,8 +282,6 @@
int ql_send_spi_cmd_to_lcd(uint32 index, uint32 cmd)
{
- int retry, ret;
- uint32 readval;
MDDI_MSG_DEBUG("\n %s(): index 0x%x, cmd 0x%x", __func__, index, cmd);
/* do the index phase */
@@ -308,18 +294,6 @@
/* set start */
ql_mddi_write(QUICKVX_SPI_CTRL_REG, QL_SPI_CTRL_LCD_START);
- retry = 0;
-
- do {
- ret = ql_mddi_read(QUICKVX_SPI_CTRL_REG, &readval);
-
- if (ret || ++retry > 5) {
- MDDI_MSG_DEBUG("\n ql_send_spi_cmd_to_lcd: retry "
- "timeout at index phase, ret = %d", ret);
- return -EIO;
- }
- mddi_wait(1);
- } while ((readval & QL_SPI_CTRL_MASK_rTxDone) == 0);
/* do the command phase */
/* send 24 bits in the cmd phase */
@@ -331,18 +305,6 @@
/* set start */
ql_mddi_write(QUICKVX_SPI_CTRL_REG, QL_SPI_CTRL_LCD_START);
- retry = 0;
-
- do {
- ret = ql_mddi_read(QUICKVX_SPI_CTRL_REG, &readval);
-
- if (ret || ++retry > 5) {
- MDDI_MSG_DEBUG("\n ql_send_spi_cmd_to_lcd: retry "
- "timeout at cmd phase, ret = %d", ret);
- return -EIO;
- }
- mddi_wait(1);
- } while ((readval & QL_SPI_CTRL_MASK_rTxDone) == 0);
return 0;
}
@@ -350,8 +312,6 @@
int ql_send_spi_data_from_lcd(uint32 index, uint32 *value)
{
- int retry, ret;
- uint32 readval;
MDDI_MSG_DEBUG("\n %s(): index 0x%x", __func__, index);
/* do the index phase */
@@ -364,19 +324,6 @@
/* set start */
ql_mddi_write(QUICKVX_SPI_CTRL_REG, QL_SPI_CTRL_LCD_START);
- retry = 0;
-
- do {
- ret = ql_mddi_read(QUICKVX_SPI_CTRL_REG, &readval);
-
- if (ret || ++retry > 5) {
- MDDI_MSG_DEBUG("\n ql_send_spi_cmd_to_lcd: retry "
- "timeout at index phase, ret = %d", ret);
- return -EIO;
- }
- mddi_wait(1);
- } while ((readval & QL_SPI_CTRL_MASK_rTxDone) == 0);
-
/* do the command phase */
/* send 8 bits and read 24 bits in the cmd phase, so total 32 bits */
ql_mddi_write(QUICKVX_SPI_TLEN_REG, 31);
@@ -387,29 +334,9 @@
/* set start */
ql_mddi_write(QUICKVX_SPI_CTRL_REG, QL_SPI_CTRL_LCD_START);
- retry = 0;
- do {
- ret = ql_mddi_read(QUICKVX_SPI_CTRL_REG, &readval);
+ return 0;
- if (ret || ++retry > 5) {
- MDDI_MSG_DEBUG("\n ql_send_spi_cmd_to_lcd: retry "
- "timeout at cmd phase, ret = %d", ret);
- return -EIO;
- }
- mddi_wait(1);
- } while ((readval & QL_SPI_CTRL_MASK_rTxDone) == 0);
-
- /* value will appear at lower 16 bits */
- ret = ql_mddi_read(QUICKVX_SPI_RX0_REG, value);
-
- if (!ret) {
- *value = *value & 0xffff;
- MDDI_MSG_DEBUG("\n QUICKVX_SPI_RX0_REG value = 0x%x", *value);
- } else
- MDDI_MSG_DEBUG("\n Read QUICKVX_SPI_RX0_REG Failed");
-
- return ret;
}
/* Global Variables */
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index 6c0d08d..946a9d7 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -2214,6 +2214,8 @@
mfd->panel.type == LCDC_PANEL ||
mfd->panel.type == LVDS_PANEL)
mdp4_lcdc_off(pdev);
+ else if (mfd->panel.type == MDDI_PANEL)
+ mdp4_mddi_off(pdev);
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
ret = panel_next_off(pdev);
@@ -2268,6 +2270,9 @@
mfd->panel.type == LCDC_PANEL ||
mfd->panel.type == LVDS_PANEL) {
mdp4_lcdc_on(pdev);
+ } else if (mfd->panel.type == MDDI_PANEL) {
+ mdp_vsync_cfg_regs(mfd, FALSE);
+ mdp4_mddi_on(pdev);
}
mdp_clk_ctrl(0);
@@ -2706,6 +2711,9 @@
mdp_vsync_resync_workqueue_handler);
mfd->hw_refresh = FALSE;
+ if (mfd->panel.type == MDDI_PANEL)
+ mdp4_mddi_rdptr_init(0);
+
if (mfd->panel.type == EXT_MDDI_PANEL) {
/* 15 fps -> 66 msec */
mfd->refresh_timer_duration = (66 * HZ / 1000);
@@ -2948,6 +2956,7 @@
mdp_clk_ctrl(0);
return -ENODEV;
}
+ mdp4_wfd_init(0);
pdata->on = mdp4_overlay_writeback_on;
pdata->off = mdp4_overlay_writeback_off;
mfd->dma_fnc = mdp4_writeback_overlay;
diff --git a/drivers/video/msm/mdp.h b/drivers/video/msm/mdp.h
index d939c62..b4a7f79 100644
--- a/drivers/video/msm/mdp.h
+++ b/drivers/video/msm/mdp.h
@@ -796,6 +796,10 @@
{
return 0;
}
+static inline int mdp4_mddi_off(struct platform_device *pdev)
+{
+ return 0;
+}
static inline int mdp4_dsi_cmd_on(struct platform_device *pdev)
{
return 0;
@@ -808,6 +812,19 @@
{
return 0;
}
+static inline int mdp4_mddi_on(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif
+
+
+#ifndef CONFIG_FB_MSM_MDDI
+static inline void mdp4_mddi_rdptr_init(int cndx)
+{
+ /* empty */
+}
+
#endif
void set_cont_splashScreen_status(int);
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index 180a18a..a73b3ad 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -393,6 +393,7 @@
ulong intr_dsi_err;
ulong kickoff_ov0;
ulong kickoff_ov1;
+ ulong kickoff_ov2;
ulong kickoff_dmap;
ulong kickoff_dmae;
ulong kickoff_dmas;
@@ -596,9 +597,10 @@
int mdp4_overlay_pipe_staged(int mixer);
void mdp4_lcdc_primary_vsyn(void);
void mdp4_overlay0_done_lcdc(int cndx);
-void mdp4_overlay0_done_mddi(struct mdp_dma_data *dma);
+void mdp4_overlay0_done_mddi(int cndx);
void mdp4_dma_p_done_mddi(struct mdp_dma_data *dma);
void mdp4_dmap_done_dsi_cmd(int cndx);
+void mdp4_dmap_done_mddi(int cndx);
void mdp4_dmap_done_dsi_video(int cndx);
void mdp4_dmap_done_lcdc(int cndx);
void mdp4_overlay1_done_dtv(void);
@@ -672,6 +674,12 @@
void mdp4_dsi_video_overlay_blt(struct msm_fb_data_type *mfd,
struct msmfb_overlay_blt *req);
void mdp4_dsi_video_base_swap(int cndx, struct mdp4_overlay_pipe *pipe);
+static inline void mdp4_mddi_blt_start(struct msm_fb_data_type *mfd)
+{
+}
+static inline void mdp4_mddi_blt_stop(struct msm_fb_data_type *mfd)
+{
+}
#ifdef CONFIG_FB_MSM_MDP40
static inline void mdp3_dsi_cmd_dma_busy_wait(struct msm_fb_data_type *mfd)
@@ -680,6 +688,8 @@
}
#endif
#else /* CONFIG_FB_MSM_MIPI_DSI */
+void mdp4_mddi_blt_start(struct msm_fb_data_type *mfd);
+void mdp4_mddi_blt_stop(struct msm_fb_data_type *mfd);
int mdp4_mddi_overlay_blt_offset(struct msm_fb_data_type *mfd,
struct msmfb_overlay_blt *req);
void mdp4_mddi_overlay_blt(struct msm_fb_data_type *mfd,
@@ -687,6 +697,7 @@
int mdp4_mddi_overlay_blt_start(struct msm_fb_data_type *mfd);
int mdp4_mddi_overlay_blt_stop(struct msm_fb_data_type *mfd);
void mdp4_mddi_blt_dmap_busy_wait(struct msm_fb_data_type *mfd);
+void mdp4_mddi_rdptr_init(int cndx);
static inline int mdp4_dsi_overlay_blt_start(struct msm_fb_data_type *mfd)
{
return -ENODEV;
@@ -776,11 +787,37 @@
{
/* empty */
}
-#else /* CONFIG_FB_MSM_MIPI_DSI */
+#else /* CONFIG_FB_MSM_MDP303 */
void mdp4_dsi_cmd_del_timer(void);
+static inline int mdp4_mddi_on(struct platform_device *pdev)
+{
+ return 0;
+}
+static inline int mdp4_mddi_off(struct platform_device *pdev)
+{
+ return 0;
+}
+static inline void mdp4_mddi_wait4vsync(int cndx, long long *vtime)
+{
+}
+static inline void mdp4_mddi_vsync_ctrl(struct fb_info *info,
+ int enable)
+{
+}
+static inline void mdp4_mddi_pipe_queue(int cndx,
+ struct mdp4_overlay_pipe *pipe)
+{
+}
#endif
#else /* CONFIG_FB_MSM_MIPI_DSI */
+int mdp4_mddi_off(struct platform_device *pdev);
+int mdp4_mddi_on(struct platform_device *pdev);
+void mdp4_mddi_wait4vsync(int cndx, long long *vtime);
+void mdp4_mddi_vsync_ctrl(struct fb_info *info, int enable);
+void mdp4_mddi_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe);
+void mdp4_overlay_update_mddi(struct msm_fb_data_type *mfd);
+
static inline int mdp4_dsi_cmd_on(struct platform_device *pdev)
{
return 0;
@@ -884,7 +921,7 @@
int mdp4_overlay_writeback_on(struct platform_device *pdev);
int mdp4_overlay_writeback_off(struct platform_device *pdev);
void mdp4_writeback_overlay(struct msm_fb_data_type *mfd);
-void mdp4_overlay1_done_writeback(struct mdp_dma_data *dma);
+void mdp4_overlay2_done_wfd(struct mdp_dma_data *dma);
int mdp4_writeback_start(struct fb_info *info);
int mdp4_writeback_stop(struct fb_info *info);
@@ -934,19 +971,17 @@
u32 mdp4_get_mixer_num(u32 panel_type);
#ifndef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL
-static inline void mdp4_writeback_dma_busy_wait(struct msm_fb_data_type *mfd)
+static inline void mdp4_wfd_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe)
{
/* empty */
}
-static inline void mdp4_writeback_kickoff_video(struct msm_fb_data_type *mfd,
- struct mdp4_overlay_pipe *pipe)
+static inline void mdp4_wfd_init(int cndx)
{
/* empty */
}
#else
-void mdp4_writeback_dma_busy_wait(struct msm_fb_data_type *mfd);
-void mdp4_writeback_kickoff_video(struct msm_fb_data_type *mfd,
- struct mdp4_overlay_pipe *pipe);
+void mdp4_wfd_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe);
+void mdp4_wfd_init(int cndx);
#endif
#endif /* MDP_H */
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index 8499e3e..ab27267 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -1989,7 +1989,8 @@
if (s_pipe->pipe_type == OVERLAY_TYPE_VIDEO &&
((s_pipe->op_mode & MDP4_OP_SCALEY_EN) ||
(s_pipe->op_mode & MDP4_OP_SCALEX_EN)) &&
- !(s_pipe->op_mode & MDP4_OP_SCALEY_PIXEL_RPT))
+ !(s_pipe->op_mode & (MDP4_OP_SCALEX_PIXEL_RPT |
+ MDP4_OP_SCALEY_PIXEL_RPT)))
alpha_drop = 1;
d_pipe = mdp4_background_layer(mixer, s_pipe);
@@ -2868,6 +2869,8 @@
mdp4_dsi_video_blt_start(mfd);
else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD)
mdp4_dsi_cmd_blt_start(mfd);
+ else if (ctrl->panel_mode & MDP4_PANEL_MDDI)
+ mdp4_mddi_blt_start(mfd);
pr_info("%s mixer0 start blt [%d] from %d to %d.\n",
__func__,
flag,
@@ -2916,6 +2919,8 @@
mdp4_dsi_video_blt_stop(mfd);
else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD)
mdp4_dsi_cmd_blt_stop(mfd);
+ else if (ctrl->panel_mode & MDP4_PANEL_MDDI)
+ mdp4_mddi_blt_stop(mfd);
pr_info("%s mixer0 stop blt [%d] from %d to %d.\n",
__func__,
flag,
@@ -3180,20 +3185,14 @@
else {
/* mixer 0 */
ctrl->mixer0_played = 0;
- if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
- if (mfd->panel_power_on)
- mdp4_mddi_blt_dmap_busy_wait(mfd);
- }
+
}
mdp4_overlay_reg_flush(pipe, 1);
mdp4_mixer_stage_down(pipe, 0);
if (pipe->mixer_num == MDP4_MIXER0) {
- if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
- if (mfd->panel_power_on)
- mdp4_mddi_overlay_restore();
- }
+
} else { /* mixer1, DTV, ATV */
if (ctrl->panel_mode & MDP4_PANEL_DTV)
mdp4_overlay_dtv_unset(mfd, pipe);
@@ -3217,6 +3216,8 @@
mdp4_dsi_cmd_wait4vsync(0, vtime);
else if (ctrl->panel_mode & MDP4_PANEL_LCDC)
mdp4_lcdc_wait4vsync(0, vtime);
+ else if (ctrl->panel_mode & MDP4_PANEL_MDDI)
+ mdp4_mddi_wait4vsync(0, vtime);
} else if (hdmi_prim_display || info->node == 1) {
mdp4_dtv_wait4vsync(0, vtime);
}
@@ -3240,6 +3241,8 @@
mdp4_dsi_cmd_vsync_ctrl(info, cmd);
else if (ctrl->panel_mode & MDP4_PANEL_LCDC)
mdp4_lcdc_vsync_ctrl(info, cmd);
+ else if (ctrl->panel_mode & MDP4_PANEL_MDDI)
+ mdp4_mddi_vsync_ctrl(info, cmd);
} else if (hdmi_prim_display || info->node == 1)
mdp4_dtv_vsync_ctrl(info, cmd);
@@ -3460,10 +3463,6 @@
mdp4_overlay_mdp_perf_req(mfd, ctrl->plist);
- if (pipe->mixer_num == MDP4_MIXER2 ||
- ctrl->panel_mode & MDP4_PANEL_MDDI)
- goto mddi;
-
if (pipe->mixer_num == MDP4_MIXER0) {
if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) {
/* cndx = 0 */
@@ -3475,44 +3474,22 @@
/* cndx = 0 */
mdp4_lcdc_pipe_queue(0, pipe);
}
+ if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
+ /* cndx = 0 */
+ mdp4_mddi_pipe_queue(0, pipe);
+ }
} else if (pipe->mixer_num == MDP4_MIXER1) {
if (ctrl->panel_mode & MDP4_PANEL_DTV)
mdp4_dtv_pipe_queue(0, pipe);/* cndx = 0 */
+ } else if (pipe->mixer_num == MDP4_MIXER2) {
+ ctrl->mixer2_played++;
+ if (ctrl->panel_mode & MDP4_PANEL_WRITEBACK)
+ mdp4_wfd_pipe_queue(0, pipe);/* cndx = 0 */
}
mutex_unlock(&mfd->dma->ov_mutex);
return ret;
-mddi:
- if (pipe->pipe_type == OVERLAY_TYPE_VIDEO) {
- mdp4_overlay_vg_setup(pipe); /* video/graphic pipe */
- } else {
- mdp4_overlay_rgb_setup(pipe); /* rgb pipe */
- }
-
- mdp4_mixer_stage_up(pipe, 0);
-
- if (pipe->mixer_num == MDP4_MIXER2) {
- ctrl->mixer2_played++;
- if (ctrl->panel_mode & MDP4_PANEL_WRITEBACK) {
- mdp4_writeback_dma_busy_wait(mfd);
- mdp4_writeback_kickoff_video(mfd, pipe);
- }
- } else if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
- if (pipe->flags & MDP_OV_PLAY_NOWAIT) {
- mdp4_stat.overlay_play[pipe->mixer_num]++;
- mutex_unlock(&mfd->dma->ov_mutex);
- goto end;
- }
- mdp4_mixer_stage_commit(pipe->mixer_num);
- mdp4_mddi_dma_busy_wait(mfd);
- mdp4_mddi_kickoff_video(mfd, pipe);
- }
-
- if (!(pipe->flags & MDP_OV_PLAY_NOWAIT))
- mdp4_iommu_unmap(pipe);
- mdp4_stat.overlay_play[pipe->mixer_num]++;
-
end:
mutex_unlock(&mfd->dma->ov_mutex);
diff --git a/drivers/video/msm/mdp4_overlay_mddi.c b/drivers/video/msm/mdp4_overlay_mddi.c
index be4a89a..ca84eca 100644
--- a/drivers/video/msm/mdp4_overlay_mddi.c
+++ b/drivers/video/msm/mdp4_overlay_mddi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-2012, 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
@@ -17,38 +17,680 @@
#include <linux/time.h>
#include <linux/init.h>
#include <linux/interrupt.h>
-#include <linux/hrtimer.h>
#include <linux/delay.h>
-#include <mach/hardware.h>
#include <linux/io.h>
-
-#include <asm/system.h>
-#include <asm/mach-types.h>
#include <linux/semaphore.h>
#include <linux/spinlock.h>
-
#include <linux/fb.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
#include "mdp.h"
#include "msm_fb.h"
#include "mdp4.h"
-static struct mdp4_overlay_pipe *mddi_pipe;
-static struct msm_fb_data_type *mddi_mfd;
-static int busy_wait_cnt;
+static int mddi_state;
+
+#define TOUT_PERIOD HZ /* 1 second */
+#define MS_100 (HZ/10) /* 100 ms */
static int vsync_start_y_adjust = 4;
-static int dmap_vsync_enable;
+#define MAX_CONTROLLER 1
+#define VSYNC_EXPIRE_TICK 8
-void mdp_dmap_vsync_set(int enable)
+static struct vsycn_ctrl {
+ struct device *dev;
+ int inited;
+ int update_ndx;
+ int expire_tick;
+ int blt_wait;
+ u32 ov_koff;
+ u32 ov_done;
+ u32 dmap_koff;
+ u32 dmap_done;
+ uint32 rdptr_intr_tot;
+ uint32 rdptr_sirq_tot;
+ atomic_t suspend;
+ int wait_vsync_cnt;
+ int blt_change;
+ int blt_free;
+ int blt_end;
+ int uevent;
+ struct mutex update_lock;
+ struct completion ov_comp;
+ struct completion dmap_comp;
+ struct completion vsync_comp;
+ spinlock_t spin_lock;
+ struct msm_fb_data_type *mfd;
+ struct mdp4_overlay_pipe *base_pipe;
+ struct vsync_update vlist[2];
+ int vsync_enabled;
+ int clk_enabled;
+ int clk_control;
+ int new_update;
+ ktime_t vsync_time;
+ struct work_struct vsync_work;
+ struct work_struct clk_work;
+} vsync_ctrl_db[MAX_CONTROLLER];
+
+static void vsync_irq_enable(int intr, int term)
{
- dmap_vsync_enable = enable;
+ unsigned long flag;
+
+ spin_lock_irqsave(&mdp_spin_lock, flag);
+ /* no need to clear other interrupts for comamnd mode */
+ mdp_intr_mask |= intr;
+ outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+ mdp_enable_irq(term);
+ spin_unlock_irqrestore(&mdp_spin_lock, flag);
}
-int mdp_dmap_vsync_get(void)
+static void vsync_irq_disable(int intr, int term)
{
- return dmap_vsync_enable;
+ unsigned long flag;
+
+ spin_lock_irqsave(&mdp_spin_lock, flag);
+ /* no need to clrear other interrupts for comamnd mode */
+ mdp_intr_mask &= ~intr;
+ outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+ mdp_disable_irq_nosync(term);
+ spin_unlock_irqrestore(&mdp_spin_lock, flag);
+}
+
+static void mdp4_mddi_blt_ov_update(struct mdp4_overlay_pipe *pipe)
+{
+ uint32 off, addr;
+ int bpp;
+ char *overlay_base;
+
+ if (pipe->ov_blt_addr == 0)
+ return;
+
+ bpp = 3; /* overlay ouput is RGB888 */
+ off = 0;
+ if (pipe->ov_cnt & 0x01)
+ off = pipe->src_height * pipe->src_width * bpp;
+ addr = pipe->ov_blt_addr + off;
+ /* overlay 0 */
+ overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
+ outpdw(overlay_base + 0x000c, addr);
+ outpdw(overlay_base + 0x001c, addr);
+}
+
+static void mdp4_mddi_blt_dmap_update(struct mdp4_overlay_pipe *pipe)
+{
+ uint32 off, addr;
+ int bpp;
+
+ if (pipe->ov_blt_addr == 0)
+ return;
+
+ bpp = 3; /* overlay ouput is RGB888 */
+ off = 0;
+ if (pipe->dmap_cnt & 0x01)
+ off = pipe->src_height * pipe->src_width * bpp;
+ addr = pipe->dma_blt_addr + off;
+
+ /* dmap */
+ MDP_OUTP(MDP_BASE + 0x90008, addr);
+}
+
+static void mdp4_mddi_wait4dmap(int cndx);
+static void mdp4_mddi_wait4ov(int cndx);
+
+static void mdp4_mddi_do_blt(struct msm_fb_data_type *mfd, int enable)
+{
+ unsigned long flags;
+ int cndx = 0;
+ struct vsycn_ctrl *vctrl;
+ struct mdp4_overlay_pipe *pipe;
+ int need_wait;
+
+ vctrl = &vsync_ctrl_db[cndx];
+ pipe = vctrl->base_pipe;
+
+ mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0);
+
+ if (mfd->ov0_wb_buf->write_addr == 0) {
+ pr_err("%s: no blt_base assigned\n", __func__);
+ return;
+ }
+
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ if (enable && pipe->ov_blt_addr == 0) {
+ vctrl->blt_change++;
+ if (vctrl->dmap_koff != vctrl->dmap_done) {
+ INIT_COMPLETION(vctrl->dmap_comp);
+ need_wait = 1;
+ }
+ } else if (enable == 0 && pipe->ov_blt_addr) {
+ vctrl->blt_change++;
+ if (vctrl->ov_koff != vctrl->dmap_done) {
+ INIT_COMPLETION(vctrl->dmap_comp);
+ need_wait = 1;
+ }
+ }
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ if (need_wait)
+ mdp4_mddi_wait4dmap(0);
+
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ if (enable && pipe->ov_blt_addr == 0) {
+ pipe->ov_blt_addr = mfd->ov0_wb_buf->write_addr;
+ pipe->dma_blt_addr = mfd->ov0_wb_buf->read_addr;
+ pipe->ov_cnt = 0;
+ pipe->dmap_cnt = 0;
+ vctrl->ov_koff = vctrl->dmap_koff;
+ vctrl->ov_done = vctrl->dmap_done;
+ vctrl->blt_free = 0;
+ vctrl->blt_wait = 0;
+ vctrl->blt_end = 0;
+ mdp4_stat.blt_mddi++;
+ } else if (enable == 0 && pipe->ov_blt_addr) {
+ pipe->ov_blt_addr = 0;
+ pipe->dma_blt_addr = 0;
+ vctrl->blt_end = 1;
+ vctrl->blt_free = 4; /* 4 commits to free wb buf */
+ }
+
+ pr_debug("%s: changed=%d enable=%d ov_blt_addr=%x\n", __func__,
+ vctrl->blt_change, enable, (int)pipe->ov_blt_addr);
+
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+}
+
+/*
+ * mdp4_mddi_do_update:
+ * called from thread context
+ */
+void mdp4_mddi_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe)
+{
+ struct vsycn_ctrl *vctrl;
+ struct vsync_update *vp;
+ struct mdp4_overlay_pipe *pp;
+ int undx;
+
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+ return;
+ }
+
+ vctrl = &vsync_ctrl_db[cndx];
+
+ if (atomic_read(&vctrl->suspend) > 0)
+ return;
+
+ mutex_lock(&vctrl->update_lock);
+ undx = vctrl->update_ndx;
+ vp = &vctrl->vlist[undx];
+
+ pp = &vp->plist[pipe->pipe_ndx - 1]; /* ndx start form 1 */
+
+ pr_debug("%s: vndx=%d pipe_ndx=%d expire=%x pid=%d\n", __func__,
+ undx, pipe->pipe_ndx, vctrl->expire_tick, current->pid);
+
+ *pp = *pipe; /* clone it */
+ vp->update_cnt++;
+
+ mutex_unlock(&vctrl->update_lock);
+ mdp4_stat.overlay_play[pipe->mixer_num]++;
+}
+
+static void mdp4_mddi_blt_ov_update(struct mdp4_overlay_pipe *pipe);
+
+int mdp4_mddi_pipe_commit(void)
+{
+ int i, undx;
+ int mixer = 0;
+ struct vsycn_ctrl *vctrl;
+ struct vsync_update *vp;
+ struct mdp4_overlay_pipe *pipe;
+ struct mdp4_overlay_pipe *real_pipe;
+ unsigned long flags;
+ int need_dmap_wait = 0;
+ int need_ov_wait = 0;
+ int cnt = 0;
+
+ vctrl = &vsync_ctrl_db[0];
+
+ mutex_lock(&vctrl->update_lock);
+ undx = vctrl->update_ndx;
+ vp = &vctrl->vlist[undx];
+ pipe = vctrl->base_pipe;
+ mixer = pipe->mixer_num;
+
+ if (vp->update_cnt == 0) {
+ mutex_unlock(&vctrl->update_lock);
+ return cnt;
+ }
+
+ vctrl->update_ndx++;
+ vctrl->update_ndx &= 0x01;
+ vp->update_cnt = 0; /* reset */
+ if (vctrl->blt_free) {
+ vctrl->blt_free--;
+ if (vctrl->blt_free == 0)
+ mdp4_free_writeback_buf(vctrl->mfd, mixer);
+ }
+ mutex_unlock(&vctrl->update_lock);
+
+ /* free previous committed iommu back to pool */
+ mdp4_overlay_iommu_unmap_freelist(mixer);
+
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ if (pipe->ov_blt_addr) {
+ /* Blt */
+ if (vctrl->blt_wait)
+ need_dmap_wait = 1;
+ if (vctrl->ov_koff != vctrl->ov_done) {
+ INIT_COMPLETION(vctrl->ov_comp);
+ need_ov_wait = 1;
+ }
+ } else {
+ /* direct out */
+ if (vctrl->dmap_koff != vctrl->dmap_done) {
+ INIT_COMPLETION(vctrl->dmap_comp);
+ pr_debug("%s: wait, ok=%d od=%d dk=%d dd=%d cpu=%d\n",
+ __func__, vctrl->ov_koff, vctrl->ov_done,
+ vctrl->dmap_koff, vctrl->dmap_done, smp_processor_id());
+ need_dmap_wait = 1;
+ }
+ }
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ if (need_dmap_wait) {
+ pr_debug("%s: wait4dmap\n", __func__);
+ mdp4_mddi_wait4dmap(0);
+ }
+
+ if (need_ov_wait) {
+ pr_debug("%s: wait4ov\n", __func__);
+ mdp4_mddi_wait4ov(0);
+ }
+
+ if (pipe->ov_blt_addr) {
+ if (vctrl->blt_end) {
+ vctrl->blt_end = 0;
+ pipe->ov_blt_addr = 0;
+ pipe->dma_blt_addr = 0;
+ }
+ }
+
+ if (vctrl->blt_change) {
+ mdp4_overlayproc_cfg(pipe);
+ mdp4_overlay_dmap_xy(pipe);
+ vctrl->blt_change = 0;
+ }
+
+ pipe = vp->plist;
+ for (i = 0; i < OVERLAY_PIPE_MAX; i++, pipe++) {
+ if (pipe->pipe_used) {
+ cnt++;
+ real_pipe = mdp4_overlay_ndx2pipe(pipe->pipe_ndx);
+ if (real_pipe && real_pipe->pipe_used) {
+ /* pipe not unset */
+ mdp4_overlay_vsync_commit(pipe);
+ }
+ /* free previous iommu to freelist
+ * which will be freed at next
+ * pipe_commit
+ */
+ mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 0);
+ pipe->pipe_used = 0; /* clear */
+ }
+ }
+
+ mdp4_mixer_stage_commit(mixer);
+
+ pipe = vctrl->base_pipe;
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ if (pipe->ov_blt_addr) {
+ mdp4_mddi_blt_ov_update(pipe);
+ pipe->ov_cnt++;
+ vctrl->ov_koff++;
+ vsync_irq_enable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM);
+ } else {
+ vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM);
+ vctrl->dmap_koff++;
+ }
+ pr_debug("%s: kickoff\n", __func__);
+ /* kickoff overlay engine */
+ mdp4_stat.kickoff_ov0++;
+ outpdw(MDP_BASE + 0x0004, 0);
+ mb(); /* make sure kickoff ececuted */
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ mdp4_stat.overlay_commit[pipe->mixer_num]++;
+
+ return cnt;
+}
+
+void mdp4_mddi_vsync_ctrl(struct fb_info *info, int enable)
+{
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+ struct vsycn_ctrl *vctrl;
+ unsigned long flags;
+ int clk_set_on = 0;
+ int cndx = 0;
+
+ vctrl = &vsync_ctrl_db[cndx];
+
+ pr_debug("%s: clk_enabled=%d vsycn_enabeld=%d req=%d\n", __func__,
+ vctrl->clk_enabled, vctrl->vsync_enabled, enable);
+
+ mutex_lock(&vctrl->update_lock);
+
+ if (vctrl->vsync_enabled == enable) {
+ mutex_unlock(&vctrl->update_lock);
+ return;
+ }
+
+ vctrl->vsync_enabled = enable;
+
+ if (enable) {
+ if (vctrl->clk_enabled == 0) {
+ pr_debug("%s: SET_CLK_ON\n", __func__);
+ mdp_clk_ctrl(1);
+ vctrl->clk_enabled = 1;
+ clk_set_on = 1;
+ }
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ vctrl->clk_control = 0;
+ vctrl->expire_tick = 0;
+ vctrl->uevent = 1;
+ vctrl->new_update = 1;
+ if (clk_set_on) {
+ vsync_irq_enable(INTR_PRIMARY_RDPTR,
+ MDP_PRIM_RDPTR_TERM);
+ }
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ mdp4_overlay_update_mddi(mfd);
+ } else {
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ vctrl->clk_control = 1;
+ vctrl->uevent = 0;
+ if (vctrl->clk_enabled)
+ vctrl->expire_tick = VSYNC_EXPIRE_TICK;
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+ }
+ mutex_unlock(&vctrl->update_lock);
+}
+
+void mdp4_mddi_wait4vsync(int cndx, long long *vtime)
+{
+ struct vsycn_ctrl *vctrl;
+ struct mdp4_overlay_pipe *pipe;
+ unsigned long flags;
+
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+ return;
+ }
+
+ vctrl = &vsync_ctrl_db[cndx];
+ pipe = vctrl->base_pipe;
+
+ if (atomic_read(&vctrl->suspend) > 0) {
+ *vtime = -1;
+ return;
+ }
+
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ if (vctrl->wait_vsync_cnt == 0)
+ INIT_COMPLETION(vctrl->vsync_comp);
+ vctrl->wait_vsync_cnt++;
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ wait_for_completion(&vctrl->vsync_comp);
+ mdp4_stat.wait4vsync0++;
+
+ *vtime = ktime_to_ns(vctrl->vsync_time);
+}
+
+static void mdp4_mddi_wait4dmap(int cndx)
+{
+ struct vsycn_ctrl *vctrl;
+
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+ return;
+ }
+
+ vctrl = &vsync_ctrl_db[cndx];
+
+ if (atomic_read(&vctrl->suspend) > 0)
+ return;
+
+ wait_for_completion(&vctrl->dmap_comp);
+}
+
+static void mdp4_mddi_wait4ov(int cndx)
+{
+ struct vsycn_ctrl *vctrl;
+
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+ return;
+ }
+
+ vctrl = &vsync_ctrl_db[cndx];
+
+ if (atomic_read(&vctrl->suspend) > 0)
+ return;
+
+ wait_for_completion(&vctrl->ov_comp);
+}
+
+/*
+ * primary_rdptr_isr:
+ * called from interrupt context
+ */
+static void primary_rdptr_isr(int cndx)
+{
+ struct vsycn_ctrl *vctrl;
+
+ vctrl = &vsync_ctrl_db[cndx];
+ pr_debug("%s: ISR, cpu=%d\n", __func__, smp_processor_id());
+ vctrl->rdptr_intr_tot++;
+ vctrl->vsync_time = ktime_get();
+
+ spin_lock(&vctrl->spin_lock);
+
+ if (vctrl->uevent)
+ schedule_work(&vctrl->vsync_work);
+
+ if (vctrl->wait_vsync_cnt) {
+ complete(&vctrl->vsync_comp);
+ vctrl->wait_vsync_cnt = 0;
+ }
+
+ if (vctrl->expire_tick) {
+ vctrl->expire_tick--;
+ if (vctrl->expire_tick == 0)
+ schedule_work(&vctrl->clk_work);
+ }
+ spin_unlock(&vctrl->spin_lock);
+}
+
+void mdp4_dmap_done_mddi(int cndx)
+{
+ struct vsycn_ctrl *vctrl;
+ struct mdp4_overlay_pipe *pipe;
+ int diff;
+
+ vctrl = &vsync_ctrl_db[cndx];
+ pipe = vctrl->base_pipe;
+
+ /* blt enabled */
+ spin_lock(&vctrl->spin_lock);
+ vsync_irq_disable(INTR_DMA_P_DONE, MDP_DMAP_TERM);
+ vctrl->dmap_done++;
+ diff = vctrl->ov_done - vctrl->dmap_done;
+ pr_debug("%s: ov_koff=%d ov_done=%d dmap_koff=%d dmap_done=%d cpu=%d\n",
+ __func__, vctrl->ov_koff, vctrl->ov_done, vctrl->dmap_koff,
+ vctrl->dmap_done, smp_processor_id());
+ complete_all(&vctrl->dmap_comp);
+ if (diff <= 0) {
+ if (vctrl->blt_wait)
+ vctrl->blt_wait = 0;
+ spin_unlock(&vctrl->spin_lock);
+ return;
+ }
+
+ /* kick dmap */
+ mdp4_mddi_blt_dmap_update(pipe);
+ pipe->dmap_cnt++;
+ mdp4_stat.kickoff_dmap++;
+ vctrl->dmap_koff++;
+ vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM);
+ outpdw(MDP_BASE + 0x000c, 0); /* kickoff dmap engine */
+ mb(); /* make sure kickoff executed */
+ spin_unlock(&vctrl->spin_lock);
+}
+
+/*
+ * mdp4_overlay0_done_mddi: called from isr
+ */
+void mdp4_overlay0_done_mddi(int cndx)
+{
+ struct vsycn_ctrl *vctrl;
+ struct mdp4_overlay_pipe *pipe;
+ int diff;
+
+ vctrl = &vsync_ctrl_db[cndx];
+ pipe = vctrl->base_pipe;
+
+ spin_lock(&vctrl->spin_lock);
+ vsync_irq_disable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM);
+ vctrl->ov_done++;
+ complete_all(&vctrl->ov_comp);
+ diff = vctrl->ov_done - vctrl->dmap_done;
+
+ pr_debug("%s: ov_koff=%d ov_done=%d dmap_koff=%d dmap_done=%d cpu=%d\n",
+ __func__, vctrl->ov_koff, vctrl->ov_done, vctrl->dmap_koff,
+ vctrl->dmap_done, smp_processor_id());
+
+ if (pipe->ov_blt_addr == 0) {
+ /* blt disabled */
+ spin_unlock(&vctrl->spin_lock);
+ return;
+ }
+
+ if (diff > 1) {
+ /*
+ * two overlay_done and none dmap_done yet
+ * let dmap_done kickoff dmap
+ * and put pipe_commit to wait
+ */
+ vctrl->blt_wait = 1;
+ pr_debug("%s: blt_wait set\n", __func__);
+ spin_unlock(&vctrl->spin_lock);
+ return;
+ }
+ mdp4_mddi_blt_dmap_update(pipe);
+ pipe->dmap_cnt++;
+ mdp4_stat.kickoff_dmap++;
+ vctrl->dmap_koff++;
+ vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM);
+ outpdw(MDP_BASE + 0x000c, 0); /* kickoff dmap engine */
+ mb(); /* make sure kickoff executed */
+ spin_unlock(&vctrl->spin_lock);
+}
+
+static void clk_ctrl_work(struct work_struct *work)
+{
+ struct vsycn_ctrl *vctrl =
+ container_of(work, typeof(*vctrl), clk_work);
+ unsigned long flags;
+
+ mutex_lock(&vctrl->update_lock);
+ if (vctrl->clk_control && vctrl->clk_enabled) {
+ pr_debug("%s: SET_CLK_OFF\n", __func__);
+ mdp_clk_ctrl(0);
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ vsync_irq_disable(INTR_PRIMARY_RDPTR, MDP_PRIM_RDPTR_TERM);
+ vctrl->clk_enabled = 0;
+ vctrl->clk_control = 0;
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+ }
+ mutex_unlock(&vctrl->update_lock);
+}
+
+static void send_vsync_work(struct work_struct *work)
+{
+ struct vsycn_ctrl *vctrl =
+ container_of(work, typeof(*vctrl), vsync_work);
+ char buf[64];
+ char *envp[2];
+
+ snprintf(buf, sizeof(buf), "VSYNC=%llu",
+ ktime_to_ns(vctrl->vsync_time));
+ envp[0] = buf;
+ envp[1] = NULL;
+ kobject_uevent_env(&vctrl->dev->kobj, KOBJ_CHANGE, envp);
+}
+
+
+void mdp4_mddi_rdptr_init(int cndx)
+{
+ struct vsycn_ctrl *vctrl;
+
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+ return;
+ }
+
+ vctrl = &vsync_ctrl_db[cndx];
+ if (vctrl->inited)
+ return;
+
+ vctrl->inited = 1;
+ vctrl->update_ndx = 0;
+ mutex_init(&vctrl->update_lock);
+ init_completion(&vctrl->ov_comp);
+ init_completion(&vctrl->dmap_comp);
+ init_completion(&vctrl->vsync_comp);
+ spin_lock_init(&vctrl->spin_lock);
+ INIT_WORK(&vctrl->vsync_work, send_vsync_work);
+ INIT_WORK(&vctrl->clk_work, clk_ctrl_work);
+}
+
+void mdp4_primary_rdptr(void)
+{
+ primary_rdptr_isr(0);
+}
+
+void mdp4_overlay_mddi_state_set(int state)
+{
+ unsigned long flag;
+
+ spin_lock_irqsave(&mdp_spin_lock, flag);
+ mddi_state = state;
+ spin_unlock_irqrestore(&mdp_spin_lock, flag);
+}
+
+int mdp4_overlay_mddi_state_get(void)
+{
+ return mddi_state;
+}
+
+static __u32 msm_fb_line_length(__u32 fb_index, __u32 xres, int bpp)
+{
+ /*
+ * The adreno GPU hardware requires that the pitch be aligned to
+ * 32 pixels for color buffers, so for the cases where the GPU
+ * is writing directly to fb0, the framebuffer pitch
+ * also needs to be 32 pixel aligned
+ */
+
+ if (fb_index == 0)
+ return ALIGN(xres, 32) * bpp;
+ else
+ return xres * bpp;
}
void mdp4_mddi_vsync_enable(struct msm_fb_data_type *mfd,
@@ -61,13 +703,6 @@
if ((mfd->use_mdp_vsync) && (mfd->ibuf.vsync_enable) &&
(mfd->panel_info.lcd.vsync_enable)) {
- if (mdp_hw_revision < MDP4_REVISION_V2_1) {
- /* need dmas dmap switch */
- if (which == 0 && dmap_vsync_enable == 0 &&
- mfd->panel_info.lcd.rev < 2) /* dma_p */
- return;
- }
-
if (vsync_start_y_adjust <= pipe->dst_y)
start_y = pipe->dst_y - vsync_start_y_adjust;
else
@@ -88,633 +723,337 @@
}
}
-#define WHOLESCREEN
+void mdp4_mddi_base_swap(int cndx, struct mdp4_overlay_pipe *pipe)
+{
+ struct vsycn_ctrl *vctrl;
-void mdp4_overlay_update_lcd(struct msm_fb_data_type *mfd)
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+ return;
+ }
+
+ vctrl = &vsync_ctrl_db[cndx];
+ vctrl->base_pipe = pipe;
+}
+
+static void mdp4_overlay_setup_pipe_addr(struct msm_fb_data_type *mfd,
+ struct mdp4_overlay_pipe *pipe)
{
MDPIBUF *iBuf = &mfd->ibuf;
+ struct fb_info *fbi;
+ int bpp;
uint8 *src;
+
+ /* whole screen for base layer */
+ src = (uint8 *) iBuf->buf;
+ fbi = mfd->fbi;
+
+ if (pipe->is_3d) {
+ bpp = fbi->var.bits_per_pixel / 8;
+ pipe->src_height = pipe->src_height_3d;
+ pipe->src_width = pipe->src_width_3d;
+ pipe->src_h = pipe->src_height_3d;
+ pipe->src_w = pipe->src_width_3d;
+ pipe->dst_h = pipe->src_height_3d;
+ pipe->dst_w = pipe->src_width_3d;
+ pipe->srcp0_ystride = msm_fb_line_length(0,
+ pipe->src_width, bpp);
+ } else {
+ /* 2D */
+ pipe->src_height = fbi->var.yres;
+ pipe->src_width = fbi->var.xres;
+ pipe->src_h = fbi->var.yres;
+ pipe->src_w = fbi->var.xres;
+ pipe->dst_h = fbi->var.yres;
+ pipe->dst_w = fbi->var.xres;
+ pipe->srcp0_ystride = fbi->fix.line_length;
+ }
+ pipe->src_y = 0;
+ pipe->src_x = 0;
+ pipe->dst_y = 0;
+ pipe->dst_x = 0;
+ pipe->srcp0_addr = (uint32)src;
+}
+
+void mdp4_overlay_update_mddi(struct msm_fb_data_type *mfd)
+{
int ptype;
uint32 mddi_ld_param;
uint16 mddi_vdo_packet_reg;
struct mdp4_overlay_pipe *pipe;
+ uint32 data;
int ret;
+ int cndx = 0;
+ struct vsycn_ctrl *vctrl;
if (mfd->key != MFD_KEY)
return;
- mddi_mfd = mfd; /* keep it */
+ vctrl = &vsync_ctrl_db[cndx];
- /* MDP cmd block enable */
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
-
- if (mddi_pipe == NULL) {
+ if (vctrl->base_pipe == NULL) {
ptype = mdp4_overlay_format2type(mfd->fb_imgType);
+
if (ptype < 0)
- printk(KERN_INFO "%s: format2type failed\n", __func__);
+ pr_err("%s: format2type failed\n", __func__);
+
pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER0);
- if (pipe == NULL)
- printk(KERN_INFO "%s: pipe_alloc failed\n", __func__);
+ if (pipe == NULL) {
+ pr_err("%s: pipe_alloc failed\n", __func__);
+ return;
+ }
pipe->pipe_used++;
+ pipe->mixer_stage = MDP4_MIXER_STAGE_BASE;
pipe->mixer_num = MDP4_MIXER0;
pipe->src_format = mfd->fb_imgType;
mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_MDDI);
ret = mdp4_overlay_format2pipe(pipe);
if (ret < 0)
- printk(KERN_INFO "%s: format2type failed\n", __func__);
+ pr_err("%s: format2type failed\n", __func__);
- mddi_pipe = pipe; /* keep it */
- mddi_ld_param = 0;
- mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt;
-
- if (mdp_hw_revision == MDP4_REVISION_V2_1) {
- uint32 data;
-
- data = inpdw(MDP_BASE + 0x0028);
- data &= ~0x0300; /* bit 8, 9, MASTER4 */
- if (mfd->fbi->var.xres == 540) /* qHD, 540x960 */
- data |= 0x0200;
- else
- data |= 0x0100;
-
- MDP_OUTP(MDP_BASE + 0x00028, data);
- }
-
- if (mfd->panel_info.type == MDDI_PANEL) {
- if (mfd->panel_info.pdest == DISPLAY_1)
- mddi_ld_param = 0;
- else
- mddi_ld_param = 1;
- } else {
- mddi_ld_param = 2;
- }
-
- MDP_OUTP(MDP_BASE + 0x00090, mddi_ld_param);
-
- if (mfd->panel_info.bpp == 24)
- MDP_OUTP(MDP_BASE + 0x00094,
- (MDDI_VDO_PACKET_DESC_24 << 16) | mddi_vdo_packet_reg);
- else if (mfd->panel_info.bpp == 16)
- MDP_OUTP(MDP_BASE + 0x00094,
- (MDDI_VDO_PACKET_DESC_16 << 16) | mddi_vdo_packet_reg);
- else
- MDP_OUTP(MDP_BASE + 0x00094,
- (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg);
-
- MDP_OUTP(MDP_BASE + 0x00098, 0x01);
+ vctrl->base_pipe = pipe; /* keep it */
mdp4_init_writeback_buf(mfd, MDP4_MIXER0);
pipe->ov_blt_addr = 0;
pipe->dma_blt_addr = 0;
} else {
- pipe = mddi_pipe;
+ pipe = vctrl->base_pipe;
}
- /* 0 for dma_p, client_id = 0 */
- MDP_OUTP(MDP_BASE + 0x00090, 0);
+ MDP_OUTP(MDP_BASE + 0x021c, 10); /* read pointer */
+ mddi_ld_param = 0;
+ mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt;
- src = (uint8 *) iBuf->buf;
+ if (mdp_hw_revision == MDP4_REVISION_V2_1) {
+ data = inpdw(MDP_BASE + 0x0028);
+ data &= ~0x0300; /* bit 8, 9, MASTER4 */
+ if (mfd->fbi->var.xres == 540) /* qHD, 540x960 */
+ data |= 0x0200;
+ else
+ data |= 0x0100;
-#ifdef WHOLESCREEN
-
- {
- struct fb_info *fbi;
-
- fbi = mfd->fbi;
- pipe->src_height = fbi->var.yres;
- pipe->src_width = fbi->var.xres;
- pipe->src_h = fbi->var.yres;
- pipe->src_w = fbi->var.xres;
- pipe->src_y = 0;
- pipe->src_x = 0;
- pipe->dst_h = fbi->var.yres;
- pipe->dst_w = fbi->var.xres;
- pipe->dst_y = 0;
- pipe->dst_x = 0;
- pipe->srcp0_addr = (uint32)src;
- pipe->srcp0_ystride = fbi->fix.line_length;
+ MDP_OUTP(MDP_BASE + 0x00028, data);
}
-#else
- if (mdp4_overlay_active(MDP4_MIXER0)) {
- struct fb_info *fbi;
-
- fbi = mfd->fbi;
- pipe->src_height = fbi->var.yres;
- pipe->src_width = fbi->var.xres;
- pipe->src_h = fbi->var.yres;
- pipe->src_w = fbi->var.xres;
- pipe->src_y = 0;
- pipe->src_x = 0;
- pipe->dst_h = fbi->var.yres;
- pipe->dst_w = fbi->var.xres;
- pipe->dst_y = 0;
- pipe->dst_x = 0;
- pipe->srcp0_addr = (uint32) src;
- pipe->srcp0_ystride = fbi->fix.line_length;
+ if (mfd->panel_info.type == MDDI_PANEL) {
+ if (mfd->panel_info.pdest == DISPLAY_1)
+ mddi_ld_param = 0;
+ else
+ mddi_ld_param = 1;
} else {
- /* starting input address */
- src += (iBuf->dma_x + iBuf->dma_y * iBuf->ibuf_width)
- * iBuf->bpp;
-
- pipe->src_height = iBuf->dma_h;
- pipe->src_width = iBuf->dma_w;
- pipe->src_h = iBuf->dma_h;
- pipe->src_w = iBuf->dma_w;
- pipe->src_y = 0;
- pipe->src_x = 0;
- pipe->dst_h = iBuf->dma_h;
- pipe->dst_w = iBuf->dma_w;
- pipe->dst_y = iBuf->dma_y;
- pipe->dst_x = iBuf->dma_x;
- pipe->srcp0_addr = (uint32) src;
- pipe->srcp0_ystride = iBuf->ibuf_width * iBuf->bpp;
+ mddi_ld_param = 2;
}
-#endif
- pipe->mixer_stage = MDP4_MIXER_STAGE_BASE;
+ MDP_OUTP(MDP_BASE + 0x00090, mddi_ld_param);
+
+ if (mfd->panel_info.bpp == 24)
+ MDP_OUTP(MDP_BASE + 0x00094,
+ (MDDI_VDO_PACKET_DESC_24 << 16) | mddi_vdo_packet_reg);
+ else if (mfd->panel_info.bpp == 16)
+ MDP_OUTP(MDP_BASE + 0x00094,
+ (MDDI_VDO_PACKET_DESC_16 << 16) | mddi_vdo_packet_reg);
+ else
+ MDP_OUTP(MDP_BASE + 0x00094,
+ (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg);
+
+ MDP_OUTP(MDP_BASE + 0x00098, 0x01);
+
+
+ mdp4_overlay_setup_pipe_addr(mfd, pipe);
mdp4_overlay_rgb_setup(pipe);
- mdp4_mixer_stage_up(pipe, 1);
+ mdp4_overlay_reg_flush(pipe, 1);
+
+ mdp4_mixer_stage_up(pipe, 0);
mdp4_overlayproc_cfg(pipe);
mdp4_overlay_dmap_xy(pipe);
mdp4_overlay_dmap_cfg(mfd, 0);
+
mdp4_mixer_stage_commit(pipe->mixer_num);
- mdp4_mddi_vsync_enable(mfd, pipe, 0);
- /* MDP cmd block disable */
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+ wmb();
}
-int mdp4_mddi_overlay_blt_start(struct msm_fb_data_type *mfd)
+void mdp4_mddi_blt_start(struct msm_fb_data_type *mfd)
{
- unsigned long flag;
-
- pr_debug("%s: blt_end=%d blt_addr=%x pid=%d\n",
- __func__, mddi_pipe->blt_end,
- (int)mddi_pipe->ov_blt_addr, current->pid);
-
- mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0);
-
- if (mfd->ov0_wb_buf->write_addr == 0) {
- pr_info("%s: no blt_base assigned\n", __func__);
- return -EBUSY;
- }
-
- if (mddi_pipe->ov_blt_addr == 0) {
- mdp4_mddi_dma_busy_wait(mfd);
- spin_lock_irqsave(&mdp_spin_lock, flag);
- mddi_pipe->blt_end = 0;
- mddi_pipe->blt_cnt = 0;
- mddi_pipe->ov_cnt = 0;
- mddi_pipe->dmap_cnt = 0;
- mddi_pipe->ov_blt_addr = mfd->ov0_wb_buf->write_addr;
- mddi_pipe->dma_blt_addr = mfd->ov0_wb_buf->write_addr;
- mdp4_stat.blt_mddi++;
- spin_unlock_irqrestore(&mdp_spin_lock, flag);
- return 0;
+ mdp4_mddi_do_blt(mfd, 1);
}
- return -EBUSY;
-}
-
-int mdp4_mddi_overlay_blt_stop(struct msm_fb_data_type *mfd)
+void mdp4_mddi_blt_stop(struct msm_fb_data_type *mfd)
{
- unsigned long flag;
-
- pr_debug("%s: blt_end=%d blt_addr=%x\n",
- __func__, mddi_pipe->blt_end, (int)mddi_pipe->ov_blt_addr);
-
- if ((mddi_pipe->blt_end == 0) && mddi_pipe->ov_blt_addr) {
- spin_lock_irqsave(&mdp_spin_lock, flag);
- mddi_pipe->blt_end = 1; /* mark as end */
- spin_unlock_irqrestore(&mdp_spin_lock, flag);
- return 0;
- }
-
- return -EBUSY;
-}
-
-int mdp4_mddi_overlay_blt_offset(struct msm_fb_data_type *mfd,
- struct msmfb_overlay_blt *req)
-{
- req->offset = 0;
- req->width = mddi_pipe->src_width;
- req->height = mddi_pipe->src_height;
- req->bpp = mddi_pipe->bpp;
-
- return sizeof(*req);
+ mdp4_mddi_do_blt(mfd, 0);
}
void mdp4_mddi_overlay_blt(struct msm_fb_data_type *mfd,
struct msmfb_overlay_blt *req)
{
- if (req->enable)
- mdp4_mddi_overlay_blt_start(mfd);
- else if (req->enable == 0)
- mdp4_mddi_overlay_blt_stop(mfd);
-
+ mdp4_mddi_do_blt(mfd, req->enable);
}
-void mdp4_blt_xy_update(struct mdp4_overlay_pipe *pipe)
+int mdp4_mddi_on(struct platform_device *pdev)
{
- uint32 off, addr, addr2;
- int bpp;
- char *overlay_base;
+ int ret = 0;
+ int cndx = 0;
+ struct msm_fb_data_type *mfd;
+ struct vsycn_ctrl *vctrl;
- if (pipe->ov_blt_addr == 0)
- return;
+ pr_debug("%s+:\n", __func__);
+ mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
-#ifdef BLT_RGB565
- bpp = 2; /* overlay ouput is RGB565 */
-#else
- bpp = 3; /* overlay ouput is RGB888 */
-#endif
- off = 0;
- if (pipe->dmap_cnt & 0x01)
- off = pipe->src_height * pipe->src_width * bpp;
+ vctrl = &vsync_ctrl_db[cndx];
+ vctrl->mfd = mfd;
+ vctrl->dev = mfd->fbi->dev;
- addr = pipe->ov_blt_addr + off;
+ mdp_clk_ctrl(1);
+ mdp4_overlay_update_mddi(mfd);
+ mdp_clk_ctrl(0);
- /* dmap */
- MDP_OUTP(MDP_BASE + 0x90008, addr);
+ mdp4_iommu_attach();
- off = 0;
- if (pipe->ov_cnt & 0x01)
- off = pipe->src_height * pipe->src_width * bpp;
- addr2 = pipe->ov_blt_addr + off;
- /* overlay 0 */
- overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
- outpdw(overlay_base + 0x000c, addr2);
- outpdw(overlay_base + 0x001c, addr2);
+ atomic_set(&vctrl->suspend, 0);
+ pr_debug("%s-:\n", __func__);
+
+ return ret;
}
-void mdp4_primary_rdptr(void)
+int mdp4_mddi_off(struct platform_device *pdev)
{
-}
+ int ret = 0;
+ int cndx = 0;
+ struct msm_fb_data_type *mfd;
+ struct vsycn_ctrl *vctrl;
+ struct mdp4_overlay_pipe *pipe;
-/*
- * mdp4_dmap_done_mddi: called from isr
- */
-void mdp4_dma_p_done_mddi(struct mdp_dma_data *dma)
-{
- int diff;
+ pr_debug("%s+:\n", __func__);
- mddi_pipe->dmap_cnt++;
- diff = mddi_pipe->ov_cnt - mddi_pipe->dmap_cnt;
- pr_debug("%s: ov_cnt=%d dmap_cnt=%d\n",
- __func__, mddi_pipe->ov_cnt, mddi_pipe->dmap_cnt);
+ mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
- if (diff <= 0) {
- spin_lock(&mdp_spin_lock);
- dma->dmap_busy = FALSE;
- complete(&dma->dmap_comp);
- spin_unlock(&mdp_spin_lock);
-
- if (mddi_pipe->blt_end) {
- mddi_pipe->blt_end = 0;
- mddi_pipe->ov_blt_addr = 0;
- mddi_pipe->dma_blt_addr = 0;
- pr_debug("%s: END, ov_cnt=%d dmap_cnt=%d\n", __func__,
- mddi_pipe->ov_cnt, mddi_pipe->dmap_cnt);
- mdp_intr_mask &= ~INTR_DMA_P_DONE;
- outp32(MDP_INTR_ENABLE, mdp_intr_mask);
- }
-
- mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE);
- mdp_disable_irq_nosync(MDP_DMA2_TERM); /* disable intr */
- return;
+ vctrl = &vsync_ctrl_db[cndx];
+ pipe = vctrl->base_pipe;
+ if (pipe == NULL) {
+ pr_err("%s: NO base pipe\n", __func__);
+ return ret;
}
- spin_lock(&mdp_spin_lock);
- dma->busy = FALSE;
- spin_unlock(&mdp_spin_lock);
- complete(&dma->comp);
- if (busy_wait_cnt)
- busy_wait_cnt--;
+ atomic_set(&vctrl->suspend, 1);
- pr_debug("%s: kickoff dmap\n", __func__);
+ /* sanity check, free pipes besides base layer */
+ mdp4_overlay_unset_mixer(pipe->mixer_num);
+ mdp4_mixer_stage_down(pipe, 1);
+ mdp4_overlay_pipe_free(pipe);
+ vctrl->base_pipe = NULL;
- mdp4_blt_xy_update(mddi_pipe);
- /* kick off dmap */
- outpdw(MDP_BASE + 0x000c, 0x0);
- mdp4_stat.kickoff_dmap++;
- mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE);
-}
-
-/*
- * mdp4_overlay0_done_mddi: called from isr
- */
-void mdp4_overlay0_done_mddi(struct mdp_dma_data *dma)
-{
- int diff;
-
- if (mddi_pipe->ov_blt_addr == 0) {
- mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE);
- spin_lock(&mdp_spin_lock);
- dma->busy = FALSE;
- spin_unlock(&mdp_spin_lock);
- complete(&dma->comp);
-
- if (busy_wait_cnt)
- busy_wait_cnt--;
- mdp_disable_irq_nosync(MDP_OVERLAY0_TERM);
-
- return;
+ if (vctrl->clk_enabled) {
+ /*
+ * in case of suspend, vsycn_ctrl off is not
+ * received from frame work which left clock on
+ * then, clock need to be turned off here
+ */
+ mdp_clk_ctrl(0);
}
- /* blt enabled */
- if (mddi_pipe->blt_end == 0)
- mddi_pipe->ov_cnt++;
+ vctrl->clk_enabled = 0;
+ vctrl->vsync_enabled = 0;
+ vctrl->clk_control = 0;
+ vctrl->expire_tick = 0;
+ vctrl->uevent = 0;
- pr_debug("%s: ov_cnt=%d dmap_cnt=%d\n",
- __func__, mddi_pipe->ov_cnt, mddi_pipe->dmap_cnt);
+ vsync_irq_disable(INTR_PRIMARY_RDPTR, MDP_PRIM_RDPTR_TERM);
- if (mddi_pipe->blt_cnt == 0) {
- /* first kickoff since blt enabled */
- mdp_intr_mask |= INTR_DMA_P_DONE;
- outp32(MDP_INTR_ENABLE, mdp_intr_mask);
- }
+ pr_debug("%s-:\n", __func__);
- mddi_pipe->blt_cnt++;
-
- diff = mddi_pipe->ov_cnt - mddi_pipe->dmap_cnt;
- if (diff >= 2) {
- mdp_disable_irq_nosync(MDP_OVERLAY0_TERM);
- return;
- }
-
- spin_lock(&mdp_spin_lock);
- dma->busy = FALSE;
- dma->dmap_busy = TRUE;
- spin_unlock(&mdp_spin_lock);
- complete(&dma->comp);
-
- if (busy_wait_cnt)
- busy_wait_cnt--;
-
- pr_debug("%s: kickoff dmap\n", __func__);
-
- mdp4_blt_xy_update(mddi_pipe);
- mdp_enable_irq(MDP_DMA2_TERM); /* enable intr */
- /* kick off dmap */
- outpdw(MDP_BASE + 0x000c, 0x0);
- mdp4_stat.kickoff_dmap++;
- mdp_disable_irq_nosync(MDP_OVERLAY0_TERM);
-}
-
-void mdp4_mddi_overlay_restore(void)
-{
- if (mddi_mfd == NULL)
- return;
-
- pr_debug("%s: resotre, pid=%d\n", __func__, current->pid);
-
- if (mddi_mfd->panel_power_on == 0)
- return;
- if (mddi_mfd && mddi_pipe) {
- mdp4_mddi_dma_busy_wait(mddi_mfd);
- mdp4_overlay_update_lcd(mddi_mfd);
-
- if (mddi_pipe->ov_blt_addr)
- mdp4_mddi_blt_dmap_busy_wait(mddi_mfd);
- mdp4_mddi_overlay_kickoff(mddi_mfd, mddi_pipe);
- mddi_mfd->dma_update_flag = 1;
- }
- if (mdp_hw_revision < MDP4_REVISION_V2_1) /* need dmas dmap switch */
- mdp4_mddi_overlay_dmas_restore();
-}
-
-void mdp4_mddi_blt_dmap_busy_wait(struct msm_fb_data_type *mfd)
-{
- unsigned long flag;
- int need_wait = 0;
-
- spin_lock_irqsave(&mdp_spin_lock, flag);
- if (mfd->dma->dmap_busy == TRUE) {
- INIT_COMPLETION(mfd->dma->dmap_comp);
- need_wait++;
- }
- spin_unlock_irqrestore(&mdp_spin_lock, flag);
-
- if (need_wait) {
- /* wait until DMA finishes the current job */
- wait_for_completion(&mfd->dma->dmap_comp);
- }
-}
-
-/*
- * mdp4_mddi_cmd_dma_busy_wait: check mddi link activity
- * mddi link is a shared resource and it can only be used
- * while it is in idle state.
- * ov_mutex need to be acquired before call this function.
- */
-void mdp4_mddi_dma_busy_wait(struct msm_fb_data_type *mfd)
-{
- unsigned long flag;
- int need_wait = 0;
-
- pr_debug("%s: START, pid=%d\n", __func__, current->pid);
- spin_lock_irqsave(&mdp_spin_lock, flag);
- if (mfd->dma->busy == TRUE) {
- if (busy_wait_cnt == 0)
- INIT_COMPLETION(mfd->dma->comp);
- busy_wait_cnt++;
- need_wait++;
- }
- spin_unlock_irqrestore(&mdp_spin_lock, flag);
-
-
- if (need_wait) {
- /* wait until DMA finishes the current job */
- pr_debug("%s: PENDING, pid=%d\n", __func__, current->pid);
- wait_for_completion(&mfd->dma->comp);
- }
- pr_debug("%s: DONE, pid=%d\n", __func__, current->pid);
-}
-
-void mdp4_mddi_kickoff_video(struct msm_fb_data_type *mfd,
- struct mdp4_overlay_pipe *pipe)
-{
/*
- * a video kickoff may happen before UI kickoff after
- * blt enabled. mdp4_overlay_update_lcd() need
- * to be called before kickoff.
- * vice versa for blt disabled.
+ * footswitch off
+ * this will casue all mdp register
+ * to be reset to default
+ * after footswitch on later
*/
- if (mddi_pipe->ov_blt_addr && mddi_pipe->blt_cnt == 0)
- mdp4_overlay_update_lcd(mfd); /* first time */
- else if (mddi_pipe->ov_blt_addr == 0 && mddi_pipe->blt_cnt) {
- mdp4_overlay_update_lcd(mfd); /* last time */
- mddi_pipe->blt_cnt = 0;
- }
- pr_debug("%s: blt_addr=%d blt_cnt=%d\n",
- __func__, (int)mddi_pipe->ov_blt_addr, mddi_pipe->blt_cnt);
-
- if (mddi_pipe->ov_blt_addr)
- mdp4_mddi_blt_dmap_busy_wait(mddi_mfd);
- mdp4_mddi_overlay_kickoff(mfd, pipe);
+ return ret;
}
-void mdp4_mddi_kickoff_ui(struct msm_fb_data_type *mfd,
- struct mdp4_overlay_pipe *pipe)
+void mdp_mddi_overlay_suspend(struct msm_fb_data_type *mfd)
{
- pr_debug("%s: pid=%d\n", __func__, current->pid);
- mdp4_mddi_overlay_kickoff(mfd, pipe);
-}
+ int cndx = 0;
+ struct vsycn_ctrl *vctrl;
+ struct mdp4_overlay_pipe *pipe;
+ vctrl = &vsync_ctrl_db[cndx];
+ pipe = vctrl->base_pipe;
+ /* dis-engage rgb0 from mixer0 */
+ if (pipe) {
+ if (mfd->ref_cnt == 0) {
+ /* adb stop */
+ if (pipe->pipe_type == OVERLAY_TYPE_BF)
+ mdp4_overlay_borderfill_stage_down(pipe);
-void mdp4_mddi_overlay_kickoff(struct msm_fb_data_type *mfd,
- struct mdp4_overlay_pipe *pipe)
-{
- unsigned long flag;
-
- mdp_enable_irq(MDP_OVERLAY0_TERM);
- spin_lock_irqsave(&mdp_spin_lock, flag);
- mfd->dma->busy = TRUE;
- if (mddi_pipe->ov_blt_addr)
- mfd->dma->dmap_busy = TRUE;
- spin_unlock_irqrestore(&mdp_spin_lock, flag);
- /* start OVERLAY pipe */
- mdp_pipe_kickoff(MDP_OVERLAY0_TERM, mfd);
- mdp4_stat.kickoff_ov0++;
-}
-
-void mdp4_dma_s_update_lcd(struct msm_fb_data_type *mfd,
- struct mdp4_overlay_pipe *pipe)
-{
- MDPIBUF *iBuf = &mfd->ibuf;
- uint32 outBpp = iBuf->bpp;
- uint16 mddi_vdo_packet_reg;
- uint32 dma_s_cfg_reg;
-
- dma_s_cfg_reg = 0;
-
- if (mfd->fb_imgType == MDP_RGBA_8888)
- dma_s_cfg_reg |= DMA_PACK_PATTERN_BGR; /* on purpose */
- else if (mfd->fb_imgType == MDP_BGR_565)
- dma_s_cfg_reg |= DMA_PACK_PATTERN_BGR;
- else
- dma_s_cfg_reg |= DMA_PACK_PATTERN_RGB;
-
- if (outBpp == 4)
- dma_s_cfg_reg |= (1 << 26); /* xRGB8888 */
- else if (outBpp == 2)
- dma_s_cfg_reg |= DMA_IBUF_FORMAT_RGB565;
-
- dma_s_cfg_reg |= DMA_DITHER_EN;
-
- /* MDP cmd block enable */
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
- /* PIXELSIZE */
- MDP_OUTP(MDP_BASE + 0xa0004, (pipe->dst_h << 16 | pipe->dst_w));
- MDP_OUTP(MDP_BASE + 0xa0008, pipe->srcp0_addr); /* ibuf address */
- MDP_OUTP(MDP_BASE + 0xa000c, pipe->srcp0_ystride);/* ystride */
-
- if (mfd->panel_info.bpp == 24) {
- dma_s_cfg_reg |= DMA_DSTC0G_8BITS | /* 666 18BPP */
- DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS;
- } else if (mfd->panel_info.bpp == 18) {
- dma_s_cfg_reg |= DMA_DSTC0G_6BITS | /* 666 18BPP */
- DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
- } else {
- dma_s_cfg_reg |= DMA_DSTC0G_6BITS | /* 565 16BPP */
- DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
- }
-
- MDP_OUTP(MDP_BASE + 0xa0010, (pipe->dst_y << 16) | pipe->dst_x);
-
- /* 1 for dma_s, client_id = 0 */
- MDP_OUTP(MDP_BASE + 0x00090, 1);
-
- mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt;
-
- if (mfd->panel_info.bpp == 24)
- MDP_OUTP(MDP_BASE + 0x00094,
- (MDDI_VDO_PACKET_DESC_24 << 16) | mddi_vdo_packet_reg);
- else if (mfd->panel_info.bpp == 16)
- MDP_OUTP(MDP_BASE + 0x00094,
- (MDDI_VDO_PACKET_DESC_16 << 16) | mddi_vdo_packet_reg);
- else
- MDP_OUTP(MDP_BASE + 0x00094,
- (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg);
-
- MDP_OUTP(MDP_BASE + 0x00098, 0x01);
-
- MDP_OUTP(MDP_BASE + 0xa0000, dma_s_cfg_reg);
-
- mdp4_mddi_vsync_enable(mfd, pipe, 1);
-
- /* MDP cmd block disable */
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
-}
-
-void mdp4_mddi_dma_s_kickoff(struct msm_fb_data_type *mfd,
- struct mdp4_overlay_pipe *pipe)
-{
- mdp_enable_irq(MDP_DMA_S_TERM);
-
- if (mddi_pipe->ov_blt_addr == 0)
- mfd->dma->busy = TRUE;
-
- mfd->ibuf_flushed = TRUE;
- /* start dma_s pipe */
- mdp_pipe_kickoff(MDP_DMA_S_TERM, mfd);
- mdp4_stat.kickoff_dmas++;
-
- /* wait until DMA finishes the current job */
- wait_for_completion(&mfd->dma->comp);
- mdp_disable_irq(MDP_DMA_S_TERM);
-}
-
-void mdp4_mddi_overlay_dmas_restore(void)
-{
- /* mutex held by caller */
- if (mddi_mfd && mddi_pipe) {
- mdp4_mddi_dma_busy_wait(mddi_mfd);
- mdp4_dma_s_update_lcd(mddi_mfd, mddi_pipe);
- mdp4_mddi_dma_s_kickoff(mddi_mfd, mddi_pipe);
- mddi_mfd->dma_update_flag = 1;
+ /* pipe == rgb1 */
+ mdp4_overlay_unset_mixer(pipe->mixer_num);
+ vctrl->base_pipe = NULL;
+ } else {
+ mdp4_mixer_stage_down(pipe, 1);
+ mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 1);
+ }
}
}
void mdp4_mddi_overlay(struct msm_fb_data_type *mfd)
{
- mutex_lock(&mfd->dma->ov_mutex);
+ int cndx = 0;
+ struct vsycn_ctrl *vctrl;
+ struct mdp4_overlay_pipe *pipe;
+ unsigned long flags;
+ long long xx;
- if (mfd && mfd->panel_power_on) {
- mdp4_mddi_dma_busy_wait(mfd);
+ vctrl = &vsync_ctrl_db[cndx];
- if (mddi_pipe && mddi_pipe->ov_blt_addr)
- mdp4_mddi_blt_dmap_busy_wait(mfd);
- mdp4_overlay_mdp_perf_upd(mfd, 0);
- mdp4_overlay_update_lcd(mfd);
+ if (!mfd->panel_power_on)
+ return;
- mdp4_overlay_mdp_perf_upd(mfd, 1);
- if (mdp_hw_revision < MDP4_REVISION_V2_1) {
- /* dmas dmap switch */
- if (mdp4_overlay_mixer_play(mddi_pipe->mixer_num)
- == 0) {
- mdp4_dma_s_update_lcd(mfd, mddi_pipe);
- mdp4_mddi_dma_s_kickoff(mfd, mddi_pipe);
- } else
- mdp4_mddi_kickoff_ui(mfd, mddi_pipe);
- } else /* no dams dmap switch */
- mdp4_mddi_kickoff_ui(mfd, mddi_pipe);
-
- /* signal if pan function is waiting for the update completion */
- if (mfd->pan_waiting) {
- mfd->pan_waiting = FALSE;
- complete(&mfd->pan_comp);
- }
+ pipe = vctrl->base_pipe;
+ if (pipe == NULL) {
+ pr_err("%s: NO base pipe\n", __func__);
+ return;
}
+
+ mutex_lock(&vctrl->update_lock);
+ if (!vctrl->clk_enabled) {
+ pr_err("%s: mdp clocks disabled\n", __func__);
+ mutex_unlock(&vctrl->update_lock);
+ return;
+
+ }
+ mutex_unlock(&vctrl->update_lock);
+
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ if (vctrl->expire_tick) {
+ /*
+ * in the middle of shutting clocks down
+ * delay to allow pan display to go through
+ */
+ vctrl->expire_tick = VSYNC_EXPIRE_TICK;
+ }
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ if (pipe->mixer_stage == MDP4_MIXER_STAGE_BASE) {
+ mdp4_mddi_vsync_enable(mfd, pipe, 0);
+ mdp4_overlay_setup_pipe_addr(mfd, pipe);
+ mdp4_mddi_pipe_queue(0, pipe);
+ }
+
+ mdp4_overlay_mdp_perf_upd(mfd, 1);
+
+ mutex_lock(&mfd->dma->ov_mutex);
+ mdp4_mddi_pipe_commit();
mutex_unlock(&mfd->dma->ov_mutex);
+ mdp4_mddi_wait4vsync(0, &xx);
+
+ mdp4_overlay_mdp_perf_upd(mfd, 0);
}
int mdp4_mddi_overlay_cursor(struct fb_info *info, struct fb_cursor *cursor)
@@ -722,7 +1061,6 @@
struct msm_fb_data_type *mfd = info->par;
mutex_lock(&mfd->dma->ov_mutex);
if (mfd && mfd->panel_power_on) {
- mdp4_mddi_dma_busy_wait(mfd);
mdp_hw_cursor_update(info, cursor);
}
mutex_unlock(&mfd->dma->ov_mutex);
diff --git a/drivers/video/msm/mdp4_overlay_writeback.c b/drivers/video/msm/mdp4_overlay_writeback.c
index ee7e9ce..18c6635 100644
--- a/drivers/video/msm/mdp4_overlay_writeback.c
+++ b/drivers/video/msm/mdp4_overlay_writeback.c
@@ -45,9 +45,49 @@
WITH_CLIENT
};
-static struct mdp4_overlay_pipe *writeback_pipe;
-static struct msm_fb_data_type *writeback_mfd;
-static int busy_wait_cnt;
+#define MAX_CONTROLLER 1
+#define VSYNC_EXPIRE_TICK 0
+
+static struct vsycn_ctrl {
+ struct device *dev;
+ int inited;
+ int update_ndx;
+ u32 ov_koff;
+ u32 ov_done;
+ atomic_t suspend;
+ struct mutex update_lock;
+ struct completion ov_comp;
+ spinlock_t spin_lock;
+ struct msm_fb_data_type *mfd;
+ struct mdp4_overlay_pipe *base_pipe;
+ struct vsync_update vlist[2];
+} vsync_ctrl_db[MAX_CONTROLLER];
+
+static void vsync_irq_enable(int intr, int term)
+{
+ unsigned long flag;
+
+ spin_lock_irqsave(&mdp_spin_lock, flag);
+ /* no need to clrear other interrupts for comamnd mode */
+ mdp_intr_mask |= intr;
+ outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+ mdp_enable_irq(term);
+ spin_unlock_irqrestore(&mdp_spin_lock, flag);
+}
+
+static void vsync_irq_disable(int intr, int term)
+{
+ unsigned long flag;
+
+ spin_lock_irqsave(&mdp_spin_lock, flag);
+ /* no need to clrear other interrupts for comamnd mode */
+ mdp_intr_mask &= ~intr;
+ outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+ mdp_disable_irq_nosync(term);
+ spin_unlock_irqrestore(&mdp_spin_lock, flag);
+}
+
+static int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd);
int mdp4_overlay_writeback_on(struct platform_device *pdev)
{
@@ -58,6 +98,8 @@
int bpp;
int ret;
uint32 data;
+ struct vsycn_ctrl *vctrl;
+ int cndx = 0;
mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
@@ -67,7 +109,9 @@
if (mfd->key != MFD_KEY)
return -EINVAL;
- writeback_mfd = mfd; /* keep it */
+ vctrl = &vsync_ctrl_db[cndx];
+ vctrl->mfd = mfd;
+ vctrl->dev = mfd->fbi->dev;
fbi = mfd->fbi;
@@ -77,12 +121,14 @@
fbi->var.yoffset * fbi->fix.line_length;
/* MDP cmd block enable */
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+ mdp_clk_ctrl(1);
- if (writeback_pipe == NULL) {
+ if (vctrl->base_pipe == NULL) {
pipe = mdp4_overlay_pipe_alloc(OVERLAY_TYPE_BF, MDP4_MIXER2);
- if (pipe == NULL)
+ if (pipe == NULL) {
pr_info("%s: pipe_alloc failed\n", __func__);
+ return -EIO;
+ }
pipe->pipe_used++;
pipe->mixer_stage = MDP4_MIXER_STAGE_BASE;
pipe->mixer_num = MDP4_MIXER2;
@@ -92,11 +138,12 @@
if (ret < 0)
pr_info("%s: format2type failed\n", __func__);
- writeback_pipe = pipe; /* keep it */
+ vctrl->base_pipe = pipe; /* keep it */
} else {
- pipe = writeback_pipe;
+ pipe = vctrl->base_pipe;
}
+
ret = panel_next_on(pdev);
/* MDP_LAYERMIXER_WB_MUX_SEL to use mixer1 axi for mixer2 writeback */
@@ -113,46 +160,68 @@
MDP_OUTP(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x5008,
(0x0 & 0xFFF)); /* 12-bit R */
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+ mdp_clk_ctrl(0);
return ret;
}
int mdp4_overlay_writeback_off(struct platform_device *pdev)
{
- int ret;
- struct msm_fb_data_type *mfd =
- (struct msm_fb_data_type *)platform_get_drvdata(pdev);
- if (mfd && writeback_pipe) {
- mdp4_writeback_dma_busy_wait(mfd);
- mdp4_overlay_pipe_free(writeback_pipe);
- mdp4_overlay_panel_mode_unset(writeback_pipe->mixer_num,
- MDP4_PANEL_WRITEBACK);
- writeback_pipe = NULL;
+ int cndx = 0;
+ struct msm_fb_data_type *mfd;
+ struct vsycn_ctrl *vctrl;
+ struct mdp4_overlay_pipe *pipe;
+ int ret = 0;
+
+ pr_debug("%s+:\n", __func__);
+
+ mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+ vctrl = &vsync_ctrl_db[cndx];
+ pipe = vctrl->base_pipe;
+ if (pipe == NULL) {
+ pr_err("%s: NO base pipe\n", __func__);
+ return ret;
}
+
+ /* sanity check, free pipes besides base layer */
+ mdp4_overlay_unset_mixer(pipe->mixer_num);
+ mdp4_mixer_stage_down(pipe, 1);
+ mdp4_overlay_pipe_free(pipe);
+ vctrl->base_pipe = NULL;
+
ret = panel_next_off(pdev);
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+ mdp_clk_ctrl(1);
/* MDP_LAYERMIXER_WB_MUX_SEL to restore to default cfg*/
outpdw(MDP_BASE + 0x100F4, 0x0);
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+ mdp_clk_ctrl(0);
+ pr_debug("%s-:\n", __func__);
return ret;
}
-int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd)
+
+static int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd)
{
struct fb_info *fbi;
uint8 *buf;
unsigned int buf_offset;
struct mdp4_overlay_pipe *pipe;
int bpp;
+ int cndx = 0;
+ struct vsycn_ctrl *vctrl;
if (mfd->key != MFD_KEY)
return -ENODEV;
- if (!writeback_pipe)
- return -EINVAL;
fbi = mfd->fbi;
- pipe = writeback_pipe;
+ vctrl = &vsync_ctrl_db[cndx];
+
+ pipe = vctrl->base_pipe;
+ if (!pipe) {
+ pr_err("%s: no base layer pipe\n", __func__);
+ return -EINVAL;
+ }
bpp = fbi->var.bits_per_pixel / 8;
buf = (uint8 *) fbi->fix.smem_start;
@@ -160,7 +229,7 @@
fbi->var.yoffset * fbi->fix.line_length;
/* MDP cmd block enable */
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+ mdp_clk_ctrl(1);
pipe->src_height = fbi->var.yres;
pipe->src_width = fbi->var.xres;
@@ -184,142 +253,190 @@
mdp4_mixer_stage_up(pipe, 0);
mdp4_overlayproc_cfg(pipe);
- mdp4_mixer_stage_commit(pipe->mixer_num);
/* MDP cmd block disable */
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+ mdp_clk_ctrl(0);
wmb();
return 0;
}
-void mdp4_writeback_dma_busy_wait(struct msm_fb_data_type *mfd)
+
+/*
+ * mdp4_wfd_piep_queue:
+ * called from thread context
+ */
+void mdp4_wfd_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe)
{
- unsigned long flag;
- int need_wait = 0;
+ struct vsycn_ctrl *vctrl;
+ struct vsync_update *vp;
+ struct mdp4_overlay_pipe *pp;
+ int undx;
- spin_lock_irqsave(&mdp_spin_lock, flag);
- if (mfd->dma->busy == TRUE) {
- if (busy_wait_cnt == 0)
- INIT_COMPLETION(mfd->dma->comp);
- busy_wait_cnt = 1;
- need_wait++;
- }
- spin_unlock_irqrestore(&mdp_spin_lock, flag);
-
- if (need_wait) {
- /* wait until DMA finishes the current job */
- pr_debug("%s: pending pid=%d\n",
- __func__, current->pid);
- wait_for_completion(&mfd->dma->comp);
- }
-}
-
-void mdp4_overlay1_done_writeback(struct mdp_dma_data *dma)
-{
- spin_lock(&mdp_spin_lock);
- dma->busy = FALSE;
- if (busy_wait_cnt)
- busy_wait_cnt = 0;
- mdp_disable_irq_nosync(MDP_OVERLAY2_TERM);
- spin_unlock(&mdp_spin_lock);
- complete_all(&dma->comp);
- pr_debug("%s ovdone interrupt\n", __func__);
-
-}
-void mdp4_writeback_overlay_kickoff(struct msm_fb_data_type *mfd,
- struct mdp4_overlay_pipe *pipe)
-{
- unsigned long flag;
- spin_lock_irqsave(&mdp_spin_lock, flag);
- mdp_enable_irq(MDP_OVERLAY2_TERM);
-
- mfd->dma->busy = TRUE;
- outp32(MDP_INTR_CLEAR, INTR_OVERLAY2_DONE);
- mdp_intr_mask |= INTR_OVERLAY2_DONE;
- outp32(MDP_INTR_ENABLE, mdp_intr_mask);
-
- wmb(); /* make sure all registers updated */
- spin_unlock_irqrestore(&mdp_spin_lock, flag);
- /* start OVERLAY pipe */
- mdp_pipe_kickoff(MDP_OVERLAY2_TERM, mfd);
- wmb();
- pr_debug("%s: before ov done interrupt\n", __func__);
-}
-void mdp4_writeback_dma_stop(struct msm_fb_data_type *mfd)
-{
- /* mutex holded by caller */
- if (mfd && writeback_pipe) {
- mdp4_writeback_dma_busy_wait(mfd);
- mdp4_overlay_writeback_update(mfd);
-
- mdp4_writeback_overlay_kickoff(mfd, writeback_pipe);
- }
-}
-
-void mdp4_writeback_kickoff_video(struct msm_fb_data_type *mfd,
- struct mdp4_overlay_pipe *pipe)
-{
- struct msmfb_writeback_data_list *node = NULL;
- mutex_lock(&mfd->unregister_mutex);
- mutex_lock(&mfd->writeback_mutex);
- if (!list_empty(&mfd->writeback_free_queue)
- && mfd->writeback_state != WB_STOPING
- && mfd->writeback_state != WB_STOP) {
- node = list_first_entry(&mfd->writeback_free_queue,
- struct msmfb_writeback_data_list, active_entry);
- }
- if (node) {
- list_del(&(node->active_entry));
- node->state = IN_BUSY_QUEUE;
- mfd->writeback_active_cnt++;
- }
- mutex_unlock(&mfd->writeback_mutex);
-
- writeback_pipe->ov_blt_addr = (ulong) (node ? node->addr : NULL);
-
- /* free previous iommu at freelist back to pool */
- mdp4_overlay_iommu_unmap_freelist(writeback_pipe->mixer_num);
-
- if (!writeback_pipe->ov_blt_addr) {
- pr_err("%s: no writeback buffer 0x%x, %p\n", __func__,
- (unsigned int)writeback_pipe->ov_blt_addr, node);
- mutex_unlock(&mfd->unregister_mutex);
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
return;
}
- if (writeback_pipe->blt_cnt == 0)
- mdp4_overlay_writeback_update(mfd);
+ vctrl = &vsync_ctrl_db[cndx];
- pr_debug("%s: pid=%d\n", __func__, current->pid);
+ if (atomic_read(&vctrl->suspend) > 0)
+ return;
- mdp4_mixer_stage_commit(pipe->mixer_num);
+ mutex_lock(&vctrl->update_lock);
+ undx = vctrl->update_ndx;
+ vp = &vctrl->vlist[undx];
- mdp4_writeback_overlay_kickoff(mfd, pipe);
- mdp4_writeback_dma_busy_wait(mfd);
+ pp = &vp->plist[pipe->pipe_ndx - 1]; /* ndx start form 1 */
- /* move current committed iommu to freelist */
- mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 0);
+ pr_debug("%s: vndx=%d pipe_ndx=%d pid=%d\n", __func__,
+ undx, pipe->pipe_ndx, current->pid);
- mutex_lock(&mfd->writeback_mutex);
- list_add_tail(&node->active_entry, &mfd->writeback_busy_queue);
- mutex_unlock(&mfd->writeback_mutex);
- mfd->writeback_active_cnt--;
- mutex_unlock(&mfd->unregister_mutex);
- wake_up(&mfd->wait_q);
+ *pp = *pipe; /* clone it */
+ vp->update_cnt++;
+
+ mutex_unlock(&vctrl->update_lock);
+ mdp4_stat.overlay_play[pipe->mixer_num]++;
}
-void mdp4_writeback_kickoff_ui(struct msm_fb_data_type *mfd,
- struct mdp4_overlay_pipe *pipe)
-{
- mdp4_mixer_stage_commit(pipe->mixer_num);
+static void mdp4_wfd_wait4ov(int cndx);
- pr_debug("%s: pid=%d\n", __func__, current->pid);
- mdp4_writeback_overlay_kickoff(mfd, pipe);
+int mdp4_wfd_pipe_commit(void)
+{
+ int i, undx;
+ int mixer = 0;
+ struct vsycn_ctrl *vctrl;
+ struct vsync_update *vp;
+ struct mdp4_overlay_pipe *pipe;
+ struct mdp4_overlay_pipe *real_pipe;
+ unsigned long flags;
+ int cnt = 0;
+
+ vctrl = &vsync_ctrl_db[0];
+
+ mutex_lock(&vctrl->update_lock);
+ undx = vctrl->update_ndx;
+ vp = &vctrl->vlist[undx];
+ pipe = vctrl->base_pipe;
+ mixer = pipe->mixer_num;
+
+ if (vp->update_cnt == 0) {
+ mutex_unlock(&vctrl->update_lock);
+ return cnt;
+ }
+
+ vctrl->update_ndx++;
+ vctrl->update_ndx &= 0x01;
+ vp->update_cnt = 0; /* reset */
+ mutex_unlock(&vctrl->update_lock);
+
+ /* free previous committed iommu back to pool */
+ mdp4_overlay_iommu_unmap_freelist(mixer);
+
+ pipe = vp->plist;
+ for (i = 0; i < OVERLAY_PIPE_MAX; i++, pipe++) {
+ if (pipe->pipe_used) {
+ cnt++;
+ real_pipe = mdp4_overlay_ndx2pipe(pipe->pipe_ndx);
+ if (real_pipe && real_pipe->pipe_used) {
+ /* pipe not unset */
+ mdp4_overlay_vsync_commit(pipe);
+ }
+ /* free previous iommu to freelist
+ * which will be freed at next
+ * pipe_commit
+ */
+ mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 0);
+ pipe->pipe_used = 0; /* clear */
+ }
+ }
+
+ mdp4_mixer_stage_commit(mixer);
+
+ pipe = vctrl->base_pipe;
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ vctrl->ov_koff++;
+ INIT_COMPLETION(vctrl->ov_comp);
+ vsync_irq_enable(INTR_OVERLAY2_DONE, MDP_OVERLAY2_TERM);
+ pr_debug("%s: kickoff\n", __func__);
+ /* kickoff overlay engine */
+ mdp4_stat.kickoff_ov2++;
+ outpdw(MDP_BASE + 0x00D0, 0);
+ mb(); /* make sure kickoff executed */
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ mdp4_stat.overlay_commit[pipe->mixer_num]++;
+
+ return cnt;
+}
+
+void mdp4_wfd_init(int cndx)
+{
+ struct vsycn_ctrl *vctrl;
+
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+ return;
+ }
+
+ vctrl = &vsync_ctrl_db[cndx];
+ if (vctrl->inited)
+ return;
+
+ vctrl->inited = 1;
+ vctrl->update_ndx = 0;
+ mutex_init(&vctrl->update_lock);
+ init_completion(&vctrl->ov_comp);
+ spin_lock_init(&vctrl->spin_lock);
+}
+
+static void mdp4_wfd_wait4ov(int cndx)
+{
+ struct vsycn_ctrl *vctrl;
+
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+ return;
+ }
+
+ vctrl = &vsync_ctrl_db[cndx];
+
+ if (atomic_read(&vctrl->suspend) > 0)
+ return;
+
+ wait_for_completion(&vctrl->ov_comp);
+}
+
+
+void mdp4_overlay2_done_wfd(struct mdp_dma_data *dma)
+{
+ struct vsycn_ctrl *vctrl;
+ struct mdp4_overlay_pipe *pipe;
+ int cndx = 0;
+
+ vctrl = &vsync_ctrl_db[cndx];
+ pipe = vctrl->base_pipe;
+
+ spin_lock(&vctrl->spin_lock);
+ vsync_irq_disable(INTR_OVERLAY2_DONE, MDP_OVERLAY2_TERM);
+ vctrl->ov_done++;
+ complete(&vctrl->ov_comp);
+
+ pr_debug("%s ovdone interrupt\n", __func__);
+ spin_unlock(&vctrl->spin_lock);
}
void mdp4_writeback_overlay(struct msm_fb_data_type *mfd)
{
- int ret = 0;
struct msmfb_writeback_data_list *node = NULL;
+ struct vsycn_ctrl *vctrl;
+ struct mdp4_overlay_pipe *pipe;
+
+ if (mfd && !mfd->panel_power_on)
+ return;
+
+ pr_debug("%s:+ mfd=%x\n", __func__, (int)mfd);
+
+ vctrl = &vsync_ctrl_db[0];
+ pipe = vctrl->base_pipe;
mutex_lock(&mfd->unregister_mutex);
mutex_lock(&mfd->writeback_mutex);
@@ -336,44 +453,36 @@
}
mutex_unlock(&mfd->writeback_mutex);
- writeback_pipe->ov_blt_addr = (ulong) (node ? node->addr : NULL);
+ pipe->ov_blt_addr = (ulong) (node ? node->addr : NULL);
+
+ if (!pipe->ov_blt_addr) {
+ pr_err("%s: no writeback buffer 0x%x, %p\n", __func__,
+ (unsigned int)pipe->ov_blt_addr, node);
+ mutex_unlock(&mfd->unregister_mutex);
+ return;
+ }
mutex_lock(&mfd->dma->ov_mutex);
- pr_debug("%s in writeback\n", __func__);
- if (writeback_pipe && !writeback_pipe->ov_blt_addr) {
+ if (pipe && !pipe->ov_blt_addr) {
pr_err("%s: no writeback buffer 0x%x\n", __func__,
- (unsigned int)writeback_pipe->ov_blt_addr);
- ret = mdp4_overlay_writeback_update(mfd);
- if (ret)
- pr_err("%s: update failed writeback pipe NULL\n",
- __func__);
+ (unsigned int)pipe->ov_blt_addr);
goto fail_no_blt_addr;
}
- if (mfd && mfd->panel_power_on) {
- pr_debug("%s in before busy wait\n", __func__);
- mdp4_writeback_dma_busy_wait(mfd);
+ if (pipe->pipe_type == OVERLAY_TYPE_RGB)
+ mdp4_wfd_pipe_queue(0, pipe);
- pr_debug("%s in before update\n", __func__);
- ret = mdp4_overlay_writeback_update(mfd);
- if (ret) {
- pr_err("%s: update failed writeback pipe NULL\n",
- __func__);
- goto fail_no_blt_addr;
- }
+ mdp4_overlay_mdp_perf_upd(mfd, 1);
- pr_debug("%s: in writeback pan display 0x%x\n", __func__,
- (unsigned int)writeback_pipe->ov_blt_addr);
- mdp4_writeback_kickoff_ui(mfd, writeback_pipe);
- mdp4_iommu_unmap(writeback_pipe);
+ mdp_clk_ctrl(1);
+ mdp4_overlay_writeback_update(mfd);
- /* signal if pan function is waiting for the
- * update completion */
- if (mfd->pan_waiting) {
- mfd->pan_waiting = FALSE;
- complete(&mfd->pan_comp);
- }
- }
+ mdp4_wfd_pipe_commit();
+
+ mdp4_overlay_mdp_perf_upd(mfd, 0);
+
+ mdp4_wfd_wait4ov(0);
+ mdp_clk_ctrl(0);
mutex_lock(&mfd->writeback_mutex);
list_add_tail(&node->active_entry, &mfd->writeback_busy_queue);
@@ -385,7 +494,9 @@
mdp4_overlay_resource_release();*/
mutex_unlock(&mfd->dma->ov_mutex);
mutex_unlock(&mfd->unregister_mutex);
+ pr_debug("%s:-\n", __func__);
}
+
static int mdp4_overlay_writeback_register_buffer(
struct msm_fb_data_type *mfd, struct msmfb_writeback_data_list *node)
{
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
index 87921e6..516722e 100644
--- a/drivers/video/msm/mdp4_util.c
+++ b/drivers/video/msm/mdp4_util.c
@@ -557,7 +557,7 @@
mdp4_dmap_done_dsi_cmd(0);
#else
else { /* MDDI */
- mdp4_dma_p_done_mddi(dma);
+ mdp4_dmap_done_mddi(0);
mdp_pipe_ctrl(MDP_DMA2_BLOCK,
MDP_BLOCK_POWER_OFF, TRUE);
complete(&dma->comp);
@@ -608,7 +608,7 @@
mdp4_overlay0_done_dsi_cmd(0);
#else
if (panel & MDP4_PANEL_MDDI)
- mdp4_overlay0_done_mddi(dma);
+ mdp4_overlay0_done_mddi(0);
#endif
}
mdp_hw_cursor_done();
@@ -635,14 +635,8 @@
if (isr & INTR_OVERLAY2_DONE) {
mdp4_stat.intr_overlay2++;
/* disable DTV interrupt */
- dma = &dma_wb_data;
- spin_lock(&mdp_spin_lock);
- mdp_intr_mask &= ~INTR_OVERLAY2_DONE;
- outp32(MDP_INTR_ENABLE, mdp_intr_mask);
- dma->waiting = FALSE;
- spin_unlock(&mdp_spin_lock);
if (panel & MDP4_PANEL_WRITEBACK)
- mdp4_overlay1_done_writeback(dma);
+ mdp4_overlay2_done_wfd(&dma_wb_data);
}
#endif
#endif /* OVERLAY */
diff --git a/drivers/video/msm/mdp_debugfs.c b/drivers/video/msm/mdp_debugfs.c
index 0fad0a7..54f5ef5 100644
--- a/drivers/video/msm/mdp_debugfs.c
+++ b/drivers/video/msm/mdp_debugfs.c
@@ -719,84 +719,6 @@
.write = pmdh_reg_write,
};
-
-
-#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDDI)
-static int vsync_reg_open(struct inode *inode, struct file *file)
-{
- /* non-seekable */
- file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
- return 0;
-}
-
-static int vsync_reg_release(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static ssize_t vsync_reg_write(
- struct file *file,
- const char __user *buff,
- size_t count,
- loff_t *ppos)
-{
- uint32 enable;
- int cnt;
-
- if (count >= sizeof(debug_buf))
- return -EFAULT;
-
- if (copy_from_user(debug_buf, buff, count))
- return -EFAULT;
-
- debug_buf[count] = 0; /* end of string */
-
- cnt = sscanf(debug_buf, "%x", &enable);
-
- mdp_dmap_vsync_set(enable);
-
- return count;
-}
-
-static ssize_t vsync_reg_read(
- struct file *file,
- char __user *buff,
- size_t count,
- loff_t *ppos)
-{
- char *bp;
- int len = 0;
- int tot = 0;
- int dlen;
-
- if (*ppos)
- return 0; /* the end */
-
- bp = debug_buf;
- dlen = sizeof(debug_buf);
- len = snprintf(bp, dlen, "%x\n", mdp_dmap_vsync_get());
- tot += len;
- bp += len;
- *bp = 0;
- tot++;
-
- if (copy_to_user(buff, debug_buf, tot))
- return -EFAULT;
-
- *ppos += tot; /* increase offset */
-
- return tot;
-}
-
-
-static const struct file_operations vsync_fops = {
- .open = vsync_reg_open,
- .release = vsync_reg_release,
- .read = vsync_reg_read,
- .write = vsync_reg_write,
-};
-#endif
-
static ssize_t emdh_reg_write(
struct file *file,
const char __user *buff,
@@ -1342,15 +1264,6 @@
return -1;
}
-#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDDI)
- if (debugfs_create_file("vsync", 0644, dent, 0, &vsync_fops)
- == NULL) {
- printk(KERN_ERR "%s(%d): debugfs_create_file: debug fail\n",
- __FILE__, __LINE__);
- return -1;
- }
-#endif
-
dent = debugfs_create_dir("emdh", NULL);
if (IS_ERR(dent)) {
diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h
index 80ebc4f..25c39f6 100644
--- a/drivers/video/msm/mdss/mdss_fb.h
+++ b/drivers/video/msm/mdss/mdss_fb.h
@@ -96,6 +96,8 @@
struct mdss_mdp_ctl *ctl;
struct mdss_mdp_wb *wb;
struct list_head overlay_list;
+ struct list_head pipes_used;
+ struct list_head pipes_cleanup;
};
int mdss_fb_get_phys_info(unsigned long *start, unsigned long *len, int fb_num);
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index 6fd642f..33028cb 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -241,7 +241,9 @@
unsigned long smp[MAX_PLANES];
struct mdss_mdp_data buffers[2];
- struct list_head list;
+ struct list_head used_list;
+ struct list_head cleanup_list;
+
struct mdp_overlay_pp_params pp_cfg;
};
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index 5966989..d21a095 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -90,18 +90,34 @@
*bus_ib_quota = 0;
*clk_rate = 0;
- if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
- struct mdss_panel_info *pinfo = &mixer->ctl->mfd->panel_info;
- v_total = (pinfo->yres + pinfo->lcdc.v_back_porch +
- pinfo->lcdc.v_front_porch + pinfo->lcdc.v_pulse_width);
- v_active = pinfo->yres;
- } else if (mixer->rotator_mode) {
+ if (mixer->rotator_mode) {
pipe = mixer->stage_pipe[0]; /* rotator pipe */
v_total = pipe->flags & MDP_ROT_90 ? pipe->dst.w : pipe->dst.h;
v_active = v_total;
} else {
- v_total = mixer->height;
- v_active = v_total;
+ int is_writeback = false;
+ if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
+ struct mdss_panel_info *pinfo;
+ pinfo = &mixer->ctl->mfd->panel_info;
+ v_total = (pinfo->yres + pinfo->lcdc.v_back_porch +
+ pinfo->lcdc.v_front_porch +
+ pinfo->lcdc.v_pulse_width);
+ v_active = pinfo->yres;
+
+ if (pinfo->type == WRITEBACK_PANEL)
+ is_writeback = true;
+ } else {
+ v_total = mixer->height;
+ v_active = v_total;
+
+ is_writeback = true;
+ }
+ *clk_rate = mixer->width * v_total * fps;
+ if (is_writeback) {
+ /* perf for bus writeback */
+ *bus_ab_quota = fps * mixer->width * mixer->height * 3;
+ *bus_ib_quota = *bus_ab_quota;
+ }
}
for (i = 0; i < MDSS_MDP_MAX_STAGE; i++) {
@@ -118,7 +134,7 @@
if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF)
quota = (quota / v_active) * v_total;
- else
+ else if (mixer->rotator_mode)
quota *= 2; /* bus read + write */
rate = pipe->dst.w;
@@ -344,6 +360,8 @@
mdss_mdp_mixer_free(mixer);
mdss_mdp_ctl_free(ctl);
+ mdss_mdp_ctl_perf_commit(MDSS_MDP_PERF_UPDATE_ALL);
+
return 0;
}
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index f76b508..452ebdc 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -282,7 +282,7 @@
}
mutex_lock(&mfd->lock);
- list_add(&pipe->list, &mfd->overlay_list);
+ list_add(&pipe->used_list, &mfd->pipes_used);
mutex_unlock(&mfd->lock);
pipe->mixer = mixer;
pipe->mfd = mfd;
@@ -395,24 +395,31 @@
static int mdss_mdp_overlay_kickoff(struct mdss_mdp_ctl *ctl)
{
- int ret;
+ struct mdss_mdp_pipe *pipe, *tmp;
+ struct msm_fb_data_type *mfd = ctl->mfd;
+ int i, ret;
- if (ctl->mfd->kickoff_fnc)
- ret = ctl->mfd->kickoff_fnc(ctl);
+ if (mfd->kickoff_fnc)
+ ret = mfd->kickoff_fnc(ctl);
else
ret = mdss_mdp_display_commit(ctl, NULL);
if (IS_ERR_VALUE(ret))
return ret;
- pr_debug("freeing previous buffers\n");
+ mutex_lock(&mfd->lock);
+ list_for_each_entry_safe(pipe, tmp, &mfd->pipes_cleanup, cleanup_list) {
+ list_del(&pipe->cleanup_list);
+ for (i = 0; i < ARRAY_SIZE(pipe->buffers); i++)
+ mdss_mdp_overlay_free_buf(&pipe->buffers[i]);
- mutex_lock(&ctl->mfd->lock);
- if (!list_empty(&ctl->mfd->overlay_list)) {
- struct mdss_mdp_pipe *pipe;
+ mdss_mdp_pipe_destroy(pipe);
+ }
+
+ if (!list_empty(&mfd->pipes_used)) {
struct mdss_mdp_data *data;
int buf_ndx;
- list_for_each_entry(pipe, &ctl->mfd->overlay_list, list) {
+ list_for_each_entry(pipe, &mfd->pipes_used, used_list) {
buf_ndx = (pipe->play_cnt - 1) & 1; /* prev buffer */
data = &pipe->buffers[buf_ndx];
@@ -423,9 +430,7 @@
}
}
}
- mutex_unlock(&ctl->mfd->lock);
-
- pr_debug("done freeing previous buffers\n");
+ mutex_unlock(&mfd->lock);
return ret;
}
@@ -433,8 +438,7 @@
static int mdss_mdp_overlay_unset(struct msm_fb_data_type *mfd, int ndx)
{
struct mdss_mdp_pipe *pipe;
- struct mdss_mdp_pipe *cleanup_pipes[MDSS_MDP_MAX_SSPP];
- int i, ret = 0, clean_cnt = 0;
+ int i, ret = 0;
u32 pipe_ndx, unset_ndx = 0;
if (!mfd || !mfd->ctl)
@@ -460,28 +464,15 @@
if (pipe_ndx & ndx) {
unset_ndx |= pipe_ndx;
pipe = mdss_mdp_pipe_get_locked(pipe_ndx);
- if (pipe) {
- mutex_lock(&mfd->lock);
- list_del(&pipe->list);
- mutex_unlock(&mfd->lock);
- mdss_mdp_mixer_pipe_unstage(pipe);
- cleanup_pipes[clean_cnt++] = pipe;
- } else {
+ if (!pipe) {
pr_warn("unknown pipe ndx=%x\n", pipe_ndx);
+ continue;
}
- }
- }
-
- if (clean_cnt) {
- int j;
- ret = mdss_mdp_overlay_kickoff(mfd->ctl);
-
- for (i = 0; i < clean_cnt; i++) {
- pipe = cleanup_pipes[i];
- for (j = 0; j < ARRAY_SIZE(pipe->buffers); j++)
- mdss_mdp_overlay_free_buf(&pipe->buffers[i]);
-
- mdss_mdp_pipe_destroy(pipe);
+ mutex_lock(&mfd->lock);
+ list_del(&pipe->used_list);
+ list_add(&pipe->cleanup_list, &mfd->pipes_cleanup);
+ mutex_unlock(&mfd->lock);
+ mdss_mdp_mixer_pipe_unstage(pipe);
}
}
@@ -495,8 +486,8 @@
int cnt = 0;
mutex_lock(&mfd->lock);
- if (!list_empty(&mfd->overlay_list)) {
- list_for_each_entry(pipe, &mfd->overlay_list, list) {
+ if (!list_empty(&mfd->pipes_used)) {
+ list_for_each_entry(pipe, &mfd->pipes_used, used_list) {
if (pipe->ndx & MDSS_MDP_ROT_SESSION_MASK) {
struct mdss_mdp_rotator_session *rot;
rot = mdss_mdp_rotator_session_get(pipe->ndx);
@@ -513,6 +504,7 @@
if (unset_ndx) {
pr_debug("%d pipes need cleanup (%x)\n", cnt, unset_ndx);
mdss_mdp_overlay_unset(mfd, unset_ndx);
+ mdss_mdp_overlay_kickoff(mfd->ctl);
}
return 0;
@@ -603,7 +595,7 @@
ctl = pipe->mixer->ctl;
mdss_mdp_pipe_unlock(pipe);
- if (ret == 0 && !(pipe->flags & MDP_OV_PLAY_NOWAIT))
+ if ((ret == 0) && (mfd->panel_info.type == WRITEBACK_PANEL))
ret = mdss_mdp_overlay_kickoff(ctl);
return ret;
@@ -1039,7 +1031,9 @@
ret = -EFAULT;
}
break;
-
+ case MSMFB_OVERLAY_COMMIT:
+ ret = mdss_mdp_overlay_kickoff(mfd->ctl);
+ break;
default:
if (mfd->panel_info.type == WRITEBACK_PANEL)
ret = mdss_mdp_wb_ioctl_handler(mfd, cmd, argp);
@@ -1063,7 +1057,8 @@
if (mfd->panel_info.type == WRITEBACK_PANEL)
mfd->kickoff_fnc = mdss_mdp_wb_kickoff;
- INIT_LIST_HEAD(&mfd->overlay_list);
+ INIT_LIST_HEAD(&mfd->pipes_used);
+ INIT_LIST_HEAD(&mfd->pipes_cleanup);
return 0;
}
diff --git a/drivers/video/msm/mdss/mdss_mdp_rotator.h b/drivers/video/msm/mdss/mdss_mdp_rotator.h
index 1e4b81e0..eb5b47a 100644
--- a/drivers/video/msm/mdss/mdss_mdp_rotator.h
+++ b/drivers/video/msm/mdss/mdss_mdp_rotator.h
@@ -17,7 +17,7 @@
#include "mdss_mdp.h"
-#define MDSS_MDP_ROT_SESSION_MASK 0x80000000
+#define MDSS_MDP_ROT_SESSION_MASK 0x40000000
struct mdss_mdp_rotator_session {
u32 session_id;
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index abcdeab..9a66ff9 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -2088,7 +2088,7 @@
ext4_ext_in_cache(struct inode *inode, ext4_lblk_t block,
struct ext4_extent *ex)
{
- struct ext4_ext_cache cex;
+ struct ext4_ext_cache cex = {0, 0, 0};
int ret = 0;
if (ext4_ext_check_cache(inode, block, &cex)) {
diff --git a/include/linux/mfd/pm8xxx/pm8038.h b/include/linux/mfd/pm8xxx/pm8038.h
index 682abc8..574dab6 100644
--- a/include/linux/mfd/pm8xxx/pm8038.h
+++ b/include/linux/mfd/pm8xxx/pm8038.h
@@ -54,6 +54,7 @@
/* PMIC Interrupts */
#define PM8038_RTC_ALARM_IRQ PM8038_IRQ_BLOCK_BIT(4, 7)
+#define PM8038_BATT_ALARM_IRQ PM8921_IRQ_BLOCK_BIT(5, 6)
#define PM8038_PWRKEY_REL_IRQ PM8038_IRQ_BLOCK_BIT(6, 2)
#define PM8038_PWRKEY_PRESS_IRQ PM8038_IRQ_BLOCK_BIT(6, 3)
#define PM8038_KEYPAD_IRQ PM8038_IRQ_BLOCK_BIT(9, 2)
diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h
index c306c75..421d758 100644
--- a/include/linux/mfd/wcd9xxx/core.h
+++ b/include/linux/mfd/wcd9xxx/core.h
@@ -15,6 +15,8 @@
#include <linux/interrupt.h>
#include <linux/pm_qos.h>
+#include <linux/platform_device.h>
+#include <linux/of_irq.h>
#define WCD9XXX_NUM_IRQ_REGS 3
@@ -44,83 +46,41 @@
enum {
- TABLA_IRQ_SLIMBUS = 0,
- TABLA_IRQ_MBHC_REMOVAL,
- TABLA_IRQ_MBHC_SHORT_TERM,
- TABLA_IRQ_MBHC_PRESS,
- TABLA_IRQ_MBHC_RELEASE,
- TABLA_IRQ_MBHC_POTENTIAL,
- TABLA_IRQ_MBHC_INSERTION,
- TABLA_IRQ_BG_PRECHARGE,
- TABLA_IRQ_PA1_STARTUP,
- TABLA_IRQ_PA2_STARTUP,
- TABLA_IRQ_PA3_STARTUP,
- TABLA_IRQ_PA4_STARTUP,
- TABLA_IRQ_PA5_STARTUP,
- TABLA_IRQ_MICBIAS1_PRECHARGE,
- TABLA_IRQ_MICBIAS2_PRECHARGE,
- TABLA_IRQ_MICBIAS3_PRECHARGE,
- TABLA_IRQ_HPH_PA_OCPL_FAULT,
- TABLA_IRQ_HPH_PA_OCPR_FAULT,
- TABLA_IRQ_EAR_PA_OCPL_FAULT,
- TABLA_IRQ_HPH_L_PA_STARTUP,
- TABLA_IRQ_HPH_R_PA_STARTUP,
- TABLA_IRQ_EAR_PA_STARTUP,
- TABLA_NUM_IRQS,
+ WCD9XXX_IRQ_SLIMBUS = 0,
+ WCD9XXX_IRQ_MBHC_REMOVAL,
+ WCD9XXX_IRQ_MBHC_SHORT_TERM,
+ WCD9XXX_IRQ_MBHC_PRESS,
+ WCD9XXX_IRQ_MBHC_RELEASE,
+ WCD9XXX_IRQ_MBHC_POTENTIAL,
+ WCD9XXX_IRQ_MBHC_INSERTION,
+ WCD9XXX_IRQ_BG_PRECHARGE,
+ WCD9XXX_IRQ_PA1_STARTUP,
+ WCD9XXX_IRQ_PA2_STARTUP,
+ WCD9XXX_IRQ_PA3_STARTUP,
+ WCD9XXX_IRQ_PA4_STARTUP,
+ WCD9XXX_IRQ_PA5_STARTUP,
+ WCD9XXX_IRQ_MICBIAS1_PRECHARGE,
+ WCD9XXX_IRQ_MICBIAS2_PRECHARGE,
+ WCD9XXX_IRQ_MICBIAS3_PRECHARGE,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+ WCD9XXX_IRQ_EAR_PA_OCPL_FAULT,
+ WCD9XXX_IRQ_HPH_L_PA_STARTUP,
+ WCD9XXX_IRQ_HPH_R_PA_STARTUP,
+ WCD9XXX_IRQ_EAR_PA_STARTUP,
+ WCD9XXX_NUM_IRQS,
};
enum {
- SITAR_IRQ_SLIMBUS = 0,
- SITAR_IRQ_MBHC_REMOVAL,
- SITAR_IRQ_MBHC_SHORT_TERM,
- SITAR_IRQ_MBHC_PRESS,
- SITAR_IRQ_MBHC_RELEASE,
- SITAR_IRQ_MBHC_POTENTIAL,
- SITAR_IRQ_MBHC_INSERTION,
- SITAR_IRQ_BG_PRECHARGE,
- SITAR_IRQ_PA1_STARTUP,
- SITAR_IRQ_PA2_STARTUP,
- SITAR_IRQ_PA3_STARTUP,
- SITAR_IRQ_PA4_STARTUP,
- SITAR_IRQ_PA5_STARTUP,
- SITAR_IRQ_MICBIAS1_PRECHARGE,
- SITAR_IRQ_MICBIAS2_PRECHARGE,
- SITAR_IRQ_MICBIAS3_PRECHARGE,
- SITAR_IRQ_HPH_PA_OCPL_FAULT,
- SITAR_IRQ_HPH_PA_OCPR_FAULT,
- SITAR_IRQ_EAR_PA_OCPL_FAULT,
- SITAR_IRQ_HPH_L_PA_STARTUP,
- SITAR_IRQ_HPH_R_PA_STARTUP,
- SITAR_IRQ_EAR_PA_STARTUP,
- SITAR_NUM_IRQS,
+ TABLA_NUM_IRQS = WCD9XXX_NUM_IRQS,
+ SITAR_NUM_IRQS = WCD9XXX_NUM_IRQS,
+ TAIKO_NUM_IRQS = WCD9XXX_NUM_IRQS,
};
-enum {
- TAIKO_IRQ_SLIMBUS = 0,
- TAIKO_IRQ_MBHC_REMOVAL,
- TAIKO_IRQ_MBHC_SHORT_TERM,
- TAIKO_IRQ_MBHC_PRESS,
- TAIKO_IRQ_MBHC_RELEASE,
- TAIKO_IRQ_MBHC_POTENTIAL,
- TAIKO_IRQ_MBHC_INSERTION,
- TAIKO_IRQ_BG_PRECHARGE,
- TAIKO_IRQ_PA1_STARTUP,
- TAIKO_IRQ_PA2_STARTUP,
- TAIKO_IRQ_PA3_STARTUP,
- TAIKO_IRQ_PA4_STARTUP,
- TAIKO_IRQ_PA5_STARTUP,
- TAIKO_IRQ_MICBIAS1_PRECHARGE,
- TAIKO_IRQ_MICBIAS2_PRECHARGE,
- TAIKO_IRQ_MICBIAS3_PRECHARGE,
- TAIKO_IRQ_HPH_PA_OCPL_FAULT,
- TAIKO_IRQ_HPH_PA_OCPR_FAULT,
- TAIKO_IRQ_EAR_PA_OCPL_FAULT,
- TAIKO_IRQ_HPH_L_PA_STARTUP,
- TAIKO_IRQ_HPH_R_PA_STARTUP,
- TAIKO_IRQ_EAR_PA_STARTUP,
- TAIKO_NUM_IRQS,
-};
+#define MAX(X, Y) (((int)X) >= ((int)Y) ? (X) : (Y))
+#define WCD9XXX_MAX_NUM_IRQS (MAX(MAX(TABLA_NUM_IRQS, SITAR_NUM_IRQS), \
+ TAIKO_NUM_IRQS))
enum wcd9xxx_pm_state {
WCD9XXX_PM_SLEEPABLE,
@@ -137,12 +97,6 @@
struct mutex irq_lock;
u8 version;
- unsigned int irq_base;
- unsigned int irq;
- u8 irq_masks_cur[WCD9XXX_NUM_IRQ_REGS];
- u8 irq_masks_cache[WCD9XXX_NUM_IRQ_REGS];
- u8 irq_level[WCD9XXX_NUM_IRQ_REGS];
-
int reset_gpio;
int (*read_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg,
@@ -164,6 +118,13 @@
int num_tx_port;
u8 idbyte[4];
+
+ unsigned int irq_base;
+ unsigned int irq;
+ u8 irq_masks_cur[WCD9XXX_NUM_IRQ_REGS];
+ u8 irq_masks_cache[WCD9XXX_NUM_IRQ_REGS];
+ bool irq_level_high[WCD9XXX_MAX_NUM_IRQS];
+ int num_irqs;
};
int wcd9xxx_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg);
@@ -187,40 +148,15 @@
enum wcd9xxx_pm_state o,
enum wcd9xxx_pm_state n);
-static inline int wcd9xxx_request_irq(struct wcd9xxx *wcd9xxx, int irq,
- irq_handler_t handler, const char *name,
- void *data)
-{
- if (!wcd9xxx->irq_base)
- return -EINVAL;
- return request_threaded_irq(wcd9xxx->irq_base + irq, NULL, handler,
- IRQF_TRIGGER_RISING, name,
- data);
-}
-static inline void wcd9xxx_free_irq(struct wcd9xxx *wcd9xxx,
- int irq, void *data)
-{
- if (!wcd9xxx->irq_base)
- return;
- free_irq(wcd9xxx->irq_base + irq, data);
-}
-static inline void wcd9xxx_enable_irq(struct wcd9xxx *wcd9xxx, int irq)
-{
- if (!wcd9xxx->irq_base)
- return;
- enable_irq(wcd9xxx->irq_base + irq);
-}
-static inline void wcd9xxx_disable_irq(struct wcd9xxx *wcd9xxx, int irq)
-{
- if (!wcd9xxx->irq_base)
- return;
- disable_irq_nosync(wcd9xxx->irq_base + irq);
-}
-static inline void wcd9xxx_disable_irq_sync(struct wcd9xxx *wcd9xxx, int irq)
-{
- if (!wcd9xxx->irq_base)
- return;
- disable_irq(wcd9xxx->irq_base + irq);
-}
+int wcd9xxx_request_irq(struct wcd9xxx *wcd9xxx, int irq,
+ irq_handler_t handler, const char *name, void *data);
+void wcd9xxx_free_irq(struct wcd9xxx *wcd9xxx, int irq, void *data);
+void wcd9xxx_enable_irq(struct wcd9xxx *wcd9xxx, int irq);
+void wcd9xxx_disable_irq(struct wcd9xxx *wcd9xxx, int irq);
+void wcd9xxx_disable_irq_sync(struct wcd9xxx *wcd9xxx, int irq);
+#ifdef CONFIG_OF
+int __init wcd9xxx_irq_of_init(struct device_node *node,
+ struct device_node *parent);
+#endif /* CONFIG_OF */
#endif
diff --git a/include/linux/mfd/wcd9xxx/wcd9304_registers.h b/include/linux/mfd/wcd9xxx/wcd9304_registers.h
index f7c483c..73919e0 100644
--- a/include/linux/mfd/wcd9xxx/wcd9304_registers.h
+++ b/include/linux/mfd/wcd9xxx/wcd9304_registers.h
@@ -409,114 +409,163 @@
#define SITAR_A_CDC_ANC1_SMLPF_CTL__POR (0x00000000)
#define SITAR_A_CDC_ANC1_DCFLT_CTL (0x20B)
#define SITAR_A_CDC_ANC1_DCFLT_CTL__POR (0x00000000)
-#define SITAR_A_CDC_TX1_VOL_CTL_TIMER (0x220)
-#define SITAR_A_CDC_TX1_VOL_CTL_TIMER__POR (0x00000000)
-#define SITAR_A_CDC_TX1_VOL_CTL_GAIN (0x221)
-#define SITAR_A_CDC_TX1_VOL_CTL_GAIN__POR (0x00000000)
-#define SITAR_A_CDC_TX2_VOL_CTL_GAIN (0x229)
-#define SITAR_A_CDC_TX2_VOL_CTL_GAIN__POR (0x00000000)
-#define SITAR_A_CDC_TX3_VOL_CTL_GAIN (0x231)
-#define SITAR_A_CDC_TX3_VOL_CTL_GAIN__POR (0x00000000)
-#define SITAR_A_CDC_TX4_VOL_CTL_GAIN (0x239)
-#define SITAR_A_CDC_TX4_VOL_CTL_GAIN__POR (0x00000000)
-#define SITAR_A_CDC_TX5_VOL_CTL_GAIN (0x241)
-#define SITAR_A_CDC_TX5_VOL_CTL_GAIN__POR (0x00000000)
-#define SITAR_A_CDC_TX1_VOL_CTL_CFG (0x222)
-#define SITAR_A_CDC_TX1_VOL_CTL_CFG__POR (0x00000000)
-#define SITAR_A_CDC_TX2_VOL_CTL_CFG (0x22A)
-#define SITAR_A_CDC_TX2_VOL_CTL_CFG__POR (0x00000000)
-#define SITAR_A_CDC_TX3_VOL_CTL_CFG (0x232)
-#define SITAR_A_CDC_TX3_VOL_CTL_CFG__POR (0x00000000)
-#define SITAR_A_CDC_TX4_VOL_CTL_CFG (0x23A)
-#define SITAR_A_CDC_TX4_VOL_CTL_CFG__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_CTL (0x280)
+#define SITAR_A_CDC_ANC2_CTL__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_SHIFT (0x281)
+#define SITAR_A_CDC_ANC2_SHIFT__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_IIR_B1_CTL (0x282)
+#define SITAR_A_CDC_ANC2_IIR_B1_CTL__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_IIR_B2_CTL (0x283)
+#define SITAR_A_CDC_ANC2_IIR_B2_CTL__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_IIR_B3_CTL (0x284)
+#define SITAR_A_CDC_ANC2_IIR_B3_CTL__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_IIR_B4_CTL (0x285)
+#define SITAR_A_CDC_ANC2_IIR_B4_CTL__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_LPF_B1_CTL (0x286)
+#define SITAR_A_CDC_ANC2_LPF_B1_CTL__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_LPF_B2_CTL (0x287)
+#define SITAR_A_CDC_ANC2_LPF_B2_CTL__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_LPF_B3_CTL (0x288)
+#define SITAR_A_CDC_ANC2_LPF_B3_CTL__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_SPARE (0x289)
+#define SITAR_A_CDC_ANC2_SPARE__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_SMLPF_CTL (0x28A)
+#define SITAR_A_CDC_ANC2_SMLPF_CTL__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_DCFLT_CTL (0x28B)
+#define SITAR_A_CDC_ANC2_DCFLT_CTL__POR (0x00000000)
+#define SITAR_A_CDC_TX1_VOL_CTL_TIMER (0x220)
+#define SITAR_A_CDC_TX1_VOL_CTL_TIMER__POR (0x00000000)
+#define SITAR_A_CDC_TX1_VOL_CTL_GAIN (0x221)
+#define SITAR_A_CDC_TX1_VOL_CTL_GAIN__POR (0x00000000)
+#define SITAR_A_CDC_TX1_VOL_CTL_CFG (0x222)
+#define SITAR_A_CDC_TX1_VOL_CTL_CFG__POR (0x00000000)
#define SITAR_A_CDC_TX1_MUX_CTL (0x223)
#define SITAR_A_CDC_TX1_MUX_CTL__POR (0x00000008)
-#define SITAR_A_CDC_TX1_CLK_FS_CTL (0x00000224)
-#define SITAR_A_CDC_TX1_CLK_FS_CTL__POR (0x00000003)
-#define SITAR_A_CDC_TX2_CLK_FS_CTL (0x0000022C)
-#define SITAR_A_CDC_TX2_CLK_FS_CTL__POR (0x00000003)
-#define SITAR_A_CDC_TX3_CLK_FS_CTL (0x00000234)
-#define SITAR_A_CDC_TX3_CLK_FS_CTL__POR (0x00000003)
-#define SITAR_A_CDC_TX4_CLK_FS_CTL (0x0000023C)
-#define SITAR_A_CDC_TX4_CLK_FS_CTL__POR (0x00000003)
-#define SITAR_A_CDC_TX1_DMIC_CTL (0x225)
+#define SITAR_A_CDC_TX1_CLK_FS_CTL (0x224)
+#define SITAR_A_CDC_TX1_CLK_FS_CTL__POR (0x00000003)
+#define SITAR_A_CDC_TX1_DMIC_CTL (0x225)
#define SITAR_A_CDC_TX1_DMIC_CTL__POR (0x00000000)
-#define SITAR_A_CDC_TX2_MUX_CTL (0x22B)
+
+#define SITAR_A_CDC_TX2_VOL_CTL_TIMER (0x228)
+#define SITAR_A_CDC_TX2_VOL_CTL_TIMER__POR (0x00000000)
+#define SITAR_A_CDC_TX2_VOL_CTL_GAIN (0x229)
+#define SITAR_A_CDC_TX2_VOL_CTL_GAIN__POR (0x00000000)
+#define SITAR_A_CDC_TX2_VOL_CTL_CFG (0x22A)
+#define SITAR_A_CDC_TX2_VOL_CTL_CFG__POR (0x00000000)
+#define SITAR_A_CDC_TX2_MUX_CTL (0x22B)
#define SITAR_A_CDC_TX2_MUX_CTL__POR (0x00000008)
-#define SITAR_A_CDC_TX3_MUX_CTL (0x233)
+#define SITAR_A_CDC_TX2_CLK_FS_CTL (0x22C)
+#define SITAR_A_CDC_TX2_CLK_FS_CTL__POR (0x00000003)
+#define SITAR_A_CDC_TX2_DMIC_CTL (0x22D)
+#define SITAR_A_CDC_TX2_DMIC_CTL__POR (0x00000000)
+
+#define SITAR_A_CDC_TX3_VOL_CTL_TIMER (0x230)
+#define SITAR_A_CDC_TX3_VOL_CTL_TIMER__POR (0x00000000)
+#define SITAR_A_CDC_TX3_VOL_CTL_GAIN (0x231)
+#define SITAR_A_CDC_TX3_VOL_CTL_GAIN__POR (0x00000000)
+#define SITAR_A_CDC_TX3_VOL_CTL_CFG (0x232)
+#define SITAR_A_CDC_TX3_VOL_CTL_CFG__POR (0x00000000)
+#define SITAR_A_CDC_TX3_MUX_CTL (0x233)
#define SITAR_A_CDC_TX3_MUX_CTL__POR (0x00000008)
-#define SITAR_A_CDC_TX4_MUX_CTL (0x23B)
+#define SITAR_A_CDC_TX3_CLK_FS_CTL (0x234)
+#define SITAR_A_CDC_TX3_CLK_FS_CTL__POR (0x00000003)
+#define SITAR_A_CDC_TX3_DMIC_CTL (0x235)
+#define SITAR_A_CDC_TX3_DMIC_CTL__POR (0x00000000)
+
+#define SITAR_A_CDC_TX4_VOL_CTL_TIMER (0x239)
+#define SITAR_A_CDC_TX4_VOL_CTL_TIMER__POR (0x00000000)
+#define SITAR_A_CDC_TX4_VOL_CTL_GAIN (0x23A)
+#define SITAR_A_CDC_TX4_VOL_CTL_GAIN__POR (0x00000000)
+#define SITAR_A_CDC_TX4_VOL_CTL_CFG (0x23B)
+#define SITAR_A_CDC_TX4_VOL_CTL_CFG__POR (0x00000000)
+#define SITAR_A_CDC_TX4_MUX_CTL (0x23C)
#define SITAR_A_CDC_TX4_MUX_CTL__POR (0x00000008)
-#define SITAR_A_CDC_TX5_MUX_CTL (0x243)
+#define SITAR_A_CDC_TX4_CLK_FS_CTL (0x23D)
+#define SITAR_A_CDC_TX4_CLK_FS_CTL__POR (0x00000003)
+#define SITAR_A_CDC_TX4_DMIC_CTL (0x23E)
+#define SITAR_A_CDC_TX4_DMIC_CTL__POR (0x00000000)
+
+#define SITAR_A_CDC_TX5_VOL_CTL_TIMER (0x240)
+#define SITAR_A_CDC_TX5_VOL_CTL_TIMER__POR (0x00000000)
+#define SITAR_A_CDC_TX5_VOL_CTL_GAIN (0x241)
+#define SITAR_A_CDC_TX5_VOL_CTL_GAIN__POR (0x00000000)
+#define SITAR_A_CDC_TX5_VOL_CTL_CFG (0x242)
+#define SITAR_A_CDC_TX5_VOL_CTL_CFG__POR (0x00000000)
+#define SITAR_A_CDC_TX5_MUX_CTL (0x243)
#define SITAR_A_CDC_TX5_MUX_CTL__POR (0x00000008)
+#define SITAR_A_CDC_TX5_CLK_FS_CTL (0x244)
+#define SITAR_A_CDC_TX5_CLK_FS_CTL__POR (0x00000003)
+#define SITAR_A_CDC_TX5_DMIC_CTL (0x245)
+#define SITAR_A_CDC_TX5_DMIC_CTL__POR (0x00000000)
#define SITAR_A_CDC_SRC1_PDA_CFG (0x2A0)
#define SITAR_A_CDC_SRC1_PDA_CFG__POR (0x00000000)
#define SITAR_A_CDC_SRC1_FS_CTL (0x2A1)
#define SITAR_A_CDC_SRC1_FS_CTL__POR (0x0000001b)
+#define SITAR_A_CDC_SRC2_PDA_CFG (0x2A8)
+#define SITAR_A_CDC_SRC2_PDA_CFG__POR (0x00000000)
+#define SITAR_A_CDC_SRC2_FS_CTL (0x2A9)
+#define SITAR_A_CDC_SRC2_FS_CTL__POR (0x0000001b)
-#define SITAR_A_CDC_RX1_B1_CTL (0x000002B0)
+#define SITAR_A_CDC_RX1_B1_CTL (0x2B0)
#define SITAR_A_CDC_RX1_B1_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX2_B1_CTL (0x000002B8)
-#define SITAR_A_CDC_RX2_B1_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX3_B1_CTL (0x000002C0)
-#define SITAR_A_CDC_RX3_B1_CTL__POR (0x00000000)
-
-#define SITAR_A_CDC_RX1_B2_CTL (0x000002B1)
+#define SITAR_A_CDC_RX1_B2_CTL (0x2B1)
#define SITAR_A_CDC_RX1_B2_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX2_B2_CTL (0x000002B9)
-#define SITAR_A_CDC_RX2_B2_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX3_B2_CTL (0x000002C1)
-#define SITAR_A_CDC_RX3_B2_CTL__POR (0x00000000)
-
-#define SITAR_A_CDC_RX1_B3_CTL (0x000002B2)
+#define SITAR_A_CDC_RX1_B3_CTL (0x2B2)
#define SITAR_A_CDC_RX1_B3_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX2_B3_CTL (0x000002BA)
-#define SITAR_A_CDC_RX2_B3_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX3_B3_CTL (0x000002C2)
-#define SITAR_A_CDC_RX3_B3_CTL__POR (0x00000000)
-
-#define SITAR_A_CDC_RX1_B4_CTL (0x000002B3)
+#define SITAR_A_CDC_RX1_B4_CTL (0x2B3)
#define SITAR_A_CDC_RX1_B4_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX2_B4_CTL (0x000002BB)
-#define SITAR_A_CDC_RX2_B4_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX3_B4_CTL (0x000002C3)
-#define SITAR_A_CDC_RX3_B4_CTL__POR (0x00000000)
-
-#define SITAR_A_CDC_RX1_B5_CTL (0x000002B4)
+#define SITAR_A_CDC_RX1_B5_CTL (0x2B4)
#define SITAR_A_CDC_RX1_B5_CTL__POR (0x00000078)
-#define SITAR_A_CDC_RX2_B5_CTL (0x000002BC)
-#define SITAR_A_CDC_RX2_B5_CTL__POR (0x00000078)
-#define SITAR_A_CDC_RX3_B5_CTL (0x000002C4)
-#define SITAR_A_CDC_RX3_B5_CTL__POR (0x00000078)
-
-#define SITAR_A_CDC_RX1_B6_CTL (0x000002B5)
+#define SITAR_A_CDC_RX1_B6_CTL (0x2B5)
#define SITAR_A_CDC_RX1_B6_CTL__POR (0x00000080)
-#define SITAR_A_CDC_RX2_B6_CTL (0x000002BD)
+#define SITAR_A_CDC_RX1_VOL_CTL_B1_CTL (0x2B6)
+#define SITAR_A_CDC_RX1_VOL_CTL_B1_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX1_VOL_CTL_B2_CTL (0x2B7)
+#define SITAR_A_CDC_RX1_VOL_CTL_B2_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX2_B1_CTL (0x2B8)
+#define SITAR_A_CDC_RX2_B1_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX2_B2_CTL (0x2B9)
+#define SITAR_A_CDC_RX2_B2_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX2_B3_CTL (0x2BA)
+#define SITAR_A_CDC_RX2_B3_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX2_B4_CTL (0x2BB)
+#define SITAR_A_CDC_RX2_B4_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX2_B5_CTL (0x2BC)
+#define SITAR_A_CDC_RX2_B5_CTL__POR (0x00000078)
+#define SITAR_A_CDC_RX2_B6_CTL (0x2BD)
#define SITAR_A_CDC_RX2_B6_CTL__POR (0x00000080)
-#define SITAR_A_CDC_RX3_B6_CTL (0x000002C5)
+#define SITAR_A_CDC_RX2_VOL_CTL_B1_CTL (0x2BE)
+#define SITAR_A_CDC_RX2_VOL_CTL_B1_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX2_VOL_CTL_B2_CTL (0x2BF)
+#define SITAR_A_CDC_RX2_VOL_CTL_B2_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX3_B1_CTL (0x2C0)
+#define SITAR_A_CDC_RX3_B1_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX3_B2_CTL (0x2C1)
+#define SITAR_A_CDC_RX3_B2_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX3_B3_CTL (0x2C2)
+#define SITAR_A_CDC_RX3_B3_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX3_B4_CTL (0x2C3)
+#define SITAR_A_CDC_RX3_B4_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX3_B5_CTL (0x2C4)
+#define SITAR_A_CDC_RX3_B5_CTL__POR (0x00000078)
+#define SITAR_A_CDC_RX3_B6_CTL (0x2C5)
#define SITAR_A_CDC_RX3_B6_CTL__POR (0x00000080)
+#define SITAR_A_CDC_RX3_VOL_CTL_B1_CTL (0x2C6)
+#define SITAR_A_CDC_RX3_VOL_CTL_B1_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX3_VOL_CTL_B2_CTL (0x2C7)
+#define SITAR_A_CDC_RX3_VOL_CTL_B2_CTL__POR (0x00000000)
-
-#define SITAR_A_CDC_RX1_VOL_CTL_B1_CTL (0x2B6)
-#define SITAR_A_CDC_RX1_VOL_CTL_B1_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX1_VOL_CTL_B2_CTL (0x2B7)
-#define SITAR_A_CDC_RX1_VOL_CTL_B2_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX2_VOL_CTL_B2_CTL (0x2BF)
-#define SITAR_A_CDC_RX2_VOL_CTL_B2_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX3_VOL_CTL_B2_CTL (0x2C7)
-#define SITAR_A_CDC_RX3_VOL_CTL_B2_CTL__POR (0x00000000)
-
-#define SITAR_A_CDC_CLK_ANC_RESET_CTL (0x300)
-#define SITAR_A_CDC_CLK_ANC_RESET_CTL__POR (0x00000000)
-#define SITAR_A_CDC_CLK_RX_RESET_CTL (0x301)
-#define SITAR_A_CDC_CLK_RX_RESET_CTL__POR (0x00000000)
-#define SITAR_A_CDC_CLK_TX_RESET_B1_CTL (0x302)
-#define SITAR_A_CDC_CLK_TX_RESET_B1_CTL__POR (0x00000000)
-#define SITAR_A_CDC_CLK_TX_RESET_B2_CTL (0x303)
-#define SITAR_A_CDC_CLK_TX_RESET_B2_CTL__POR (0x00000000)
-#define SITAR_A_CDC_CLK_DMIC_CTL (0x304)
+#define SITAR_A_CDC_CLK_ANC_RESET_CTL (0x300)
+#define SITAR_A_CDC_CLK_ANC_RESET_CTL__POR (0x00000000)
+#define SITAR_A_CDC_CLK_RX_RESET_CTL (0x301)
+#define SITAR_A_CDC_CLK_RX_RESET_CTL__POR (0x00000000)
+#define SITAR_A_CDC_CLK_TX_RESET_B1_CTL (0x302)
+#define SITAR_A_CDC_CLK_TX_RESET_B1_CTL__POR (0x00000000)
+#define SITAR_A_CDC_CLK_TX_RESET_B2_CTL (0x303)
+#define SITAR_A_CDC_CLK_TX_RESET_B2_CTL__POR (0x00000000)
+#define SITAR_A_CDC_CLK_DMIC_CTL (0x304)
#define SITAR_A_CDC_CLK_DMIC_CTL__POR (0x00000000)
#define SITAR_A_CDC_CLK_RX_I2S_CTL (0x305)
#define SITAR_A_CDC_CLK_RX_I2S_CTL__POR (0x00000003)
@@ -654,7 +703,23 @@
#define SITAR_A_CDC_COMP1_SHUT_DOWN_STATUS__POR (0x00000003)
#define SITAR_A_CDC_COMP1_FS_CFG (0x377)
#define SITAR_A_CDC_COMP1_FS_CFG__POR (0x0000001b)
-#define SITAR_A_CDC_CONN_RX1_B1_CTL (0x380)
+#define SITAR_A_CDC_COMP2_B1_CTL (0x378)
+#define SITAR_A_CDC_COMP2_B1_CTL__POR (0x00000030)
+#define SITAR_A_CDC_COMP2_B2_CTL (0x379)
+#define SITAR_A_CDC_COMP2_B2_CTL__POR (0x000000b5)
+#define SITAR_A_CDC_COMP2_B3_CTL (0x37A)
+#define SITAR_A_CDC_COMP2_B3_CTL__POR (0x00000028)
+#define SITAR_A_CDC_COMP2_B4_CTL (0x37B)
+#define SITAR_A_CDC_COMP2_B4_CTL__POR (0x0000003c)
+#define SITAR_A_CDC_COMP2_B5_CTL (0x37C)
+#define SITAR_A_CDC_COMP2_B5_CTL__POR (0x0000001f)
+#define SITAR_A_CDC_COMP2_B6_CTL (0x37D)
+#define SITAR_A_CDC_COMP2_B6_CTL__POR (0x00000000)
+#define SITAR_A_CDC_COMP2_SHUT_DOWN_STATUS (0x37E)
+#define SITAR_A_CDC_COMP2_SHUT_DOWN_STATUS__POR (0x00000003)
+#define SITAR_A_CDC_COMP2_FS_CFG (0x37F)
+#define SITAR_A_CDC_COMP2_FS_CFG__POR (0x0000001b)
+#define SITAR_A_CDC_CONN_RX1_B1_CTL (0x380)
#define SITAR_A_CDC_CONN_RX1_B1_CTL__POR (0x00000000)
#define SITAR_A_CDC_CONN_RX1_B2_CTL (0x381)
#define SITAR_A_CDC_CONN_RX1_B2_CTL__POR (0x00000000)
diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h b/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
index c66e953..357f400 100644
--- a/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
+++ b/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
@@ -39,4 +39,12 @@
#define WCD9XXX_A_CDC_CTL__POR (0x00000000)
#define WCD9XXX_A_LEAKAGE_CTL (0x88)
#define WCD9XXX_A_LEAKAGE_CTL__POR (0x00000004)
+#define WCD9XXX_A_INTR_MODE (0x90)
+#define WCD9XXX_A_INTR_MASK0 (0x94)
+#define WCD9XXX_A_INTR_STATUS0 (0x98)
+#define WCD9XXX_A_INTR_CLEAR0 (0x9C)
+#define WCD9XXX_A_INTR_LEVEL0 (0xA0)
+#define WCD9XXX_A_INTR_LEVEL1 (0xA1)
+#define WCD9XXX_A_INTR_LEVEL2 (0xA2)
+
#endif
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 0330dfb..477733c 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -187,6 +187,26 @@
#define SDIO_MAX_FUNCS 7
+enum mmc_packed_stop_reasons {
+ EXCEEDS_SEGMENTS = 0,
+ EXCEEDS_SECTORS,
+ WRONG_DATA_DIR,
+ FLUSH_OR_DISCARD,
+ EMPTY_QUEUE,
+ REL_WRITE,
+ THRESHOLD,
+ LARGE_SEC_ALIGN,
+ MAX_REASONS,
+};
+
+struct mmc_wr_pack_stats {
+ u32 *packing_events;
+ u32 pack_stop_reason[MAX_REASONS];
+ spinlock_t lock;
+ bool enabled;
+ bool print_in_read;
+};
+
/* The number of MMC physical partitions. These consist of:
* boot partitions (2), general purpose partitions (4) in MMC v4.4.
*/
@@ -209,6 +229,49 @@
#define MMC_BLK_DATA_AREA_GP (1<<2)
};
+/**
+ * struct mmc_bkops_info - BKOPS data
+ * @dw: Idle time bkops delayed work
+ * @host_suspend_tout_ms: The host controller idle time,
+ * before getting into suspend
+ * @delay_ms: The time to start the BKOPS
+ * delayed work once MMC thread is idle
+ * @poll_for_completion: Poll on BKOPS completion
+ * @cancel_delayed_work: A flag to indicate if the delayed work
+ * should be cancelled
+ * @started_delayed_bkops: A flag to indicate if the delayed
+ * work was scheduled
+ * @sectors_changed: number of sectors written or
+ * discard since the last idle BKOPS were scheduled
+ */
+struct mmc_bkops_info {
+ struct delayed_work dw;
+ unsigned int host_suspend_tout_ms;
+ unsigned int delay_ms;
+ unsigned int min_sectors_to_queue_delayed_work;
+/*
+ * A default time for checking the need for non urgent BKOPS once mmcqd
+ * is idle.
+ */
+#define MMC_IDLE_BKOPS_TIME_MS 2000
+ struct work_struct poll_for_completion;
+/* Polling timeout and interval for waiting on non-blocking BKOPs completion */
+#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS 10000 /* in ms */
+#define BKOPS_COMPLETION_POLLING_INTERVAL_MS 1000 /* in ms */
+ bool cancel_delayed_work;
+ bool started_delayed_bkops;
+ unsigned int sectors_changed;
+/*
+ * Since canceling the delayed work might have significant effect on the
+ * performance of small requests we won't queue the delayed work every time
+ * mmcqd thread is idle.
+ * The delayed work for idle BKOPS will be scheduled only after a significant
+ * amount of write or discard data.
+ * 100MB is chosen based on benchmark tests.
+ */
+#define BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK 204800 /* 100MB */
+};
+
/*
* MMC device
*/
@@ -283,6 +346,9 @@
struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */
unsigned int nr_parts;
+ struct mmc_wr_pack_stats wr_pack_stats; /* packed commands stats*/
+
+ struct mmc_bkops_info bkops_info;
};
/*
@@ -511,5 +577,8 @@
extern void mmc_fixup_device(struct mmc_card *card,
const struct mmc_fixup *table);
+extern struct mmc_wr_pack_stats *mmc_blk_get_packed_statistics(
+ struct mmc_card *card);
+extern void mmc_blk_init_packed_statistics(struct mmc_card *card);
#endif /* LINUX_MMC_CARD_H */
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 3f26a80..2795734 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -149,6 +149,9 @@
extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
struct mmc_command *, int);
extern void mmc_start_bkops(struct mmc_card *card, bool from_exception);
+extern void mmc_start_delayed_bkops(struct mmc_card *card);
+extern void mmc_start_idle_time_bkops(struct work_struct *work);
+extern void mmc_bkops_completion_polling(struct work_struct *work);
extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool);
extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
extern int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 714cc76..9e536be 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -245,6 +245,8 @@
#define MMC_CAP2_PACKED_WR (1 << 11) /* Allow packed write */
#define MMC_CAP2_PACKED_CMD (MMC_CAP2_PACKED_RD | \
MMC_CAP2_PACKED_WR) /* Allow packed commands */
+#define MMC_CAP2_PACKED_WR_CONTROL (1 << 12) /* Allow write packing control */
+
#define MMC_CAP2_SANITIZE (1 << 13) /* Support Sanitize */
#define MMC_CAP2_INIT_BKOPS (1 << 15) /* Need to set BKOPS_EN */
#define MMC_CAP2_POWER_OFF_VCCQ_DURING_SUSPEND (1 << 16)
diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h
index 5e1395e..6912087 100644
--- a/include/linux/msm_kgsl.h
+++ b/include/linux/msm_kgsl.h
@@ -2,7 +2,7 @@
#define _MSM_KGSL_H
#define KGSL_VERSION_MAJOR 3
-#define KGSL_VERSION_MINOR 13
+#define KGSL_VERSION_MINOR 14
/*context flags */
#define KGSL_CONTEXT_SAVE_GMEM 0x00000001
@@ -12,6 +12,7 @@
#define KGSL_CONTEXT_PREAMBLE 0x00000010
#define KGSL_CONTEXT_TRASH_STATE 0x00000020
#define KGSL_CONTEXT_PER_CONTEXT_TS 0x00000040
+#define KGSL_CONTEXT_USER_GENERATED_TS 0x00000080
#define KGSL_CONTEXT_INVALID 0xffffffff
@@ -45,6 +46,13 @@
#define KGSL_MEMTYPE_MULTISAMPLE 20
#define KGSL_MEMTYPE_KERNEL 255
+/*
+ * Alignment hint, passed as the power of 2 exponent.
+ * i.e 4k (2^12) would be 12, 64k (2^16)would be 16.
+ */
+#define KGSL_MEMALIGN_MASK 0x00FF0000
+#define KGSL_MEMALIGN_SHIFT 16
+
/* generic flag values */
#define KGSL_FLAGS_NORMALMODE 0x00000000
#define KGSL_FLAGS_SAFEMODE 0x00000001
@@ -66,6 +74,9 @@
#define KGSL_CLK_MEM_IFACE 0x00000010
#define KGSL_CLK_AXI 0x00000020
+/* Server Side Sync Timeout in milliseconds */
+#define KGSL_SYNCOBJ_SERVER_TIMEOUT 2000
+
/*
* Reset status values for context
*/
diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h
index a54b825..c3ea237 100644
--- a/include/linux/usb/audio.h
+++ b/include/linux/usb/audio.h
@@ -175,6 +175,7 @@
__u8 baInterfaceNr[n]; \
} __attribute__ ((packed))
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
/* 4.3.2.1 Input Terminal Descriptor */
struct uac_input_terminal_descriptor {
__u8 bLength; /* in bytes: 12 */
@@ -454,6 +455,7 @@
__u8 tSamFreq[n][3]; \
} __attribute__ ((packed))
+DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
#define UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(n) (8 + (n * 3))
struct uac_format_type_i_ext_descriptor {
diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h
index 5b2dcf9..61ebe0a 100644
--- a/include/linux/usb/f_accessory.h
+++ b/include/linux/usb/f_accessory.h
@@ -36,13 +36,15 @@
#define ACCESSORY_STRING_URI 4
#define ACCESSORY_STRING_SERIAL 5
-/* Control request for retrieving device's protocol version (currently 1)
+/* Control request for retrieving device's protocol version
*
* requestType: USB_DIR_IN | USB_TYPE_VENDOR
* request: ACCESSORY_GET_PROTOCOL
* value: 0
* index: 0
* data version number (16 bits little endian)
+ * 1 for original accessory support
+ * 2 adds HID and device to host audio support
*/
#define ACCESSORY_GET_PROTOCOL 51
@@ -70,6 +72,65 @@
*/
#define ACCESSORY_START 53
+/* Control request for registering a HID device.
+ * Upon registering, a unique ID is sent by the accessory in the
+ * value parameter. This ID will be used for future commands for
+ * the device
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_REGISTER_HID_DEVICE
+ * value: Accessory assigned ID for the HID device
+ * index: total length of the HID report descriptor
+ * data none
+ */
+#define ACCESSORY_REGISTER_HID 54
+
+/* Control request for unregistering a HID device.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_REGISTER_HID
+ * value: Accessory assigned ID for the HID device
+ * index: 0
+ * data none
+ */
+#define ACCESSORY_UNREGISTER_HID 55
+
+/* Control request for sending the HID report descriptor.
+ * If the HID descriptor is longer than the endpoint zero max packet size,
+ * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC
+ * commands. The data for the descriptor must be sent sequentially
+ * if multiple packets are needed.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SET_HID_REPORT_DESC
+ * value: Accessory assigned ID for the HID device
+ * index: offset of data in descriptor
+ * (needed when HID descriptor is too big for one packet)
+ * data the HID report descriptor
+ */
+#define ACCESSORY_SET_HID_REPORT_DESC 56
+
+/* Control request for sending HID events.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SEND_HID_EVENT
+ * value: Accessory assigned ID for the HID device
+ * index: 0
+ * data the HID report for the event
+ */
+#define ACCESSORY_SEND_HID_EVENT 57
+
+/* Control request for setting the audio mode.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SET_AUDIO_MODE
+ * value: 0 - no audio
+ * 1 - device to host, 44100 16-bit stereo PCM
+ * index: 0
+ * data none
+ */
+#define ACCESSORY_SET_AUDIO_MODE 58
+
/* ioctls for retrieving strings set by the host */
#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256])
#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256])
@@ -79,5 +140,7 @@
#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256])
/* returns 1 if there is a start request pending */
#define ACCESSORY_IS_START_REQUESTED _IO('M', 7)
+/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */
+#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8)
#endif /* __LINUX_USB_F_ACCESSORY_H */
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 920cf77..d45889c 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -192,9 +192,6 @@
* @mhl_enable: indicates MHL connector or not.
* @disable_reset_on_disconnect: perform USB PHY and LINK reset
* on USB cable disconnection.
- * @enable_dcd: Enable Data Contact Detection circuit. if not set
- * wait for 600msec before proceeding to primary
- * detection.
* @enable_lpm_on_suspend: Enable the USB core to go into Low
* Power Mode, when USB bus is suspended but cable
* is connected.
@@ -216,7 +213,6 @@
unsigned int mpm_otgsessvld_int;
bool mhl_enable;
bool disable_reset_on_disconnect;
- bool enable_dcd;
bool enable_lpm_on_dev_suspend;
bool core_clk_always_on_workaround;
struct msm_bus_scale_pdata *bus_scale_table;
diff --git a/include/media/vcap_v4l2.h b/include/media/vcap_v4l2.h
index b2a538c..dd39124 100644
--- a/include/media/vcap_v4l2.h
+++ b/include/media/vcap_v4l2.h
@@ -41,6 +41,22 @@
#define VCAP_BASE (dev->vcapbase)
#define VCAP_OFFSET(off) (VCAP_BASE + off)
+struct reg_range {
+ u32 min_val;
+ u32 max_val;
+};
+
+#define VCAP_REG_RANGE_1_MIN 0x0
+#define VCAP_REG_RANGE_1_MAX 0x48
+#define VCAP_REG_RANGE_2_MIN 0x100
+#define VCAP_REG_RANGE_2_MAX 0x104
+#define VCAP_REG_RANGE_3_MIN 0x400
+#define VCAP_REG_RANGE_3_MAX 0x7F0
+#define VCAP_REG_RANGE_4_MIN 0x800
+#define VCAP_REG_RANGE_4_MAX 0x8A0
+#define VCAP_REG_RANGE_5_MIN 0xC00
+#define VCAP_REG_RANGE_5_MAX 0xDF0
+
#define VCAP_SW_RESET_REQ (VCAP_BASE + 0x024)
#define VCAP_SW_RESET_STATUS (VCAP_BASE + 0x028)
@@ -128,6 +144,16 @@
struct vcap_client_data *cd;
};
+struct vcap_debugfs_params {
+ atomic_t vc_drop_count;
+ uint32_t vc_timestamp;
+ uint32_t vp_timestamp;
+ uint32_t vp_ewma;/* Exponential moving average */
+ uint32_t clk_rate;
+ uint32_t bw_request;
+ uint32_t reg_addr;
+};
+
struct vcap_dev {
struct v4l2_device v4l2_dev;
@@ -176,6 +202,7 @@
struct nr_param nr_param;
bool nr_update;
+ struct vcap_debugfs_params dbg_p;
};
struct vp_format_data {
diff --git a/include/net/scm.h b/include/net/scm.h
index d456f4c..0c0017c 100644
--- a/include/net/scm.h
+++ b/include/net/scm.h
@@ -71,9 +71,11 @@
}
static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,
- struct scm_cookie *scm)
+ struct scm_cookie *scm, bool forcecreds)
{
memset(scm, 0, sizeof(*scm));
+ if (forcecreds)
+ scm_set_cred(scm, task_tgid(current), current_cred());
unix_get_peersec_dgram(sock, scm);
if (msg->msg_controllen <= 0)
return 0;
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
index 90872c9..afbe7e0 100644
--- a/include/sound/apr_audio.h
+++ b/include/sound/apr_audio.h
@@ -1170,6 +1170,7 @@
#define MPEG4_MULTI_AAC 0x00010D86
#define US_POINT_EPOS_FORMAT 0x00012310
#define US_RAW_FORMAT 0x0001127C
+#define US_PROX_FORMAT 0x00012721
#define MULTI_CHANNEL_PCM 0x00010C66
#define ASM_ENCDEC_SBCRATE 0x00010C13
diff --git a/include/trace/events/mpdcvs_trace.h b/include/trace/events/mpdcvs_trace.h
new file mode 100644
index 0000000..0db1378
--- /dev/null
+++ b/include/trace/events/mpdcvs_trace.h
@@ -0,0 +1,156 @@
+/* Copyright (c) 2012, Free Software Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM mpdcvs_trace
+
+#if !defined(_TRACE_MPDCVS_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_MPDCVS_H
+
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(msm_mp,
+
+ TP_PROTO(const char *name, int mp_val),
+
+ TP_ARGS(name, mp_val),
+
+ TP_STRUCT__entry(
+ __string(name, name)
+ __field(int, mp_val)
+ ),
+
+ TP_fast_assign(
+ __assign_str(name, name);
+ __entry->mp_val = mp_val;
+ ),
+
+ TP_printk("ev_name=%s ev_level=%d",
+ __get_str(name),
+ __entry->mp_val)
+);
+
+/* Core function of run_q */
+
+DEFINE_EVENT(msm_mp, msm_mp_runq,
+
+ TP_PROTO(const char *name, int mp_val),
+
+ TP_ARGS(name, mp_val)
+);
+
+DEFINE_EVENT(msm_mp, msm_mp_cpusonline,
+
+ TP_PROTO(const char *name, int mp_val),
+
+ TP_ARGS(name, mp_val)
+);
+
+DEFINE_EVENT(msm_mp, msm_mp_slacktime,
+
+ TP_PROTO(const char *name, int mp_val),
+
+ TP_ARGS(name, mp_val)
+);
+
+DECLARE_EVENT_CLASS(msm_dcvs,
+
+ TP_PROTO(const char *name, const char *cpuid, int val),
+
+ TP_ARGS(name, cpuid, val),
+
+ TP_STRUCT__entry(
+ __string(name, name)
+ __string(cpuid, cpuid)
+ __field(int, val)
+ ),
+
+ TP_fast_assign(
+ __assign_str(name, name);
+ __assign_str(cpuid, cpuid);
+ __entry->val = val;
+ ),
+
+ TP_printk("ev_name=%s d_name=%s ev_level=%d",
+ __get_str(name),
+ __get_str(cpuid),
+ __entry->val)
+);
+
+/* Core function of dcvs */
+
+DEFINE_EVENT(msm_dcvs, msm_dcvs_idle,
+
+ TP_PROTO(const char *name, const char *cpuid, int val),
+
+ TP_ARGS(name, cpuid, val)
+);
+
+DEFINE_EVENT(msm_dcvs, msm_dcvs_iowait,
+
+ TP_PROTO(const char *name, const char *cpuid, int val),
+
+ TP_ARGS(name, cpuid, val)
+);
+
+DEFINE_EVENT(msm_dcvs, msm_dcvs_slack_time,
+
+ TP_PROTO(const char *name, const char *cpuid, int val),
+
+ TP_ARGS(name, cpuid, val)
+);
+
+DECLARE_EVENT_CLASS(msm_dcvs_scm,
+
+ TP_PROTO(unsigned long cpuid, int ev_type, unsigned long param0,
+ unsigned long param1, unsigned long ret0, unsigned long ret1),
+
+ TP_ARGS(cpuid, ev_type, param0, param1, ret0, ret1),
+
+ TP_STRUCT__entry(
+ __field(unsigned long, cpuid)
+ __field(int, ev_type)
+ __field(unsigned long, param0)
+ __field(unsigned long, param1)
+ __field(unsigned long, ret0)
+ __field(unsigned long, ret1)
+ ),
+
+ TP_fast_assign(
+ __entry->cpuid = cpuid;
+ __entry->ev_type = ev_type;
+ __entry->param0 = param0;
+ __entry->param1 = param1;
+ __entry->ret0 = ret0;
+ __entry->ret1 = ret1;
+ ),
+
+ TP_printk("dev=%lu ev_type=%d ev_param0=%lu ev_param1=%lu ev_ret0=%lu ev_ret1=%lu",
+ __entry->cpuid,
+ __entry->ev_type,
+ __entry->param0,
+ __entry->param1,
+ __entry->ret0,
+ __entry->ret1)
+);
+
+DEFINE_EVENT(msm_dcvs_scm, msm_dcvs_scm_event,
+
+ TP_PROTO(unsigned long cpuid, int ev_type, unsigned long param0,
+ unsigned long param1, unsigned long ret0, unsigned long ret1),
+
+ TP_ARGS(cpuid, ev_type, param0, param1, ret0, ret1)
+);
+
+#endif /* _TRACE_MPDCVS_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/kernel/printk.c b/kernel/printk.c
index 4cf4670..7f51b03 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -1224,13 +1224,13 @@
unsigned long action, void *hcpu)
{
switch (action) {
- case CPU_ONLINE:
case CPU_DEAD:
case CPU_DOWN_FAILED:
case CPU_UP_CANCELED:
console_lock();
console_unlock();
break;
+ case CPU_ONLINE:
case CPU_DYING:
/* invoked with preemption disabled, so defer */
if (!console_trylock())
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index faa48f7..59debb7 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1329,7 +1329,7 @@
if (NULL == siocb->scm)
siocb->scm = &scm;
- err = scm_send(sock, msg, siocb->scm);
+ err = scm_send(sock, msg, siocb->scm, true);
if (err < 0)
return err;
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index d510353..109e30b 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1446,7 +1446,7 @@
if (NULL == siocb->scm)
siocb->scm = &tmp_scm;
wait_for_unix_gc();
- err = scm_send(sock, msg, siocb->scm);
+ err = scm_send(sock, msg, siocb->scm, false);
if (err < 0)
return err;
@@ -1607,7 +1607,7 @@
if (NULL == siocb->scm)
siocb->scm = &tmp_scm;
wait_for_unix_gc();
- err = scm_send(sock, msg, siocb->scm);
+ err = scm_send(sock, msg, siocb->scm, false);
if (err < 0)
return err;
diff --git a/sound/soc/codecs/wcd9304-tables.c b/sound/soc/codecs/wcd9304-tables.c
index f0d76e8..ba0b6b6 100644
--- a/sound/soc/codecs/wcd9304-tables.c
+++ b/sound/soc/codecs/wcd9304-tables.c
@@ -419,6 +419,10 @@
[SITAR_A_QFUSE_DATA_OUT1] = 1,
[SITAR_A_QFUSE_DATA_OUT2] = 1,
[SITAR_A_QFUSE_DATA_OUT3] = 1,
+ [SITAR_A_QFUSE_DATA_OUT4] = 1,
+ [SITAR_A_QFUSE_DATA_OUT5] = 1,
+ [SITAR_A_QFUSE_DATA_OUT6] = 1,
+ [SITAR_A_QFUSE_DATA_OUT7] = 1,
[SITAR_A_CDC_CTL] = 1,
[SITAR_A_LEAKAGE_CTL] = 1,
[SITAR_A_INTR_MODE] = 1,
@@ -428,6 +432,9 @@
[SITAR_A_INTR_STATUS0] = 1,
[SITAR_A_INTR_STATUS1] = 1,
[SITAR_A_INTR_STATUS2] = 1,
+ [SITAR_A_INTR_CLEAR0] = 1,
+ [SITAR_A_INTR_CLEAR1] = 1,
+ [SITAR_A_INTR_CLEAR2] = 1,
[SITAR_A_INTR_LEVEL0] = 1,
[SITAR_A_INTR_LEVEL1] = 1,
[SITAR_A_INTR_LEVEL2] = 1,
@@ -593,24 +600,76 @@
[SITAR_A_CDC_ANC1_SPARE] = 1,
[SITAR_A_CDC_ANC1_SMLPF_CTL] = 1,
[SITAR_A_CDC_ANC1_DCFLT_CTL] = 1,
+ [SITAR_A_CDC_ANC2_CTL] = 1,
+ [SITAR_A_CDC_ANC2_SHIFT] = 1,
+ [SITAR_A_CDC_ANC2_IIR_B1_CTL] = 1,
+ [SITAR_A_CDC_ANC2_IIR_B2_CTL] = 1,
+ [SITAR_A_CDC_ANC2_IIR_B3_CTL] = 1,
+ [SITAR_A_CDC_ANC2_IIR_B4_CTL] = 1,
+ [SITAR_A_CDC_ANC2_LPF_B1_CTL] = 1,
+ [SITAR_A_CDC_ANC2_LPF_B2_CTL] = 1,
+ [SITAR_A_CDC_ANC2_LPF_B3_CTL] = 1,
+ [SITAR_A_CDC_ANC2_SPARE] = 1,
+ [SITAR_A_CDC_ANC2_SMLPF_CTL] = 1,
+ [SITAR_A_CDC_ANC2_DCFLT_CTL] = 1,
[SITAR_A_CDC_TX1_VOL_CTL_TIMER] = 1,
[SITAR_A_CDC_TX1_VOL_CTL_GAIN] = 1,
[SITAR_A_CDC_TX1_VOL_CTL_CFG] = 1,
[SITAR_A_CDC_TX1_MUX_CTL] = 1,
[SITAR_A_CDC_TX1_CLK_FS_CTL] = 1,
[SITAR_A_CDC_TX1_DMIC_CTL] = 1,
+ [SITAR_A_CDC_TX2_VOL_CTL_TIMER] = 1,
+ [SITAR_A_CDC_TX2_VOL_CTL_GAIN] = 1,
+ [SITAR_A_CDC_TX2_VOL_CTL_CFG] = 1,
+ [SITAR_A_CDC_TX2_MUX_CTL] = 1,
+ [SITAR_A_CDC_TX2_CLK_FS_CTL] = 1,
+ [SITAR_A_CDC_TX2_DMIC_CTL] = 1,
+ [SITAR_A_CDC_TX3_VOL_CTL_TIMER] = 1,
+ [SITAR_A_CDC_TX3_VOL_CTL_GAIN] = 1,
+ [SITAR_A_CDC_TX3_VOL_CTL_CFG] = 1,
+ [SITAR_A_CDC_TX3_MUX_CTL] = 1,
+ [SITAR_A_CDC_TX3_CLK_FS_CTL] = 1,
+ [SITAR_A_CDC_TX3_DMIC_CTL] = 1,
+ [SITAR_A_CDC_TX4_VOL_CTL_TIMER] = 1,
+ [SITAR_A_CDC_TX4_VOL_CTL_GAIN] = 1,
+ [SITAR_A_CDC_TX4_VOL_CTL_CFG] = 1,
+ [SITAR_A_CDC_TX4_MUX_CTL] = 1,
+ [SITAR_A_CDC_TX4_CLK_FS_CTL] = 1,
+ [SITAR_A_CDC_TX4_DMIC_CTL] = 1,
+ [SITAR_A_CDC_TX5_VOL_CTL_TIMER] = 1,
+ [SITAR_A_CDC_TX5_VOL_CTL_GAIN] = 1,
+ [SITAR_A_CDC_TX5_VOL_CTL_CFG] = 1,
+ [SITAR_A_CDC_TX5_MUX_CTL] = 1,
+ [SITAR_A_CDC_TX5_CLK_FS_CTL] = 1,
+ [SITAR_A_CDC_TX5_DMIC_CTL] = 1,
[SITAR_A_CDC_SRC1_PDA_CFG] = 1,
[SITAR_A_CDC_SRC1_FS_CTL] = 1,
+ [SITAR_A_CDC_SRC2_PDA_CFG] = 1,
+ [SITAR_A_CDC_SRC2_FS_CTL] = 1,
[SITAR_A_CDC_RX1_B1_CTL] = 1,
[SITAR_A_CDC_RX1_B2_CTL] = 1,
[SITAR_A_CDC_RX1_B3_CTL] = 1,
[SITAR_A_CDC_RX1_B4_CTL] = 1,
[SITAR_A_CDC_RX1_B5_CTL] = 1,
- [SITAR_A_CDC_RX2_B5_CTL] = 1,
- [SITAR_A_CDC_RX3_B5_CTL] = 1,
[SITAR_A_CDC_RX1_B6_CTL] = 1,
[SITAR_A_CDC_RX1_VOL_CTL_B1_CTL] = 1,
[SITAR_A_CDC_RX1_VOL_CTL_B2_CTL] = 1,
+ [SITAR_A_CDC_RX2_B1_CTL] = 1,
+ [SITAR_A_CDC_RX2_B2_CTL] = 1,
+ [SITAR_A_CDC_RX2_B3_CTL] = 1,
+ [SITAR_A_CDC_RX2_B4_CTL] = 1,
+ [SITAR_A_CDC_RX2_B5_CTL] = 1,
+ [SITAR_A_CDC_RX2_B6_CTL] = 1,
+ [SITAR_A_CDC_RX2_VOL_CTL_B1_CTL] = 1,
+ [SITAR_A_CDC_RX2_VOL_CTL_B2_CTL] = 1,
+ [SITAR_A_CDC_RX3_B1_CTL] = 1,
+ [SITAR_A_CDC_RX3_B2_CTL] = 1,
+ [SITAR_A_CDC_RX3_B3_CTL] = 1,
+ [SITAR_A_CDC_RX3_B4_CTL] = 1,
+ [SITAR_A_CDC_RX3_B5_CTL] = 1,
+ [SITAR_A_CDC_RX3_B6_CTL] = 1,
+ [SITAR_A_CDC_RX3_VOL_CTL_B1_CTL] = 1,
+ [SITAR_A_CDC_RX3_VOL_CTL_B2_CTL] = 1,
[SITAR_A_CDC_CLK_ANC_RESET_CTL] = 1,
[SITAR_A_CDC_CLK_RX_RESET_CTL] = 1,
[SITAR_A_CDC_CLK_TX_RESET_B1_CTL] = 1,
@@ -652,6 +711,21 @@
[SITAR_A_CDC_IIR1_COEF_B3_CTL] = 1,
[SITAR_A_CDC_IIR1_COEF_B4_CTL] = 1,
[SITAR_A_CDC_IIR1_COEF_B5_CTL] = 1,
+ [SITAR_A_CDC_IIR2_GAIN_B1_CTL] = 1,
+ [SITAR_A_CDC_IIR2_GAIN_B2_CTL] = 1,
+ [SITAR_A_CDC_IIR2_GAIN_B3_CTL] = 1,
+ [SITAR_A_CDC_IIR2_GAIN_B4_CTL] = 1,
+ [SITAR_A_CDC_IIR2_GAIN_B5_CTL] = 1,
+ [SITAR_A_CDC_IIR2_GAIN_B6_CTL] = 1,
+ [SITAR_A_CDC_IIR2_GAIN_B7_CTL] = 1,
+ [SITAR_A_CDC_IIR2_GAIN_B8_CTL] = 1,
+ [SITAR_A_CDC_IIR2_CTL] = 1,
+ [SITAR_A_CDC_IIR2_GAIN_TIMER_CTL] = 1,
+ [SITAR_A_CDC_IIR2_COEF_B1_CTL] = 1,
+ [SITAR_A_CDC_IIR2_COEF_B2_CTL] = 1,
+ [SITAR_A_CDC_IIR2_COEF_B3_CTL] = 1,
+ [SITAR_A_CDC_IIR2_COEF_B4_CTL] = 1,
+ [SITAR_A_CDC_IIR2_COEF_B5_CTL] = 1,
[SITAR_A_CDC_TOP_GAIN_UPDATE] = 1,
[SITAR_A_CDC_TOP_RDAC_DOUT_CTL] = 1,
[SITAR_A_CDC_DEBUG_B1_CTL] = 1,
@@ -669,6 +743,14 @@
[SITAR_A_CDC_COMP1_B6_CTL] = 1,
[SITAR_A_CDC_COMP1_SHUT_DOWN_STATUS] = 1,
[SITAR_A_CDC_COMP1_FS_CFG] = 1,
+ [SITAR_A_CDC_COMP2_B1_CTL] = 1,
+ [SITAR_A_CDC_COMP2_B2_CTL] = 1,
+ [SITAR_A_CDC_COMP2_B3_CTL] = 1,
+ [SITAR_A_CDC_COMP2_B4_CTL] = 1,
+ [SITAR_A_CDC_COMP2_B5_CTL] = 1,
+ [SITAR_A_CDC_COMP2_B6_CTL] = 1,
+ [SITAR_A_CDC_COMP2_SHUT_DOWN_STATUS] = 1,
+ [SITAR_A_CDC_COMP2_FS_CFG] = 1,
[SITAR_A_CDC_CONN_RX1_B1_CTL] = 1,
[SITAR_A_CDC_CONN_RX1_B2_CTL] = 1,
[SITAR_A_CDC_CONN_RX1_B3_CTL] = 1,
diff --git a/sound/soc/codecs/wcd9304.c b/sound/soc/codecs/wcd9304.c
index b303878..fa7abb1 100644
--- a/sound/soc/codecs/wcd9304.c
+++ b/sound/soc/codecs/wcd9304.c
@@ -41,6 +41,7 @@
#define ADC_DMIC_SEL_ADC 0
#define ADC_DMIC_SEL_DMIC 1
+#define NUM_AMIC 3
#define NUM_DECIMATORS 4
#define NUM_INTERPOLATORS 3
#define BITS_PER_REG 8
@@ -1712,7 +1713,7 @@
/* reset retry counter as PA is turned off signifying
* start of new OCP detection session
*/
- if (SITAR_IRQ_HPH_PA_OCPL_FAULT)
+ if (WCD9XXX_IRQ_HPH_PA_OCPL_FAULT)
sitar->hphlocp_cnt = 0;
else
sitar->hphrocp_cnt = 0;
@@ -1724,14 +1725,16 @@
{
struct sitar_priv *sitar = container_of(work, struct sitar_priv,
hphlocp_work);
- hphocp_off_report(sitar, SND_JACK_OC_HPHL, SITAR_IRQ_HPH_PA_OCPL_FAULT);
+ hphocp_off_report(sitar, SND_JACK_OC_HPHL,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
}
static void hphrocp_off_report(struct work_struct *work)
{
struct sitar_priv *sitar = container_of(work, struct sitar_priv,
hphrocp_work);
- hphocp_off_report(sitar, SND_JACK_OC_HPHR, SITAR_IRQ_HPH_PA_OCPR_FAULT);
+ hphocp_off_report(sitar, SND_JACK_OC_HPHR,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
}
static int sitar_hph_pa_event(struct snd_soc_dapm_widget *w,
@@ -3186,7 +3189,7 @@
short bias_value;
struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
- wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
if (noreldetection)
sitar_turn_onoff_rel_detection(codec, false);
@@ -3222,7 +3225,7 @@
if (noreldetection)
sitar_turn_onoff_rel_detection(codec, true);
- wcd9xxx_enable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
return bias_value;
}
@@ -3440,7 +3443,7 @@
sitar = snd_soc_codec_get_drvdata(codec);
calibration = sitar->mbhc_cfg.calibration;
- wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
sitar_turn_onoff_rel_detection(codec, false);
/* First compute the DCE / STA wait times
@@ -3523,7 +3526,7 @@
snd_soc_write(codec, SITAR_A_MBHC_SCALING_MUX_1, 0x84);
usleep_range(100, 100);
- wcd9xxx_enable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
sitar_turn_onoff_rel_detection(codec, true);
}
@@ -3807,9 +3810,9 @@
}
sitar_set_and_turnoff_hph_padac(codec);
hphocp_off_report(sitar, SND_JACK_OC_HPHR,
- SITAR_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
hphocp_off_report(sitar, SND_JACK_OC_HPHL,
- SITAR_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
sitar->current_plug = PLUG_TYPE_NONE;
sitar->mbhc_polling_active = false;
} else {
@@ -4231,9 +4234,9 @@
snd_soc_update_bits(codec, SITAR_A_RX_HPH_OCP_CTL,
0x10, 0x10);
wcd9xxx_enable_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
wcd9xxx_enable_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
/* Bootup time detection */
sitar_hs_gpio_handler(codec);
}
@@ -4623,7 +4626,7 @@
0x10);
} else {
wcd9xxx_disable_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
sitar->hphlocp_cnt = 0;
sitar->hph_status |= SND_JACK_OC_HPHL;
if (sitar->mbhc_cfg.headset_jack)
@@ -4656,7 +4659,7 @@
0x10);
} else {
wcd9xxx_disable_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
sitar->hphrocp_cnt = 0;
sitar->hph_status |= SND_JACK_OC_HPHR;
if (sitar->mbhc_cfg.headset_jack)
@@ -4679,7 +4682,7 @@
pr_debug("%s: enter\n", __func__);
SITAR_ACQUIRE_LOCK(priv->codec_resource_lock);
- wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_INSERTION);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
@@ -4860,6 +4863,7 @@
u8 flag = pdata->amic_settings.use_pdata;
u8 i = 0, j = 0;
u8 val_txfe = 0, value = 0;
+ int amic_reg_count = 0;
if (!pdata) {
rc = -ENODEV;
@@ -4905,7 +4909,8 @@
snd_soc_update_bits(codec, SITAR_A_MICB_2_CTL, 0x10,
(pdata->micbias.bias2_cap_mode << 4));
- for (i = 0; i < 6; j++, i += 2) {
+ amic_reg_count = (NUM_AMIC % 2) ? NUM_AMIC + 1 : NUM_AMIC;
+ for (i = 0; i < amic_reg_count; j++, i += 2) {
if (flag & (0x01 << i)) {
value = (leg_mode & (0x01 << i)) ? 0x10 : 0x00;
val_txfe = (txfe_bypass & (0x01 << i)) ? 0x20 : 0x00;
@@ -5133,44 +5138,49 @@
snd_soc_dapm_sync(dapm);
- ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_MBHC_INSERTION,
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_INSERTION,
sitar_hs_insert_irq, "Headset insert detect", sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_MBHC_INSERTION);
+ WCD9XXX_IRQ_MBHC_INSERTION);
goto err_insert_irq;
}
- wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_INSERTION);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
- ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_MBHC_REMOVAL,
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_REMOVAL,
sitar_hs_remove_irq, "Headset remove detect", sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_MBHC_REMOVAL);
+ WCD9XXX_IRQ_MBHC_REMOVAL);
goto err_remove_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL,
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_POTENTIAL,
sitar_dce_handler, "DC Estimation detect", sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_MBHC_POTENTIAL);
+ WCD9XXX_IRQ_MBHC_POTENTIAL);
goto err_potential_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_MBHC_RELEASE,
- sitar_release_handler, "Button Release detect", sitar);
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_RELEASE,
+ sitar_release_handler,
+ "Button Release detect", sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_MBHC_RELEASE);
+ WCD9XXX_IRQ_MBHC_RELEASE);
goto err_release_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_SLIMBUS,
- sitar_slimbus_irq, "SLIMBUS Slave", sitar);
+ ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
+ sitar_slimbus_irq, "SLIMBUS Slave", sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_SLIMBUS);
+ WCD9XXX_IRQ_SLIMBUS);
goto err_slimbus_irq;
}
@@ -5180,24 +5190,27 @@
ret = wcd9xxx_request_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPL_FAULT, sitar_hphl_ocp_irq,
- "HPH_L OCP detect", sitar);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+ sitar_hphl_ocp_irq,
+ "HPH_L OCP detect", sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
goto err_hphl_ocp_irq;
}
- wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_HPH_PA_OCPL_FAULT);
+ wcd9xxx_disable_irq(codec->control_data,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
ret = wcd9xxx_request_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPR_FAULT, sitar_hphr_ocp_irq,
- "HPH_R OCP detect", sitar);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+ sitar_hphr_ocp_irq, "HPH_R OCP detect",
+ sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
goto err_hphr_ocp_irq;
}
- wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_HPH_PA_OCPR_FAULT);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
for (i = 0; i < ARRAY_SIZE(sitar_dai); i++) {
switch (sitar_dai[i].id) {
@@ -5224,23 +5237,20 @@
return ret;
err_hphr_ocp_irq:
- wcd9xxx_free_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPL_FAULT, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+ sitar);
err_hphl_ocp_irq:
- wcd9xxx_free_irq(codec->control_data,
- SITAR_IRQ_SLIMBUS, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, sitar);
err_slimbus_irq:
- wcd9xxx_free_irq(codec->control_data,
- SITAR_IRQ_MBHC_RELEASE, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, sitar);
err_release_irq:
- wcd9xxx_free_irq(codec->control_data,
- SITAR_IRQ_MBHC_POTENTIAL, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL,
+ sitar);
err_potential_irq:
- wcd9xxx_free_irq(codec->control_data,
- SITAR_IRQ_MBHC_REMOVAL, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, sitar);
err_remove_irq:
- wcd9xxx_free_irq(codec->control_data,
- SITAR_IRQ_MBHC_INSERTION, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION,
+ sitar);
err_insert_irq:
err_pdata:
mutex_destroy(&sitar->codec_resource_lock);
@@ -5251,11 +5261,13 @@
{
int i;
struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
- wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_SLIMBUS, sitar);
- wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_MBHC_RELEASE, sitar);
- wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL, sitar);
- wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_MBHC_REMOVAL, sitar);
- wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_MBHC_INSERTION, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL,
+ sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION,
+ sitar);
SITAR_ACQUIRE_LOCK(sitar->codec_resource_lock);
sitar_codec_disable_clock_block(codec);
SITAR_RELEASE_LOCK(sitar->codec_resource_lock);
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 5a819c9..b64a6a7 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -2911,7 +2911,7 @@
/* reset retry counter as PA is turned off signifying
* start of new OCP detection session
*/
- if (TABLA_IRQ_HPH_PA_OCPL_FAULT)
+ if (WCD9XXX_IRQ_HPH_PA_OCPL_FAULT)
tabla->hphlocp_cnt = 0;
else
tabla->hphrocp_cnt = 0;
@@ -2923,14 +2923,16 @@
{
struct tabla_priv *tabla = container_of(work, struct tabla_priv,
hphlocp_work);
- hphocp_off_report(tabla, SND_JACK_OC_HPHL, TABLA_IRQ_HPH_PA_OCPL_FAULT);
+ hphocp_off_report(tabla, SND_JACK_OC_HPHL,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
}
static void hphrocp_off_report(struct work_struct *work)
{
struct tabla_priv *tabla = container_of(work, struct tabla_priv,
hphrocp_work);
- hphocp_off_report(tabla, SND_JACK_OC_HPHR, TABLA_IRQ_HPH_PA_OCPR_FAULT);
+ hphocp_off_report(tabla, SND_JACK_OC_HPHR,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
}
static int tabla_hph_pa_event(struct snd_soc_dapm_widget *w,
@@ -5120,7 +5122,7 @@
short bias_value;
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
if (noreldetection)
tabla_turn_onoff_rel_detection(codec, false);
@@ -5156,7 +5158,7 @@
if (noreldetection)
tabla_turn_onoff_rel_detection(codec, true);
- wcd9xxx_enable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
return bias_value;
}
@@ -5345,9 +5347,9 @@
}
tabla_set_and_turnoff_hph_padac(codec);
hphocp_off_report(tabla, SND_JACK_OC_HPHR,
- TABLA_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
hphocp_off_report(tabla, SND_JACK_OC_HPHL,
- TABLA_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
tabla->current_plug = PLUG_TYPE_NONE;
tabla->mbhc_polling_active = false;
} else {
@@ -5510,7 +5512,7 @@
snd_soc_update_bits(codec, tabla->reg_addr.micb_4_mbhc, 0x3,
tabla->mbhc_cfg.micbias);
- wcd9xxx_enable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
pr_debug("%s: leave\n", __func__);
return 0;
@@ -5640,7 +5642,7 @@
tabla = snd_soc_codec_get_drvdata(codec);
calibration = tabla->mbhc_cfg.calibration;
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
tabla_turn_onoff_rel_detection(codec, false);
/* First compute the DCE / STA wait times
@@ -5746,7 +5748,7 @@
snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x84);
usleep_range(100, 100);
- wcd9xxx_enable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
tabla_turn_onoff_rel_detection(codec, true);
}
@@ -6334,7 +6336,7 @@
0x10);
} else {
wcd9xxx_disable_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
tabla->hph_status |= SND_JACK_OC_HPHL;
if (tabla->mbhc_cfg.headset_jack)
tabla_snd_soc_jack_report(tabla,
@@ -6368,7 +6370,7 @@
0x10);
} else {
wcd9xxx_disable_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
tabla->hph_status |= SND_JACK_OC_HPHR;
if (tabla->mbhc_cfg.headset_jack)
tabla_snd_soc_jack_report(tabla,
@@ -6977,7 +6979,7 @@
wcd9xxx_unlock_sleep(core);
} else {
wcd9xxx_enable_irq(codec->control_data,
- TABLA_IRQ_MBHC_INSERTION);
+ WCD9XXX_IRQ_MBHC_INSERTION);
pr_err("%s: Error detecting plug insertion\n",
__func__);
}
@@ -7015,7 +7017,7 @@
pr_debug("%s: enter\n", __func__);
TABLA_ACQUIRE_LOCK(priv->codec_resource_lock);
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
is_mb_trigger = !!(snd_soc_read(codec, priv->mbhc_bias_regs.mbhc_reg) &
0x10);
@@ -7262,7 +7264,8 @@
snd_soc_update_bits(codec, tabla->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x13, 0x00);
snd_soc_update_bits(codec, tabla->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
- wcd9xxx_disable_irq_sync(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+ wcd9xxx_disable_irq_sync(codec->control_data,
+ WCD9XXX_IRQ_MBHC_INSERTION);
tabla_codec_detect_plug_type(codec);
wcd9xxx_unlock_sleep(tabla_core);
}
@@ -7485,9 +7488,9 @@
if (!IS_ERR_VALUE(ret)) {
snd_soc_update_bits(codec, TABLA_A_RX_HPH_OCP_CTL, 0x10, 0x10);
wcd9xxx_enable_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
wcd9xxx_enable_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
if (tabla->mbhc_cfg.gpio) {
ret = request_threaded_irq(tabla->mbhc_cfg.gpio_irq,
@@ -8218,44 +8221,50 @@
snd_soc_dapm_sync(dapm);
- ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION,
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_INSERTION,
tabla_hs_insert_irq, "Headset insert detect", tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_MBHC_INSERTION);
+ WCD9XXX_IRQ_MBHC_INSERTION);
goto err_insert_irq;
}
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
- ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL,
- tabla_hs_remove_irq, "Headset remove detect", tabla);
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_REMOVAL,
+ tabla_hs_remove_irq,
+ "Headset remove detect", tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_MBHC_REMOVAL);
+ WCD9XXX_IRQ_MBHC_REMOVAL);
goto err_remove_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL,
- tabla_dce_handler, "DC Estimation detect", tabla);
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_POTENTIAL,
+ tabla_dce_handler, "DC Estimation detect",
+ tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_MBHC_POTENTIAL);
+ WCD9XXX_IRQ_MBHC_POTENTIAL);
goto err_potential_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE,
- tabla_release_handler, "Button Release detect", tabla);
+ ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE,
+ tabla_release_handler,
+ "Button Release detect", tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_MBHC_RELEASE);
+ WCD9XXX_IRQ_MBHC_RELEASE);
goto err_release_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_SLIMBUS,
- tabla_slimbus_irq, "SLIMBUS Slave", tabla);
+ ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
+ tabla_slimbus_irq, "SLIMBUS Slave", tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_SLIMBUS);
+ WCD9XXX_IRQ_SLIMBUS);
goto err_slimbus_irq;
}
@@ -8264,24 +8273,26 @@
TABLA_SLIM_PGD_PORT_INT_EN0 + i, 0xFF);
ret = wcd9xxx_request_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPL_FAULT, tabla_hphl_ocp_irq,
- "HPH_L OCP detect", tabla);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+ tabla_hphl_ocp_irq,
+ "HPH_L OCP detect", tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
goto err_hphl_ocp_irq;
}
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_HPH_PA_OCPL_FAULT);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
ret = wcd9xxx_request_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPR_FAULT, tabla_hphr_ocp_irq,
- "HPH_R OCP detect", tabla);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+ tabla_hphr_ocp_irq,
+ "HPH_R OCP detect", tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
goto err_hphr_ocp_irq;
}
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_HPH_PA_OCPR_FAULT);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
/*
* Register suspend lock and notifier to resend edge triggered
@@ -8334,17 +8345,19 @@
err_hphr_ocp_irq:
wcd9xxx_free_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPL_FAULT, tabla);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, tabla);
err_hphl_ocp_irq:
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_SLIMBUS, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, tabla);
err_slimbus_irq:
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, tabla);
err_release_irq:
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL,
+ tabla);
err_potential_irq:
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, tabla);
err_remove_irq:
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION,
+ tabla);
err_insert_irq:
err_pdata:
mutex_destroy(&tabla->codec_resource_lock);
@@ -8358,11 +8371,13 @@
wake_lock_destroy(&tabla->irq_resend_wlock);
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_SLIMBUS, tabla);
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE, tabla);
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL, tabla);
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL, tabla);
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL,
+ tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION,
+ tabla);
TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
tabla_codec_disable_clock_block(codec);
TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index e8bb652..d187ea5 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -778,6 +778,9 @@
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
u32 rate = taiko->comp_fs[w->shift];
+ pr_debug("%s: %s event %d enabled = %d", __func__, w->name,
+ event, taiko->comp_enabled[w->shift]);
+
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (taiko->comp_enabled[w->shift] != 0) {
@@ -2766,7 +2769,7 @@
/* reset retry counter as PA is turned off signifying
* start of new OCP detection session
*/
- if (TAIKO_IRQ_HPH_PA_OCPL_FAULT)
+ if (WCD9XXX_IRQ_HPH_PA_OCPL_FAULT)
taiko->hphlocp_cnt = 0;
else
taiko->hphrocp_cnt = 0;
@@ -2777,15 +2780,17 @@
static void hphlocp_off_report(struct work_struct *work)
{
struct taiko_priv *taiko = container_of(work, struct taiko_priv,
- hphlocp_work);
- hphocp_off_report(taiko, SND_JACK_OC_HPHL, TAIKO_IRQ_HPH_PA_OCPL_FAULT);
+ hphlocp_work);
+ hphocp_off_report(taiko, SND_JACK_OC_HPHL,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
}
static void hphrocp_off_report(struct work_struct *work)
{
struct taiko_priv *taiko = container_of(work, struct taiko_priv,
- hphrocp_work);
- hphocp_off_report(taiko, SND_JACK_OC_HPHR, TAIKO_IRQ_HPH_PA_OCPR_FAULT);
+ hphrocp_work);
+ hphocp_off_report(taiko, SND_JACK_OC_HPHR,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
}
static int taiko_hph_pa_event(struct snd_soc_dapm_widget *w,
@@ -3123,7 +3128,7 @@
{"LINEOUT4 DAC", NULL, "RX6 DSM MUX"},
{"SPK PA", NULL, "SPK DAC"},
- {"SPK DAC", NULL, "RX7 MIX1"},
+ {"SPK DAC", NULL, "RX7 MIX2"},
{"RX1 CHAIN", NULL, "RX1 MIX2"},
{"RX2 CHAIN", NULL, "RX2 MIX2"},
@@ -4297,13 +4302,17 @@
taiko_spk_dac_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX7 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+
SND_SOC_DAPM_MIXER_E("RX1 MIX2", TAIKO_A_CDC_CLK_RX_B1_CTL, 0, 0, NULL,
0, taiko_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_MIXER_E("RX2 MIX2", TAIKO_A_CDC_CLK_RX_B1_CTL, 1, 0, NULL,
0, taiko_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU),
- SND_SOC_DAPM_MIXER_E("RX7 MIX2", TAIKO_A_CDC_CLK_RX_B1_CTL, 2, 0, NULL,
+ SND_SOC_DAPM_MIXER_E("RX3 MIX1", TAIKO_A_CDC_CLK_RX_B1_CTL, 2, 0, NULL,
0, taiko_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_MIXER_E("RX4 MIX1", TAIKO_A_CDC_CLK_RX_B1_CTL, 3, 0, NULL,
@@ -4315,14 +4324,10 @@
SND_SOC_DAPM_MIXER_E("RX6 MIX1", TAIKO_A_CDC_CLK_RX_B1_CTL, 5, 0, NULL,
0, taiko_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU),
- SND_SOC_DAPM_MIXER_E("RX7 MIX1", TAIKO_A_CDC_CLK_RX_B1_CTL, 6, 0, NULL,
+ SND_SOC_DAPM_MIXER_E("RX7 MIX2", TAIKO_A_CDC_CLK_RX_B1_CTL, 6, 0, NULL,
0, taiko_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU),
- SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("RX3 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
-
SND_SOC_DAPM_MUX_E("RX4 DSM MUX", TAIKO_A_CDC_CLK_RX_B1_CTL, 3, 0,
&rx4_dsm_mux, taiko_codec_enable_interpolator,
SND_SOC_DAPM_PRE_PMU),
@@ -4681,7 +4686,7 @@
short bias_value;
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
if (noreldetection)
taiko_turn_onoff_rel_detection(codec, false);
@@ -4717,7 +4722,7 @@
if (noreldetection)
taiko_turn_onoff_rel_detection(codec, true);
- wcd9xxx_enable_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
return bias_value;
}
@@ -4904,9 +4909,9 @@
}
taiko_set_and_turnoff_hph_padac(codec);
hphocp_off_report(taiko, SND_JACK_OC_HPHR,
- TAIKO_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
hphocp_off_report(taiko, SND_JACK_OC_HPHL,
- TAIKO_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
taiko->current_plug = PLUG_TYPE_NONE;
taiko->mbhc_polling_active = false;
} else {
@@ -5050,7 +5055,7 @@
snd_soc_update_bits(codec, taiko->reg_addr.micb_4_mbhc, 0x3,
taiko->mbhc_cfg.micbias);
- wcd9xxx_enable_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION);
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
return 0;
}
@@ -5159,7 +5164,7 @@
taiko = snd_soc_codec_get_drvdata(codec);
calibration = taiko->mbhc_cfg.calibration;
- wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
taiko_turn_onoff_rel_detection(codec, false);
/* First compute the DCE / STA wait times
@@ -5242,7 +5247,7 @@
snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x84);
usleep_range(100, 100);
- wcd9xxx_enable_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
taiko_turn_onoff_rel_detection(codec, true);
}
@@ -5792,7 +5797,7 @@
0x10);
} else {
wcd9xxx_disable_irq(codec->control_data,
- TAIKO_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
taiko->hphlocp_cnt = 0;
taiko->hph_status |= SND_JACK_OC_HPHL;
if (taiko->mbhc_cfg.headset_jack)
@@ -5825,7 +5830,7 @@
0x10);
} else {
wcd9xxx_disable_irq(codec->control_data,
- TAIKO_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
taiko->hphrocp_cnt = 0;
taiko->hph_status |= SND_JACK_OC_HPHR;
if (taiko->mbhc_cfg.headset_jack)
@@ -6351,7 +6356,7 @@
wcd9xxx_unlock_sleep(core);
} else {
wcd9xxx_enable_irq(codec->control_data,
- TAIKO_IRQ_MBHC_INSERTION);
+ WCD9XXX_IRQ_MBHC_INSERTION);
pr_err("%s: Error detecting plug insertion\n",
__func__);
}
@@ -6366,7 +6371,7 @@
pr_debug("%s: enter\n", __func__);
TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
- wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
is_mb_trigger = !!(snd_soc_read(codec, priv->mbhc_bias_regs.mbhc_reg) &
0x10);
@@ -6590,7 +6595,8 @@
snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x13, 0x00);
snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
- wcd9xxx_disable_irq_sync(codec->control_data, TAIKO_IRQ_MBHC_INSERTION);
+ wcd9xxx_disable_irq_sync(codec->control_data,
+ WCD9XXX_IRQ_MBHC_INSERTION);
taiko_codec_detect_plug_type(codec);
wcd9xxx_unlock_sleep(taiko_core);
}
@@ -6725,9 +6731,9 @@
if (!IS_ERR_VALUE(ret)) {
snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10, 0x10);
wcd9xxx_enable_irq(codec->control_data,
- TAIKO_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
wcd9xxx_enable_irq(codec->control_data,
- TAIKO_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
if (taiko->mbhc_cfg.gpio) {
ret = request_threaded_irq(taiko->mbhc_cfg.gpio_irq,
@@ -7378,48 +7384,51 @@
int i;
struct snd_soc_codec *codec = taiko->codec;
- ret = wcd9xxx_request_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION,
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_INSERTION,
taiko_hs_insert_irq, "Headset insert detect",
taiko);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_MBHC_INSERTION);
+ WCD9XXX_IRQ_MBHC_INSERTION);
goto err_insert_irq;
}
- wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION);
+ wcd9xxx_disable_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_INSERTION);
- ret = wcd9xxx_request_irq(codec->control_data, TAIKO_IRQ_MBHC_REMOVAL,
+ ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL,
taiko_hs_remove_irq, "Headset remove detect",
taiko);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_MBHC_REMOVAL);
+ WCD9XXX_IRQ_MBHC_REMOVAL);
goto err_remove_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL,
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_POTENTIAL,
taiko_dce_handler, "DC Estimation detect",
taiko);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_MBHC_POTENTIAL);
+ WCD9XXX_IRQ_MBHC_POTENTIAL);
goto err_potential_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, TAIKO_IRQ_MBHC_RELEASE,
+ ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE,
taiko_release_handler, "Button Release detect",
taiko);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_MBHC_RELEASE);
+ WCD9XXX_IRQ_MBHC_RELEASE);
goto err_release_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, TAIKO_IRQ_SLIMBUS,
+ ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
taiko_slimbus_irq, "SLIMBUS Slave", taiko);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_SLIMBUS);
+ WCD9XXX_IRQ_SLIMBUS);
goto err_slimbus_irq;
}
@@ -7429,40 +7438,44 @@
0xFF);
ret = wcd9xxx_request_irq(codec->control_data,
- TAIKO_IRQ_HPH_PA_OCPL_FAULT,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
taiko_hphl_ocp_irq,
"HPH_L OCP detect", taiko);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
goto err_hphl_ocp_irq;
}
- wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_HPH_PA_OCPL_FAULT);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
ret = wcd9xxx_request_irq(codec->control_data,
- TAIKO_IRQ_HPH_PA_OCPR_FAULT,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
taiko_hphr_ocp_irq,
"HPH_R OCP detect", taiko);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
goto err_hphr_ocp_irq;
}
- wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_HPH_PA_OCPR_FAULT);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+
+ return 0;
err_hphr_ocp_irq:
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_HPH_PA_OCPL_FAULT,
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
taiko);
err_hphl_ocp_irq:
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_SLIMBUS, taiko);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, taiko);
err_slimbus_irq:
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_RELEASE, taiko);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, taiko);
err_release_irq:
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL, taiko);
+ wcd9xxx_free_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_POTENTIAL, taiko);
err_potential_irq:
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_REMOVAL, taiko);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, taiko);
err_remove_irq:
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION, taiko);
+ wcd9xxx_free_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_INSERTION, taiko);
err_insert_irq:
return ret;
@@ -7597,11 +7610,13 @@
{
int i;
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_SLIMBUS, taiko);
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_RELEASE, taiko);
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL, taiko);
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_REMOVAL, taiko);
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION, taiko);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, taiko);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, taiko);
+ wcd9xxx_free_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_POTENTIAL, taiko);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, taiko);
+ wcd9xxx_free_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_INSERTION, taiko);
TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
taiko_codec_disable_clock_block(codec);
TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
diff --git a/sound/soc/msm/mdm9615.c b/sound/soc/msm/mdm9615.c
index 1000a8b..57b846c 100644
--- a/sound/soc/msm/mdm9615.c
+++ b/sound/soc/msm/mdm9615.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -89,6 +89,20 @@
.drv = GPIOMUX_DRV_8MA,
.pull = GPIOMUX_PULL_NONE,
};
+static struct gpiomux_setting audio_sec_i2s[] = {
+ /* Suspend state */
+ {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ },
+ /* Active state */
+ {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+ }
+};
static struct gpiomux_setting cdc_i2s_dout = {
.func = GPIOMUX_FUNC_1,
@@ -142,6 +156,42 @@
},
};
+static struct msm_gpiomux_config msm9615_audio_sec_i2s_codec_configs[] = {
+ {
+ .gpio = GPIO_SPKR_I2S_MCLK,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &cdc_i2s_mclk,
+ },
+ },
+ {
+ .gpio = GPIO_SEC_I2S_SCK,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &audio_sec_i2s[0],
+ [GPIOMUX_ACTIVE] = &audio_sec_i2s[1],
+ },
+ },
+ {
+ .gpio = GPIO_SEC_I2S_DOUT,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &audio_sec_i2s[0],
+ [GPIOMUX_ACTIVE] = &audio_sec_i2s[1],
+ },
+ },
+ {
+ .gpio = GPIO_SEC_I2S_WS,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &audio_sec_i2s[0],
+ [GPIOMUX_ACTIVE] = &audio_sec_i2s[1],
+ },
+ },
+ {
+ .gpio = GPIO_SEC_I2S_DIN,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &audio_sec_i2s[0],
+ [GPIOMUX_ACTIVE] = &audio_sec_i2s[1],
+ },
+ },
+};
/* Physical address for LPA CSR
* LPA SIF mux registers. These are
* ioremap( ) for Virtual address.
@@ -274,6 +324,9 @@
static struct platform_device *mdm9615_snd_device_slim;
static struct platform_device *mdm9615_snd_device_i2s;
+static u32 sif_reg_value = 0x0000;
+static u32 spare_reg_value = 0x0000;
+
static bool hs_detect_use_gpio;
module_param(hs_detect_use_gpio, bool, 0444);
MODULE_PARM_DESC(hs_detect_use_gpio, "Use GPIO for headset detection");
@@ -947,6 +1000,10 @@
msm9615_i2s_rx_ch_get, msm9615_i2s_rx_ch_put),
SOC_ENUM_EXT("PRI_TX Channels", mdm9615_enum[2],
msm9615_i2s_tx_ch_get, msm9615_i2s_tx_ch_put),
+ SOC_ENUM_EXT("SEC_RX Channels", mdm9615_enum[3],
+ msm9615_i2s_rx_ch_get, msm9615_i2s_rx_ch_put),
+ SOC_ENUM_EXT("SEC_TX Channels", mdm9615_enum[4],
+ msm9615_i2s_tx_ch_get, msm9615_i2s_tx_ch_put),
};
static int msm9615_i2s_audrx_init(struct snd_soc_pcm_runtime *rtd)
@@ -1234,47 +1291,64 @@
return ret;
}
-static void msm9615_config_i2s_sif_mux(u8 value)
+static void msm9615_config_i2s_sif_mux(u8 value, u8 i2s_intf)
{
struct msm_i2s_ctl *pintf = &msm9x15_i2s_ctl;
- u32 sif_shadow = 0x0000;
- /* Make this variable global if both secondary and
- * primary needs to be supported. This is required
- * to retain bits in interace and set only specific
- * bits in the register. Also set Sec Intf bits.
- * Secondary interface bits are 0,1.
- **/
- sif_shadow = (sif_shadow & LPASS_SIF_MUX_CTL_PRI_MUX_SEL_BMSK) |
- (value << LPASS_SIF_MUX_CTL_PRI_MUX_SEL_SHFT);
+ u32 sif_shadow = 0x0000;
+
+ pr_debug("%s() Value = 0x%x intf = 0x%x\n", __func__, value, i2s_intf);
+ if (i2s_intf == MSM_INTF_PRIM) {
+ sif_shadow = (sif_shadow & LPASS_SIF_MUX_CTL_PRI_MUX_SEL_BMSK) |
+ (value << LPASS_SIF_MUX_CTL_PRI_MUX_SEL_SHFT);
+ pr_debug("%s() Sif shadow = 0x%x\n", __func__, sif_shadow);
+ sif_reg_value =
+ ((sif_reg_value & LPASS_SIF_MUX_CTL_SEC_MUX_SEL_BMSK) |
+ sif_shadow);
+ }
+ if (i2s_intf == MSM_INTF_SECN) {
+ sif_shadow = (sif_shadow & LPASS_SIF_MUX_CTL_SEC_MUX_SEL_BMSK) |
+ (value << LPASS_SIF_MUX_CTL_SEC_MUX_SEL_SHFT);
+ pr_debug("%s() Sif shadow = 0x%x\n", __func__, sif_shadow);
+ sif_reg_value =
+ ((sif_reg_value & LPASS_SIF_MUX_CTL_PRI_MUX_SEL_BMSK) |
+ sif_shadow);
+ }
if (pintf->sif_virt_addr != NULL)
- iowrite32(sif_shadow, pintf->sif_virt_addr);
+ iowrite32(sif_reg_value, pintf->sif_virt_addr);
/* Dont read SIF register. Device crashes. */
- pr_debug("%s() SIF Reg = 0x%x\n", __func__, sif_shadow);
+ pr_debug("%s() SIF Reg = 0x%x\n", __func__, sif_reg_value);
}
static void msm9615_config_i2s_spare_mux(u8 value, u8 i2s_intf)
{
struct msm_i2s_ctl *pintf = &msm9x15_i2s_ctl;
u32 spare_shadow = 0x0000;
- /* Make this variable global if both secondary and
- * primary needs to be supported. This is required
- * to retain bits in interace and set only specific
- * bits in the register. Also set Sec Intf bits.
- **/
+
+ pr_debug("%s() Value = 0x%x intf = 0x%x\n", __func__, value, i2s_intf);
if (i2s_intf == MSM_INTF_PRIM) {
/* Configure Primary SIF */
- spare_shadow = (spare_shadow & LPAIF_SPARE_MUX_CTL_PRI_MUX_SEL_BMSK
- ) | (value << LPAIF_SPARE_MUX_CTL_PRI_MUX_SEL_SHFT);
+ spare_shadow =
+ (spare_shadow & LPAIF_SPARE_MUX_CTL_PRI_MUX_SEL_BMSK) |
+ (value << LPAIF_SPARE_MUX_CTL_PRI_MUX_SEL_SHFT);
+ pr_debug("%s() Spare shadow = 0x%x\n", __func__, spare_shadow);
+ spare_reg_value =
+ ((spare_shadow & LPAIF_SPARE_MUX_CTL_SEC_MUX_SEL_BMSK) |
+ spare_shadow);
}
if (i2s_intf == MSM_INTF_SECN) {
/*Secondary interface configuration*/
- spare_shadow = (spare_shadow & LPAIF_SPARE_MUX_CTL_SEC_MUX_SEL_BMSK
- ) | (value << LPAIF_SPARE_MUX_CTL_SEC_MUX_SEL_SHFT);
+ spare_shadow =
+ (spare_shadow & LPAIF_SPARE_MUX_CTL_SEC_MUX_SEL_BMSK) |
+ (value << LPAIF_SPARE_MUX_CTL_SEC_MUX_SEL_SHFT);
+ pr_debug("%s() Spare shadow = 0x%x\n", __func__, spare_shadow);
+ spare_reg_value =
+ ((spare_shadow & LPAIF_SPARE_MUX_CTL_PRI_MUX_SEL_BMSK) |
+ spare_shadow);
}
if (pintf->spare_virt_addr != NULL)
- iowrite32(spare_shadow, pintf->spare_virt_addr);
+ iowrite32(spare_reg_value, pintf->spare_virt_addr);
/* Dont read SPARE register. Device crashes. */
- pr_debug("%s( ): SPARE Reg =0x%x\n", __func__, spare_shadow);
+ pr_debug("%s( ): SPARE Reg =0x%x\n", __func__, spare_reg_value);
}
static int msm9615_i2s_hw_params(struct snd_pcm_substream *substream,
@@ -1342,7 +1416,8 @@
return -EINVAL;
}
msm9615_config_i2s_sif_mux(
- pintf->mux_ctl[MSM_DIR_BOTH].sifconfig);
+ pintf->mux_ctl[MSM_DIR_BOTH].sifconfig,
+ i2s_intf);
msm9615_config_i2s_spare_mux(
pintf->mux_ctl[MSM_DIR_BOTH].spareconfig,
i2s_intf);
@@ -1368,7 +1443,8 @@
return -EINVAL;
}
msm9615_config_i2s_sif_mux(
- pintf->mux_ctl[MSM_DIR_TX].sifconfig);
+ pintf->mux_ctl[MSM_DIR_TX].sifconfig,
+ i2s_intf);
msm9615_config_i2s_spare_mux(
pintf->mux_ctl[MSM_DIR_TX].spareconfig,
i2s_intf);
@@ -1397,7 +1473,8 @@
return -EINVAL;
}
msm9615_config_i2s_sif_mux(
- pintf->mux_ctl[MSM_DIR_RX].sifconfig);
+ pintf->mux_ctl[MSM_DIR_RX].sifconfig,
+ i2s_intf);
msm9615_config_i2s_spare_mux(
pintf->mux_ctl[MSM_DIR_RX].spareconfig,
i2s_intf);
@@ -1451,11 +1528,34 @@
pintf->intf_status[i2s_intf][MSM_DIR_TX]);
}
-static void mdm9615_install_codec_i2s_gpio(void)
+void msm9615_config_port_select(void)
{
- msm_gpiomux_install(msm9615_audio_prim_i2s_codec_configs,
- ARRAY_SIZE(msm9615_audio_prim_i2s_codec_configs));
+ iowrite32(SEC_PCM_PORT_SLC_VALUE, secpcm_portslc_virt_addr);
+ pr_debug("%s() port select after updating = 0x%x\n",
+ __func__, ioread32(secpcm_portslc_virt_addr));
}
+static void mdm9615_install_codec_i2s_gpio(struct snd_pcm_substream *substream)
+{
+ u8 i2s_intf, i2s_dir;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+
+ if (!msm9615_i2s_intf_dir_sel(cpu_dai->name, &i2s_intf, &i2s_dir)) {
+ pr_debug("%s( ): cpu name = %s intf =%d dir = %d\n",
+ __func__, cpu_dai->name, i2s_intf, i2s_dir);
+ if (i2s_intf == MSM_INTF_PRIM) {
+ msm_gpiomux_install(
+ msm9615_audio_prim_i2s_codec_configs,
+ ARRAY_SIZE(msm9615_audio_prim_i2s_codec_configs));
+ } else if (i2s_intf == MSM_INTF_SECN) {
+ msm_gpiomux_install(msm9615_audio_sec_i2s_codec_configs,
+ ARRAY_SIZE(msm9615_audio_sec_i2s_codec_configs));
+ msm9615_config_port_select();
+
+ }
+ }
+}
+
static int msm9615_i2s_prepare(struct snd_pcm_substream *substream)
{
u8 ret = 0;
@@ -1463,7 +1563,7 @@
if (wcd9xxx_get_intf_type() < 0)
ret = -ENODEV;
else if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
- mdm9615_install_codec_i2s_gpio();
+ mdm9615_install_codec_i2s_gpio(substream);
return ret;
}
@@ -1734,15 +1834,6 @@
pr_debug("%s() SIF Reg = 0x%x\n", __func__, sif_shadow);
}
-void msm9615_config_port_select(void)
-{
- pr_debug("%s() port select defualt = 0x%x\n",
- __func__, ioread32(secpcm_portslc_virt_addr));
- iowrite32(SEC_PCM_PORT_SLC_VALUE, secpcm_portslc_virt_addr);
- pr_debug("%s() port select after updating = 0x%x\n",
- __func__, ioread32(secpcm_portslc_virt_addr));
-}
-
static int mdm9615_auxpcm_startup(struct snd_pcm_substream *substream)
{
int ret = 0;
@@ -2049,6 +2140,30 @@
.be_hw_params_fixup = msm9615_i2s_tx_be_hw_params_fixup,
.ops = &msm9615_i2s_be_ops,
},
+ {
+ .name = LPASS_BE_SEC_I2S_RX,
+ .stream_name = "Secondary I2S Playback",
+ .cpu_dai_name = "msm-dai-q6.4",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_I2S_RX,
+ .be_hw_params_fixup = msm9615_i2s_rx_be_hw_params_fixup,
+ .ops = &msm9615_i2s_be_ops,
+ },
+ {
+ .name = LPASS_BE_SEC_I2S_TX,
+ .stream_name = "Secondary I2S Capture",
+ .cpu_dai_name = "msm-dai-q6.5",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_I2S_TX,
+ .be_hw_params_fixup = msm9615_i2s_tx_be_hw_params_fixup,
+ .ops = &msm9615_i2s_be_ops,
+ },
};
static struct snd_soc_dai_link mdm9615_dai_slimbus_tabla[] = {
@@ -2097,8 +2212,8 @@
},
[1] = {
.name = "mdm9615-tabla-snd-card-i2s",
- .controls = tabla_mdm9615_controls,
- .num_controls = ARRAY_SIZE(tabla_mdm9615_controls),
+ .controls = tabla_msm9615_i2s_controls,
+ .num_controls = ARRAY_SIZE(tabla_msm9615_i2s_controls),
},
};
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index 16a4aaa..dc8d9e6 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -270,9 +270,9 @@
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rate_min = 8000,
- .rate_max = 48000,
+ .rate_max = 192000,
},
.capture = {
.stream_name = "SLIMBUS0 Hostless Capture",
@@ -280,9 +280,9 @@
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rate_min = 8000,
- .rate_max = 48000,
+ .rate_max = 192000,
},
.ops = &msm_fe_dai_ops,
.name = "SLIMBUS0_HOSTLESS",
diff --git a/sound/soc/msm/msm-dai-q6.c b/sound/soc/msm/msm-dai-q6.c
index ee1ab79..18c2329 100644
--- a/sound/soc/msm/msm-dai-q6.c
+++ b/sound/soc/msm/msm-dai-q6.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -674,6 +674,7 @@
case PRIMARY_I2S_TX:
case PRIMARY_I2S_RX:
case SECONDARY_I2S_RX:
+ case SECONDARY_I2S_TX:
rc = msm_dai_q6_cdc_hw_params(params, dai, substream->stream);
break;
@@ -1379,6 +1380,7 @@
case PRIMARY_I2S_TX:
case PRIMARY_I2S_RX:
case SECONDARY_I2S_RX:
+ case SECONDARY_I2S_TX:
rc = msm_dai_q6_cdc_set_fmt(dai, fmt);
break;
default:
@@ -1831,6 +1833,7 @@
rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_i2s_rx_dai);
break;
case PRIMARY_I2S_TX:
+ case SECONDARY_I2S_TX:
rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_i2s_tx_dai);
break;
case PCM_RX:
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index f28d01a..5967bb2 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -183,6 +183,7 @@
{ MI2S_RX, 0, 0, 0, 0, 0},
{ MI2S_TX, 0, 0, 0, 0},
{ SECONDARY_I2S_RX, 0, 0, 0, 0, 0},
+ { SECONDARY_I2S_TX, 0, 0, 0, 0, 0},
{ SLIMBUS_1_RX, 0, 0, 0, 0, 0},
{ SLIMBUS_1_TX, 0, 0, 0, 0, 0},
{ SLIMBUS_4_RX, 0, 0, 0, 0, 0},
@@ -1417,6 +1418,9 @@
SOC_SINGLE_EXT("PRI_TX", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("SEC_TX", MSM_BACKEND_DAI_SEC_I2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
@@ -1661,6 +1665,9 @@
SOC_SINGLE_EXT("PRI_TX_Voice", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SEC_TX_Voice", MSM_BACKEND_DAI_SEC_I2S_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("MI2S_TX_Voice", MSM_BACKEND_DAI_MI2S_TX,
MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -1685,6 +1692,9 @@
SOC_SINGLE_EXT("PRI_TX_VoLTE", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SEC_TX_VoLTE", MSM_BACKEND_DAI_SEC_I2S_TX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("SLIM_0_TX_VoLTE", MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -1706,6 +1716,9 @@
SOC_SINGLE_EXT("PRI_TX_SGLTE", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SEC_TX_SGLTE", MSM_BACKEND_DAI_SEC_I2S_TX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("MI2S_TX_SGLTE", MSM_BACKEND_DAI_MI2S_TX,
MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -1729,6 +1742,9 @@
SOC_SINGLE_EXT("PRI_TX_Voip", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SEC_TX_Voip", MSM_BACKEND_DAI_SEC_I2S_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("MI2S_TX_Voip", MSM_BACKEND_DAI_MI2S_TX,
MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -1771,6 +1787,9 @@
SOC_SINGLE_EXT("PRIMARY_I2S_TX", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("SECONDARY_I2S_TX", MSM_BACKEND_DAI_SEC_I2S_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
@@ -2206,6 +2225,7 @@
SND_SOC_DAPM_AIF_OUT("HDMI", "HDMI Playback", 0, 0, 0 , 0),
SND_SOC_DAPM_AIF_OUT("MI2S_RX", "MI2S Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("PRI_I2S_TX", "Primary I2S Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_I2S_TX", "Secondary I2S Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("MI2S_TX", "MI2S Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("INT_BT_SCO_RX", "Internal BT-SCO Playback",
@@ -2442,6 +2462,7 @@
{"MI2S_RX", NULL, "MI2S_RX Audio Mixer"},
{"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
+ {"MultiMedia1 Mixer", "SEC_TX", "SEC_I2S_TX"},
{"MultiMedia1 Mixer", "MI2S_TX", "MI2S_TX"},
{"MultiMedia2 Mixer", "MI2S_TX", "MI2S_TX"},
{"MultiMedia4 Mixer", "MI2S_TX", "MI2S_TX"},
@@ -2547,6 +2568,7 @@
{"HDMI", NULL, "HDMI_DL_HL"},
{"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"},
+ {"Voice_Tx Mixer", "SEC_TX_Voice", "SEC_I2S_TX"},
{"Voice_Tx Mixer", "MI2S_TX_Voice", "MI2S_TX"},
{"Voice_Tx Mixer", "SLIM_0_TX_Voice", "SLIMBUS_0_TX"},
{"Voice_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice", "INT_BT_SCO_TX"},
@@ -2555,6 +2577,7 @@
{"Voice_Tx Mixer", "SEC_AUX_PCM_TX_Voice", "SEC_AUX_PCM_TX"},
{"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"},
{"VoLTE_Tx Mixer", "PRI_TX_VoLTE", "PRI_I2S_TX"},
+ {"VoLTE_Tx Mixer", "SEC_TX_VoLTE", "SEC_I2S_TX"},
{"VoLTE_Tx Mixer", "SLIM_0_TX_VoLTE", "SLIMBUS_0_TX"},
{"VoLTE_Tx Mixer", "INTERNAL_BT_SCO_TX_VoLTE", "INT_BT_SCO_TX"},
{"VoLTE_Tx Mixer", "AFE_PCM_TX_VoLTE", "PCM_TX"},
@@ -2562,6 +2585,7 @@
{"VoLTE_Tx Mixer", "SEC_AUX_PCM_TX_VoLTE", "SEC_AUX_PCM_TX"},
{"VoLTE_UL", NULL, "VoLTE_Tx Mixer"},
{"SGLTE_Tx Mixer", "PRI_TX_SGLTE", "PRI_I2S_TX"},
+ {"SGLTE_Tx Mixer", "SEC_TX_SGLTE", "SEC_I2S_TX"},
{"SGLTE_Tx Mixer", "MI2S_TX_SGLTE", "MI2S_TX"},
{"SGLTE_Tx Mixer", "SLIM_0_TX_SGLTE", "SLIMBUS_0_TX"},
{"SGLTE_Tx Mixer", "INTERNAL_BT_SCO_TX_SGLTE", "INT_BT_SCO_TX"},
@@ -2570,6 +2594,7 @@
{"SGLTE_Tx Mixer", "SEC_AUX_PCM_TX_SGLTE", "SEC_AUX_PCM_TX"},
{"SGLTE_UL", NULL, "SGLTE_Tx Mixer"},
{"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"},
+ {"Voip_Tx Mixer", "SEC_TX_Voip", "SEC_I2S_TX"},
{"Voip_Tx Mixer", "MI2S_TX_Voip", "MI2S_TX"},
{"Voip_Tx Mixer", "SLIM_0_TX_Voip", "SLIMBUS_0_TX"},
{"Voip_Tx Mixer", "INTERNAL_BT_SCO_TX_Voip", "INT_BT_SCO_TX"},
@@ -2609,6 +2634,7 @@
{"Voice Stub Tx Mixer", "MI2S_TX", "MI2S_TX"},
{"Voice Stub Tx Mixer", "SLIM_3_TX", "SLIMBUS_3_TX"},
{"Voice Stub Tx Mixer", "PRIMARY_I2S_TX", "PRI_I2S_TX"},
+ {"Voice Stub Tx Mixer", "SECONDARY_I2S_TX", "SEC_I2S_TX"},
{"Voice Stub Tx Mixer", "AFE_PCM_TX", "PCM_TX"},
{"VOICE_STUB_UL", NULL, "Voice Stub Tx Mixer"},
@@ -2661,6 +2687,7 @@
{"BE_OUT", NULL, "HDMI"},
{"BE_OUT", NULL, "MI2S_RX"},
{"PRI_I2S_TX", NULL, "BE_IN"},
+ {"SEC_I2S_TX", NULL, "BE_IN"},
{"MI2S_TX", NULL, "BE_IN"},
{"SLIMBUS_0_TX", NULL, "BE_IN" },
{"SLIMBUS_1_TX", NULL, "BE_IN" },
diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h
index 14f330b..e11133e 100644
--- a/sound/soc/msm/msm-pcm-routing.h
+++ b/sound/soc/msm/msm-pcm-routing.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -32,6 +32,7 @@
#define LPASS_BE_INCALL_RECORD_RX "INCALL_RECORD_TX"
#define LPASS_BE_INCALL_RECORD_TX "INCALL_RECORD_RX"
#define LPASS_BE_SEC_I2S_RX "SECONDARY_I2S_RX"
+#define LPASS_BE_SEC_I2S_TX "SECONDARY_I2S_TX"
#define LPASS_BE_MI2S_RX "MI2S_RX"
#define LPASS_BE_MI2S_TX "MI2S_TX"
@@ -93,6 +94,7 @@
MSM_BACKEND_DAI_MI2S_RX,
MSM_BACKEND_DAI_MI2S_TX,
MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_BACKEND_DAI_SEC_I2S_TX,
MSM_BACKEND_DAI_SLIMBUS_1_RX,
MSM_BACKEND_DAI_SLIMBUS_1_TX,
MSM_BACKEND_DAI_SLIMBUS_4_RX,
diff --git a/sound/soc/msm/qdsp6v2/audio_ocmem.c b/sound/soc/msm/qdsp6v2/audio_ocmem.c
index 86a82e2..c046b63 100644
--- a/sound/soc/msm/qdsp6v2/audio_ocmem.c
+++ b/sound/soc/msm/qdsp6v2/audio_ocmem.c
@@ -106,9 +106,13 @@
break;
case OCMEM_ALLOC_GROW:
audio_ocmem_lcl.buf = data;
+ pr_debug("%s: Alloc grow request received buf->addr: 0x%ld\n",
+ __func__,
+ (audio_ocmem_lcl.buf)->addr);
atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_GROW);
break;
case OCMEM_ALLOC_SHRINK:
+ pr_debug("%s: Alloc shrink request received\n", __func__);
atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_SHRINK);
break;
default:
@@ -150,11 +154,14 @@
audio_ocmem_lcl.buf = buf;
atomic_set(&audio_ocmem_lcl.audio_exit, 0);
+ atomic_set(&audio_ocmem_lcl.audio_cond, 1);
+ pr_debug("%s: buf->len: %ld\n", __func__, buf->len);
if (!buf->len) {
+ pr_debug("%s: buf.len is 0, waiting for ocmem region\n",
+ __func__);
wait_event_interruptible(audio_ocmem_lcl.audio_wait,
(atomic_read(&audio_ocmem_lcl.audio_cond) == 0) ||
(atomic_read(&audio_ocmem_lcl.audio_exit) == 1));
-
if (atomic_read(&audio_ocmem_lcl.audio_exit)) {
pr_err("%s: audio playback ended while waiting for ocmem\n",
__func__);
@@ -162,6 +169,7 @@
goto fail_cmd;
}
}
+ pr_debug("%s: buf->len: %ld\n", __func__, (audio_ocmem_lcl.buf)->len);
if (audio_ocmem_lcl.lp_memseg_ptr == NULL) {
/* Retrieve low power segments */
ret = core_get_low_power_segments(
@@ -190,19 +198,28 @@
/* vote for ocmem bus bandwidth */
ret = msm_bus_scale_client_update_request(
audio_ocmem_lcl.audio_ocmem_bus_client,
- 0);
+ 1);
if (ret)
pr_err("%s: failed to vote for bus bandwidth\n", __func__);
atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_MAP_TRANSITION);
+ pr_debug("%s: buf->addr: 0x%ld, len: %ld, audio_state[0x%x]\n",
+ __func__,
+ audio_ocmem_lcl.buf->addr,
+ audio_ocmem_lcl.buf->len,
+ atomic_read(&audio_ocmem_lcl.audio_state));
+
+ atomic_set(&audio_ocmem_lcl.audio_cond, 1);
ret = ocmem_map(cid, audio_ocmem_lcl.buf, &audio_ocmem_lcl.mlist);
if (ret) {
pr_err("%s: ocmem_map failed\n", __func__);
goto fail_cmd;
}
-
+ pr_debug("%s: audio_cond[%d] audio_state[0x%x]\n", __func__,
+ atomic_read(&audio_ocmem_lcl.audio_cond),
+ atomic_read(&audio_ocmem_lcl.audio_state));
while ((atomic_read(&audio_ocmem_lcl.audio_state) !=
OCMEM_STATE_EXIT)) {
@@ -219,6 +236,8 @@
atomic_set(&audio_ocmem_lcl.audio_cond, 1);
break;
case OCMEM_STATE_SHRINK:
+ pr_debug("%s: ocmem shrink request process\n",
+ __func__);
atomic_set(&audio_ocmem_lcl.audio_cond, 1);
ret = ocmem_unmap(cid, audio_ocmem_lcl.buf,
&audio_ocmem_lcl.mlist);
@@ -242,9 +261,11 @@
atomic_read(&audio_ocmem_lcl.audio_state));
goto fail_cmd;
}
-
+ atomic_set(&audio_ocmem_lcl.audio_cond, 1);
break;
case OCMEM_STATE_GROW:
+ pr_debug("%s: ocmem grow request process\n",
+ __func__);
atomic_set(&audio_ocmem_lcl.audio_cond, 1);
ret = ocmem_map(cid, audio_ocmem_lcl.buf,
&audio_ocmem_lcl.mlist);
@@ -260,6 +281,7 @@
atomic_read(&audio_ocmem_lcl.audio_cond) == 0);
atomic_set(&audio_ocmem_lcl.audio_state,
OCMEM_STATE_MAP_COMPL);
+ atomic_set(&audio_ocmem_lcl.audio_cond, 1);
break;
}
}
@@ -280,8 +302,10 @@
{
int ret;
+ pr_debug("%s: disable\n", __func__);
if (atomic_read(&audio_ocmem_lcl.audio_cond))
atomic_set(&audio_ocmem_lcl.audio_cond, 0);
+
pr_debug("%s: audio_cond[0x%x], audio_state[0x%x]\n", __func__,
atomic_read(&audio_ocmem_lcl.audio_cond),
atomic_read(&audio_ocmem_lcl.audio_state));
@@ -309,6 +333,7 @@
atomic_read(&audio_ocmem_lcl.audio_state));
goto fail_cmd;
}
+ atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_EXIT);
pr_debug("%s: ocmem_free success\n", __func__);
default:
pr_debug("%s: state=%d", __func__,
@@ -316,6 +341,9 @@
break;
}
+ msm_bus_scale_client_update_request(
+ audio_ocmem_lcl.audio_ocmem_bus_client,
+ 0);
return 0;
fail_cmd:
return ret;
@@ -345,6 +373,7 @@
rc = -EINVAL;
}
+ return;
}
/**
* voice_ocmem_process_req() - disable/enable OCMEM during voice call
@@ -441,6 +470,7 @@
rc = -EINVAL;
}
+ return;
}
/**
@@ -484,86 +514,21 @@
static int audio_ocmem_platform_data_populate(struct platform_device *pdev)
{
- int ret;
- struct msm_bus_scale_pdata *audio_ocmem_bus_scale_pdata = NULL;
- struct msm_bus_vectors *audio_ocmem_bus_vectors = NULL;
- struct msm_bus_paths *ocmem_audio_bus_paths = NULL;
- u32 val;
+ struct msm_bus_scale_pdata *audio_ocmem_adata = NULL;
if (!pdev->dev.of_node) {
pr_err("%s: device tree information missing\n", __func__);
return -ENODEV;
}
-
- audio_ocmem_bus_vectors = kzalloc(sizeof(struct msm_bus_vectors),
- GFP_KERNEL);
- if (!audio_ocmem_bus_vectors) {
- dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
- return -ENOMEM;
+ audio_ocmem_adata = msm_bus_cl_get_pdata(pdev);
+ if (!audio_ocmem_adata) {
+ pr_err("%s: bus device tree allocation failed\n", __func__);
+ return -EINVAL;
}
- ret = of_property_read_u32(pdev->dev.of_node,
- "qcom,msm-ocmem-audio-src-id", &val);
- if (ret) {
- dev_err(&pdev->dev, "%s: qcom,msm-ocmem-audio-src-id missing in DT node\n",
- __func__);
- goto fail1;
- }
- audio_ocmem_bus_vectors->src = val;
- ret = of_property_read_u32(pdev->dev.of_node,
- "qcom,msm-ocmem-audio-dst-id", &val);
- if (ret) {
- dev_err(&pdev->dev, "%s: qcom,msm-ocmem-audio-dst-id missing in DT node\n",
- __func__);
- goto fail1;
- }
- audio_ocmem_bus_vectors->dst = val;
- ret = of_property_read_u32(pdev->dev.of_node,
- "qcom,msm-ocmem-audio-ab", &val);
- if (ret) {
- dev_err(&pdev->dev, "%s: qcom,msm-ocmem-audio-ab missing in DT node\n",
- __func__);
- goto fail1;
- }
- audio_ocmem_bus_vectors->ab = val;
- ret = of_property_read_u32(pdev->dev.of_node,
- "qcom,msm-ocmem-audio-ib", &val);
- if (ret) {
- dev_err(&pdev->dev, "%s: qcom,msm-ocmem-audio-ib missing in DT node\n",
- __func__);
- goto fail1;
- }
- audio_ocmem_bus_vectors->ib = val;
+ dev_set_drvdata(&pdev->dev, audio_ocmem_adata);
- ocmem_audio_bus_paths = kzalloc(sizeof(struct msm_bus_paths),
- GFP_KERNEL);
- if (!ocmem_audio_bus_paths) {
- dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
- goto fail1;
- }
- ocmem_audio_bus_paths->num_paths = 1;
- ocmem_audio_bus_paths->vectors = audio_ocmem_bus_vectors;
-
- audio_ocmem_bus_scale_pdata =
- kzalloc(sizeof(struct msm_bus_scale_pdata), GFP_KERNEL);
-
- if (!audio_ocmem_bus_scale_pdata) {
- dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
- goto fail2;
- }
-
- audio_ocmem_bus_scale_pdata->usecase = ocmem_audio_bus_paths;
- audio_ocmem_bus_scale_pdata->num_usecases = 1;
- audio_ocmem_bus_scale_pdata->name = "audio-ocmem";
-
- dev_set_drvdata(&pdev->dev, audio_ocmem_bus_scale_pdata);
- return ret;
-
-fail2:
- kfree(ocmem_audio_bus_paths);
-fail1:
- kfree(audio_ocmem_bus_vectors);
- return ret;
+ return 0;
}
static int ocmem_audio_client_probe(struct platform_device *pdev)
{
@@ -628,9 +593,7 @@
audio_ocmem_bus_scale_pdata = (struct msm_bus_scale_pdata *)
dev_get_drvdata(&pdev->dev);
- kfree(audio_ocmem_bus_scale_pdata->usecase->vectors);
- kfree(audio_ocmem_bus_scale_pdata->usecase);
- kfree(audio_ocmem_bus_scale_pdata);
+ msm_bus_cl_clear_pdata(audio_ocmem_bus_scale_pdata);
ocmem_notifier_unregister(audio_ocmem_lcl.audio_hdl,
&audio_ocmem_client_nb);
return 0;
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
index bbd43f7..01a9538 100644
--- a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
@@ -34,6 +34,7 @@
#include "msm-compr-q6-v2.h"
#include "msm-pcm-routing-v2.h"
+#include "audio_ocmem.h"
#define COMPRE_CAPTURE_NUM_PERIODS 16
/* Allocate the worst case frame size for compressed audio */
@@ -440,6 +441,9 @@
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
prtd->pcm_irq_pos = 0;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ audio_ocmem_process_req(AUDIO, true);
+
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
switch (compr->info.codec_param.codec.id) {
case SND_AUDIOCODEC_AMRWB:
@@ -459,6 +463,9 @@
break;
case SNDRV_PCM_TRIGGER_STOP:
pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ audio_ocmem_process_req(AUDIO, false);
+
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
switch (compr->info.codec_param.codec.id) {
case SND_AUDIOCODEC_AMRWB:
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index a6cdad2..dacd59c 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -1091,7 +1091,7 @@
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
@@ -1106,7 +1106,7 @@
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
index 7483bb6..af1e19c 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
@@ -56,7 +56,7 @@
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 4,
.buffer_bytes_max = CAPTURE_NUM_PERIODS * CAPTURE_PERIOD_SIZE,
.period_bytes_min = CAPTURE_PERIOD_SIZE,
.period_bytes_max = CAPTURE_PERIOD_SIZE,
@@ -297,7 +297,6 @@
static int msm_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
struct msm_audio *prtd;
int ret = 0;
@@ -315,36 +314,14 @@
kfree(prtd);
return -ENOMEM;
}
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- runtime->hw = msm_pcm_hardware_playback;
- ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
- if (ret < 0) {
- pr_err("%s: pcm out open failed\n", __func__);
- q6asm_audio_client_free(prtd->audio_client);
- kfree(prtd);
- return -ENOMEM;
- }
- }
- /* Capture path */
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- runtime->hw = msm_pcm_hardware_capture;
- ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM);
- if (ret < 0) {
- pr_err("%s: pcm in open failed\n", __func__);
- q6asm_audio_client_free(prtd->audio_client);
- kfree(prtd);
- return -ENOMEM;
- }
- }
-
- pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
-
- prtd->session_id = prtd->audio_client->session;
- msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
- prtd->session_id, substream->stream);
-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- prtd->cmd_ack = 1;
+ runtime->hw = msm_pcm_hardware_playback;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ runtime->hw = msm_pcm_hardware_capture;
+ else {
+ pr_err("Invalid Stream type %d\n", substream->stream);
+ return -EINVAL;
+ }
ret = snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
@@ -530,12 +507,15 @@
int dir = OUT;
pr_debug("%s\n", __func__);
- q6asm_cmd(prtd->audio_client, CMD_CLOSE);
- q6asm_audio_client_buf_free_contiguous(dir,
+ if (prtd->audio_client) {
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_buf_free_contiguous(dir,
prtd->audio_client);
+ q6asm_audio_client_free(prtd->audio_client);
+ }
+
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
- SNDRV_PCM_STREAM_CAPTURE);
- q6asm_audio_client_free(prtd->audio_client);
+ SNDRV_PCM_STREAM_CAPTURE);
kfree(prtd);
return 0;
@@ -617,13 +597,43 @@
struct snd_pcm_runtime *runtime = substream->runtime;
struct msm_audio *prtd = runtime->private_data;
struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
struct audio_buffer *buf;
int dir, ret;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
dir = IN;
- else
+ pr_debug("%s Opening %d-ch PCM Write stream\n",
+ __func__, params_channels(params));
+
+ ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
+ if (ret < 0) {
+ pr_err("%s: pcm out open failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ } else {
dir = OUT;
+ pr_debug("%s Opening %d-ch PCM read stream\n",
+ __func__, params_channels(params));
+ ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM);
+ if (ret < 0) {
+ pr_err("%s: pcm in open failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ prtd->audio_client = NULL;
+ return -ENOMEM;
+ }
+ }
+
+ pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
+ prtd->session_id = prtd->audio_client->session;
+ msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+ prtd->session_id, substream->stream);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ prtd->cmd_ack = 1;
+
pr_debug("%s: before buf alloc\n", __func__);
ret = q6asm_audio_client_buf_alloc_contiguous(dir,
prtd->audio_client,
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index fac55b4..0466eb6 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -470,6 +470,21 @@
} else if (channel_mode == 2) {
open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ } else if (channel_mode == 3) {
+ open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open.dev_channel_mapping[0] = PCM_CHANNEL_FR;
+ open.dev_channel_mapping[1] = PCM_CHANNEL_FC;
+ } else if (channel_mode == 4) {
+ open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open.dev_channel_mapping[2] = PCM_CHANNEL_RB;
+ open.dev_channel_mapping[3] = PCM_CHANNEL_LB;
+ } else if (channel_mode == 5) {
+ open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open.dev_channel_mapping[2] = PCM_CHANNEL_FC;
+ open.dev_channel_mapping[3] = PCM_CHANNEL_LB;
+ open.dev_channel_mapping[4] = PCM_CHANNEL_RB;
} else if (channel_mode == 6) {
open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
@@ -477,6 +492,15 @@
open.dev_channel_mapping[3] = PCM_CHANNEL_FC;
open.dev_channel_mapping[4] = PCM_CHANNEL_LB;
open.dev_channel_mapping[5] = PCM_CHANNEL_RB;
+ } else if (channel_mode == 8) {
+ open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open.dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+ open.dev_channel_mapping[3] = PCM_CHANNEL_FC;
+ open.dev_channel_mapping[4] = PCM_CHANNEL_LB;
+ open.dev_channel_mapping[5] = PCM_CHANNEL_RB;
+ open.dev_channel_mapping[6] = PCM_CHANNEL_FLC;
+ open.dev_channel_mapping[7] = PCM_CHANNEL_FRC;
} else {
pr_err("%s invalid num_chan %d\n", __func__,
channel_mode);
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index 0dd6faf..072e293 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -1693,7 +1693,7 @@
enc_cfg.bits_per_sample = 16;
enc_cfg.sample_rate = rate;
enc_cfg.is_signed = 1;
- channel_mapping = enc_cfg.channel_mapping; /* ??? PHANI */
+ channel_mapping = enc_cfg.channel_mapping;
memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
@@ -1743,7 +1743,8 @@
enc_cfg.bits_per_sample = 16;
enc_cfg.sample_rate = 0;/*rate;*/
enc_cfg.is_signed = 1;
- channel_mapping = enc_cfg.channel_mapping; /* ??? PHANI */
+ channel_mapping = enc_cfg.channel_mapping;
+
memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
@@ -1778,27 +1779,36 @@
lchannel_mapping[0] = PCM_CHANNEL_FL;
lchannel_mapping[1] = PCM_CHANNEL_FR;
} else if (channels == 3) {
- lchannel_mapping[0] = PCM_CHANNEL_FC;
- lchannel_mapping[1] = PCM_CHANNEL_FL;
- lchannel_mapping[2] = PCM_CHANNEL_FR;
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ lchannel_mapping[2] = PCM_CHANNEL_FC;
} else if (channels == 4) {
- lchannel_mapping[0] = PCM_CHANNEL_FC;
- lchannel_mapping[1] = PCM_CHANNEL_FL;
- lchannel_mapping[2] = PCM_CHANNEL_FR;
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ lchannel_mapping[2] = PCM_CHANNEL_RB;
lchannel_mapping[3] = PCM_CHANNEL_LB;
} else if (channels == 5) {
- lchannel_mapping[0] = PCM_CHANNEL_FC;
- lchannel_mapping[1] = PCM_CHANNEL_FL;
- lchannel_mapping[2] = PCM_CHANNEL_FR;
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ lchannel_mapping[2] = PCM_CHANNEL_FC;
lchannel_mapping[3] = PCM_CHANNEL_LB;
lchannel_mapping[4] = PCM_CHANNEL_RB;
} else if (channels == 6) {
- lchannel_mapping[0] = PCM_CHANNEL_FC;
- lchannel_mapping[1] = PCM_CHANNEL_FL;
- lchannel_mapping[2] = PCM_CHANNEL_FR;
- lchannel_mapping[3] = PCM_CHANNEL_LB;
- lchannel_mapping[4] = PCM_CHANNEL_RB;
- lchannel_mapping[5] = PCM_CHANNEL_LFE;
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ lchannel_mapping[2] = PCM_CHANNEL_LFE;
+ lchannel_mapping[3] = PCM_CHANNEL_FC;
+ lchannel_mapping[4] = PCM_CHANNEL_LB;
+ lchannel_mapping[5] = PCM_CHANNEL_RB;
+ } else if (channels == 6) {
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ lchannel_mapping[2] = PCM_CHANNEL_LFE;
+ lchannel_mapping[3] = PCM_CHANNEL_FC;
+ lchannel_mapping[4] = PCM_CHANNEL_LB;
+ lchannel_mapping[5] = PCM_CHANNEL_RB;
+ lchannel_mapping[6] = PCM_CHANNEL_FLC;
+ lchannel_mapping[7] = PCM_CHANNEL_FRC;
} else {
pr_err("%s: ERROR.unsupported num_ch = %u\n",
__func__, channels);