Merge "marimba-tsadc: Move to new clk_prepare/unprepare API." into msm-3.0
diff --git a/Documentation/devicetree/bindings/iommu/msm_iommu.txt b/Documentation/devicetree/bindings/iommu/msm_iommu.txt
new file mode 100644
index 0000000..67933e7
--- /dev/null
+++ b/Documentation/devicetree/bindings/iommu/msm_iommu.txt
@@ -0,0 +1,35 @@
+* Qualcomm MSM IOMMU
+
+Required properties:
+- compatible : one of:
+ - "qcom,msm-smmu-v2"
+- reg : offset and length of the register set for the device.
+
+- List of sub nodes, one for each of the translation context banks supported.
+ Each sub node has the following required properties:
+
+ - reg : offset and length of the register set for the context bank.
+ - interrupts : should contain the context bank interrupt.
+ - qcom,iommu-ctx-sids : List of stream identifiers associated with this
+ translation context.
+ - qcom,iommu-ctx-name : Name of the context bank
+
+Example:
+
+ qcom,iommu@fda64000 {
+ compatible = "qcom,msm-smmu-v2";
+ reg = <0xfda64000 0x10000>;
+
+ qcom,iommu-ctx@fda6c000 {
+ reg = <0xfda6c000 0x1000>;
+ interrupts = <0 70 0>;
+ qcom,iommu-ctx-sids = <0 2>;
+ qcom,iommu-ctx-name = "ctx_0";
+ };
+ qcom,iommu-ctx@fda6d000 {
+ reg = <0xfda6d000 0x1000>;
+ interrupts = <0 71 0>;
+ qcom,iommu-ctx-sids = <1>;
+ qcom,iommu-ctx-name = "ctx_1";
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pil/pil-mba.txt b/Documentation/devicetree/bindings/pil/pil-mba.txt
new file mode 100644
index 0000000..7aafd219
--- /dev/null
+++ b/Documentation/devicetree/bindings/pil/pil-mba.txt
@@ -0,0 +1,27 @@
+Qualcomm Modem Boot Authenticator Peripheral Image Loader
+
+pil-mba is a peripheral image loader (PIL) driver. It is used for loading
+modem images using the self-authenticating hardware and software features
+of the Modem Boot Authenticator.
+
+Required properties:
+- compatible: Must be "qcom,pil-mba"
+- reg: Two pairs of physical base addresses and sizes. The
+ first corresponds to the Relay Message Buffer (RMB)
+ register base. The second specifies the address at which
+ the primary modem image metadata should be stored.
+- qcom,firmware-name: Base name of the firmware image. Ex. "modem"
+
+Optional properties:
+- qcom,depends-on: firmware-name of a prerequisite image that must already
+ be running.
+
+Example:
+ qcom,mba@fc820000 {
+ compatible = "qcom,pil-mba";
+ reg = <0xfc820000 0x0020>,
+ <0x0d1f0000 0x4000>;
+
+ qcom,firmware-name = "modem";
+ qcom,depends-on = "mba";
+ };
diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
new file mode 100644
index 0000000..95e7f88
--- /dev/null
+++ b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
@@ -0,0 +1,32 @@
+Qualcomm MSS QDSP6v5 Peripheral Image Loader
+
+pil-qdsp6v5-mss is a peripheral image loader (PIL) driver. It is used for
+loading QDSP6v5 (Hexagon) firmware images for modem subsystems into memory and
+preparing the subsystem's processor to execute code. It's also responsible for
+shutting down the processor when it's not needed.
+
+Required properties:
+- compatible: Must be "qcom,pil-q6v5-mss"
+- reg: Four pairs of physical base addresses and region sizes of
+ memory mapped registers. The first region corresponds to
+ QDSP6SS_PUB, the second to the bus port halt register
+ base, the third to the MSS_RELAY_MSG_BUFFER base, and the
+ fourth to the MSS_RESTART register.
+- vdd_mss-supply: Reference to the regulator that supplies the processor.
+- qcom,firmware-name: Base name of the firmware image. Ex. "mdsp"
+- qcom,pil-self-auth: <0> if the hardware does not require self-authenticating
+ images and self-authentication is not desired;
+ <1> if the hardware requires self-authenticating images.
+
+Example:
+ qcom,mss@fc880000 {
+ compatible = "qcom,pil-q6v5-mss";
+ reg = <0xfc880000 0x100>,
+ <0xfd485000 0x400>,
+ <0xfc820000 0x020>,
+ <0xfc401680 0x004>;
+ vdd_mss-supply = <&pm8841_s3>;
+
+ qcom,firmware-name = "mba";
+ qcom,pil-self-auth = <1>;
+ };
diff --git a/Documentation/devicetree/bindings/prng/msm-rng.txt b/Documentation/devicetree/bindings/prng/msm-rng.txt
new file mode 100644
index 0000000..3d55808
--- /dev/null
+++ b/Documentation/devicetree/bindings/prng/msm-rng.txt
@@ -0,0 +1,12 @@
+* RNG (Random Number Generator)
+
+Required properties:
+- compatible : Should be "qcom,msm-rng"
+- reg : Offset and length of the register set for the device
+
+Example:
+
+ qcom,msm-rng@f9bff000 {
+ compatible = "qcom,msm-rng";
+ reg = <0xf9bff000 0x200>;
+ };
diff --git a/Documentation/devicetree/bindings/regulator/gdsc-regulator.txt b/Documentation/devicetree/bindings/regulator/gdsc-regulator.txt
new file mode 100644
index 0000000..cd7bdce
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/gdsc-regulator.txt
@@ -0,0 +1,21 @@
+Qualcomm Global Distributed Switch Controller (GDSC) Regulator Driver
+
+The GDSC driver, implemented under the regulator framework, is responsible for
+safely collapsing and restoring power to peripheral cores on chipsets like
+msm-copper for power savings.
+
+Required properties:
+ - compatible: Must be "qcom,gdsc"
+ - regulator-name: A string used as a descriptive name for regulator outputs
+ - reg: The address of the GDSCR register
+
+Optional properties:
+ - parent-supply: phandle to the parent supply/regulator node
+
+Example:
+ gdsc_oxili_gx: qcom,gdsc@fd8c4024 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_oxili_gx";
+ parent-supply = <&pm8841_s4>;
+ reg = <0xfd8c4024 0x4>;
+ };
diff --git a/Documentation/devicetree/bindings/regulator/regulator.txt b/Documentation/devicetree/bindings/regulator/regulator.txt
index 82bef20..0d22aa1 100644
--- a/Documentation/devicetree/bindings/regulator/regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/regulator.txt
@@ -10,6 +10,11 @@
- regulator-always-on: boolean, regulator should never be disabled
- regulator-boot-on: bootloader/firmware enabled regulator
- <name>-supply: phandle to the parent supply/regulator node
+- qcom,consumer-supplies: flattened list of supply and dev_name pairs
+ This property is used to support regulator consumers that have no device
+ tree node. An empty string, "", can be used to specify a null device
+ name. A null device name is used to allow calls such as:
+ regulator_get(NULL, "pll_vdd").
Example:
@@ -18,6 +23,7 @@
regulator-max-microvolt = <2500000>;
regulator-always-on;
vin-supply = <&vin>;
+ qcom,consumer-supplies = "pll_vdd", "", "lcd_vcc", "foo.1";
};
Regulator Consumers:
diff --git a/Documentation/mmc/mmc-dev-attrs.txt b/Documentation/mmc/mmc-dev-attrs.txt
index 8898a95..70fa101 100644
--- a/Documentation/mmc/mmc-dev-attrs.txt
+++ b/Documentation/mmc/mmc-dev-attrs.txt
@@ -8,6 +8,23 @@
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.
+
SD and MMC Device Attributes
============================
diff --git a/arch/arm/boot/dts/msm-gdsc.dtsi b/arch/arm/boot/dts/msm-gdsc.dtsi
new file mode 100644
index 0000000..f83fe76
--- /dev/null
+++ b/arch/arm/boot/dts/msm-gdsc.dtsi
@@ -0,0 +1,58 @@
+/*
+ * 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/ "skeleton.dtsi"
+
+/ {
+ gdsc_venus: qcom,gdsc@fd8c1024 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_venus";
+ reg = <0xfd8c1024 0x4>;
+ };
+
+ gdsc_mdss: qcom,gdsc@fd8c2304 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_mdss";
+ reg = <0xfd8c2304 0x4>;
+ };
+
+ gdsc_jpeg: qcom,gdsc@fd8c35a4 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_jpeg";
+ reg = <0xfd8c35a4 0x4>;
+ };
+
+ gdsc_vfe: qcom,gdsc@fd8c36a4 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_vfe";
+ reg = <0xfd8c36a4 0x4>;
+ };
+
+ gdsc_oxili_gx: qcom,gdsc@fd8c4024 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_oxili_gx";
+ reg = <0xfd8c4024 0x4>;
+ };
+
+ gdsc_oxili_cx: qcom,gdsc@fd8c4034 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_oxili_cx";
+ reg = <0xfd8c4034 0x4>;
+ };
+
+ gdsc_usb_hsic: qcom,gdsc@fc400404 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_usb_hsic";
+ reg = <0xfc400404 0x4>;
+ };
+};
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index 2698ea7..e62dfbd 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -315,9 +315,9 @@
};
};
- regulator@1d00 {
+ regulator@a000 {
regulator-name = "8941_boost";
- reg = <0x1d00 0x100>;
+ reg = <0xa000 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
diff --git a/arch/arm/boot/dts/msm9625.dts b/arch/arm/boot/dts/msm9625.dts
new file mode 100644
index 0000000..d5aed00
--- /dev/null
+++ b/arch/arm/boot/dts/msm9625.dts
@@ -0,0 +1,47 @@
+/* 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.
+ */
+
+/dts-v1/;
+/include/ "skeleton.dtsi"
+
+/ {
+ model = "Qualcomm MSM 9625";
+ compatible = "qcom,msm9625";
+ 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>;
+ };
+
+ timer {
+ compatible = "qcom,msm-qtimer", "arm,armv7-timer";
+ interrupts = <0 7 0>;
+ clock-frequency = <5000000>;
+ };
+
+ serial@f991f000 {
+ compatible = "qcom,msm-lsuart-v14";
+ reg = <0xf991f000 0x1000>;
+ interrupts = <0 109 0>;
+ };
+};
diff --git a/arch/arm/boot/dts/msmcopper-iommu.dtsi b/arch/arm/boot/dts/msmcopper-iommu.dtsi
new file mode 100644
index 0000000..e0ce8ac
--- /dev/null
+++ b/arch/arm/boot/dts/msmcopper-iommu.dtsi
@@ -0,0 +1,88 @@
+/* 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.
+ */
+
+/ {
+ jpeg: qcom,iommu@fda64000 {
+ compatible = "qcom,msm-smmu-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ reg = <0xfda64000 0x10000>;
+
+ qcom,iommu-ctx@fda6c000 {
+ reg = <0xfda6c000 0x1000>;
+ interrupts = <0 69 0>;
+ qcom,iommu-ctx-sids = <0>;
+ qcom,iommu-ctx-name = "jpeg_enc0";
+ };
+ qcom,iommu-ctx@fda6d000 {
+ reg = <0xfda6d000 0x1000>;
+ interrupts = <0 70 0>;
+ qcom,iommu-ctx-sids = <1>;
+ qcom,iommu-ctx-name = "jpeg_enc1";
+ };
+ qcom,iommu-ctx@fda6e000 {
+ reg = <0xfda6e000 0x1000>;
+ interrupts = <0 71 0>;
+ qcom,iommu-ctx-sids = <2>;
+ qcom,iommu-ctx-name = "jpeg_dec";
+ };
+ };
+
+ mdp: qcom,iommu@fd928000 {
+ compatible = "qcom,msm-smmu-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ reg = <0xfd928000 0x10000>;
+
+ qcom,iommu-ctx@fd930000 {
+ reg = <0xfd930000 0x1000>;
+ interrupts = <0 74 0>;
+ qcom,iommu-ctx-sids = <0>;
+ qcom,iommu-ctx-name = "mdp_0";
+ };
+ qcom,iommu-ctx@fd931000 {
+ reg = <0xfd931000 0x1000>;
+ interrupts = <0 75 0>;
+ qcom,iommu-ctx-sids = <1>;
+ qcom,iommu-ctx-name = "mdp_1";
+ };
+ };
+
+ venus: qcom,iommu@fdc84000 {
+ compatible = "qcom,msm-smmu-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ reg = <0xfdc84000 0x10000>;
+
+ qcom,iommu-ctx@fdc8c000 {
+ reg = <0xfdc8c000 0x1000>;
+ interrupts = <0 43 0>;
+ qcom,iommu-ctx-sids = <0 1 2 3 4 5>;
+ qcom,iommu-ctx-name = "venus_ns";
+ };
+ qcom,iommu-ctx@fdc8d000 {
+ reg = <0xfdc8d000 0x1000>;
+ interrupts = <0 42 0>;
+ qcom,iommu-ctx-sids = <0x80 0x81 0x82 0x83 0x84 0x85>;
+ qcom,iommu-ctx-name = "venus_cp";
+ };
+ qcom,iommu-ctx@fdc8e000 {
+ reg = <0xfdc8e000 0x1000>;
+ interrupts = <0 41 0>;
+ qcom,iommu-ctx-sids = <0xc0 0xc6>;
+ qcom,iommu-ctx-name = "venus_fw";
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/msmcopper-regulator.dtsi b/arch/arm/boot/dts/msmcopper-regulator.dtsi
index bb26e00..0d0c587 100644
--- a/arch/arm/boot/dts/msmcopper-regulator.dtsi
+++ b/arch/arm/boot/dts/msmcopper-regulator.dtsi
@@ -41,7 +41,7 @@
status = "okay";
};
- pm8941_boost: regulator@1d00 {
+ pm8941_boost: regulator@a000 {
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
qcom,enable-time = <500>;
diff --git a/arch/arm/boot/dts/msmcopper-rumi.dts b/arch/arm/boot/dts/msmcopper-rumi.dts
index d6e23ad..dff1973 100644
--- a/arch/arm/boot/dts/msmcopper-rumi.dts
+++ b/arch/arm/boot/dts/msmcopper-rumi.dts
@@ -31,17 +31,17 @@
};
qcom,sdcc@f9824000 {
- status = "disable";
- };
+ qcom,sdcc-clk-rates = <400000 19200000>;
+ };
+
+ qcom,sdcc@f98a4000 {
+ qcom,sdcc-clk-rates = <400000 19200000>;
+ };
qcom,sdcc@f9864000 {
status = "disable";
};
- qcom,sdcc@f98a4000 {
- status = "disable";
- };
-
qcom,sdcc@f98e4000 {
status = "disable";
};
@@ -75,6 +75,23 @@
};
};
+ i2c@f9966000 {
+ status = "disable";
+ };
+
+ i2c@f9967000 {
+ cell-index = <0>;
+ compatible = "qcom,i2c-qup";
+ reg = <0Xf9967000 0x1000>;
+ reg-names = "qup_phys_addr";
+ interrupts = <0 105 0>;
+ interrupt-names = "qup_err_intr";
+ qcom,i2c-bus-freq = <100000>;
+ qcom,i2c-src-freq = <24000000>;
+ gpios = <&msmgpio 83 0>, /* DAT */
+ <&msmgpio 84 0>; /* CLK */
+ };
+
slim@fe12f000 {
status = "disable";
};
@@ -83,10 +100,6 @@
status = "disable";
};
- i2c@f9966000 {
- status = "disable";
- };
-
qcom,ssusb@F9200000 {
status = "disable";
};
diff --git a/arch/arm/boot/dts/msmcopper.dtsi b/arch/arm/boot/dts/msmcopper.dtsi
index 8e74aac..a902ba5 100644
--- a/arch/arm/boot/dts/msmcopper.dtsi
+++ b/arch/arm/boot/dts/msmcopper.dtsi
@@ -17,6 +17,8 @@
/include/ "msm-pm8941.dtsi"
/include/ "msmcopper-regulator.dtsi"
/include/ "msmcopper-gpio.dtsi"
+/include/ "msmcopper-iommu.dtsi"
+/include/ "msm-gdsc.dtsi"
/ {
model = "Qualcomm MSM Copper";
@@ -131,6 +133,7 @@
qcom,bam-dma-res-pipes = <6>;
};
+
spi@f9924000 {
compatible = "qcom,spi-qup-v2";
reg = <0xf9924000 0x1000>;
@@ -274,6 +277,10 @@
qcom,dwc-usb3-msm-dbm-eps = <4>;
};
+ gdsc_oxili_gx: qcom,gdsc@fd8c4024 {
+ parent-supply = <&pm8841_s4>;
+ };
+
qcom,lpass@fe200000 {
compatible = "qcom,pil-q6v5-lpass";
reg = <0xfe200000 0x00100>,
@@ -282,6 +289,27 @@
qcom,firmware-name = "adsp";
};
+ qcom,mss@fc880000 {
+ compatible = "qcom,pil-q6v5-mss";
+ reg = <0xfc880000 0x100>,
+ <0xfd485000 0x400>,
+ <0xfc820000 0x020>,
+ <0xfc401680 0x004>;
+ vdd_mss-supply = <&pm8841_s3>;
+
+ qcom,firmware-name = "mba";
+ qcom,pil-self-auth = <1>;
+ };
+
+ qcom,mba@fc820000 {
+ compatible = "qcom,pil-mba";
+ reg = <0xfc820000 0x0020>,
+ <0x0d1fc000 0x4000>;
+
+ qcom,firmware-name = "modem";
+ qcom,depends-on = "mba";
+ };
+
qcom,pronto@fb21b000 {
compatible = "qcom,pil-pronto";
reg = <0xfb21b000 0x3000>,
@@ -301,4 +329,9 @@
rpm-channel-name = "rpm_requests";
rpm-channel-type = <15>; /* SMD_APPS_RPM */
};
+
+ qcom,msm-rng@f9bff000 {
+ compatible = "qcom,msm-rng";
+ reg = <0xf9bff000 0x200>;
+ };
};
diff --git a/arch/arm/configs/msm-copper_defconfig b/arch/arm/configs/msm-copper_defconfig
index feb5aa5..39f04cb 100644
--- a/arch/arm/configs/msm-copper_defconfig
+++ b/arch/arm/configs/msm-copper_defconfig
@@ -43,6 +43,8 @@
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
# CONFIG_MSM_HW3D is not set
CONFIG_MSM_PIL_LPASS_QDSP6V5=y
+CONFIG_MSM_PIL_PRONTO=y
+CONFIG_MSM_IOMMU=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_OCMEM=y
CONFIG_NO_HZ=y
diff --git a/arch/arm/configs/msm7630-perf_defconfig b/arch/arm/configs/msm7630-perf_defconfig
index 736f2bb..456eb9c 100644
--- a/arch/arm/configs/msm7630-perf_defconfig
+++ b/arch/arm/configs/msm7630-perf_defconfig
@@ -279,6 +279,7 @@
CONFIG_FB_MSM_TRIPLE_BUFFER=y
CONFIG_FB_MSM_MDP40=y
CONFIG_FB_MSM_OVERLAY=y
+CONFIG_FB_MSM_OVERLAY0_WRITEBACK=y
CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM=y
CONFIG_FB_MSM_HDMI_ADV7520_PANEL=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
diff --git a/arch/arm/configs/msm7630_defconfig b/arch/arm/configs/msm7630_defconfig
index bc464b4..fd11d67 100644
--- a/arch/arm/configs/msm7630_defconfig
+++ b/arch/arm/configs/msm7630_defconfig
@@ -281,6 +281,7 @@
CONFIG_FB_MSM_TRIPLE_BUFFER=y
CONFIG_FB_MSM_MDP40=y
CONFIG_FB_MSM_OVERLAY=y
+CONFIG_FB_MSM_OVERLAY0_WRITEBACK=y
CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM=y
CONFIG_FB_MSM_HDMI_ADV7520_PANEL=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
diff --git a/arch/arm/configs/msm8660-perf_defconfig b/arch/arm/configs/msm8660-perf_defconfig
index 87296cc..a6bbaf4 100644
--- a/arch/arm/configs/msm8660-perf_defconfig
+++ b/arch/arm/configs/msm8660-perf_defconfig
@@ -69,7 +69,6 @@
CONFIG_MSM_SUBSYSTEM_RESTART=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
-CONFIG_MSM_IOMMU=y
CONFIG_MSM_WATCHDOG=y
CONFIG_MSM_DLOAD_MODE=y
CONFIG_MSM_ETM=y
diff --git a/arch/arm/configs/msm8660_defconfig b/arch/arm/configs/msm8660_defconfig
index fd06714..df49bdc 100644
--- a/arch/arm/configs/msm8660_defconfig
+++ b/arch/arm/configs/msm8660_defconfig
@@ -68,7 +68,6 @@
CONFIG_MSM_SUBSYSTEM_RESTART=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
-CONFIG_MSM_IOMMU=y
CONFIG_MSM_WATCHDOG=y
CONFIG_MSM_DLOAD_MODE=y
CONFIG_MSM_ETM=y
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index 4cbd1d1..14ba936 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -309,6 +309,7 @@
CONFIG_SMB349_CHARGER=y
CONFIG_PM8921_CHARGER=y
CONFIG_PM8921_BMS=y
+CONFIG_PM8921_BCL=y
CONFIG_SENSORS_PM8XXX_ADC=y
CONFIG_SENSORS_EPM_ADC=y
CONFIG_THERMAL=y
@@ -445,6 +446,8 @@
CONFIG_MSM_SSBI=y
CONFIG_SPS=y
CONFIG_SPS_SUPPORT_BAMDMA=y
+CONFIG_MOBICORE_SUPPORT=m
+CONFIG_MOBICORE_API=m
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index a901684..24b1c7d 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -318,6 +318,7 @@
CONFIG_SMB349_CHARGER=y
CONFIG_PM8921_CHARGER=y
CONFIG_PM8921_BMS=y
+CONFIG_PM8921_BCL=y
CONFIG_SENSORS_PM8XXX_ADC=y
CONFIG_SENSORS_EPM_ADC=y
CONFIG_THERMAL=y
@@ -454,6 +455,8 @@
CONFIG_MSM_SSBI=y
CONFIG_SPS=y
CONFIG_SPS_SUPPORT_BAMDMA=y
+CONFIG_MOBICORE_SUPPORT=m
+CONFIG_MOBICORE_API=m
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
new file mode 100644
index 0000000..89fb888
--- /dev/null
+++ b/arch/arm/configs/msm9625_defconfig
@@ -0,0 +1,107 @@
+CONFIG_EXPERIMENTAL=y
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SYSVIPC=y
+CONFIG_SPARSE_IRQ=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_ARCH_MSM=y
+CONFIG_ARCH_MSM9625=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_ARM_APPENDED_DTB=y
+CONFIG_ARM_ATAG_DTB_COMPAT=y
+CONFIG_VFP=y
+CONFIG_NEON=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_MISC_DEVICES=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_MFD_SUPPORT 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_PARTITION_ADVANCED=y
+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_DEBUG_KERNEL=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/include/asm/mach/flash.h b/arch/arm/include/asm/mach/flash.h
index 36938ea..7ed9630 100644
--- a/arch/arm/include/asm/mach/flash.h
+++ b/arch/arm/include/asm/mach/flash.h
@@ -13,6 +13,11 @@
struct mtd_partition;
struct mtd_info;
+enum sw_version {
+ VERSION_1 = 0,
+ VERSION_2,
+};
+
/*
* map_name: the map probe function name
* name: flash device name (eg, as used with mtdparts=)
@@ -24,6 +29,7 @@
* mmcontrol: method called to enable or disable Sync. Burst Read in OneNAND
* parts: optional array of mtd_partitions for static partitioning
* nr_parts: number of mtd_partitions for static partitoning
+ * version: software register interface version
*/
struct flash_platform_data {
const char *map_name;
@@ -36,6 +42,7 @@
void (*mmcontrol)(struct mtd_info *mtd, int sync_read);
struct mtd_partition *parts;
unsigned int nr_parts;
+ enum sw_version version;
};
#endif
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
index 7f7e5c0..cf2d473 100644
--- a/arch/arm/kernel/vmlinux.lds.S
+++ b/arch/arm/kernel/vmlinux.lds.S
@@ -91,8 +91,10 @@
#ifndef CONFIG_XIP_KERNEL
. = ALIGN(PAGE_SIZE);
+#ifndef CONFIG_STRICT_MEMORY_RWX
__init_end = .;
#endif
+#endif
/*
* unwind exit sections must be discarded before the rest of the
@@ -115,6 +117,7 @@
#ifdef CONFIG_STRICT_MEMORY_RWX
. = ALIGN(1<<SECTION_SHIFT);
+ __init_end = .;
#endif
.text : { /* Real text segment */
_text = .; /* Text and read-only data */
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 16d221e..06a2c49 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -94,6 +94,7 @@
select MSM_SECURE_IO
select MSM_DALRPC
select MSM_QDSP6_APR
+ select MSM_QDSP6_CODECS
select MSM_NATIVE_RESTART
select ARCH_INLINE_SPIN_TRYLOCK
select ARCH_INLINE_SPIN_TRYLOCK_BH
@@ -144,6 +145,7 @@
select MSM_RPM
select MSM_XO
select MSM_QDSP6_APR
+ select MSM_QDSP6_CODECS
select MSM_PIL
select MSM_AUDIO_QDSP6 if SND_SOC
select CPU_HAS_L2_PMU
@@ -185,6 +187,7 @@
select MSM_RPM
select MSM_XO
select MSM_QDSP6_APR
+ select MSM_QDSP6_CODECS
select MSM_PIL
select MSM_AUDIO_QDSP6 if SND_SOC
select CPU_HAS_L2_PMU
@@ -220,6 +223,7 @@
select MSM_REMOTE_SPINLOCK_SFPB
select MSM_PIL
select MSM_QDSP6_APR
+ select MSM_QDSP6_CODECS
select MSM_AUDIO_QDSP6 if SND_SOC
select MULTI_IRQ_HANDLER
select MSM_RPM
@@ -308,6 +312,7 @@
select SMP
select MSM_SMP
select CPU_V7
+ select MSM_GPIOMUX
select MULTI_IRQ_HANDLER
select MSM_V2_TLMM
@@ -880,7 +885,7 @@
default "0x80200000" if ARCH_APQ8064
default "0x80200000" if ARCH_MSM8960
default "0x80200000" if ARCH_MSM8930
- default "0x20200000" if ARCH_MSMCOPPER
+ default "0x00000000" if ARCH_MSMCOPPER
default "0x10000000" if ARCH_FSM9XXX
default "0x20200000" if ARCH_MSM9625
default "0x00200000" if !MSM_STACKED_MEMORY
@@ -989,7 +994,6 @@
config DEBUG_MSM8960_UART
bool "Kernel low-level debugging messages via MSM 8960 UART"
depends on ARCH_MSM8960 && DEBUG_LL
- select DEBUG_MSM8930_UART
select MSM_HAS_DEBUG_UART_HS
help
Say Y here if you want the debug print routines to direct
@@ -1853,8 +1857,22 @@
tristate "LPASS QDSP6v5 (Hexagon) Boot Support"
depends on MSM_PIL
help
- Support for booting and shutting down QDSP6v5 processors (Hexagon)
- processors in low power audio subsystems.
+ Support for booting and shutting down QDSP6v5 (Hexagon) processors
+ in low power audio subsystems.
+
+config MSM_PIL_MSS_QDSP6V5
+ tristate "MSS QDSP6v5 (Hexagon) Boot Support"
+ depends on MSM_PIL
+ help
+ Support for booting and shutting down QDSP6v5 (Hexagon) processors
+ in modem subsystems.
+
+config MSM_PIL_MBA
+ tristate "Support for modem self-authentication"
+ depends on MSM_PIL_MSS_QDSP6V5
+ help
+ Support for booting self-authenticating modems using the Modem Boot
+ Authenticator.
config MSM_PIL_RIVA
tristate "RIVA (WCNSS) Boot Support"
@@ -2012,7 +2030,7 @@
config MSM_IOMMU
bool "MSM IOMMU Support"
- depends on ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_APQ8064
+ depends on ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_APQ8064 || ARCH_MSMCOPPER
select IOMMU_API
default n
help
@@ -2192,6 +2210,15 @@
used by audio driver to configure QDSP6's
ASM, ADM and AFE.
+config MSM_QDSP6_CODECS
+ bool "Audio Codecs on QDSP6 APR "
+ depends on MSM_SMD
+ default n
+ help
+ Enable Audio codecs with APR IPC protocol support between
+ application processor and QDSP6. APR is
+ used by audio driver to configure QDSP6's
+ ASM, ADM and AFE.
config MSM_AUDIO_QDSP6
bool "QDSP6 HW Audio support"
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 865f6f6..2ad51cd 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -68,6 +68,8 @@
obj-$(CONFIG_MSM_PIL_QDSP6V3) += pil-q6v3.o
obj-$(CONFIG_MSM_PIL_QDSP6V4) += pil-q6v4.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
obj-$(CONFIG_MSM_PIL_RIVA) += pil-riva.o
obj-$(CONFIG_MSM_PIL_TZAPPS) += pil-tzapps.o
obj-$(CONFIG_MSM_PIL_VIDC) += pil-vidc.o
@@ -107,11 +109,13 @@
ifndef CONFIG_ARCH_MSM8X60
ifndef CONFIG_ARCH_APQ8064
ifndef CONFIG_ARCH_MSMCOPPER
+ifndef CONFIG_ARCH_MSM9625
obj-y += nand_partitions.o
endif
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
@@ -283,6 +287,8 @@
obj-$(CONFIG_ARCH_MSMCOPPER) += board-copper.o board-dt.o board-copper-regulator.o board-copper-gpiomux.o
obj-$(CONFIG_ARCH_MSMCOPPER) += acpuclock-krait.o acpuclock-copper.o
obj-$(CONFIG_ARCH_MSMCOPPER) += clock-local2.o clock-pll.o clock-copper.o
+obj-$(CONFIG_ARCH_MSMCOPPER) += gdsc.o
+obj-$(CONFIG_ARCH_MSM9625) += board-9625.o board-9625-gpiomux.o
obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire.o board-sapphire-gpio.o
obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-keypad.o board-sapphire-panel.o
@@ -305,6 +311,8 @@
obj-$(CONFIG_ARCH_MSM8960) += rpm_resources.o
obj-$(CONFIG_ARCH_MSM8X60) += rpm_resources.o
obj-$(CONFIG_ARCH_MSM9615) += rpm_resources.o
+endif
+ifdef CONFIG_MSM_RPM_SMD
obj-$(CONFIG_ARCH_MSMCOPPER) += lpm_levels.o
endif
obj-$(CONFIG_MSM_MPM) += mpm.o
@@ -315,7 +323,10 @@
obj-$(CONFIG_MSM_BUS_SCALING) += msm_bus/
obj-$(CONFIG_MSM_BUSPM_DEV) += msm-buspm-dev.o
-obj-$(CONFIG_MSM_IOMMU) += iommu.o iommu_dev.o devices-iommu.o iommu_domains.o
+obj-$(CONFIG_MSM_IOMMU) += iommu.o iommu_dev.o devices-iommu.o iommu_domains.o
+ifdef CONFIG_OF
+obj-$(CONFIG_MSM_IOMMU) += iommu-v2.o iommu_dev-v2.o iommu_pagetable.o
+endif
ifdef CONFIG_VCM
obj-$(CONFIG_ARCH_MSM8X60) += board-msm8x60-vcm.o
@@ -330,6 +341,8 @@
obj-$(CONFIG_ARCH_APQ8064) += gpiomux-v2.o gpiomux.o
obj-$(CONFIG_ARCH_MSM9615) += gpiomux-v2.o gpiomux.o
obj-$(CONFIG_ARCH_MSMCOPPER) += gpiomux-v2.o gpiomux.o
+obj-$(CONFIG_ARCH_MSM9625) += gpiomux-v2.o gpiomux.o
+
ifdef CONFIG_FSM9XXX_TLMM
obj-y += gpio-fsm9xxx.o
diff --git a/arch/arm/mach-msm/Makefile.boot b/arch/arm/mach-msm/Makefile.boot
index 58c630e..bd8d153 100644
--- a/arch/arm/mach-msm/Makefile.boot
+++ b/arch/arm/mach-msm/Makefile.boot
@@ -46,11 +46,14 @@
zreladdr-$(CONFIG_ARCH_APQ8064) := 0x80208000
# MSMCOPPER
- zreladdr-$(CONFIG_ARCH_MSMCOPPER) := 0x20208000
+ zreladdr-$(CONFIG_ARCH_MSMCOPPER) := 0x00008000
# MSM9615
zreladdr-$(CONFIG_ARCH_MSM9615) := 0x40808000
+# MSM9625
+ zreladdr-$(CONFIG_ARCH_MSM9625) := 0x20208000
+
# FSM9XXX
zreladdr-$(CONFIG_ARCH_FSM9XXX) := 0x10008000
params_phys-$(CONFIG_ARCH_FSM9XXX) := 0x10000100
diff --git a/arch/arm/mach-msm/acpuclock-8960.c b/arch/arm/mach-msm/acpuclock-8960.c
index 6986a29..f467aba 100644
--- a/arch/arm/mach-msm/acpuclock-8960.c
+++ b/arch/arm/mach-msm/acpuclock-8960.c
@@ -513,15 +513,15 @@
{ 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(7), 1100000 },
{ 0, { 972000, HFPLL, 1, 0, 0x24 }, L2(7), 1125000 },
{ 1, { 1026000, HFPLL, 1, 0, 0x26 }, L2(7), 1125000 },
- { 0, { 1080000, HFPLL, 1, 0, 0x28 }, L2(16), 1175000 },
- { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(16), 1175000 },
- { 0, { 1188000, HFPLL, 1, 0, 0x2C }, L2(16), 1200000 },
- { 1, { 1242000, HFPLL, 1, 0, 0x2E }, L2(16), 1200000 },
- { 0, { 1296000, HFPLL, 1, 0, 0x30 }, L2(16), 1225000 },
- { 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(16), 1225000 },
- { 0, { 1404000, HFPLL, 1, 0, 0x34 }, L2(16), 1237500 },
- { 1, { 1458000, HFPLL, 1, 0, 0x36 }, L2(16), 1237500 },
- { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(16), 1250000 },
+ { 0, { 1080000, HFPLL, 1, 0, 0x28 }, L2(19), 1175000 },
+ { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(19), 1175000 },
+ { 0, { 1188000, HFPLL, 1, 0, 0x2C }, L2(19), 1200000 },
+ { 1, { 1242000, HFPLL, 1, 0, 0x2E }, L2(19), 1200000 },
+ { 0, { 1296000, HFPLL, 1, 0, 0x30 }, L2(19), 1225000 },
+ { 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(19), 1225000 },
+ { 0, { 1404000, HFPLL, 1, 0, 0x34 }, L2(19), 1237500 },
+ { 1, { 1458000, HFPLL, 1, 0, 0x36 }, L2(19), 1237500 },
+ { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(19), 1250000 },
{ 0, { 0 } }
};
@@ -540,15 +540,15 @@
{ 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(7), 1050000 },
{ 0, { 972000, HFPLL, 1, 0, 0x24 }, L2(7), 1075000 },
{ 1, { 1026000, HFPLL, 1, 0, 0x26 }, L2(7), 1075000 },
- { 0, { 1080000, HFPLL, 1, 0, 0x28 }, L2(16), 1125000 },
- { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(16), 1125000 },
- { 0, { 1188000, HFPLL, 1, 0, 0x2C }, L2(16), 1150000 },
- { 1, { 1242000, HFPLL, 1, 0, 0x2E }, L2(16), 1150000 },
- { 0, { 1296000, HFPLL, 1, 0, 0x30 }, L2(16), 1175000 },
- { 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(16), 1175000 },
- { 0, { 1404000, HFPLL, 1, 0, 0x34 }, L2(16), 1187500 },
- { 1, { 1458000, HFPLL, 1, 0, 0x36 }, L2(16), 1187500 },
- { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(16), 1200000 },
+ { 0, { 1080000, HFPLL, 1, 0, 0x28 }, L2(19), 1125000 },
+ { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(19), 1125000 },
+ { 0, { 1188000, HFPLL, 1, 0, 0x2C }, L2(19), 1150000 },
+ { 1, { 1242000, HFPLL, 1, 0, 0x2E }, L2(19), 1150000 },
+ { 0, { 1296000, HFPLL, 1, 0, 0x30 }, L2(19), 1175000 },
+ { 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(19), 1175000 },
+ { 0, { 1404000, HFPLL, 1, 0, 0x34 }, L2(19), 1187500 },
+ { 1, { 1458000, HFPLL, 1, 0, 0x36 }, L2(19), 1187500 },
+ { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(19), 1200000 },
{ 0, { 0 } }
};
@@ -567,15 +567,15 @@
{ 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(7), 1000000 },
{ 0, { 972000, HFPLL, 1, 0, 0x24 }, L2(7), 1025000 },
{ 1, { 1026000, HFPLL, 1, 0, 0x26 }, L2(7), 1025000 },
- { 0, { 1080000, HFPLL, 1, 0, 0x28 }, L2(16), 1075000 },
- { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(16), 1075000 },
- { 0, { 1188000, HFPLL, 1, 0, 0x2C }, L2(16), 1100000 },
- { 1, { 1242000, HFPLL, 1, 0, 0x2E }, L2(16), 1100000 },
- { 0, { 1296000, HFPLL, 1, 0, 0x30 }, L2(16), 1125000 },
- { 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(16), 1125000 },
- { 0, { 1404000, HFPLL, 1, 0, 0x34 }, L2(16), 1137500 },
- { 1, { 1458000, HFPLL, 1, 0, 0x36 }, L2(16), 1137500 },
- { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(16), 1150000 },
+ { 0, { 1080000, HFPLL, 1, 0, 0x28 }, L2(19), 1075000 },
+ { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(19), 1075000 },
+ { 0, { 1188000, HFPLL, 1, 0, 0x2C }, L2(19), 1100000 },
+ { 1, { 1242000, HFPLL, 1, 0, 0x2E }, L2(19), 1100000 },
+ { 0, { 1296000, HFPLL, 1, 0, 0x30 }, L2(19), 1125000 },
+ { 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(19), 1125000 },
+ { 0, { 1404000, HFPLL, 1, 0, 0x34 }, L2(19), 1137500 },
+ { 1, { 1458000, HFPLL, 1, 0, 0x36 }, L2(19), 1137500 },
+ { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(19), 1150000 },
{ 0, { 0 } }
};
diff --git a/arch/arm/mach-msm/acpuclock-8x60.c b/arch/arm/mach-msm/acpuclock-8x60.c
index 787483b..48efa18 100644
--- a/arch/arm/mach-msm/acpuclock-8x60.c
+++ b/arch/arm/mach-msm/acpuclock-8x60.c
@@ -217,40 +217,7 @@
};
/* SCPLL frequencies = 2 * 27 MHz * L_VAL */
-static struct clkctl_acpu_speed acpu_freq_tbl_slowest[] = {
- { {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 800000, 0x03006000},
- /* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */
- { {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 825000, 0x03006000},
- { {1, 1}, 384000, ACPU_PLL_8, 3, 0, 0, 0, L2(1), 825000, 0x03006000},
- { {1, 1}, 432000, ACPU_SCPLL, 0, 0, 1, 0x08, L2(1), 850000, 0x03006000},
- { {1, 1}, 486000, ACPU_SCPLL, 0, 0, 1, 0x09, L2(2), 850000, 0x03006000},
- { {1, 1}, 540000, ACPU_SCPLL, 0, 0, 1, 0x0A, L2(3), 875000, 0x03006000},
- { {1, 1}, 594000, ACPU_SCPLL, 0, 0, 1, 0x0B, L2(4), 875000, 0x03006000},
- { {1, 1}, 648000, ACPU_SCPLL, 0, 0, 1, 0x0C, L2(5), 900000, 0x03006000},
- { {1, 1}, 702000, ACPU_SCPLL, 0, 0, 1, 0x0D, L2(6), 900000, 0x03006000},
- { {1, 1}, 756000, ACPU_SCPLL, 0, 0, 1, 0x0E, L2(7), 925000, 0x03006000},
- { {1, 1}, 810000, ACPU_SCPLL, 0, 0, 1, 0x0F, L2(8), 975000, 0x03006000},
- { {1, 1}, 864000, ACPU_SCPLL, 0, 0, 1, 0x10, L2(9), 975000, 0x03006000},
- { {1, 1}, 918000, ACPU_SCPLL, 0, 0, 1, 0x11, L2(10), 1000000, 0x03006000},
- { {1, 1}, 972000, ACPU_SCPLL, 0, 0, 1, 0x12, L2(11), 1025000, 0x03006000},
- { {1, 1}, 1026000, ACPU_SCPLL, 0, 0, 1, 0x13, L2(12), 1025000, 0x03006000},
- { {1, 1}, 1080000, ACPU_SCPLL, 0, 0, 1, 0x14, L2(13), 1050000, 0x03006000},
- { {1, 1}, 1134000, ACPU_SCPLL, 0, 0, 1, 0x15, L2(14), 1075000, 0x03006000},
- { {1, 1}, 1188000, ACPU_SCPLL, 0, 0, 1, 0x16, L2(15), 1100000, 0x03006000},
- { {1, 1}, 1242000, ACPU_SCPLL, 0, 0, 1, 0x17, L2(16), 1125000, 0x03006000},
- { {1, 1}, 1296000, ACPU_SCPLL, 0, 0, 1, 0x18, L2(17), 1150000, 0x03006000},
- { {1, 1}, 1350000, ACPU_SCPLL, 0, 0, 1, 0x19, L2(18), 1175000, 0x03006000},
- { {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1200000, 0x03006000},
- { {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1225000, 0x03006000},
- { {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1250000, 0x03006000},
- { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1275000, 0x03006000},
- { {1, 1}, 1620000, ACPU_SCPLL, 0, 0, 1, 0x1E, L2(19), 1300000, 0x03006000},
- { {1, 1}, 1674000, ACPU_SCPLL, 0, 0, 1, 0x1F, L2(19), 1325000, 0x03006000},
- { {0, 0}, 0 },
-};
-
-/* SCPLL frequencies = 2 * 27 MHz * L_VAL */
-static struct clkctl_acpu_speed acpu_freq_tbl_slower[] = {
+static struct clkctl_acpu_speed acpu_freq_tbl_1512mhz_slow[] = {
{ {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 800000, 0x03006000},
/* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */
{ {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 825000, 0x03006000},
@@ -276,47 +243,11 @@
{ {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1175000, 0x03006000},
{ {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1200000, 0x03006000},
{ {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1225000, 0x03006000},
- { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1250000, 0x03006000},
- { {1, 1}, 1620000, ACPU_SCPLL, 0, 0, 1, 0x1E, L2(19), 1275000, 0x03006000},
- { {1, 1}, 1674000, ACPU_SCPLL, 0, 0, 1, 0x1F, L2(19), 1300000, 0x03006000},
{ {0, 0}, 0 },
};
/* SCPLL frequencies = 2 * 27 MHz * L_VAL */
-static struct clkctl_acpu_speed acpu_freq_tbl_slow[] = {
- { {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 800000, 0x03006000},
- /* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */
- { {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 825000, 0x03006000},
- { {1, 1}, 384000, ACPU_PLL_8, 3, 0, 0, 0, L2(1), 825000, 0x03006000},
- { {1, 1}, 432000, ACPU_SCPLL, 0, 0, 1, 0x08, L2(1), 850000, 0x03006000},
- { {1, 1}, 486000, ACPU_SCPLL, 0, 0, 1, 0x09, L2(2), 850000, 0x03006000},
- { {1, 1}, 540000, ACPU_SCPLL, 0, 0, 1, 0x0A, L2(3), 875000, 0x03006000},
- { {1, 1}, 594000, ACPU_SCPLL, 0, 0, 1, 0x0B, L2(4), 875000, 0x03006000},
- { {1, 1}, 648000, ACPU_SCPLL, 0, 0, 1, 0x0C, L2(5), 900000, 0x03006000},
- { {1, 1}, 702000, ACPU_SCPLL, 0, 0, 1, 0x0D, L2(6), 900000, 0x03006000},
- { {1, 1}, 756000, ACPU_SCPLL, 0, 0, 1, 0x0E, L2(7), 925000, 0x03006000},
- { {1, 1}, 810000, ACPU_SCPLL, 0, 0, 1, 0x0F, L2(8), 975000, 0x03006000},
- { {1, 1}, 864000, ACPU_SCPLL, 0, 0, 1, 0x10, L2(9), 975000, 0x03006000},
- { {1, 1}, 918000, ACPU_SCPLL, 0, 0, 1, 0x11, L2(10), 1000000, 0x03006000},
- { {1, 1}, 972000, ACPU_SCPLL, 0, 0, 1, 0x12, L2(11), 1025000, 0x03006000},
- { {1, 1}, 1026000, ACPU_SCPLL, 0, 0, 1, 0x13, L2(12), 1025000, 0x03006000},
- { {1, 1}, 1080000, ACPU_SCPLL, 0, 0, 1, 0x14, L2(13), 1050000, 0x03006000},
- { {1, 1}, 1134000, ACPU_SCPLL, 0, 0, 1, 0x15, L2(14), 1075000, 0x03006000},
- { {1, 1}, 1188000, ACPU_SCPLL, 0, 0, 1, 0x16, L2(15), 1100000, 0x03006000},
- { {1, 1}, 1242000, ACPU_SCPLL, 0, 0, 1, 0x17, L2(16), 1125000, 0x03006000},
- { {1, 1}, 1296000, ACPU_SCPLL, 0, 0, 1, 0x18, L2(17), 1150000, 0x03006000},
- { {1, 1}, 1350000, ACPU_SCPLL, 0, 0, 1, 0x19, L2(18), 1150000, 0x03006000},
- { {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1175000, 0x03006000},
- { {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1200000, 0x03006000},
- { {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1225000, 0x03006000},
- { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1225000, 0x03006000},
- { {1, 1}, 1620000, ACPU_SCPLL, 0, 0, 1, 0x1E, L2(19), 1225000, 0x03006000},
- { {1, 1}, 1674000, ACPU_SCPLL, 0, 0, 1, 0x1F, L2(19), 1250000, 0x03006000},
- { {0, 0}, 0 },
-};
-
-/* SCPLL frequencies = 2 * 27 MHz * L_VAL */
-static struct clkctl_acpu_speed acpu_freq_tbl_nom[] = {
+static struct clkctl_acpu_speed acpu_freq_tbl_1512mhz_nom[] = {
{ {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 800000, 0x03006000},
/* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */
{ {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 825000, 0x03006000},
@@ -342,14 +273,11 @@
{ {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1150000, 0x03006000},
{ {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1150000, 0x03006000},
{ {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1175000, 0x03006000},
- { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1175000, 0x03006000},
- { {1, 1}, 1620000, ACPU_SCPLL, 0, 0, 1, 0x1E, L2(19), 1200000, 0x03006000},
- { {1, 1}, 1674000, ACPU_SCPLL, 0, 0, 1, 0x1F, L2(19), 1200000, 0x03006000},
{ {0, 0}, 0 },
};
/* SCPLL frequencies = 2 * 27 MHz * L_VAL */
-static struct clkctl_acpu_speed acpu_freq_tbl_fast[] = {
+static struct clkctl_acpu_speed acpu_freq_tbl_1512mhz_fast[] = {
{ {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 800000, 0x03006000},
/* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */
{ {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 825000, 0x03006000},
@@ -375,13 +303,141 @@
{ {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1100000, 0x03006000},
{ {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1100000, 0x03006000},
{ {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1125000, 0x03006000},
- { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1125000, 0x03006000},
+ { {0, 0}, 0 },
+};
+
+/* SCPLL frequencies = 2 * 27 MHz * L_VAL */
+static struct clkctl_acpu_speed acpu_freq_tbl_1674mhz_slower[] = {
+ { {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 775000, 0x03006000},
+ /* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */
+ { {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 775000, 0x03006000},
+ { {1, 1}, 384000, ACPU_PLL_8, 3, 0, 0, 0, L2(1), 775000, 0x03006000},
+ { {1, 1}, 432000, ACPU_SCPLL, 0, 0, 1, 0x08, L2(1), 775000, 0x03006000},
+ { {1, 1}, 486000, ACPU_SCPLL, 0, 0, 1, 0x09, L2(2), 775000, 0x03006000},
+ { {1, 1}, 540000, ACPU_SCPLL, 0, 0, 1, 0x0A, L2(3), 787500, 0x03006000},
+ { {1, 1}, 594000, ACPU_SCPLL, 0, 0, 1, 0x0B, L2(4), 800000, 0x03006000},
+ { {1, 1}, 648000, ACPU_SCPLL, 0, 0, 1, 0x0C, L2(5), 825000, 0x03006000},
+ { {1, 1}, 702000, ACPU_SCPLL, 0, 0, 1, 0x0D, L2(6), 837500, 0x03006000},
+ { {1, 1}, 756000, ACPU_SCPLL, 0, 0, 1, 0x0E, L2(7), 850000, 0x03006000},
+ { {1, 1}, 810000, ACPU_SCPLL, 0, 0, 1, 0x0F, L2(8), 875000, 0x03006000},
+ { {1, 1}, 864000, ACPU_SCPLL, 0, 0, 1, 0x10, L2(9), 900000, 0x03006000},
+ { {1, 1}, 918000, ACPU_SCPLL, 0, 0, 1, 0x11, L2(10), 912500, 0x03006000},
+ { {1, 1}, 972000, ACPU_SCPLL, 0, 0, 1, 0x12, L2(11), 937500, 0x03006000},
+ { {1, 1}, 1026000, ACPU_SCPLL, 0, 0, 1, 0x13, L2(12), 962500, 0x03006000},
+ { {1, 1}, 1080000, ACPU_SCPLL, 0, 0, 1, 0x14, L2(13), 987500, 0x03006000},
+ { {1, 1}, 1134000, ACPU_SCPLL, 0, 0, 1, 0x15, L2(14), 1012500, 0x03006000},
+ { {1, 1}, 1188000, ACPU_SCPLL, 0, 0, 1, 0x16, L2(15), 1025000, 0x03006000},
+ { {1, 1}, 1242000, ACPU_SCPLL, 0, 0, 1, 0x17, L2(16), 1062500, 0x03006000},
+ { {1, 1}, 1296000, ACPU_SCPLL, 0, 0, 1, 0x18, L2(17), 1087500, 0x03006000},
+ { {1, 1}, 1350000, ACPU_SCPLL, 0, 0, 1, 0x19, L2(18), 1100000, 0x03006000},
+ { {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1125000, 0x03006000},
+ { {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1150000, 0x03006000},
+ { {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1187500, 0x03006000},
+ { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1225000, 0x03006000},
+ { {1, 1}, 1620000, ACPU_SCPLL, 0, 0, 1, 0x1E, L2(19), 1262500, 0x03006000},
+ { {1, 1}, 1674000, ACPU_SCPLL, 0, 0, 1, 0x1F, L2(19), 1300000, 0x03006000},
+ { {0, 0}, 0 },
+};
+
+/* SCPLL frequencies = 2 * 27 MHz * L_VAL */
+static struct clkctl_acpu_speed acpu_freq_tbl_1674mhz_slow[] = {
+ { {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 775000, 0x03006000},
+ /* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */
+ { {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 775000, 0x03006000},
+ { {1, 1}, 384000, ACPU_PLL_8, 3, 0, 0, 0, L2(1), 775000, 0x03006000},
+ { {1, 1}, 432000, ACPU_SCPLL, 0, 0, 1, 0x08, L2(1), 775000, 0x03006000},
+ { {1, 1}, 486000, ACPU_SCPLL, 0, 0, 1, 0x09, L2(2), 775000, 0x03006000},
+ { {1, 1}, 540000, ACPU_SCPLL, 0, 0, 1, 0x0A, L2(3), 787500, 0x03006000},
+ { {1, 1}, 594000, ACPU_SCPLL, 0, 0, 1, 0x0B, L2(4), 800000, 0x03006000},
+ { {1, 1}, 648000, ACPU_SCPLL, 0, 0, 1, 0x0C, L2(5), 825000, 0x03006000},
+ { {1, 1}, 702000, ACPU_SCPLL, 0, 0, 1, 0x0D, L2(6), 837500, 0x03006000},
+ { {1, 1}, 756000, ACPU_SCPLL, 0, 0, 1, 0x0E, L2(7), 850000, 0x03006000},
+ { {1, 1}, 810000, ACPU_SCPLL, 0, 0, 1, 0x0F, L2(8), 862500, 0x03006000},
+ { {1, 1}, 864000, ACPU_SCPLL, 0, 0, 1, 0x10, L2(9), 887500, 0x03006000},
+ { {1, 1}, 918000, ACPU_SCPLL, 0, 0, 1, 0x11, L2(10), 900000, 0x03006000},
+ { {1, 1}, 972000, ACPU_SCPLL, 0, 0, 1, 0x12, L2(11), 925000, 0x03006000},
+ { {1, 1}, 1026000, ACPU_SCPLL, 0, 0, 1, 0x13, L2(12), 937500, 0x03006000},
+ { {1, 1}, 1080000, ACPU_SCPLL, 0, 0, 1, 0x14, L2(13), 962500, 0x03006000},
+ { {1, 1}, 1134000, ACPU_SCPLL, 0, 0, 1, 0x15, L2(14), 987500, 0x03006000},
+ { {1, 1}, 1188000, ACPU_SCPLL, 0, 0, 1, 0x16, L2(15), 1000000, 0x03006000},
+ { {1, 1}, 1242000, ACPU_SCPLL, 0, 0, 1, 0x17, L2(16), 1025000, 0x03006000},
+ { {1, 1}, 1296000, ACPU_SCPLL, 0, 0, 1, 0x18, L2(17), 1050000, 0x03006000},
+ { {1, 1}, 1350000, ACPU_SCPLL, 0, 0, 1, 0x19, L2(18), 1062500, 0x03006000},
+ { {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1087500, 0x03006000},
+ { {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1112500, 0x03006000},
+ { {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1150000, 0x03006000},
+ { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1175000, 0x03006000},
+ { {1, 1}, 1620000, ACPU_SCPLL, 0, 0, 1, 0x1E, L2(19), 1212500, 0x03006000},
+ { {1, 1}, 1674000, ACPU_SCPLL, 0, 0, 1, 0x1F, L2(19), 1250000, 0x03006000},
+ { {0, 0}, 0 },
+};
+
+/* SCPLL frequencies = 2 * 27 MHz * L_VAL */
+static struct clkctl_acpu_speed acpu_freq_tbl_1674mhz_nom[] = {
+ { {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 775000, 0x03006000},
+ /* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */
+ { {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 775000, 0x03006000},
+ { {1, 1}, 384000, ACPU_PLL_8, 3, 0, 0, 0, L2(1), 775000, 0x03006000},
+ { {1, 1}, 432000, ACPU_SCPLL, 0, 0, 1, 0x08, L2(1), 775000, 0x03006000},
+ { {1, 1}, 486000, ACPU_SCPLL, 0, 0, 1, 0x09, L2(2), 775000, 0x03006000},
+ { {1, 1}, 540000, ACPU_SCPLL, 0, 0, 1, 0x0A, L2(3), 787500, 0x03006000},
+ { {1, 1}, 594000, ACPU_SCPLL, 0, 0, 1, 0x0B, L2(4), 800000, 0x03006000},
+ { {1, 1}, 648000, ACPU_SCPLL, 0, 0, 1, 0x0C, L2(5), 812500, 0x03006000},
+ { {1, 1}, 702000, ACPU_SCPLL, 0, 0, 1, 0x0D, L2(6), 825000, 0x03006000},
+ { {1, 1}, 756000, ACPU_SCPLL, 0, 0, 1, 0x0E, L2(7), 837500, 0x03006000},
+ { {1, 1}, 810000, ACPU_SCPLL, 0, 0, 1, 0x0F, L2(8), 850000, 0x03006000},
+ { {1, 1}, 864000, ACPU_SCPLL, 0, 0, 1, 0x10, L2(9), 875000, 0x03006000},
+ { {1, 1}, 918000, ACPU_SCPLL, 0, 0, 1, 0x11, L2(10), 887500, 0x03006000},
+ { {1, 1}, 972000, ACPU_SCPLL, 0, 0, 1, 0x12, L2(11), 900000, 0x03006000},
+ { {1, 1}, 1026000, ACPU_SCPLL, 0, 0, 1, 0x13, L2(12), 912500, 0x03006000},
+ { {1, 1}, 1080000, ACPU_SCPLL, 0, 0, 1, 0x14, L2(13), 937500, 0x03006000},
+ { {1, 1}, 1134000, ACPU_SCPLL, 0, 0, 1, 0x15, L2(14), 950000, 0x03006000},
+ { {1, 1}, 1188000, ACPU_SCPLL, 0, 0, 1, 0x16, L2(15), 975000, 0x03006000},
+ { {1, 1}, 1242000, ACPU_SCPLL, 0, 0, 1, 0x17, L2(16), 987500, 0x03006000},
+ { {1, 1}, 1296000, ACPU_SCPLL, 0, 0, 1, 0x18, L2(17), 1012500, 0x03006000},
+ { {1, 1}, 1350000, ACPU_SCPLL, 0, 0, 1, 0x19, L2(18), 1025000, 0x03006000},
+ { {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1050000, 0x03006000},
+ { {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1075000, 0x03006000},
+ { {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1112500, 0x03006000},
+ { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1137500, 0x03006000},
+ { {1, 1}, 1620000, ACPU_SCPLL, 0, 0, 1, 0x1E, L2(19), 1175000, 0x03006000},
+ { {1, 1}, 1674000, ACPU_SCPLL, 0, 0, 1, 0x1F, L2(19), 1200000, 0x03006000},
+ { {0, 0}, 0 },
+};
+
+/* SCPLL frequencies = 2 * 27 MHz * L_VAL */
+static struct clkctl_acpu_speed acpu_freq_tbl_1674mhz_fast[] = {
+ { {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 775000, 0x03006000},
+ /* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */
+ { {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 775000, 0x03006000},
+ { {1, 1}, 384000, ACPU_PLL_8, 3, 0, 0, 0, L2(1), 775000, 0x03006000},
+ { {1, 1}, 432000, ACPU_SCPLL, 0, 0, 1, 0x08, L2(1), 775000, 0x03006000},
+ { {1, 1}, 486000, ACPU_SCPLL, 0, 0, 1, 0x09, L2(2), 775000, 0x03006000},
+ { {1, 1}, 540000, ACPU_SCPLL, 0, 0, 1, 0x0A, L2(3), 775000, 0x03006000},
+ { {1, 1}, 594000, ACPU_SCPLL, 0, 0, 1, 0x0B, L2(4), 787500, 0x03006000},
+ { {1, 1}, 648000, ACPU_SCPLL, 0, 0, 1, 0x0C, L2(5), 800000, 0x03006000},
+ { {1, 1}, 702000, ACPU_SCPLL, 0, 0, 1, 0x0D, L2(6), 812500, 0x03006000},
+ { {1, 1}, 756000, ACPU_SCPLL, 0, 0, 1, 0x0E, L2(7), 825000, 0x03006000},
+ { {1, 1}, 810000, ACPU_SCPLL, 0, 0, 1, 0x0F, L2(8), 837500, 0x03006000},
+ { {1, 1}, 864000, ACPU_SCPLL, 0, 0, 1, 0x10, L2(9), 862500, 0x03006000},
+ { {1, 1}, 918000, ACPU_SCPLL, 0, 0, 1, 0x11, L2(10), 875000, 0x03006000},
+ { {1, 1}, 972000, ACPU_SCPLL, 0, 0, 1, 0x12, L2(11), 887500, 0x03006000},
+ { {1, 1}, 1026000, ACPU_SCPLL, 0, 0, 1, 0x13, L2(12), 900000, 0x03006000},
+ { {1, 1}, 1080000, ACPU_SCPLL, 0, 0, 1, 0x14, L2(13), 925000, 0x03006000},
+ { {1, 1}, 1134000, ACPU_SCPLL, 0, 0, 1, 0x15, L2(14), 937500, 0x03006000},
+ { {1, 1}, 1188000, ACPU_SCPLL, 0, 0, 1, 0x16, L2(15), 950000, 0x03006000},
+ { {1, 1}, 1242000, ACPU_SCPLL, 0, 0, 1, 0x17, L2(16), 962500, 0x03006000},
+ { {1, 1}, 1296000, ACPU_SCPLL, 0, 0, 1, 0x18, L2(17), 975000, 0x03006000},
+ { {1, 1}, 1350000, ACPU_SCPLL, 0, 0, 1, 0x19, L2(18), 1000000, 0x03006000},
+ { {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1025000, 0x03006000},
+ { {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1050000, 0x03006000},
+ { {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1075000, 0x03006000},
+ { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1100000, 0x03006000},
{ {1, 1}, 1620000, ACPU_SCPLL, 0, 0, 1, 0x1E, L2(19), 1125000, 0x03006000},
{ {1, 1}, 1674000, ACPU_SCPLL, 0, 0, 1, 0x1F, L2(19), 1150000, 0x03006000},
{ {0, 0}, 0 },
};
-
/* acpu_freq_tbl row to use when reconfiguring SC/L2 PLLs. */
#define CAL_IDX 1
@@ -930,7 +986,7 @@
static __init struct clkctl_acpu_speed *select_freq_plan(void)
{
- uint32_t pte_efuse, speed_bin, pvs, max_khz;
+ uint32_t pte_efuse, speed_bin, pvs;
struct clkctl_acpu_speed *f;
pte_efuse = readl_relaxed(QFPROM_PTE_EFUSE_ADDR);
@@ -944,67 +1000,55 @@
pvs = (pte_efuse >> 13) & 0x7;
if (speed_bin == 0x2) {
- max_khz = 1674000;
switch (pvs) {
case 0x7:
- case 0x5:
- acpu_freq_tbl = acpu_freq_tbl_slowest;
- pr_info("ACPU PVS: Slowest\n");
- break;
case 0x4:
- acpu_freq_tbl = acpu_freq_tbl_slower;
+ acpu_freq_tbl = acpu_freq_tbl_1674mhz_slower;
pr_info("ACPU PVS: Slower\n");
break;
case 0x0:
- acpu_freq_tbl = acpu_freq_tbl_slow;
+ acpu_freq_tbl = acpu_freq_tbl_1674mhz_slow;
pr_info("ACPU PVS: Slow\n");
break;
case 0x1:
- acpu_freq_tbl = acpu_freq_tbl_nom;
+ acpu_freq_tbl = acpu_freq_tbl_1674mhz_nom;
pr_info("ACPU PVS: Nominal\n");
break;
case 0x3:
- acpu_freq_tbl = acpu_freq_tbl_fast;
+ acpu_freq_tbl = acpu_freq_tbl_1674mhz_fast;
pr_info("ACPU PVS: Fast\n");
break;
default:
- acpu_freq_tbl = acpu_freq_tbl_slowest;
- pr_warn("ACPU PVS: Unknown. Defaulting to slowest.\n");
+ acpu_freq_tbl = acpu_freq_tbl_1674mhz_slower;
+ pr_warn("ACPU PVS: Unknown. Defaulting to slower.\n");
break;
}
} else if (speed_bin == 0x1) {
- max_khz = 1512000;
switch (pvs) {
case 0x0:
case 0x7:
- acpu_freq_tbl = acpu_freq_tbl_slow;
+ acpu_freq_tbl = acpu_freq_tbl_1512mhz_slow;
pr_info("ACPU PVS: Slow\n");
break;
case 0x1:
- acpu_freq_tbl = acpu_freq_tbl_nom;
+ acpu_freq_tbl = acpu_freq_tbl_1512mhz_nom;
pr_info("ACPU PVS: Nominal\n");
break;
case 0x3:
- acpu_freq_tbl = acpu_freq_tbl_fast;
+ acpu_freq_tbl = acpu_freq_tbl_1512mhz_fast;
pr_info("ACPU PVS: Fast\n");
break;
default:
- acpu_freq_tbl = acpu_freq_tbl_slow;
+ acpu_freq_tbl = acpu_freq_tbl_1512mhz_slow;
pr_warn("ACPU PVS: Unknown. Defaulting to slow.\n");
break;
}
} else {
- max_khz = 1188000;
acpu_freq_tbl = acpu_freq_tbl_1188mhz;
}
- /* Truncate the table based to max_khz. */
- for (f = acpu_freq_tbl; f->acpuclk_khz != 0; f++) {
- if (f->acpuclk_khz > max_khz) {
- f->acpuclk_khz = 0;
- break;
- }
- }
+ for (f = acpu_freq_tbl; f->acpuclk_khz != 0; f++)
+ ;
f--;
pr_info("Max ACPU freq: %u KHz\n", f->acpuclk_khz);
diff --git a/arch/arm/mach-msm/board-8064-display.c b/arch/arm/mach-msm/board-8064-display.c
index 60bc26c..040e573 100644
--- a/arch/arm/mach-msm/board-8064-display.c
+++ b/arch/arm/mach-msm/board-8064-display.c
@@ -227,7 +227,7 @@
};
static int mdp_core_clk_rate_table[] = {
- 85330000,
+ 59080000,
128000000,
160000000,
200000000,
@@ -235,7 +235,7 @@
static struct msm_panel_common_pdata mdp_pdata = {
.gpio = MDP_VSYNC_GPIO,
- .mdp_core_clk_rate = 85330000,
+ .mdp_core_clk_rate = 59080000,
.mdp_core_clk_table = mdp_core_clk_rate_table,
.num_mdp_clk = ARRAY_SIZE(mdp_core_clk_rate_table),
.mdp_bus_scale_table = &mdp_bus_scale_pdata,
diff --git a/arch/arm/mach-msm/board-8064-gpiomux.c b/arch/arm/mach-msm/board-8064-gpiomux.c
index 48d1129..6ef1335a 100644
--- a/arch/arm/mach-msm/board-8064-gpiomux.c
+++ b/arch/arm/mach-msm/board-8064-gpiomux.c
@@ -55,7 +55,7 @@
/* Chip selects for EPM SPI clients */
static struct gpiomux_setting gpio_epm_spi_cs_config = {
- .func = GPIOMUX_FUNC_3,
+ .func = GPIOMUX_FUNC_6,
.drv = GPIOMUX_DRV_12MA,
.pull = GPIOMUX_PULL_UP,
};
@@ -507,6 +507,19 @@
.dir = GPIOMUX_OUT_LOW,
};
+static struct gpiomux_setting hsic_wakeup_act_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ .dir = GPIOMUX_IN,
+};
+
+static struct gpiomux_setting hsic_wakeup_sus_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ .dir = GPIOMUX_IN,
+};
static struct msm_gpiomux_config apq8064_hsic_configs[] = {
{
@@ -523,6 +536,13 @@
[GPIOMUX_SUSPENDED] = &hsic_sus_cfg,
},
},
+ {
+ .gpio = 47, /* wake up */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &hsic_wakeup_act_cfg,
+ [GPIOMUX_SUSPENDED] = &hsic_wakeup_sus_cfg,
+ },
+ },
};
#endif
@@ -1003,6 +1023,151 @@
},
};
+#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT
+static struct gpiomux_setting sdc2_clk_active_cfg = {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting sdc2_cmd_data_0_3_active_cfg = {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting sdc2_suspended_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct gpiomux_setting sdc2_data_1_suspended_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct msm_gpiomux_config apq8064_sdc2_configs[] __initdata = {
+ {
+ .gpio = 59,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc2_clk_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc2_suspended_cfg,
+ },
+ },
+ {
+ .gpio = 57,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc2_cmd_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc2_suspended_cfg,
+ },
+
+ },
+ {
+ .gpio = 62,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc2_cmd_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc2_suspended_cfg,
+ },
+ },
+ {
+ .gpio = 61,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc2_cmd_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc2_data_1_suspended_cfg,
+ },
+ },
+ {
+ .gpio = 60,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc2_cmd_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc2_suspended_cfg,
+ },
+ },
+ {
+ .gpio = 58,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc2_cmd_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc2_suspended_cfg,
+ },
+ },
+};
+#endif
+
+
+#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT
+static struct gpiomux_setting sdc4_clk_active_cfg = {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting sdc4_cmd_data_0_3_active_cfg = {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting sdc4_suspended_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct gpiomux_setting sdc4_data_1_suspended_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct msm_gpiomux_config apq8064_sdc4_configs[] __initdata = {
+ {
+ .gpio = 68,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc4_clk_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc4_suspended_cfg,
+ },
+ },
+ {
+ .gpio = 67,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc4_cmd_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc4_suspended_cfg,
+ },
+
+ },
+ {
+ .gpio = 66,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc4_cmd_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc4_suspended_cfg,
+ },
+ },
+ {
+ .gpio = 65,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc4_cmd_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc4_data_1_suspended_cfg,
+ },
+ },
+ {
+ .gpio = 64,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc4_cmd_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc4_suspended_cfg,
+ },
+ },
+ {
+ .gpio = 63,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc4_cmd_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc4_suspended_cfg,
+ },
+ },
+};
+#endif
+
void __init apq8064_init_gpiomux(void)
{
int rc;
@@ -1084,4 +1249,14 @@
if (machine_is_mpq8064_cdp())
msm_gpiomux_install(mpq8064_ir_configs,
ARRAY_SIZE(mpq8064_ir_configs));
+
+#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT
+ msm_gpiomux_install(apq8064_sdc2_configs,
+ ARRAY_SIZE(apq8064_sdc2_configs));
+#endif
+
+#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT
+ msm_gpiomux_install(apq8064_sdc4_configs,
+ ARRAY_SIZE(apq8064_sdc4_configs));
+#endif
}
diff --git a/arch/arm/mach-msm/board-8064-storage.c b/arch/arm/mach-msm/board-8064-storage.c
index 72126c8..96e54b6 100644
--- a/arch/arm/mach-msm/board-8064-storage.c
+++ b/arch/arm/mach-msm/board-8064-storage.c
@@ -188,13 +188,50 @@
},
};
+static struct msm_mmc_gpio sdc2_gpio[] = {
+ {59, "sdc2_clk"},
+ {57, "sdc2_cmd"},
+ {62, "sdc2_dat_0"},
+ {61, "sdc2_dat_1"},
+ {60, "sdc2_dat_2"},
+ {58, "sdc2_dat_3"},
+};
+
+static struct msm_mmc_gpio sdc4_gpio[] = {
+ {68, "sdc4_clk"},
+ {67, "sdc4_cmd"},
+ {66, "sdc4_dat_0"},
+ {65, "sdc4_dat_1"},
+ {64, "sdc4_dat_2"},
+ {63, "sdc4_dat_3"},
+};
+
+static struct msm_mmc_gpio_data mmc_gpio_data[MAX_SDCC_CONTROLLER] = {
+ [SDCC2] = {
+ .gpio = sdc2_gpio,
+ .size = ARRAY_SIZE(sdc2_gpio),
+ },
+ [SDCC4] = {
+ .gpio = sdc4_gpio,
+ .size = ARRAY_SIZE(sdc4_gpio),
+ }
+};
+
static struct msm_mmc_pin_data mmc_slot_pin_data[MAX_SDCC_CONTROLLER] = {
[SDCC1] = {
.pad_data = &mmc_pad_data[SDCC1],
},
+ [SDCC2] = {
+ .is_gpio = 1,
+ .gpio_data = &mmc_gpio_data[SDCC2],
+ },
[SDCC3] = {
.pad_data = &mmc_pad_data[SDCC3],
},
+ [SDCC4] = {
+ .is_gpio = 1,
+ .gpio_data = &mmc_gpio_data[SDCC4],
+ },
};
#define MSM_MPM_PIN_SDC1_DAT1 17
@@ -227,6 +264,26 @@
static struct mmc_platform_data *apq8064_sdc1_pdata;
#endif
+#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT
+static unsigned int sdc2_sup_clk_rates[] = {
+ 400000, 24000000, 48000000
+};
+
+static struct mmc_platform_data sdc2_data = {
+ .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29,
+ .mmc_bus_width = MMC_CAP_4_BIT_DATA,
+ .sup_clk_table = sdc2_sup_clk_rates,
+ .sup_clk_cnt = ARRAY_SIZE(sdc2_sup_clk_rates),
+ .pclk_src_dfab = 1,
+ .pin_data = &mmc_slot_pin_data[SDCC2],
+ .sdiowakeup_irq = MSM_GPIO_TO_INT(61),
+ .msm_bus_voting_data = &sps_to_ddr_bus_voting_data,
+};
+static struct mmc_platform_data *apq8064_sdc2_pdata = &sdc2_data;
+#else
+static struct mmc_platform_data *apq8064_sdc2_pdata;
+#endif
+
#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT
static unsigned int sdc3_sup_clk_rates[] = {
400000, 24000000, 48000000, 96000000, 192000000
@@ -258,6 +315,27 @@
static struct mmc_platform_data *apq8064_sdc3_pdata;
#endif
+
+#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT
+static unsigned int sdc4_sup_clk_rates[] = {
+ 400000, 24000000, 48000000
+};
+
+static struct mmc_platform_data sdc4_data = {
+ .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29,
+ .mmc_bus_width = MMC_CAP_4_BIT_DATA,
+ .sup_clk_table = sdc4_sup_clk_rates,
+ .sup_clk_cnt = ARRAY_SIZE(sdc4_sup_clk_rates),
+ .pclk_src_dfab = 1,
+ .pin_data = &mmc_slot_pin_data[SDCC4],
+ .sdiowakeup_irq = MSM_GPIO_TO_INT(65),
+ .msm_bus_voting_data = &sps_to_ddr_bus_voting_data,
+};
+static struct mmc_platform_data *apq8064_sdc4_pdata = &sdc4_data;
+#else
+static struct mmc_platform_data *apq8064_sdc4_pdata;
+#endif
+
void __init apq8064_init_mmc(void)
{
if ((machine_is_apq8064_rumi3()) || machine_is_apq8064_sim()) {
@@ -278,6 +356,9 @@
if (apq8064_sdc1_pdata)
apq8064_add_sdcc(1, apq8064_sdc1_pdata);
+ if (apq8064_sdc2_pdata)
+ apq8064_add_sdcc(2, apq8064_sdc2_pdata);
+
if (apq8064_sdc3_pdata) {
if (!machine_is_apq8064_cdp()) {
apq8064_sdc3_pdata->wpswitch_gpio = 0;
@@ -292,4 +373,7 @@
}
apq8064_add_sdcc(3, apq8064_sdc3_pdata);
}
+
+ if (apq8064_sdc4_pdata)
+ apq8064_add_sdcc(4, apq8064_sdc4_pdata);
}
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index dc65c8c..6a3b29c 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -91,6 +91,7 @@
#endif
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+#define HOLE_SIZE 0x20000
#define MSM_PMEM_KERNEL_EBI1_SIZE 0x65000
#ifdef CONFIG_MSM_IOMMU
#define MSM_ION_MM_SIZE 0x3800000
@@ -103,7 +104,7 @@
#define MSM_ION_QSECOM_SIZE 0x600000 /* (6MB) */
#define MSM_ION_HEAP_NUM 8
#endif
-#define MSM_ION_MM_FW_SIZE 0x200000 /* (2MB) */
+#define MSM_ION_MM_FW_SIZE (0x200000 - HOLE_SIZE) /* (2MB - 128KB) */
#define MSM_ION_MFC_SIZE SZ_8K
#define MSM_ION_AUDIO_SIZE MSM_PMEM_AUDIO_SIZE
#else
@@ -111,10 +112,11 @@
#define MSM_ION_HEAP_NUM 1
#endif
-#define APQ8064_FIXED_AREA_START 0xa0000000
+#define APQ8064_FIXED_AREA_START (0xa0000000 - (MSM_ION_MM_FW_SIZE + \
+ HOLE_SIZE))
#define MAX_FIXED_AREA_SIZE 0x10000000
-#define MSM_MM_FW_SIZE 0x200000
-#define APQ8064_FW_START (APQ8064_FIXED_AREA_START - MSM_MM_FW_SIZE)
+#define MSM_MM_FW_SIZE (0x200000 - HOLE_SIZE)
+#define APQ8064_FW_START APQ8064_FIXED_AREA_START
/* PCIe power enable pmic gpio */
#define PCIE_PWR_EN_PMIC_GPIO 13
@@ -505,19 +507,20 @@
return;
if (apq8064_fmem_pdata.size) {
- apq8064_fmem_pdata.reserved_size_low = fixed_low_size;
+ apq8064_fmem_pdata.reserved_size_low = fixed_low_size +
+ HOLE_SIZE;
apq8064_fmem_pdata.reserved_size_high = fixed_high_size;
}
/* Since the fixed area may be carved out of lowmem,
* make sure the length is a multiple of 1M.
*/
- fixed_size = (fixed_size + MSM_MM_FW_SIZE + SECTION_SIZE - 1)
+ fixed_size = (fixed_size + HOLE_SIZE + SECTION_SIZE - 1)
& SECTION_MASK;
apq8064_reserve_fixed_area(fixed_size);
fixed_low_start = APQ8064_FIXED_AREA_START;
- fixed_middle_start = fixed_low_start + fixed_low_size;
+ fixed_middle_start = fixed_low_start + fixed_low_size + HOLE_SIZE;
fixed_high_start = fixed_middle_start + fixed_middle_size;
for (i = 0; i < apq8064_ion_pdata.nr; ++i) {
@@ -525,11 +528,13 @@
if (heap->extra_data) {
int fixed_position = NOT_FIXED;
+ struct ion_cp_heap_pdata *pdata;
switch (heap->type) {
case ION_HEAP_TYPE_CP:
- fixed_position = ((struct ion_cp_heap_pdata *)
- heap->extra_data)->fixed_position;
+ pdata =
+ (struct ion_cp_heap_pdata *)heap->extra_data;
+ fixed_position = pdata->fixed_position;
break;
case ION_HEAP_TYPE_CARVEOUT:
fixed_position = ((struct ion_co_heap_pdata *)
@@ -545,6 +550,9 @@
break;
case FIXED_MIDDLE:
heap->base = fixed_middle_start;
+ pdata->secure_base = fixed_middle_start
+ - HOLE_SIZE;
+ pdata->secure_size = HOLE_SIZE + heap->size;
break;
case FIXED_HIGH:
heap->base = fixed_high_start;
@@ -562,6 +570,17 @@
apq8064_mdp_writeback(apq8064_reserve_table);
}
+static void __init reserve_cache_dump_memory(void)
+{
+#ifdef CONFIG_MSM_CACHE_DUMP
+ unsigned int total;
+
+ total = apq8064_cache_dump_pdata.l1_size +
+ apq8064_cache_dump_pdata.l2_size;
+ apq8064_reserve_table[MEMTYPE_EBI1].size += total;
+#endif
+}
+
static void __init apq8064_calculate_reserve_sizes(void)
{
size_pmem_devices();
@@ -569,6 +588,7 @@
reserve_ion_memory();
reserve_mdp_memory();
reserve_rtb_memory();
+ reserve_cache_dump_memory();
}
static struct reserve_info apq8064_reserve_info __initdata = {
@@ -1225,7 +1245,7 @@
/* T6 Object */
0, 0, 0, 0, 0, 0,
/* T38 Object */
- 14, 1, 0, 22, 2, 12, 0, 0, 0, 0,
+ 14, 2, 0, 24, 5, 12, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -1237,7 +1257,7 @@
/* T8 Object */
25, 0, 20, 20, 0, 0, 0, 0, 0, 0,
/* T9 Object */
- 131, 0, 0, 26, 42, 0, 32, 80, 2, 5,
+ 139, 0, 0, 26, 42, 0, 32, 80, 2, 5,
0, 5, 5, 0, 10, 30, 10, 10, 255, 2,
85, 5, 0, 5, 9, 5, 12, 35, 70, 40,
20, 5, 0, 0, 0,
@@ -1247,13 +1267,13 @@
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
/* T25 Object */
- 3, 0, 60, 115, 156, 99,
+ 1, 0, 60, 115, 156, 99,
/* T27 Object */
0, 0, 0, 0, 0, 0, 0,
/* T40 Object */
0, 0, 0, 0, 0,
/* T42 Object */
- 2, 0, 255, 0, 255, 0, 0, 0, 0, 0,
+ 0, 0, 255, 0, 255, 0, 0, 0, 0, 0,
/* T43 Object */
0, 0, 0, 0, 0, 0, 0, 64, 0, 8,
16,
@@ -1262,7 +1282,7 @@
/* T47 Object */
0, 0, 0, 0, 0, 0, 3, 64, 66, 0,
/* T48 Object */
- 31, 64, 64, 0, 0, 0, 0, 0, 0, 0,
+ 1, 64, 64, 0, 0, 0, 0, 0, 0, 0,
32, 40, 0, 10, 10, 0, 0, 100, 10, 90,
0, 0, 0, 0, 0, 0, 0, 10, 1, 10,
52, 10, 12, 0, 33, 0, 1, 0, 0, 0,
@@ -1756,62 +1776,56 @@
MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT,
MSM_RPMRS_LIMITS(ON, ACTIVE, MAX, ACTIVE),
true,
- 100, 650, 801, 200,
+ 1, 784, 180000, 100,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE,
MSM_RPMRS_LIMITS(ON, ACTIVE, MAX, ACTIVE),
true,
- 2000, 200, 576000, 2000,
+ 1300, 228, 1200000, 2000,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
MSM_RPMRS_LIMITS(ON, GDHS, MAX, ACTIVE),
false,
- 8500, 51, 1122000, 8500,
+ 2000, 138, 1208400, 3200,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
- MSM_RPMRS_LIMITS(ON, HSFS_OPEN, MAX, ACTIVE),
- false,
- 9000, 51, 1130300, 9000,
- },
- {
- MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
MSM_RPMRS_LIMITS(ON, HSFS_OPEN, ACTIVE, RET_HIGH),
false,
- 10000, 51, 1130300, 10000,
+ 6000, 119, 1850300, 9000,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
MSM_RPMRS_LIMITS(OFF, GDHS, MAX, ACTIVE),
false,
- 12000, 14, 2205900, 12000,
+ 9200, 68, 2839200, 16400,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, MAX, ACTIVE),
false,
- 18000, 12, 2364250, 18000,
+ 10300, 63, 3128000, 18200,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, ACTIVE, RET_HIGH),
false,
- 23500, 10, 2667000, 23500,
+ 18000, 10, 4602600, 27000,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, RET_HIGH, RET_LOW),
false,
- 29700, 5, 2867000, 30000,
+ 20000, 2, 5752000, 32000,
},
};
@@ -2174,9 +2188,11 @@
&apq_cpudai_slimbus_1_tx,
&apq_cpudai_slimbus_2_tx,
&apq_cpudai_slimbus_3_rx,
+ &apq_cpudai_slimbus_3_tx,
&apq8064_rpm_device,
&apq8064_rpm_log_device,
&apq8064_rpm_stat_device,
+ &apq_device_tz_log,
&msm_bus_8064_apps_fabric,
&msm_bus_8064_sys_fabric,
&msm_bus_8064_mm_fabric,
@@ -2203,6 +2219,7 @@
&msm8960_gemini_device,
&apq8064_iommu_domain_device,
&msm_tsens_device,
+ &apq8064_cache_dump_device,
};
static struct platform_device *sim_devices[] __initdata = {
diff --git a/arch/arm/mach-msm/board-8064.h b/arch/arm/mach-msm/board-8064.h
index 67e0e6f..c992865 100644
--- a/arch/arm/mach-msm/board-8064.h
+++ b/arch/arm/mach-msm/board-8064.h
@@ -20,6 +20,7 @@
#include <mach/irqs.h>
#include <mach/rpm-regulator.h>
#include <mach/msm_rtb.h>
+#include <mach/msm_cache_dump.h>
/* Macros assume PMIC GPIOs and MPPs start at 1 */
#define PM8921_GPIO_BASE NR_GPIO_IRQS
@@ -143,4 +144,5 @@
};
extern struct msm_rtb_platform_data apq8064_rtb_pdata;
+extern struct msm_cache_dump_platform_data apq8064_cache_dump_pdata;
#endif
diff --git a/arch/arm/mach-msm/board-8930-gpiomux.c b/arch/arm/mach-msm/board-8930-gpiomux.c
index 936a798..3013070 100644
--- a/arch/arm/mach-msm/board-8930-gpiomux.c
+++ b/arch/arm/mach-msm/board-8930-gpiomux.c
@@ -624,6 +624,22 @@
},
};
+static struct gpiomux_setting sd_det_line = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct msm_gpiomux_config msm8930_sd_det_config[] __initdata = {
+ {
+ .gpio = 94, /* SD Card Detect Line */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &sd_det_line,
+ [GPIOMUX_ACTIVE] = &sd_det_line,
+ },
+ },
+};
+
int __init msm8930_init_gpiomux(void)
{
int rc = msm_gpiomux_init(NR_GPIO_IRQS);
@@ -687,5 +703,9 @@
msm_gpiomux_install(msm8960_mdp_vsync_configs,
ARRAY_SIZE(msm8960_mdp_vsync_configs));
+
+ msm_gpiomux_install(msm8930_sd_det_config,
+ ARRAY_SIZE(msm8930_sd_det_config));
+
return 0;
}
diff --git a/arch/arm/mach-msm/board-8930-storage.c b/arch/arm/mach-msm/board-8930-storage.c
index 6dd7add..89c54fe 100644
--- a/arch/arm/mach-msm/board-8930-storage.c
+++ b/arch/arm/mach-msm/board-8930-storage.c
@@ -53,7 +53,19 @@
.name = "sdc_vdd",
.high_vol_level = 2950000,
.low_vol_level = 2950000,
+ /*
+ * Normally this is not an always ON regulator. On this
+ * platform, unfortunately the sd detect line is connected
+ * to this via esd circuit and so turn this off/on while card
+ * is not present causes the sd detect line to toggle
+ * continuously. This is expected to be fixed in the newer
+ * hardware revisions - maybe once that is done, this can be
+ * reverted.
+ */
+ .always_on = 1,
+ .lpm_sup = 1,
.hpm_uA = 800000, /* 800mA */
+ .lpm_uA = 9000,
}
};
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index 7a43518..19cc762 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -1790,6 +1790,7 @@
.gpio = GPIO_VOLUME_UP,
.wakeup = 1,
.active_low = 1,
+ .debounce_interval = 15,
},
{
.code = KEY_VOLUMEDOWN,
@@ -1798,6 +1799,7 @@
.gpio = GPIO_VOLUME_DOWN,
.wakeup = 1,
.active_low = 1,
+ .debounce_interval = 15,
},
{
.code = KEY_CAMERA_FOCUS,
@@ -1806,6 +1808,7 @@
.gpio = GPIO_CAMERA_FOCUS,
.wakeup = 1,
.active_low = 1,
+ .debounce_interval = 15,
},
{
.code = KEY_CAMERA_SNAPSHOT,
@@ -1814,6 +1817,7 @@
.gpio = GPIO_CAMERA_SNAPSHOT,
.wakeup = 1,
.active_low = 1,
+ .debounce_interval = 15,
},
};
@@ -2126,62 +2130,56 @@
MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT,
MSM_RPMRS_LIMITS(ON, ACTIVE, MAX, ACTIVE),
true,
- 100, 650, 801, 200,
+ 1, 784, 180000, 100,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE,
MSM_RPMRS_LIMITS(ON, ACTIVE, MAX, ACTIVE),
true,
- 2000, 200, 576000, 2000,
+ 1300, 228, 1200000, 2000,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
MSM_RPMRS_LIMITS(ON, GDHS, MAX, ACTIVE),
false,
- 8500, 51, 1122000, 8500,
+ 2000, 138, 1208400, 3200,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
- MSM_RPMRS_LIMITS(ON, HSFS_OPEN, MAX, ACTIVE),
- false,
- 9000, 51, 1130300, 9000,
- },
- {
- MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
MSM_RPMRS_LIMITS(ON, HSFS_OPEN, ACTIVE, RET_HIGH),
false,
- 10000, 51, 1130300, 10000,
+ 6000, 119, 1850300, 9000,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
MSM_RPMRS_LIMITS(OFF, GDHS, MAX, ACTIVE),
false,
- 12000, 14, 2205900, 12000,
+ 9200, 68, 2839200, 16400,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, MAX, ACTIVE),
false,
- 18000, 12, 2364250, 18000,
+ 10300, 63, 3128000, 18200,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, ACTIVE, RET_HIGH),
false,
- 23500, 10, 2667000, 23500,
+ 18000, 10, 4602600, 27000,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, RET_HIGH, RET_LOW),
false,
- 29700, 5, 2867000, 30000,
+ 20000, 2, 5752000, 32000,
},
};
diff --git a/arch/arm/mach-msm/board-8960-regulator.c b/arch/arm/mach-msm/board-8960-regulator.c
index edb6f03..8291cc7 100644
--- a/arch/arm/mach-msm/board-8960-regulator.c
+++ b/arch/arm/mach-msm/board-8960-regulator.c
@@ -543,7 +543,7 @@
RPM_LDO(L22, 0, 1, 0, 2750000, 2750000, NULL, 0, 0),
RPM_LDO(L23, 1, 1, 1, 1800000, 1800000, "8921_s8", 10000, 10000),
RPM_LDO(L24, 0, 1, 1, 750000, 1150000, "8921_s1", 10000, 10000),
- RPM_LDO(L25, 1, 1, 0, 1225000, 1225000, "8921_s1", 10000, 10000),
+ RPM_LDO(L25, 1, 1, 0, 1250000, 1250000, "8921_s1", 10000, 10000),
/* ID a_on pd ss supply */
RPM_VS(LVS1, 0, 1, 0, "8921_s4"),
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 629e48e..79af1a7 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -895,13 +895,13 @@
{
.name = "VDDD_CDC_D",
.min_uV = 1225000,
- .max_uV = 1225000,
+ .max_uV = 1250000,
.optimum_uA = WCD9XXX_VDDD_CDC_D_CUR_MAX,
},
{
.name = "CDC_VDDA_A_1P2V",
.min_uV = 1225000,
- .max_uV = 1225000,
+ .max_uV = 1250000,
.optimum_uA = WCD9XXX_VDDD_CDC_A_CUR_MAX,
},
},
@@ -962,13 +962,13 @@
{
.name = "VDDD_CDC_D",
.min_uV = 1225000,
- .max_uV = 1225000,
+ .max_uV = 1250000,
.optimum_uA = WCD9XXX_VDDD_CDC_D_CUR_MAX,
},
{
.name = "CDC_VDDA_A_1P2V",
.min_uV = 1225000,
- .max_uV = 1225000,
+ .max_uV = 1250000,
.optimum_uA = WCD9XXX_VDDD_CDC_A_CUR_MAX,
},
},
@@ -1281,6 +1281,7 @@
static struct mdm_platform_data sglte_platform_data = {
.mdm_version = "4.0",
.ramdump_delay_ms = 1000,
+ .soft_reset_inverted = 1,
.peripheral_platform_device = NULL,
};
@@ -1956,7 +1957,7 @@
/* T6 Object */
0, 0, 0, 0, 0, 0,
/* T38 Object */
- 12, 2, 0, 17, 1, 12, 0, 0, 0, 0,
+ 12, 3, 0, 24, 5, 12, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -1968,7 +1969,7 @@
/* T8 Object */
25, 0, 20, 20, 0, 0, 20, 50, 0, 0,
/* T9 Object */
- 131, 0, 0, 26, 42, 0, 32, 80, 2, 5,
+ 139, 0, 0, 26, 42, 0, 32, 80, 2, 5,
0, 5, 5, 0, 10, 30, 10, 10, 255, 2,
85, 5, 10, 10, 10, 10, 135, 55, 70, 40,
10, 5, 0, 0, 0,
@@ -1978,13 +1979,13 @@
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
/* T25 Object */
- 3, 0, 60, 115, 156, 99,
+ 1, 0, 60, 115, 156, 99,
/* T27 Object */
0, 0, 0, 0, 0, 0, 0,
/* T40 Object */
0, 0, 0, 0, 0,
/* T42 Object */
- 2, 0, 255, 0, 255, 0, 0, 0, 0, 0,
+ 0, 0, 255, 0, 255, 0, 0, 0, 0, 0,
/* T43 Object */
0, 0, 0, 0, 0, 0, 0, 64, 0, 8,
16,
@@ -1993,7 +1994,7 @@
/* T47 Object */
0, 0, 0, 0, 0, 0, 3, 64, 66, 0,
/* T48 Object */
- 31, 64, 64, 0, 0, 0, 0, 0, 0, 0,
+ 1, 64, 64, 0, 0, 0, 0, 0, 0, 0,
48, 40, 0, 10, 10, 0, 0, 100, 10, 80,
0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
52, 0, 12, 0, 17, 0, 1, 0, 0, 0,
@@ -2646,62 +2647,56 @@
MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT,
MSM_RPMRS_LIMITS(ON, ACTIVE, MAX, ACTIVE),
true,
- 100, 650, 801, 200,
+ 1, 784, 180000, 100,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE,
MSM_RPMRS_LIMITS(ON, ACTIVE, MAX, ACTIVE),
true,
- 2000, 200, 576000, 2000,
+ 1300, 228, 1200000, 2000,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
MSM_RPMRS_LIMITS(ON, GDHS, MAX, ACTIVE),
false,
- 8500, 51, 1122000, 8500,
+ 2000, 138, 1208400, 3200,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
- MSM_RPMRS_LIMITS(ON, HSFS_OPEN, MAX, ACTIVE),
- false,
- 9000, 51, 1130300, 9000,
- },
- {
- MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
MSM_RPMRS_LIMITS(ON, HSFS_OPEN, ACTIVE, RET_HIGH),
false,
- 10000, 51, 1130300, 10000,
+ 6000, 119, 1850300, 9000,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
MSM_RPMRS_LIMITS(OFF, GDHS, MAX, ACTIVE),
false,
- 12000, 14, 2205900, 12000,
+ 9200, 68, 2839200, 16400,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, MAX, ACTIVE),
false,
- 18000, 12, 2364250, 18000,
+ 10300, 63, 3128000, 18200,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, ACTIVE, RET_HIGH),
false,
- 23500, 10, 2667000, 23500,
+ 18000, 10, 4602600, 27000,
},
{
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, RET_HIGH, RET_LOW),
false,
- 29700, 5, 2867000, 30000,
+ 20000, 2, 5752000, 32000,
},
};
diff --git a/arch/arm/mach-msm/board-9625-gpiomux.c b/arch/arm/mach-msm/board-9625-gpiomux.c
new file mode 100644
index 0000000..e28c734
--- /dev/null
+++ b/arch/arm/mach-msm/board-9625-gpiomux.c
@@ -0,0 +1,53 @@
+/* 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/init.h>
+#include <linux/ioport.h>
+#include <mach/board.h>
+#include <mach/gpio.h>
+#include <mach/gpiomux.h>
+
+static struct gpiomux_setting gpio_uart_config = {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_16MA,
+ .pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_OUT_HIGH,
+};
+
+static struct msm_gpiomux_config msm_blsp_configs[] __initdata = {
+ {
+ .gpio = 45, /* BLSP1 UART TX */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_uart_config,
+ },
+ },
+ {
+ .gpio = 46, /* BLSP1 UART RX */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_uart_config,
+ },
+ },
+};
+
+void __init msm9625_init_gpiomux(void)
+{
+ int rc;
+
+ rc = msm_gpiomux_init(NR_GPIO_IRQS);
+ if (rc) {
+ pr_err(KERN_ERR "msm9625_init_gpiomux failed %d\n", rc);
+ return;
+ }
+
+ msm_gpiomux_install(msm_blsp_configs, ARRAY_SIZE(msm_blsp_configs));
+}
diff --git a/arch/arm/mach-msm/board-9625.c b/arch/arm/mach-msm/board-9625.c
new file mode 100644
index 0000000..60dfe3c
--- /dev/null
+++ b/arch/arm/mach-msm/board-9625.c
@@ -0,0 +1,104 @@
+/* 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/platform_device.h>
+#include <linux/io.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_irq.h>
+#include <linux/memory.h>
+#include <asm/mach/map.h>
+#include <asm/hardware/gic.h>
+#include <asm/arch_timer.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <mach/socinfo.h>
+#include <mach/board.h>
+#include <mach/gpio.h>
+#include "clock.h"
+
+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("phy_clk", NULL, "msm_otg", OFF),
+ CLK_DUMMY("core_clk", NULL, "msm_otg", OFF),
+ CLK_DUMMY("alt_core_clk", NULL, "msm_otg", OFF),
+ CLK_DUMMY("iface_clk", NULL, "msm_otg", OFF),
+ CLK_DUMMY("xo", NULL, "msm_otg", OFF),
+ CLK_DUMMY("dfab_clk", DFAB_CLK, NULL, 0),
+ CLK_DUMMY("dma_bam_pclk", DMA_BAM_P_CLK, NULL, 0),
+ CLK_DUMMY("mem_clk", NULL, NULL, 0),
+ CLK_DUMMY("core_clk", NULL, "spi_qsd.1", OFF),
+ CLK_DUMMY("iface_clk", NULL, "spi_qsd.1", OFF),
+ CLK_DUMMY("core_clk", NULL, "f9966000.i2c", 0),
+ CLK_DUMMY("iface_clk", NULL, "f9966000.i2c", 0),
+ CLK_DUMMY("core_clk", NULL, "fe12f000.slim", 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 const char *msm9625_dt_match[] __initconst = {
+ "qcom,msm9625",
+ NULL
+};
+
+static struct of_dev_auxdata msm9625_auxdata_lookup[] __initdata = {
+ OF_DEV_AUXDATA("qcom,msm-lsuart-v14", 0xF991F000, \
+ "msm_serial_hsl.0", NULL),
+ {}
+};
+
+void __init msm9625_init_irq(void)
+{
+ of_irq_init(irq_match);
+}
+
+static void __init msm_dt_timer_init(void)
+{
+ arch_timer_of_register();
+}
+
+static struct sys_timer msm_dt_timer = {
+ .init = msm_dt_timer_init
+};
+
+void __init msm9625_init(void)
+{
+ if (socinfo_init() < 0)
+ pr_err("%s: socinfo_init() failed\n", __func__);
+ msm_clock_init(&msm_dummy_clock_init_data);
+ of_platform_populate(NULL, of_default_bus_match_table,
+ msm9625_auxdata_lookup, NULL);
+}
+
+DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)")
+ .map_io = msm_map_msm9625_io,
+ .init_irq = msm9625_init_irq,
+ .init_machine = msm9625_init,
+ .handle_irq = gic_handle_irq,
+ .timer = &msm_dt_timer,
+ .dt_compat = msm9625_dt_match,
+ .nr_irqs = -1,
+MACHINE_END
diff --git a/arch/arm/mach-msm/board-copper-gpiomux.c b/arch/arm/mach-msm/board-copper-gpiomux.c
index 0ea33c7..caba698 100644
--- a/arch/arm/mach-msm/board-copper-gpiomux.c
+++ b/arch/arm/mach-msm/board-copper-gpiomux.c
@@ -54,6 +54,12 @@
},
};
#endif
+static struct gpiomux_setting gpio_i2c_config = {
+ .func = GPIOMUX_FUNC_3,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
static struct msm_gpiomux_config msm_blsp_configs[] __initdata = {
#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE)
@@ -83,6 +89,18 @@
},
#endif
{
+ .gpio = 83, /* BLSP11 QUP I2C_DAT */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_i2c_config,
+ },
+ },
+ {
+ .gpio = 84, /* BLSP11 QUP I2C_CLK */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_i2c_config,
+ },
+ },
+ {
.gpio = 45, /* BLSP8 UART TX */
.settings = {
[GPIOMUX_SUSPENDED] = &gpio_uart_config,
diff --git a/arch/arm/mach-msm/board-copper.c b/arch/arm/mach-msm/board-copper.c
index 90cfee4..53de3b2 100644
--- a/arch/arm/mach-msm/board-copper.c
+++ b/arch/arm/mach-msm/board-copper.c
@@ -408,23 +408,6 @@
.resource = copper_tzlog_resources,
};
-#ifdef CONFIG_HW_RANDOM_MSM
-/* PRNG device */
-#define MSM_PRNG_PHYS 0xF9BFF000
-static struct resource rng_resources = {
- .flags = IORESOURCE_MEM,
- .start = MSM_PRNG_PHYS,
- .end = MSM_PRNG_PHYS + SZ_512 - 1,
-};
-
-struct platform_device msm8974_device_rng = {
- .name = "msm_rng",
- .id = 0,
- .num_resources = 1,
- .resource = &rng_resources,
-};
-#endif
-
void __init msm_copper_add_devices(void)
{
@@ -436,9 +419,6 @@
platform_add_devices(msm_copper_stub_regulator_devices,
msm_copper_stub_regulator_devices_len);
platform_device_register(&copper_device_tz_log);
-#ifdef CONFIG_HW_RANDOM_MSM
- platform_device_register(&msm8974_device_rng);
-#endif
}
/*
@@ -515,8 +495,12 @@
"msm_sdcc.4", NULL),
OF_DEV_AUXDATA("qcom,pil-q6v5-lpass", 0xFE200000, \
"pil-q6v5-lpass", NULL),
+ OF_DEV_AUXDATA("qcom,pil-q6v5-mss", 0xFC880000, "pil-q6v5-mss", NULL),
+ OF_DEV_AUXDATA("qcom,pil-mba", 0xFC820000, "pil-mba", NULL),
OF_DEV_AUXDATA("qcom,pil-pronto", 0xFB21B000, \
"pil_pronto", NULL),
+ OF_DEV_AUXDATA("qcom,msm-rng", 0xF9BFF000, \
+ "msm_rng", NULL),
{}
};
diff --git a/arch/arm/mach-msm/board-msm7627a-bt.c b/arch/arm/mach-msm/board-msm7627a-bt.c
index 75d5f15..4567c76 100644
--- a/arch/arm/mach-msm/board-msm7627a-bt.c
+++ b/arch/arm/mach-msm/board-msm7627a-bt.c
@@ -627,13 +627,16 @@
for (i = 0; i < ARRAY_SIZE(bt_vregs); i++) {
if (IS_ERR_OR_NULL(bt_vregs[i].reg)) {
- rc = bt_vregs[i].reg ?
- PTR_ERR(bt_vregs[i].reg) :
- -ENODEV;
- dev_err(&msm_bt_power_device.dev,
- "%s: invalid regulator handle for %s: %d\n",
+ bt_vregs[i].reg =
+ regulator_get(&msm_bt_power_device.dev,
+ bt_vregs[i].name);
+ if (IS_ERR(bt_vregs[i].reg)) {
+ rc = PTR_ERR(bt_vregs[i].reg);
+ dev_err(&msm_bt_power_device.dev,
+ "%s: could not get regulator %s: %d\n",
__func__, bt_vregs[i].name, rc);
- goto reg_disable;
+ goto reg_disable;
+ }
}
rc = on ? regulator_set_voltage(bt_vregs[i].reg,
@@ -687,6 +690,7 @@
i--;
regulator_disable(bt_vregs[i].reg);
regulator_put(bt_vregs[i].reg);
+ bt_vregs[i].reg = NULL;
}
}
return rc;
diff --git a/arch/arm/mach-msm/board-msm7x27a.c b/arch/arm/mach-msm/board-msm7x27a.c
index 9ec618d..951fadc 100644
--- a/arch/arm/mach-msm/board-msm7x27a.c
+++ b/arch/arm/mach-msm/board-msm7x27a.c
@@ -635,8 +635,9 @@
static u32 msm_calculate_batt_capacity(u32 current_voltage);
static struct msm_psy_batt_pdata msm_psy_batt_data = {
- .voltage_min_design = 2800,
- .voltage_max_design = 4300,
+ .voltage_min_design = 3200,
+ .voltage_max_design = 4200,
+ .voltage_fail_safe = 3340,
.avail_chg_sources = AC_CHG | USB_CHG ,
.batt_technology = POWER_SUPPLY_TECHNOLOGY_LION,
.calculate_capacity = &msm_calculate_batt_capacity,
diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c
index 7fa4968..4ec9743 100644
--- a/arch/arm/mach-msm/board-msm7x30.c
+++ b/arch/arm/mach-msm/board-msm7x30.c
@@ -95,15 +95,31 @@
#define MSM_PMEM_SF_SIZE 0x1700000
#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER
-#define MSM_FB_SIZE 0x780000
+#define MSM_FB_PRIM_BUF_SIZE (864 * 480 * 4 * 3) /* 4bpp * 3 Pages */
#else
-#define MSM_FB_SIZE 0x500000
+#define MSM_FB_PRIM_BUF_SIZE (864 * 480 * 4 * 2) /* 4bpp * 2 Pages */
#endif
/*
* Reserve space for double buffered full screen
* res V4L2 video overlay - i.e. 1280x720x1.5x2
*/
#define MSM_V4L2_VIDEO_OVERLAY_BUF_SIZE 2764800
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL
+#define MSM_FB_EXT_BUF_SIZE (1280 * 720 * 2 * 1) /* 2 bpp x 1 page */
+#else
+#define MSM_FB_EXT_BUF_SIZE 0
+#endif
+
+#ifdef CONFIG_FB_MSM_OVERLAY0_WRITEBACK
+/* width x height x 3 bpp x 2 frame buffer */
+#define MSM_FB_OVERLAY0_WRITEBACK_SIZE roundup((864 * 480 * 3 * 2), 4096)
+#else
+#define MSM_FB_OVERLAY0_WRITEBACK_SIZE 0
+#endif
+
+#define MSM_FB_SIZE roundup(MSM_FB_PRIM_BUF_SIZE + MSM_FB_EXT_BUF_SIZE, 4096)
+
#define MSM_PMEM_ADSP_SIZE 0x1E00000
#define MSM_FLUID_PMEM_ADSP_SIZE 0x2800000
#define PMEM_KERNEL_EBI0_SIZE 0x600000
@@ -4533,6 +4549,7 @@
.mdp_core_clk_table = mdp_core_clk_rate_table,
.num_mdp_clk = ARRAY_SIZE(mdp_core_clk_rate_table),
.mdp_rev = MDP_REV_40,
+ .mem_hid = MEMTYPE_EBI0,
};
static int lcd_panel_spi_gpio_num[] = {
@@ -7095,7 +7112,7 @@
}
early_param("pmem_sf_size", pmem_sf_size_setup);
-static unsigned fb_size = MSM_FB_SIZE;
+static unsigned fb_size;
static int __init fb_size_setup(char *p)
{
fb_size = memparse(p, NULL);
@@ -7176,10 +7193,17 @@
#endif
}
+static void __init reserve_mdp_memory(void)
+{
+ mdp_pdata.ov0_wb_size = MSM_FB_OVERLAY0_WRITEBACK_SIZE;
+ msm7x30_reserve_table[mdp_pdata.mem_hid].size += mdp_pdata.ov0_wb_size;
+}
+
static void __init msm7x30_calculate_reserve_sizes(void)
{
size_pmem_devices();
reserve_pmem_memory();
+ reserve_mdp_memory();
}
static int msm7x30_paddr_to_memtype(unsigned int paddr)
diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c
index b74b285..b7b2bf5 100644
--- a/arch/arm/mach-msm/board-qrd7627a.c
+++ b/arch/arm/mach-msm/board-qrd7627a.c
@@ -582,8 +582,9 @@
static u32 msm_calculate_batt_capacity(u32 current_voltage);
static struct msm_psy_batt_pdata msm_psy_batt_data = {
- .voltage_min_design = 2800,
- .voltage_max_design = 4300,
+ .voltage_min_design = 3200,
+ .voltage_max_design = 4200,
+ .voltage_fail_safe = 3340,
.avail_chg_sources = AC_CHG | USB_CHG ,
.batt_technology = POWER_SUPPLY_TECHNOLOGY_LION,
.calculate_capacity = &msm_calculate_batt_capacity,
diff --git a/arch/arm/mach-msm/clock-7x30.c b/arch/arm/mach-msm/clock-7x30.c
index fcd6386..cc48fe7 100644
--- a/arch/arm/mach-msm/clock-7x30.c
+++ b/arch/arm/mach-msm/clock-7x30.c
@@ -221,9 +221,15 @@
pcom_xo_enable(PCOM_XO_TCXO, PCOM_XO_DISABLE);
}
+static enum handoff xo_clk_handoff(struct clk *clk)
+{
+ return HANDOFF_ENABLED_CLK;
+}
+
static struct clk_ops clk_ops_tcxo = {
.enable = tcxo_clk_enable,
.disable = tcxo_clk_disable,
+ .handoff = xo_clk_handoff,
.is_local = pcom_is_local,
};
@@ -250,6 +256,7 @@
static struct clk_ops clk_ops_lpxo = {
.enable = lpxo_clk_enable,
.disable = lpxo_clk_disable,
+ .handoff = xo_clk_handoff,
.is_local = pcom_is_local,
};
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index 3ee59b1..7b4ca29 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -770,6 +770,8 @@
.b = {
.ctl_reg = MAXI_EN5_REG,
.en_mask = BIT(12),
+ .hwcg_reg = MAXI_EN5_REG,
+ .hwcg_mask = BIT(11),
.reset_reg = SW_RESET_AXI_REG,
.reset_mask = BIT(16),
.halt_reg = DBG_BUS_VEC_J_REG,
@@ -787,6 +789,8 @@
.b = {
.ctl_reg = MAXI_EN5_REG,
.en_mask = BIT(25),
+ .hwcg_reg = MAXI_EN5_REG,
+ .hwcg_mask = BIT(24),
.reset_reg = SW_RESET_AXI_REG,
.reset_mask = BIT(17),
.halt_reg = DBG_BUS_VEC_J_REG,
@@ -1176,6 +1180,8 @@
.b = {
.ctl_reg = AHB_EN3_REG,
.en_mask = BIT(1),
+ .hwcg_reg = AHB_EN3_REG,
+ .hwcg_mask = BIT(0),
.reset_reg = SW_RESET_AHB2_REG,
.reset_mask = BIT(2),
.halt_reg = DBG_BUS_VEC_J_REG,
@@ -5991,7 +5997,7 @@
* the clock is halted. The sleep and wake-up delays are set to safe
* values.
*/
- if (cpu_is_msm8960()) {
+ if (cpu_is_msm8960() || cpu_is_apq8064()) {
rmwreg(0x44000000, AHB_EN_REG, 0x6C000103);
writel_relaxed(0x3C7097F9, AHB_EN2_REG);
} else {
@@ -5999,7 +6005,7 @@
writel_relaxed(0x000007F9, AHB_EN2_REG);
}
if (cpu_is_apq8064())
- rmwreg(0x00000000, AHB_EN3_REG, 0x00000001);
+ rmwreg(0x00000001, AHB_EN3_REG, 0x00000001);
/* Deassert all locally-owned MM AHB resets. */
rmwreg(0, SW_RESET_AHB_REG, 0xFFF7DFFF);
@@ -6008,8 +6014,9 @@
/* Initialize MM AXI registers: Enable HW gating for all clocks that
* support it. Also set FORCE_CORE_ON bits, and any sleep and wake-up
* delays to safe values. */
- if (cpu_is_msm8960() &&
- SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 3) {
+ if ((cpu_is_msm8960() &&
+ SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 3) ||
+ cpu_is_apq8064()) {
rmwreg(0x0003AFF9, MAXI_EN_REG, 0x0803FFFF);
rmwreg(0x3A27FCFF, MAXI_EN2_REG, 0x3A3FFFFF);
rmwreg(0x0027FCFF, MAXI_EN4_REG, 0x017FFFFF);
@@ -6020,10 +6027,10 @@
}
rmwreg(0x0027FCFF, MAXI_EN3_REG, 0x003FFFFF);
if (cpu_is_apq8064())
- rmwreg(0x009FE4FF, MAXI_EN5_REG, 0x01FFEFFF);
+ rmwreg(0x019FECFF, MAXI_EN5_REG, 0x01FFEFFF);
if (cpu_is_msm8930())
rmwreg(0x000004FF, MAXI_EN5_REG, 0x00000FFF);
- if (cpu_is_msm8960())
+ if (cpu_is_msm8960() || cpu_is_apq8064())
rmwreg(0x00003C38, SAXI_EN_REG, 0x00003FFF);
else
rmwreg(0x000003C7, SAXI_EN_REG, 0x00003FFF);
@@ -6100,6 +6107,9 @@
if (cpu_is_msm8960() || cpu_is_apq8064())
rmwreg(0x2, DSI2_BYTE_NS_REG, 0x7);
+ /* Source the dsi1_esc_clk from the DSI1 PHY PLLs */
+ rmwreg(0x1, DSI1_ESC_NS_REG, 0x7);
+
/*
* Source the sata_phy_ref_clk from PXO and set predivider of
* sata_pmalive_clk to 1.
diff --git a/arch/arm/mach-msm/clock-copper.c b/arch/arm/mach-msm/clock-copper.c
index 7812321..c33d486 100644
--- a/arch/arm/mach-msm/clock-copper.c
+++ b/arch/arm/mach-msm/clock-copper.c
@@ -598,9 +598,16 @@
return;
}
+static enum handoff cxo_clk_handoff(struct clk *clk)
+{
+ /* TODO: Remove from here once the rpm xo clock is ready. */
+ return HANDOFF_ENABLED_CLK;
+}
+
static struct clk_ops clk_ops_cxo = {
.enable = cxo_clk_enable,
.disable = cxo_clk_disable,
+ .handoff = cxo_clk_handoff,
};
static struct fixed_clk cxo_clk_src = {
@@ -1391,7 +1398,6 @@
.cbcr_reg = BAM_DMA_AHB_CBCR,
.vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
.en_mask = BIT(12),
- .bcr_reg = BAM_DMA_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_bam_dma_ahb_clk",
@@ -1404,7 +1410,6 @@
.cbcr_reg = BLSP1_AHB_CBCR,
.vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
.en_mask = BIT(17),
- .bcr_reg = BLSP1_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp1_ahb_clk",
@@ -1417,7 +1422,6 @@
.cbcr_reg = BLSP1_QUP1_I2C_APPS_CBCR,
.parent = &cxo_clk_src.c,
.has_sibling = 1,
- .bcr_reg = BLSP1_QUP1_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp1_qup1_i2c_apps_clk",
@@ -1429,7 +1433,6 @@
static struct branch_clk gcc_blsp1_qup1_spi_apps_clk = {
.cbcr_reg = BLSP1_QUP1_SPI_APPS_CBCR,
.parent = &blsp1_qup1_spi_apps_clk_src.c,
- .bcr_reg = BLSP1_QUP1_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp1_qup1_spi_apps_clk",
@@ -1442,7 +1445,6 @@
.cbcr_reg = BLSP1_QUP2_I2C_APPS_CBCR,
.parent = &cxo_clk_src.c,
.has_sibling = 1,
- .bcr_reg = BLSP1_QUP2_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp1_qup2_i2c_apps_clk",
@@ -1454,7 +1456,6 @@
static struct branch_clk gcc_blsp1_qup2_spi_apps_clk = {
.cbcr_reg = BLSP1_QUP2_SPI_APPS_CBCR,
.parent = &blsp1_qup2_spi_apps_clk_src.c,
- .bcr_reg = BLSP1_QUP2_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp1_qup2_spi_apps_clk",
@@ -1467,7 +1468,6 @@
.cbcr_reg = BLSP1_QUP3_I2C_APPS_CBCR,
.parent = &cxo_clk_src.c,
.has_sibling = 1,
- .bcr_reg = BLSP1_QUP3_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp1_qup3_i2c_apps_clk",
@@ -1479,7 +1479,6 @@
static struct branch_clk gcc_blsp1_qup3_spi_apps_clk = {
.cbcr_reg = BLSP1_QUP3_SPI_APPS_CBCR,
.parent = &blsp1_qup3_spi_apps_clk_src.c,
- .bcr_reg = BLSP1_QUP3_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp1_qup3_spi_apps_clk",
@@ -1492,7 +1491,6 @@
.cbcr_reg = BLSP1_QUP4_I2C_APPS_CBCR,
.parent = &cxo_clk_src.c,
.has_sibling = 1,
- .bcr_reg = BLSP1_QUP4_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp1_qup4_i2c_apps_clk",
@@ -1504,7 +1502,6 @@
static struct branch_clk gcc_blsp1_qup4_spi_apps_clk = {
.cbcr_reg = BLSP1_QUP4_SPI_APPS_CBCR,
.parent = &blsp1_qup4_spi_apps_clk_src.c,
- .bcr_reg = BLSP1_QUP4_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp1_qup4_spi_apps_clk",
@@ -1517,7 +1514,6 @@
.cbcr_reg = BLSP1_QUP5_I2C_APPS_CBCR,
.parent = &cxo_clk_src.c,
.has_sibling = 1,
- .bcr_reg = BLSP1_QUP5_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp1_qup5_i2c_apps_clk",
@@ -1529,7 +1525,6 @@
static struct branch_clk gcc_blsp1_qup5_spi_apps_clk = {
.cbcr_reg = BLSP1_QUP5_SPI_APPS_CBCR,
.parent = &blsp1_qup5_spi_apps_clk_src.c,
- .bcr_reg = BLSP1_QUP5_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp1_qup5_spi_apps_clk",
@@ -1542,7 +1537,6 @@
.cbcr_reg = BLSP1_QUP6_I2C_APPS_CBCR,
.parent = &cxo_clk_src.c,
.has_sibling = 1,
- .bcr_reg = BLSP1_QUP6_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp1_qup6_i2c_apps_clk",
@@ -1554,7 +1548,6 @@
static struct branch_clk gcc_blsp1_qup6_spi_apps_clk = {
.cbcr_reg = BLSP1_QUP6_SPI_APPS_CBCR,
.parent = &blsp1_qup6_spi_apps_clk_src.c,
- .bcr_reg = BLSP1_QUP6_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp1_qup6_spi_apps_clk",
@@ -1566,7 +1559,6 @@
static struct branch_clk gcc_blsp1_uart1_apps_clk = {
.cbcr_reg = BLSP1_UART1_APPS_CBCR,
.parent = &blsp1_uart1_apps_clk_src.c,
- .bcr_reg = BLSP1_UART1_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp1_uart1_apps_clk",
@@ -1578,7 +1570,6 @@
static struct branch_clk gcc_blsp1_uart2_apps_clk = {
.cbcr_reg = BLSP1_UART2_APPS_CBCR,
.parent = &blsp1_uart2_apps_clk_src.c,
- .bcr_reg = BLSP1_UART2_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp1_uart2_apps_clk",
@@ -1590,7 +1581,6 @@
static struct branch_clk gcc_blsp1_uart3_apps_clk = {
.cbcr_reg = BLSP1_UART3_APPS_CBCR,
.parent = &blsp1_uart3_apps_clk_src.c,
- .bcr_reg = BLSP1_UART3_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp1_uart3_apps_clk",
@@ -1602,7 +1592,6 @@
static struct branch_clk gcc_blsp1_uart4_apps_clk = {
.cbcr_reg = BLSP1_UART4_APPS_CBCR,
.parent = &blsp1_uart4_apps_clk_src.c,
- .bcr_reg = BLSP1_UART4_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp1_uart4_apps_clk",
@@ -1614,7 +1603,6 @@
static struct branch_clk gcc_blsp1_uart5_apps_clk = {
.cbcr_reg = BLSP1_UART5_APPS_CBCR,
.parent = &blsp1_uart5_apps_clk_src.c,
- .bcr_reg = BLSP1_UART5_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp1_uart5_apps_clk",
@@ -1626,7 +1614,6 @@
static struct branch_clk gcc_blsp1_uart6_apps_clk = {
.cbcr_reg = BLSP1_UART6_APPS_CBCR,
.parent = &blsp1_uart6_apps_clk_src.c,
- .bcr_reg = BLSP1_UART6_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp1_uart6_apps_clk",
@@ -1639,7 +1626,6 @@
.cbcr_reg = BOOT_ROM_AHB_CBCR,
.vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
.en_mask = BIT(10),
- .bcr_reg = BOOT_ROM_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_boot_rom_ahb_clk",
@@ -1652,7 +1638,6 @@
.cbcr_reg = BLSP2_AHB_CBCR,
.vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
.en_mask = BIT(15),
- .bcr_reg = BLSP2_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp2_ahb_clk",
@@ -1665,7 +1650,6 @@
.cbcr_reg = BLSP2_QUP1_I2C_APPS_CBCR,
.parent = &cxo_clk_src.c,
.has_sibling = 1,
- .bcr_reg = BLSP2_QUP1_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp2_qup1_i2c_apps_clk",
@@ -1677,7 +1661,6 @@
static struct branch_clk gcc_blsp2_qup1_spi_apps_clk = {
.cbcr_reg = BLSP2_QUP1_SPI_APPS_CBCR,
.parent = &blsp2_qup1_spi_apps_clk_src.c,
- .bcr_reg = BLSP2_QUP1_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp2_qup1_spi_apps_clk",
@@ -1690,7 +1673,6 @@
.cbcr_reg = BLSP2_QUP2_I2C_APPS_CBCR,
.parent = &cxo_clk_src.c,
.has_sibling = 1,
- .bcr_reg = BLSP2_QUP2_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp2_qup2_i2c_apps_clk",
@@ -1702,7 +1684,6 @@
static struct branch_clk gcc_blsp2_qup2_spi_apps_clk = {
.cbcr_reg = BLSP2_QUP2_SPI_APPS_CBCR,
.parent = &blsp2_qup2_spi_apps_clk_src.c,
- .bcr_reg = BLSP2_QUP2_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp2_qup2_spi_apps_clk",
@@ -1715,7 +1696,6 @@
.cbcr_reg = BLSP2_QUP3_I2C_APPS_CBCR,
.parent = &cxo_clk_src.c,
.has_sibling = 1,
- .bcr_reg = BLSP2_QUP3_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp2_qup3_i2c_apps_clk",
@@ -1727,7 +1707,6 @@
static struct branch_clk gcc_blsp2_qup3_spi_apps_clk = {
.cbcr_reg = BLSP2_QUP3_SPI_APPS_CBCR,
.parent = &blsp2_qup3_spi_apps_clk_src.c,
- .bcr_reg = BLSP2_QUP3_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp2_qup3_spi_apps_clk",
@@ -1740,7 +1719,6 @@
.cbcr_reg = BLSP2_QUP4_I2C_APPS_CBCR,
.parent = &cxo_clk_src.c,
.has_sibling = 1,
- .bcr_reg = BLSP2_QUP4_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp2_qup4_i2c_apps_clk",
@@ -1752,7 +1730,6 @@
static struct branch_clk gcc_blsp2_qup4_spi_apps_clk = {
.cbcr_reg = BLSP2_QUP4_SPI_APPS_CBCR,
.parent = &blsp2_qup4_spi_apps_clk_src.c,
- .bcr_reg = BLSP2_QUP4_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp2_qup4_spi_apps_clk",
@@ -1765,7 +1742,6 @@
.cbcr_reg = BLSP2_QUP5_I2C_APPS_CBCR,
.parent = &cxo_clk_src.c,
.has_sibling = 1,
- .bcr_reg = BLSP2_QUP5_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp2_qup5_i2c_apps_clk",
@@ -1777,7 +1753,6 @@
static struct branch_clk gcc_blsp2_qup5_spi_apps_clk = {
.cbcr_reg = BLSP2_QUP5_SPI_APPS_CBCR,
.parent = &blsp2_qup5_spi_apps_clk_src.c,
- .bcr_reg = BLSP2_QUP5_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp2_qup5_spi_apps_clk",
@@ -1790,7 +1765,6 @@
.cbcr_reg = BLSP2_QUP6_I2C_APPS_CBCR,
.parent = &cxo_clk_src.c,
.has_sibling = 1,
- .bcr_reg = BLSP2_QUP6_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp2_qup6_i2c_apps_clk",
@@ -1802,7 +1776,6 @@
static struct branch_clk gcc_blsp2_qup6_spi_apps_clk = {
.cbcr_reg = BLSP2_QUP6_SPI_APPS_CBCR,
.parent = &blsp2_qup6_spi_apps_clk_src.c,
- .bcr_reg = BLSP2_QUP6_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp2_qup6_spi_apps_clk",
@@ -1814,7 +1787,6 @@
static struct branch_clk gcc_blsp2_uart1_apps_clk = {
.cbcr_reg = BLSP2_UART1_APPS_CBCR,
.parent = &blsp2_uart1_apps_clk_src.c,
- .bcr_reg = BLSP2_UART1_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp2_uart1_apps_clk",
@@ -1826,7 +1798,6 @@
static struct branch_clk gcc_blsp2_uart2_apps_clk = {
.cbcr_reg = BLSP2_UART2_APPS_CBCR,
.parent = &blsp2_uart2_apps_clk_src.c,
- .bcr_reg = BLSP2_UART2_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp2_uart2_apps_clk",
@@ -1838,7 +1809,6 @@
static struct branch_clk gcc_blsp2_uart3_apps_clk = {
.cbcr_reg = BLSP2_UART3_APPS_CBCR,
.parent = &blsp2_uart3_apps_clk_src.c,
- .bcr_reg = BLSP2_UART3_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp2_uart3_apps_clk",
@@ -1850,7 +1820,6 @@
static struct branch_clk gcc_blsp2_uart4_apps_clk = {
.cbcr_reg = BLSP2_UART4_APPS_CBCR,
.parent = &blsp2_uart4_apps_clk_src.c,
- .bcr_reg = BLSP2_UART4_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp2_uart4_apps_clk",
@@ -1862,7 +1831,6 @@
static struct branch_clk gcc_blsp2_uart5_apps_clk = {
.cbcr_reg = BLSP2_UART5_APPS_CBCR,
.parent = &blsp2_uart5_apps_clk_src.c,
- .bcr_reg = BLSP2_UART5_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp2_uart5_apps_clk",
@@ -1874,7 +1842,6 @@
static struct branch_clk gcc_blsp2_uart6_apps_clk = {
.cbcr_reg = BLSP2_UART6_APPS_CBCR,
.parent = &blsp2_uart6_apps_clk_src.c,
- .bcr_reg = BLSP2_UART6_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_blsp2_uart6_apps_clk",
@@ -1887,7 +1854,6 @@
.cbcr_reg = CE1_CBCR,
.vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
.en_mask = BIT(5),
- .bcr_reg = CE1_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_ce1_clk",
@@ -1900,7 +1866,6 @@
.cbcr_reg = CE1_AHB_CBCR,
.vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
.en_mask = BIT(3),
- .bcr_reg = CE1_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_ce1_ahb_clk",
@@ -1913,7 +1878,6 @@
.cbcr_reg = CE1_AXI_CBCR,
.vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
.en_mask = BIT(4),
- .bcr_reg = CE1_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_ce1_axi_clk",
@@ -1926,7 +1890,6 @@
.cbcr_reg = CE2_CBCR,
.vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
.en_mask = BIT(2),
- .bcr_reg = CE2_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_ce2_clk",
@@ -1939,7 +1902,6 @@
.cbcr_reg = CE2_AHB_CBCR,
.vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
.en_mask = BIT(0),
- .bcr_reg = CE2_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_ce1_ahb_clk",
@@ -1952,7 +1914,6 @@
.cbcr_reg = CE2_AXI_CBCR,
.vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
.en_mask = BIT(1),
- .bcr_reg = CE2_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_ce1_axi_clk",
@@ -1997,7 +1958,6 @@
static struct branch_clk gcc_pdm2_clk = {
.cbcr_reg = PDM2_CBCR,
.parent = &pdm2_clk_src.c,
- .bcr_reg = PDM_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_pdm2_clk",
@@ -2009,7 +1969,6 @@
static struct branch_clk gcc_pdm_ahb_clk = {
.cbcr_reg = PDM_AHB_CBCR,
.has_sibling = 1,
- .bcr_reg = PDM_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_pdm_ahb_clk",
@@ -2022,7 +1981,6 @@
.cbcr_reg = PRNG_AHB_CBCR,
.vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
.en_mask = BIT(13),
- .bcr_reg = PRNG_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_prng_ahb_clk",
@@ -2034,7 +1992,6 @@
static struct branch_clk gcc_sdcc1_ahb_clk = {
.cbcr_reg = SDCC1_AHB_CBCR,
.has_sibling = 1,
- .bcr_reg = SDCC1_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_sdcc1_ahb_clk",
@@ -2046,7 +2003,6 @@
static struct branch_clk gcc_sdcc1_apps_clk = {
.cbcr_reg = SDCC1_APPS_CBCR,
.parent = &sdcc1_apps_clk_src.c,
- .bcr_reg = SDCC1_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_sdcc1_apps_clk",
@@ -2058,7 +2014,6 @@
static struct branch_clk gcc_sdcc2_ahb_clk = {
.cbcr_reg = SDCC2_AHB_CBCR,
.has_sibling = 1,
- .bcr_reg = SDCC2_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_sdcc2_ahb_clk",
@@ -2070,7 +2025,6 @@
static struct branch_clk gcc_sdcc2_apps_clk = {
.cbcr_reg = SDCC2_APPS_CBCR,
.parent = &sdcc2_apps_clk_src.c,
- .bcr_reg = SDCC2_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_sdcc2_apps_clk",
@@ -2082,7 +2036,6 @@
static struct branch_clk gcc_sdcc3_ahb_clk = {
.cbcr_reg = SDCC3_AHB_CBCR,
.has_sibling = 1,
- .bcr_reg = SDCC3_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_sdcc3_ahb_clk",
@@ -2094,7 +2047,6 @@
static struct branch_clk gcc_sdcc3_apps_clk = {
.cbcr_reg = SDCC3_APPS_CBCR,
.parent = &sdcc3_apps_clk_src.c,
- .bcr_reg = SDCC3_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_sdcc3_apps_clk",
@@ -2106,7 +2058,6 @@
static struct branch_clk gcc_sdcc4_ahb_clk = {
.cbcr_reg = SDCC4_AHB_CBCR,
.has_sibling = 1,
- .bcr_reg = SDCC4_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_sdcc4_ahb_clk",
@@ -2118,7 +2069,6 @@
static struct branch_clk gcc_sdcc4_apps_clk = {
.cbcr_reg = SDCC4_APPS_CBCR,
.parent = &sdcc4_apps_clk_src.c,
- .bcr_reg = SDCC4_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_sdcc4_apps_clk",
@@ -2130,7 +2080,6 @@
static struct branch_clk gcc_tsif_ahb_clk = {
.cbcr_reg = TSIF_AHB_CBCR,
.has_sibling = 1,
- .bcr_reg = TSIF_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_tsif_ahb_clk",
@@ -2142,7 +2091,6 @@
static struct branch_clk gcc_tsif_ref_clk = {
.cbcr_reg = TSIF_REF_CBCR,
.parent = &tsif_ref_clk_src.c,
- .bcr_reg = TSIF_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_tsif_ref_clk",
@@ -2155,7 +2103,6 @@
.cbcr_reg = USB30_MASTER_CBCR,
.parent = &usb30_master_clk_src.c,
.has_sibling = 1,
- .bcr_reg = USB_30_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_usb30_master_clk",
@@ -2167,7 +2114,6 @@
static struct branch_clk gcc_usb30_mock_utmi_clk = {
.cbcr_reg = USB30_MOCK_UTMI_CBCR,
.parent = &usb30_mock_utmi_clk_src.c,
- .bcr_reg = USB_30_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_usb30_mock_utmi_clk",
@@ -2179,7 +2125,6 @@
static struct branch_clk gcc_usb_hs_ahb_clk = {
.cbcr_reg = USB_HS_AHB_CBCR,
.has_sibling = 1,
- .bcr_reg = USB_HS_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_usb_hs_ahb_clk",
@@ -2191,7 +2136,6 @@
static struct branch_clk gcc_usb_hs_system_clk = {
.cbcr_reg = USB_HS_SYSTEM_CBCR,
.parent = &usb_hs_system_clk_src.c,
- .bcr_reg = USB_HS_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_usb_hs_system_clk",
@@ -2203,7 +2147,6 @@
static struct branch_clk gcc_usb_hsic_ahb_clk = {
.cbcr_reg = USB_HSIC_AHB_CBCR,
.has_sibling = 1,
- .bcr_reg = USB_HS_HSIC_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_usb_hsic_ahb_clk",
@@ -2215,7 +2158,6 @@
static struct branch_clk gcc_usb_hsic_clk = {
.cbcr_reg = USB_HSIC_CBCR,
.parent = &usb_hsic_clk_src.c,
- .bcr_reg = USB_HS_HSIC_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_usb_hsic_clk",
@@ -2227,7 +2169,6 @@
static struct branch_clk gcc_usb_hsic_io_cal_clk = {
.cbcr_reg = USB_HSIC_IO_CAL_CBCR,
.parent = &usb_hsic_io_cal_clk_src.c,
- .bcr_reg = USB_HS_HSIC_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_usb_hsic_io_cal_clk",
@@ -2239,7 +2180,6 @@
static struct branch_clk gcc_usb_hsic_system_clk = {
.cbcr_reg = USB_HSIC_SYSTEM_CBCR,
.parent = &usb_hsic_system_clk_src.c,
- .bcr_reg = USB_HS_HSIC_BCR,
.base = &virt_bases[GCC_BASE],
.c = {
.dbg_name = "gcc_usb_hsic_system_clk",
@@ -2931,7 +2871,6 @@
.cbcr_reg = CAMSS_CCI_CCI_AHB_CBCR,
.parent = &ahb_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CCI_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_cci_cci_ahb_clk",
@@ -2944,7 +2883,6 @@
.cbcr_reg = CAMSS_CCI_CCI_CBCR,
.parent = &cci_clk_src.c,
.has_sibling = 0,
- .bcr_reg = CAMSS_CCI_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_cci_cci_clk",
@@ -2957,7 +2895,6 @@
.cbcr_reg = CAMSS_CSI0_AHB_CBCR,
.parent = &ahb_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI0_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi0_ahb_clk",
@@ -2970,7 +2907,6 @@
.cbcr_reg = CAMSS_CSI0_CBCR,
.parent = &csi0_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI0_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi0_clk",
@@ -2983,7 +2919,6 @@
.cbcr_reg = CAMSS_CSI0PHY_CBCR,
.parent = &csi0_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI0PHY_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi0phy_clk",
@@ -2996,7 +2931,6 @@
.cbcr_reg = CAMSS_CSI0PIX_CBCR,
.parent = &csi0_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI0PIX_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi0pix_clk",
@@ -3009,7 +2943,6 @@
.cbcr_reg = CAMSS_CSI0RDI_CBCR,
.parent = &csi0_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI0RDI_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi0rdi_clk",
@@ -3022,7 +2955,6 @@
.cbcr_reg = CAMSS_CSI1_AHB_CBCR,
.parent = &ahb_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI1_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi1_ahb_clk",
@@ -3035,7 +2967,6 @@
.cbcr_reg = CAMSS_CSI1_CBCR,
.parent = &csi1_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI1_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi1_clk",
@@ -3048,7 +2979,6 @@
.cbcr_reg = CAMSS_CSI1PHY_CBCR,
.parent = &csi1_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI1PHY_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi1phy_clk",
@@ -3061,7 +2991,6 @@
.cbcr_reg = CAMSS_CSI1PIX_CBCR,
.parent = &csi1_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI1PIX_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi1pix_clk",
@@ -3074,7 +3003,6 @@
.cbcr_reg = CAMSS_CSI1RDI_CBCR,
.parent = &csi1_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI1RDI_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi1rdi_clk",
@@ -3087,7 +3015,6 @@
.cbcr_reg = CAMSS_CSI2_AHB_CBCR,
.parent = &ahb_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI2_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi2_ahb_clk",
@@ -3100,7 +3027,6 @@
.cbcr_reg = CAMSS_CSI2_CBCR,
.parent = &csi2_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI2_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi2_clk",
@@ -3113,7 +3039,6 @@
.cbcr_reg = CAMSS_CSI2PHY_CBCR,
.parent = &csi2_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI2PHY_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi2phy_clk",
@@ -3126,7 +3051,6 @@
.cbcr_reg = CAMSS_CSI2PIX_CBCR,
.parent = &csi2_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI2PIX_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi2pix_clk",
@@ -3139,7 +3063,6 @@
.cbcr_reg = CAMSS_CSI2RDI_CBCR,
.parent = &csi2_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI2RDI_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi2rdi_clk",
@@ -3152,7 +3075,6 @@
.cbcr_reg = CAMSS_CSI3_AHB_CBCR,
.parent = &ahb_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI3_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi3_ahb_clk",
@@ -3165,7 +3087,6 @@
.cbcr_reg = CAMSS_CSI3_CBCR,
.parent = &csi3_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI3_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi3_clk",
@@ -3178,7 +3099,6 @@
.cbcr_reg = CAMSS_CSI3PHY_CBCR,
.parent = &csi3_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI3PHY_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi3phy_clk",
@@ -3191,7 +3111,6 @@
.cbcr_reg = CAMSS_CSI3PIX_CBCR,
.parent = &csi3_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI3PIX_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi3pix_clk",
@@ -3204,7 +3123,6 @@
.cbcr_reg = CAMSS_CSI3RDI_CBCR,
.parent = &csi3_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI3RDI_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi3rdi_clk",
@@ -3217,7 +3135,6 @@
.cbcr_reg = CAMSS_CSI_VFE0_CBCR,
.parent = &vfe0_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI_VFE0_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi_vfe0_clk",
@@ -3230,7 +3147,6 @@
.cbcr_reg = CAMSS_CSI_VFE1_CBCR,
.parent = &vfe1_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_CSI_VFE1_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_csi_vfe1_clk",
@@ -3243,7 +3159,6 @@
.cbcr_reg = CAMSS_GP0_CBCR,
.parent = &mmss_gp0_clk_src.c,
.has_sibling = 0,
- .bcr_reg = CAMSS_GP0_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_gp0_clk",
@@ -3256,7 +3171,6 @@
.cbcr_reg = CAMSS_GP1_CBCR,
.parent = &mmss_gp1_clk_src.c,
.has_sibling = 0,
- .bcr_reg = CAMSS_GP1_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_gp1_clk",
@@ -3269,7 +3183,6 @@
.cbcr_reg = CAMSS_ISPIF_AHB_CBCR,
.parent = &ahb_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_ISPIF_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_ispif_ahb_clk",
@@ -3282,7 +3195,6 @@
.cbcr_reg = CAMSS_JPEG_JPEG0_CBCR,
.parent = &jpeg0_clk_src.c,
.has_sibling = 0,
- .bcr_reg = CAMSS_JPEG_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_jpeg_jpeg0_clk",
@@ -3295,7 +3207,6 @@
.cbcr_reg = CAMSS_JPEG_JPEG1_CBCR,
.parent = &jpeg1_clk_src.c,
.has_sibling = 0,
- .bcr_reg = CAMSS_JPEG_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_jpeg_jpeg1_clk",
@@ -3308,7 +3219,6 @@
.cbcr_reg = CAMSS_JPEG_JPEG2_CBCR,
.parent = &jpeg2_clk_src.c,
.has_sibling = 0,
- .bcr_reg = CAMSS_JPEG_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_jpeg_jpeg2_clk",
@@ -3321,7 +3231,6 @@
.cbcr_reg = CAMSS_JPEG_JPEG_AHB_CBCR,
.parent = &ahb_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_JPEG_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_jpeg_jpeg_ahb_clk",
@@ -3334,7 +3243,6 @@
.cbcr_reg = CAMSS_JPEG_JPEG_AXI_CBCR,
.parent = &axi_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_JPEG_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_jpeg_jpeg_axi_clk",
@@ -3346,7 +3254,6 @@
static struct branch_clk camss_jpeg_jpeg_ocmemnoc_clk = {
.cbcr_reg = CAMSS_JPEG_JPEG_OCMEMNOC_CBCR,
.has_sibling = 1,
- .bcr_reg = CAMSS_JPEG_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_jpeg_jpeg_ocmemnoc_clk",
@@ -3359,7 +3266,6 @@
.cbcr_reg = CAMSS_MCLK0_CBCR,
.parent = &mclk0_clk_src.c,
.has_sibling = 0,
- .bcr_reg = CAMSS_MCLK0_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_mclk0_clk",
@@ -3372,7 +3278,6 @@
.cbcr_reg = CAMSS_MCLK1_CBCR,
.parent = &mclk1_clk_src.c,
.has_sibling = 0,
- .bcr_reg = CAMSS_MCLK1_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_mclk1_clk",
@@ -3385,7 +3290,6 @@
.cbcr_reg = CAMSS_MCLK2_CBCR,
.parent = &mclk2_clk_src.c,
.has_sibling = 0,
- .bcr_reg = CAMSS_MCLK2_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_mclk2_clk",
@@ -3398,7 +3302,6 @@
.cbcr_reg = CAMSS_MCLK3_CBCR,
.parent = &mclk3_clk_src.c,
.has_sibling = 0,
- .bcr_reg = CAMSS_MCLK3_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_mclk3_clk",
@@ -3411,7 +3314,6 @@
.cbcr_reg = CAMSS_MICRO_AHB_CBCR,
.parent = &ahb_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_MICRO_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_micro_ahb_clk",
@@ -3424,7 +3326,6 @@
.cbcr_reg = CAMSS_PHY0_CSI0PHYTIMER_CBCR,
.parent = &csi0phytimer_clk_src.c,
.has_sibling = 0,
- .bcr_reg = CAMSS_PHY0_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_phy0_csi0phytimer_clk",
@@ -3437,7 +3338,6 @@
.cbcr_reg = CAMSS_PHY1_CSI1PHYTIMER_CBCR,
.parent = &csi1phytimer_clk_src.c,
.has_sibling = 0,
- .bcr_reg = CAMSS_PHY1_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_phy1_csi1phytimer_clk",
@@ -3450,7 +3350,6 @@
.cbcr_reg = CAMSS_PHY2_CSI2PHYTIMER_CBCR,
.parent = &csi2phytimer_clk_src.c,
.has_sibling = 0,
- .bcr_reg = CAMSS_PHY2_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_phy2_csi2phytimer_clk",
@@ -3463,7 +3362,6 @@
.cbcr_reg = CAMSS_TOP_AHB_CBCR,
.parent = &ahb_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_TOP_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_top_ahb_clk",
@@ -3476,7 +3374,6 @@
.cbcr_reg = CAMSS_VFE_CPP_AHB_CBCR,
.parent = &ahb_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_VFE_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_vfe_cpp_ahb_clk",
@@ -3489,7 +3386,6 @@
.cbcr_reg = CAMSS_VFE_CPP_CBCR,
.parent = &cpp_clk_src.c,
.has_sibling = 0,
- .bcr_reg = CAMSS_VFE_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_vfe_cpp_clk",
@@ -3502,7 +3398,6 @@
.cbcr_reg = CAMSS_VFE_VFE0_CBCR,
.parent = &vfe0_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_VFE_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_vfe_vfe0_clk",
@@ -3515,7 +3410,6 @@
.cbcr_reg = CAMSS_VFE_VFE1_CBCR,
.parent = &vfe1_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_VFE_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_vfe_vfe1_clk",
@@ -3528,7 +3422,6 @@
.cbcr_reg = CAMSS_VFE_VFE_AHB_CBCR,
.parent = &ahb_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_VFE_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_vfe_vfe_ahb_clk",
@@ -3541,7 +3434,6 @@
.cbcr_reg = CAMSS_VFE_VFE_AXI_CBCR,
.parent = &axi_clk_src.c,
.has_sibling = 1,
- .bcr_reg = CAMSS_VFE_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_vfe_vfe_axi_clk",
@@ -3553,7 +3445,6 @@
static struct branch_clk camss_vfe_vfe_ocmemnoc_clk = {
.cbcr_reg = CAMSS_VFE_VFE_OCMEMNOC_CBCR,
.has_sibling = 1,
- .bcr_reg = CAMSS_VFE_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "camss_vfe_vfe_ocmemnoc_clk",
@@ -3566,7 +3457,6 @@
.cbcr_reg = MDSS_AHB_CBCR,
.parent = &ahb_clk_src.c,
.has_sibling = 1,
- .bcr_reg = MDSS_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mdss_ahb_clk",
@@ -3579,7 +3469,6 @@
.cbcr_reg = MDSS_AXI_CBCR,
.parent = &axi_clk_src.c,
.has_sibling = 1,
- .bcr_reg = MDSS_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mdss_axi_clk",
@@ -3592,7 +3481,6 @@
.cbcr_reg = MDSS_BYTE0_CBCR,
.parent = &byte0_clk_src.c,
.has_sibling = 0,
- .bcr_reg = MDSS_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mdss_byte0_clk",
@@ -3605,7 +3493,6 @@
.cbcr_reg = MDSS_BYTE1_CBCR,
.parent = &byte1_clk_src.c,
.has_sibling = 0,
- .bcr_reg = MDSS_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mdss_byte1_clk",
@@ -3618,7 +3505,6 @@
.cbcr_reg = MDSS_EDPAUX_CBCR,
.parent = &edpaux_clk_src.c,
.has_sibling = 0,
- .bcr_reg = MDSS_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mdss_edpaux_clk",
@@ -3631,7 +3517,6 @@
.cbcr_reg = MDSS_EDPLINK_CBCR,
.parent = &edplink_clk_src.c,
.has_sibling = 0,
- .bcr_reg = MDSS_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mdss_edplink_clk",
@@ -3644,7 +3529,6 @@
.cbcr_reg = MDSS_EDPPIXEL_CBCR,
.parent = &edppixel_clk_src.c,
.has_sibling = 0,
- .bcr_reg = MDSS_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mdss_edppixel_clk",
@@ -3657,7 +3541,6 @@
.cbcr_reg = MDSS_ESC0_CBCR,
.parent = &esc0_clk_src.c,
.has_sibling = 0,
- .bcr_reg = MDSS_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mdss_esc0_clk",
@@ -3670,7 +3553,6 @@
.cbcr_reg = MDSS_ESC1_CBCR,
.parent = &esc1_clk_src.c,
.has_sibling = 0,
- .bcr_reg = MDSS_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mdss_esc1_clk",
@@ -3683,7 +3565,6 @@
.cbcr_reg = MDSS_EXTPCLK_CBCR,
.parent = &extpclk_clk_src.c,
.has_sibling = 0,
- .bcr_reg = MDSS_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mdss_extpclk_clk",
@@ -3696,7 +3577,6 @@
.cbcr_reg = MDSS_HDMI_AHB_CBCR,
.parent = &ahb_clk_src.c,
.has_sibling = 1,
- .bcr_reg = MDSS_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mdss_hdmi_ahb_clk",
@@ -3709,7 +3589,6 @@
.cbcr_reg = MDSS_HDMI_CBCR,
.parent = &hdmi_clk_src.c,
.has_sibling = 0,
- .bcr_reg = MDSS_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mdss_hdmi_clk",
@@ -3722,7 +3601,6 @@
.cbcr_reg = MDSS_MDP_CBCR,
.parent = &mdp_clk_src.c,
.has_sibling = 1,
- .bcr_reg = MDSS_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mdss_mdp_clk",
@@ -3735,7 +3613,6 @@
.cbcr_reg = MDSS_MDP_LUT_CBCR,
.parent = &mdp_clk_src.c,
.has_sibling = 1,
- .bcr_reg = MDSS_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mdss_mdp_lut_clk",
@@ -3748,7 +3625,6 @@
.cbcr_reg = MDSS_PCLK0_CBCR,
.parent = &pclk0_clk_src.c,
.has_sibling = 0,
- .bcr_reg = MDSS_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mdss_pclk0_clk",
@@ -3761,7 +3637,6 @@
.cbcr_reg = MDSS_PCLK1_CBCR,
.parent = &pclk1_clk_src.c,
.has_sibling = 0,
- .bcr_reg = MDSS_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mdss_pclk1_clk",
@@ -3774,7 +3649,6 @@
.cbcr_reg = MDSS_VSYNC_CBCR,
.parent = &vsync_clk_src.c,
.has_sibling = 0,
- .bcr_reg = MDSS_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mdss_vsync_clk",
@@ -3787,7 +3661,6 @@
.cbcr_reg = MMSS_MISC_AHB_CBCR,
.parent = &ahb_clk_src.c,
.has_sibling = 1,
- .bcr_reg = MMSSNOCAHB_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mmss_misc_ahb_clk",
@@ -3800,7 +3673,6 @@
.cbcr_reg = MMSS_MMSSNOC_AHB_CBCR,
.parent = &ahb_clk_src.c,
.has_sibling = 1,
- .bcr_reg = MMSSNOCAHB_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mmss_mmssnoc_ahb_clk",
@@ -3813,7 +3685,6 @@
.cbcr_reg = MMSS_MMSSNOC_BTO_AHB_CBCR,
.parent = &ahb_clk_src.c,
.has_sibling = 1,
- .bcr_reg = MMSSNOCAHB_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mmss_mmssnoc_bto_ahb_clk",
@@ -3826,7 +3697,6 @@
.cbcr_reg = MMSS_MMSSNOC_AXI_CBCR,
.parent = &axi_clk_src.c,
.has_sibling = 1,
- .bcr_reg = MMSSNOCAXI_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mmss_mmssnoc_axi_clk",
@@ -3839,7 +3709,6 @@
.cbcr_reg = MMSS_S0_AXI_CBCR,
.parent = &axi_clk_src.c,
.has_sibling = 1,
- .bcr_reg = MMSSNOCAXI_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mmss_s0_axi_clk",
@@ -3852,7 +3721,6 @@
.cbcr_reg = VENUS0_AHB_CBCR,
.parent = &ahb_clk_src.c,
.has_sibling = 1,
- .bcr_reg = VENUS0_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "venus0_ahb_clk",
@@ -3865,7 +3733,6 @@
.cbcr_reg = VENUS0_AXI_CBCR,
.parent = &axi_clk_src.c,
.has_sibling = 1,
- .bcr_reg = VENUS0_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "venus0_axi_clk",
@@ -3877,7 +3744,6 @@
static struct branch_clk venus0_ocmemnoc_clk = {
.cbcr_reg = VENUS0_OCMEMNOC_CBCR,
.has_sibling = 1,
- .bcr_reg = VENUS0_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "venus0_ocmemnoc_clk",
@@ -3890,7 +3756,6 @@
.cbcr_reg = VENUS0_VCODEC0_CBCR,
.parent = &vcodec0_clk_src.c,
.has_sibling = 0,
- .bcr_reg = VENUS0_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "venus0_vcodec0_clk",
@@ -3902,7 +3767,6 @@
static struct branch_clk oxili_gfx3d_clk = {
.cbcr_reg = OXILI_GFX3D_CBCR,
.has_sibling = 1,
- .bcr_reg = OXILI_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "oxili_gfx3d_clk",
@@ -3915,7 +3779,6 @@
.cbcr_reg = OXILICX_AHB_CBCR,
.parent = &ahb_clk_src.c,
.has_sibling = 1,
- .bcr_reg = OXILICX_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "oxilicx_ahb_clk",
@@ -3928,7 +3791,6 @@
.cbcr_reg = OXILICX_AXI_CBCR,
.parent = &axi_clk_src.c,
.has_sibling = 1,
- .bcr_reg = OXILICX_BCR,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "oxilicx_axi_clk",
@@ -4363,7 +4225,6 @@
static struct branch_clk mss_bus_q6_clk = {
.cbcr_reg = MSS_BUS_Q6_CBCR,
- .bcr_reg = MSS_Q6SS_BCR,
.has_sibling = 1,
.base = &virt_bases[MSS_BASE],
.c = {
@@ -4716,6 +4577,9 @@
static struct clk_lookup msm_clocks_copper[] = {
CLK_LOOKUP("xo", cxo_clk_src.c, "msm_otg"),
CLK_LOOKUP("xo", cxo_clk_src.c, "pil-q6v5-lpass"),
+ CLK_LOOKUP("xo", cxo_clk_src.c, "pil-q6v5-mss"),
+ CLK_LOOKUP("xo", cxo_clk_src.c, "pil-mba"),
+ CLK_LOOKUP("xo", cxo_clk_src.c, "pil_pronto"),
CLK_LOOKUP("measure", measure_clk.c, "debug"),
CLK_LOOKUP("dma_bam_pclk", gcc_bam_dma_ahb_clk.c, "msm_sps"),
@@ -4846,7 +4710,10 @@
CLK_LOOKUP("core_clk", camss_jpeg_jpeg0_clk.c, ""),
CLK_LOOKUP("core_clk", camss_jpeg_jpeg1_clk.c, ""),
CLK_LOOKUP("core_clk", camss_jpeg_jpeg2_clk.c, ""),
- CLK_LOOKUP("iface_clk", camss_jpeg_jpeg_ahb_clk.c, ""),
+ CLK_LOOKUP("iface_clk", camss_jpeg_jpeg_ahb_clk.c,
+ "fda64000.qcom,iommu"),
+ CLK_LOOKUP("core_clk", camss_jpeg_jpeg_axi_clk.c,
+ "fda64000.qcom,iommu"),
CLK_LOOKUP("bus_clk", camss_jpeg_jpeg_axi_clk.c, ""),
CLK_LOOKUP("bus_clk", camss_jpeg_jpeg_ocmemnoc_clk.c, ""),
CLK_LOOKUP("core_clk", camss_mclk0_clk.c, ""),
@@ -4867,11 +4734,15 @@
CLK_LOOKUP("iface_clk", camss_vfe_vfe_ahb_clk.c, ""),
CLK_LOOKUP("bus_clk", camss_vfe_vfe_axi_clk.c, ""),
CLK_LOOKUP("bus_clk", camss_vfe_vfe_ocmemnoc_clk.c, ""),
- CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, ""),
+ 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, ""),
CLK_LOOKUP("core_clk", oxili_gfx3d_clk.c, ""),
CLK_LOOKUP("iface_clk", oxilicx_ahb_clk.c, ""),
CLK_LOOKUP("bus_clk", oxilicx_axi_clk.c, ""),
+ CLK_LOOKUP("iface_clk", venus0_ahb_clk.c, "fdc84000.qcom,iommu"),
+ CLK_LOOKUP("core_clk", venus0_axi_clk.c, "fdc84000.qcom,iommu"),
+ CLK_LOOKUP("bus_clk", venus0_axi_clk.c, ""),
/* LPASS clocks */
CLK_LOOKUP("core_clk", audio_core_slimbus_core_clk.c, "fe12f000.slim"),
@@ -4904,13 +4775,13 @@
CLK_LOOKUP("ebit_clk", audio_core_lpaif_pcm1_ebit_clk.c, ""),
CLK_LOOKUP("ibit_clk", audio_core_lpaif_pcm1_ibit_clk.c, ""),
- CLK_LOOKUP("core_clk", mss_xo_q6_clk.c, ""),
- CLK_LOOKUP("bus_clk", mss_bus_q6_clk.c, ""),
+ CLK_LOOKUP("core_clk", mss_xo_q6_clk.c, "pil-q6v5-mss"),
+ CLK_LOOKUP("bus_clk", mss_bus_q6_clk.c, "pil-q6v5-mss"),
+ CLK_LOOKUP("bus_clk", gcc_mss_cfg_ahb_clk.c, ""),
+ CLK_LOOKUP("mem_clk", gcc_boot_rom_ahb_clk.c, "pil-q6v5-mss"),
CLK_LOOKUP("core_clk", q6ss_xo_clk.c, "pil-q6v5-lpass"),
CLK_LOOKUP("bus_clk", q6ss_ahb_lfabif_clk.c, "pil-q6v5-lpass"),
- CLK_LOOKUP("mem_clk", gcc_boot_rom_ahb_clk.c, ""),
- CLK_LOOKUP("bus_clk", gcc_mss_cfg_ahb_clk.c, ""),
- CLK_LOOKUP("core_clk", gcc_prng_ahb_clk.c, "msm_rng.0"),
+ CLK_LOOKUP("core_clk", gcc_prng_ahb_clk.c, "msm_rng"),
/* TODO: Remove dummy clocks as soon as they become unnecessary */
CLK_DUMMY("phy_clk", NULL, "msm_otg", OFF),
diff --git a/arch/arm/mach-msm/clock-local2.c b/arch/arm/mach-msm/clock-local2.c
index e8e88d7..cf45e63 100644
--- a/arch/arm/mach-msm/clock-local2.c
+++ b/arch/arm/mach-msm/clock-local2.c
@@ -462,14 +462,16 @@
}
static int __branch_clk_reset(void __iomem *bcr_reg,
- enum clk_reset_action action)
+ enum clk_reset_action action, const char *name)
{
int ret = 0;
unsigned long flags;
u32 reg_val;
- if (!bcr_reg)
+ if (!bcr_reg) {
+ WARN("clk_reset called on an unsupported clock (%s)\n", name);
return -EPERM;
+ }
spin_lock_irqsave(&local_clock_reg_lock, flags);
reg_val = readl_relaxed(bcr_reg);
@@ -495,7 +497,7 @@
static int branch_clk_reset(struct clk *c, enum clk_reset_action action)
{
struct branch_clk *branch = to_branch_clk(c);
- return __branch_clk_reset(BCR_REG(branch), action);
+ return __branch_clk_reset(BCR_REG(branch), action, c->dbg_name);
}
/*
@@ -503,8 +505,8 @@
*/
static int local_vote_clk_reset(struct clk *c, enum clk_reset_action action)
{
- struct branch_clk *vclk = to_branch_clk(c);
- return __branch_clk_reset(BCR_REG(vclk), action);
+ struct local_vote_clk *vclk = to_local_vote_clk(c);
+ return __branch_clk_reset(BCR_REG(vclk), action, c->dbg_name);
}
static int local_vote_clk_enable(struct clk *c)
diff --git a/arch/arm/mach-msm/clock-pcom.c b/arch/arm/mach-msm/clock-pcom.c
index 58cea99..658fa2a 100644
--- a/arch/arm/mach-msm/clock-pcom.c
+++ b/arch/arm/mach-msm/clock-pcom.c
@@ -175,6 +175,18 @@
return false;
}
+static enum handoff pc_clk_handoff(struct clk *clk)
+{
+ /*
+ * Handoff clock state only since querying and caching the rate here
+ * would incur more overhead than it would ever save.
+ */
+ if (pc_clk_is_enabled(clk))
+ return HANDOFF_ENABLED_CLK;
+
+ return HANDOFF_DISABLED_CLK;
+}
+
struct clk_ops clk_ops_pcom = {
.enable = pc_clk_enable,
.disable = pc_clk_disable,
@@ -187,6 +199,7 @@
.is_enabled = pc_clk_is_enabled,
.round_rate = pc_clk_round_rate,
.is_local = pc_clk_is_local,
+ .handoff = pc_clk_handoff,
};
struct clk_ops clk_ops_pcom_ext_config = {
@@ -201,5 +214,6 @@
.is_enabled = pc_clk_is_enabled,
.round_rate = pc_clk_round_rate,
.is_local = pc_clk_is_local,
+ .handoff = pc_clk_handoff,
};
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index 6131590..00572dd 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -42,6 +42,7 @@
#include "rpm_log.h"
#include <mach/mpm.h>
#include <mach/iommu_domains.h>
+#include <mach/msm_cache_dump.h>
/* Address of GSBI blocks */
#define MSM_GSBI1_PHYS 0x12440000
@@ -617,6 +618,11 @@
.id = 0x4006,
};
+struct platform_device apq_cpudai_slimbus_3_tx = {
+ .name = "msm-dai-q6",
+ .id = 0x4007,
+};
+
static struct resource resources_ssbi_pmic1[] = {
{
.start = MSM_PMIC1_SSBI_CMD_PHYS,
@@ -771,10 +777,10 @@
.flags = IORESOURCE_IRQ,
},
{
- .start = MSM_GPIO_TO_INT(88),
- .end = MSM_GPIO_TO_INT(88),
- .name = "wakeup_irq",
- .flags = IORESOURCE_IRQ,
+ .start = 47,
+ .end = 47,
+ .name = "wakeup",
+ .flags = IORESOURCE_IO,
},
};
@@ -849,6 +855,22 @@
},
};
+#define SHARED_IMEM_TZ_BASE 0x2a03f720
+static struct resource tzlog_resources[] = {
+ {
+ .start = SHARED_IMEM_TZ_BASE,
+ .end = SHARED_IMEM_TZ_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct platform_device apq_device_tz_log = {
+ .name = "tz_log",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(tzlog_resources),
+ .resource = tzlog_resources,
+};
+
/* MSM Video core device */
#ifdef CONFIG_MSM_BUS_SCALING
static struct msm_bus_vectors vidc_init_vectors[] = {
@@ -2001,6 +2023,16 @@
/* Sensors DSPS platform data */
+#define PPSS_DSPS_TCM_CODE_BASE 0x12000000
+#define PPSS_DSPS_TCM_CODE_SIZE 0x28000
+#define PPSS_DSPS_TCM_BUF_BASE 0x12040000
+#define PPSS_DSPS_TCM_BUF_SIZE 0x4000
+#define PPSS_DSPS_PIPE_BASE 0x12800000
+#define PPSS_DSPS_PIPE_SIZE 0x4000
+#define PPSS_DSPS_DDR_BASE 0x8fe00000
+#define PPSS_DSPS_DDR_SIZE 0x100000
+#define PPSS_SMEM_BASE 0x80000000
+#define PPSS_SMEM_SIZE 0x200000
#define PPSS_REG_PHYS_BASE 0x12080000
static struct dsps_clk_info dsps_clks[] = {};
@@ -2019,6 +2051,16 @@
.regs = dsps_regs,
.regs_num = ARRAY_SIZE(dsps_regs),
.dsps_pwr_ctl_en = 1,
+ .tcm_code_start = PPSS_DSPS_TCM_CODE_BASE,
+ .tcm_code_size = PPSS_DSPS_TCM_CODE_SIZE,
+ .tcm_buf_start = PPSS_DSPS_TCM_BUF_BASE,
+ .tcm_buf_size = PPSS_DSPS_TCM_BUF_SIZE,
+ .pipe_start = PPSS_DSPS_PIPE_BASE,
+ .pipe_size = PPSS_DSPS_PIPE_SIZE,
+ .ddr_start = PPSS_DSPS_DDR_BASE,
+ .ddr_size = PPSS_DSPS_DDR_SIZE,
+ .smem_start = PPSS_SMEM_BASE,
+ .smem_size = PPSS_SMEM_SIZE,
.signature = DSPS_SIGNATURE,
};
@@ -2588,3 +2630,23 @@
.platform_data = &apq8064_rtb_pdata,
},
};
+
+#define APQ8064_L1_SIZE SZ_1M
+/*
+ * The actual L2 size is smaller but we need a larger buffer
+ * size to store other dump information
+ */
+#define APQ8064_L2_SIZE SZ_8M
+
+struct msm_cache_dump_platform_data apq8064_cache_dump_pdata = {
+ .l2_size = APQ8064_L2_SIZE,
+ .l1_size = APQ8064_L1_SIZE,
+};
+
+struct platform_device apq8064_cache_dump_device = {
+ .name = "msm_cache_dump",
+ .id = -1,
+ .dev = {
+ .platform_data = &apq8064_cache_dump_pdata,
+ },
+};
diff --git a/arch/arm/mach-msm/devices-8930.c b/arch/arm/mach-msm/devices-8930.c
index 4ad73f9..9de2213 100644
--- a/arch/arm/mach-msm/devices-8930.c
+++ b/arch/arm/mach-msm/devices-8930.c
@@ -126,6 +126,7 @@
MSM_RPM_MAP(8930, CXO_BUFFERS, CXO_BUFFERS, 1),
MSM_RPM_MAP(8930, USB_OTG_SWITCH, USB_OTG_SWITCH, 1),
MSM_RPM_MAP(8930, HDMI_SWITCH, HDMI_SWITCH, 1),
+ MSM_RPM_MAP(8930, DDR_DMM_0, DDR_DMM, 2),
MSM_RPM_MAP(8930, QDSS_CLK, QDSS_CLK, 1),
MSM_RPM_MAP(8930, VOLTAGE_CORNER, VOLTAGE_CORNER, 1),
},
@@ -230,6 +231,8 @@
MSM_RPM_STATUS_ID_MAP(8930, CXO_BUFFERS),
MSM_RPM_STATUS_ID_MAP(8930, USB_OTG_SWITCH),
MSM_RPM_STATUS_ID_MAP(8930, HDMI_SWITCH),
+ MSM_RPM_STATUS_ID_MAP(8930, DDR_DMM_0),
+ MSM_RPM_STATUS_ID_MAP(8930, DDR_DMM_1),
MSM_RPM_STATUS_ID_MAP(8930, QDSS_CLK),
MSM_RPM_STATUS_ID_MAP(8930, VOLTAGE_CORNER),
},
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index 775debe..e7d8f42 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -3246,6 +3246,16 @@
/* Sensors DSPS platform data */
#ifdef CONFIG_MSM_DSPS
+#define PPSS_DSPS_TCM_CODE_BASE 0x12000000
+#define PPSS_DSPS_TCM_CODE_SIZE 0x28000
+#define PPSS_DSPS_TCM_BUF_BASE 0x12040000
+#define PPSS_DSPS_TCM_BUF_SIZE 0x4000
+#define PPSS_DSPS_PIPE_BASE 0x12800000
+#define PPSS_DSPS_PIPE_SIZE 0x4000
+#define PPSS_DSPS_DDR_BASE 0x8fe00000
+#define PPSS_DSPS_DDR_SIZE 0x100000
+#define PPSS_SMEM_BASE 0x80000000
+#define PPSS_SMEM_SIZE 0x200000
#define PPSS_REG_PHYS_BASE 0x12080000
static struct dsps_clk_info dsps_clks[] = {};
@@ -3264,6 +3274,16 @@
.regs = dsps_regs,
.regs_num = ARRAY_SIZE(dsps_regs),
.dsps_pwr_ctl_en = 1,
+ .tcm_code_start = PPSS_DSPS_TCM_CODE_BASE,
+ .tcm_code_size = PPSS_DSPS_TCM_CODE_SIZE,
+ .tcm_buf_start = PPSS_DSPS_TCM_BUF_BASE,
+ .tcm_buf_size = PPSS_DSPS_TCM_BUF_SIZE,
+ .pipe_start = PPSS_DSPS_PIPE_BASE,
+ .pipe_size = PPSS_DSPS_PIPE_SIZE,
+ .ddr_start = PPSS_DSPS_DDR_BASE,
+ .ddr_size = PPSS_DSPS_DDR_SIZE,
+ .smem_start = PPSS_SMEM_BASE,
+ .smem_size = PPSS_SMEM_SIZE,
.signature = DSPS_SIGNATURE,
};
@@ -3274,7 +3294,6 @@
.name = "ppss_reg",
.flags = IORESOURCE_MEM,
},
-
{
.start = PPSS_WDOG_TIMER_IRQ,
.end = PPSS_WDOG_TIMER_IRQ,
@@ -3631,9 +3650,16 @@
},
};
+#define MSM_8960_L1_SIZE SZ_1M
+/*
+ * The actual L2 size is smaller but we need a larger buffer
+ * size to store other dump information
+ */
+#define MSM_8960_L2_SIZE SZ_4M
+
struct msm_cache_dump_platform_data msm8960_cache_dump_pdata = {
- .l2_size = L2_BUFFER_SIZE,
- .l1_size = L1_BUFFER_SIZE,
+ .l2_size = MSM_8960_L2_SIZE,
+ .l1_size = MSM_8960_L1_SIZE,
};
struct platform_device msm8960_cache_dump_device = {
diff --git a/arch/arm/mach-msm/devices-9615.c b/arch/arm/mach-msm/devices-9615.c
index 76d79a6..85d00eb 100644
--- a/arch/arm/mach-msm/devices-9615.c
+++ b/arch/arm/mach-msm/devices-9615.c
@@ -627,8 +627,7 @@
};
struct flash_platform_data msm_nand_data = {
- .parts = NULL,
- .nr_parts = 0,
+ .version = VERSION_2,
};
struct platform_device msm_device_nand = {
diff --git a/arch/arm/mach-msm/devices-iommu.c b/arch/arm/mach-msm/devices-iommu.c
index 2e0253b..f5676d3 100644
--- a/arch/arm/mach-msm/devices-iommu.c
+++ b/arch/arm/mach-msm/devices-iommu.c
@@ -987,8 +987,8 @@
static int __init iommu_init(void)
{
int ret;
- if (!msm_soc_version_supports_iommu()) {
- pr_err("IOMMU is not supported on this SoC version.\n");
+ if (!msm_soc_version_supports_iommu_v1()) {
+ pr_err("IOMMU v1 is not supported on this SoC version.\n");
return -ENODEV;
}
diff --git a/arch/arm/mach-msm/devices-msm7x27a.c b/arch/arm/mach-msm/devices-msm7x27a.c
index 2d79f62..4654606 100644
--- a/arch/arm/mach-msm/devices-msm7x27a.c
+++ b/arch/arm/mach-msm/devices-msm7x27a.c
@@ -417,7 +417,9 @@
},
};
-struct flash_platform_data msm_nand_data;
+struct flash_platform_data msm_nand_data = {
+ .version = VERSION_2,
+};
struct platform_device msm_device_nand = {
.name = "msm_nand",
diff --git a/arch/arm/mach-msm/devices-msm7x30.c b/arch/arm/mach-msm/devices-msm7x30.c
index e055579..069714a 100644
--- a/arch/arm/mach-msm/devices-msm7x30.c
+++ b/arch/arm/mach-msm/devices-msm7x30.c
@@ -656,9 +656,7 @@
};
struct flash_platform_data msm_nand_data = {
- .parts = NULL,
- .nr_parts = 0,
- .interleave = 0,
+ .version = VERSION_2,
};
struct platform_device msm_device_nand = {
diff --git a/arch/arm/mach-msm/devices-msm8x60.c b/arch/arm/mach-msm/devices-msm8x60.c
index d622af2..005ed1f 100644
--- a/arch/arm/mach-msm/devices-msm8x60.c
+++ b/arch/arm/mach-msm/devices-msm8x60.c
@@ -1568,6 +1568,16 @@
/* Sensors DSPS platform data */
#ifdef CONFIG_MSM_DSPS
+#define PPSS_DSPS_TCM_CODE_BASE 0x12000000
+#define PPSS_DSPS_TCM_CODE_SIZE 0x28000
+#define PPSS_DSPS_TCM_BUF_BASE 0x12040000
+#define PPSS_DSPS_TCM_BUF_SIZE 0x4000
+#define PPSS_DSPS_PIPE_BASE 0x12800000
+#define PPSS_DSPS_PIPE_SIZE 0x0 /* 8660 V2 does not use PIPE memory */
+#define PPSS_DSPS_DDR_BASE 0x8fe00000
+#define PPSS_DSPS_DDR_SIZE 0x0 /* 8660 V2 does not use DDR memory */
+#define PPSS_SMEM_BASE 0x40000000
+#define PPSS_SMEM_SIZE 0x4000
#define PPSS_REG_PHYS_BASE 0x12080000
#define MHZ (1000*1000)
@@ -1631,6 +1641,16 @@
.regs = dsps_regs,
.regs_num = ARRAY_SIZE(dsps_regs),
.init = dsps_init1,
+ .tcm_code_start = PPSS_DSPS_TCM_CODE_BASE,
+ .tcm_code_size = PPSS_DSPS_TCM_CODE_SIZE,
+ .tcm_buf_start = PPSS_DSPS_TCM_BUF_BASE,
+ .tcm_buf_size = PPSS_DSPS_TCM_BUF_SIZE,
+ .pipe_start = PPSS_DSPS_PIPE_BASE,
+ .pipe_size = PPSS_DSPS_PIPE_SIZE,
+ .ddr_start = PPSS_DSPS_DDR_BASE,
+ .ddr_size = PPSS_DSPS_DDR_SIZE,
+ .smem_start = PPSS_SMEM_BASE,
+ .smem_size = PPSS_SMEM_SIZE,
.signature = DSPS_SIGNATURE,
};
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index 5734804..4fd262f 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -269,6 +269,7 @@
extern struct platform_device apq_cpudai_slimbus_1_tx;
extern struct platform_device apq_cpudai_slimbus_2_tx;
extern struct platform_device apq_cpudai_slimbus_3_rx;
+extern struct platform_device apq_cpudai_slimbus_3_tx;
extern struct platform_device apq_cpudai_slim_4_rx;
extern struct platform_device apq_cpudai_slim_4_tx;
@@ -396,9 +397,10 @@
extern struct platform_device apq8064_rtb_device;
extern struct platform_device msm8960_cache_dump_device;
+extern struct platform_device apq8064_cache_dump_device;
extern struct platform_device copper_device_tz_log;
-extern struct platform_device msm8974_device_rng;
-
extern struct platform_device mdm_sglte_device;
+
+extern struct platform_device apq_device_tz_log;
diff --git a/arch/arm/mach-msm/gdsc.c b/arch/arm/mach-msm/gdsc.c
new file mode 100644
index 0000000..a77ac24
--- /dev/null
+++ b/arch/arm/mach-msm/gdsc.c
@@ -0,0 +1,191 @@
+/*
+ * 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/io.h>
+#include <linux/iopoll.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+#define PWR_ON_MASK BIT(31)
+#define EN_REST_WAIT_MASK (0xF << 20)
+#define EN_FEW_WAIT_MASK (0xF << 16)
+#define CLK_DIS_WAIT_MASK (0xF << 12)
+#define SW_OVERRIDE_MASK BIT(2)
+#define HW_CONTROL_MASK BIT(1)
+#define SW_COLLAPSE_MASK BIT(0)
+
+/* Wait 2^n CXO cycles between all states. Here, n=2 (4 cycles). */
+#define EN_REST_WAIT_VAL (0x2 << 20)
+#define EN_FEW_WAIT_VAL (0x2 << 16)
+#define CLK_DIS_WAIT_VAL (0x2 << 12)
+
+#define TIMEOUT_US 10
+
+struct gdsc {
+ struct regulator_dev *rdev;
+ struct regulator_desc rdesc;
+ void __iomem *gdscr;
+};
+
+static int gdsc_is_enabled(struct regulator_dev *rdev)
+{
+ struct gdsc *sc = rdev_get_drvdata(rdev);
+
+ return !!(readl_relaxed(sc->gdscr) & PWR_ON_MASK);
+}
+
+static int gdsc_enable(struct regulator_dev *rdev)
+{
+ struct gdsc *sc = rdev_get_drvdata(rdev);
+ uint32_t regval;
+ int ret;
+
+ regval = readl_relaxed(sc->gdscr);
+ regval &= ~SW_COLLAPSE_MASK;
+ writel_relaxed(regval, sc->gdscr);
+
+ ret = readl_tight_poll_timeout(sc->gdscr, regval, regval & PWR_ON_MASK,
+ TIMEOUT_US);
+ if (ret)
+ dev_err(&rdev->dev, "%s enable timed out\n", sc->rdesc.name);
+
+ return ret;
+}
+
+static int gdsc_disable(struct regulator_dev *rdev)
+{
+ struct gdsc *sc = rdev_get_drvdata(rdev);
+ uint32_t regval;
+ int ret;
+
+ regval = readl_relaxed(sc->gdscr);
+ regval |= SW_COLLAPSE_MASK;
+ writel_relaxed(regval, sc->gdscr);
+
+ ret = readl_tight_poll_timeout(sc->gdscr, regval,
+ !(regval & PWR_ON_MASK), TIMEOUT_US);
+ if (ret)
+ dev_err(&rdev->dev, "%s disable timed out\n", sc->rdesc.name);
+
+ return ret;
+}
+
+static struct regulator_ops gdsc_ops = {
+ .is_enabled = gdsc_is_enabled,
+ .enable = gdsc_enable,
+ .disable = gdsc_disable,
+};
+
+static int __devinit gdsc_probe(struct platform_device *pdev)
+{
+ static atomic_t gdsc_count = ATOMIC_INIT(-1);
+ struct regulator_init_data *init_data;
+ struct resource *res;
+ struct gdsc *sc;
+ uint32_t regval;
+ int ret;
+
+ sc = devm_kzalloc(&pdev->dev, sizeof(struct gdsc), GFP_KERNEL);
+ if (sc == NULL)
+ return -ENOMEM;
+
+ init_data = of_get_regulator_init_data(&pdev->dev);
+ if (init_data == NULL)
+ return -ENOMEM;
+
+ if (of_get_property(pdev->dev.of_node, "parent-supply", NULL))
+ init_data->supply_regulator = "parent";
+
+ ret = of_property_read_string(pdev->dev.of_node, "regulator-name",
+ &sc->rdesc.name);
+ if (ret)
+ return ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL)
+ return -EINVAL;
+ sc->gdscr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (sc->gdscr == NULL)
+ return -ENOMEM;
+
+ sc->rdesc.id = atomic_inc_return(&gdsc_count);
+ sc->rdesc.ops = &gdsc_ops;
+ sc->rdesc.type = REGULATOR_VOLTAGE;
+ sc->rdesc.owner = THIS_MODULE;
+ platform_set_drvdata(pdev, sc);
+
+ /*
+ * Disable HW trigger: collapse/restore occur based on registers writes.
+ * Disable SW override: Use hardware state-machine for sequencing.
+ */
+ regval = readl_relaxed(sc->gdscr);
+ regval &= ~(HW_CONTROL_MASK | SW_OVERRIDE_MASK);
+
+ /* Configure wait time between states. */
+ regval &= ~(EN_REST_WAIT_MASK | EN_FEW_WAIT_MASK | CLK_DIS_WAIT_MASK);
+ regval |= EN_REST_WAIT_VAL | EN_FEW_WAIT_VAL | CLK_DIS_WAIT_VAL;
+ writel_relaxed(regval, sc->gdscr);
+
+ sc->rdev = regulator_register(&sc->rdesc, &pdev->dev, init_data, sc,
+ pdev->dev.of_node);
+ if (IS_ERR(sc->rdev)) {
+ dev_err(&pdev->dev, "regulator_register(\"%s\") failed.\n",
+ sc->rdesc.name);
+ return PTR_ERR(sc->rdev);
+ }
+
+ return 0;
+}
+
+static int __devexit gdsc_remove(struct platform_device *pdev)
+{
+ struct gdsc *sc = platform_get_drvdata(pdev);
+ regulator_unregister(sc->rdev);
+ return 0;
+}
+
+static struct of_device_id gdsc_match_table[] = {
+ { .compatible = "qcom,gdsc" },
+ {}
+};
+
+static struct platform_driver gdsc_driver = {
+ .probe = gdsc_probe,
+ .remove = __devexit_p(gdsc_remove),
+ .driver = {
+ .name = "gdsc",
+ .of_match_table = gdsc_match_table,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init gdsc_init(void)
+{
+ return platform_driver_register(&gdsc_driver);
+}
+subsys_initcall(gdsc_init);
+
+static void __exit gdsc_exit(void)
+{
+ platform_driver_unregister(&gdsc_driver);
+}
+module_exit(gdsc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Copper GDSC power rail regulator driver");
diff --git a/arch/arm/mach-msm/gss-8064.c b/arch/arm/mach-msm/gss-8064.c
index 3c475d6..126f8e0 100644
--- a/arch/arm/mach-msm/gss-8064.c
+++ b/arch/arm/mach-msm/gss-8064.c
@@ -23,6 +23,7 @@
#include <linux/fs.h>
#include <mach/irqs.h>
+#include <mach/msm_smsm.h>
#include <mach/scm.h>
#include <mach/peripheral-loader.h>
#include <mach/subsystem_restart.h>
@@ -42,6 +43,32 @@
static int crash_shutdown;
+#define MAX_SSR_REASON_LEN 81U
+
+static void log_gss_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("GSS subsystem failure reason: (unknown, smem_get_entry failed).\n");
+ return;
+ }
+ if (!smem_reason[0]) {
+ pr_err("GSS 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("GSS subsystem failure reason: %s.\n", reason);
+
+ smem_reason[0] = '\0';
+ wmb();
+}
+
static void gss_fatal_fn(struct work_struct *work)
{
uint32_t panic_smsm_states = SMSM_RESET | SMSM_SYSTEM_DOWNLOAD;
@@ -58,6 +85,7 @@
pr_err("GSS SMSM state changed to SMSM_RESET.\n"
"Probable err_fatal on the GSS. "
"Calling subsystem restart...\n");
+ log_gss_sfr();
subsystem_restart("gss");
} else if (gss_state & reset_smsm_states) {
@@ -68,6 +96,7 @@
kernel_restart(NULL);
} else {
/* TODO: Bus unlock code/sequence goes _here_ */
+ log_gss_sfr();
subsystem_restart("gss");
}
}
@@ -84,6 +113,7 @@
pr_err("GSS SMSM state changed to SMSM_RESET.\n"
"Probable err_fatal on the GSS. "
"Calling subsystem restart...\n");
+ log_gss_sfr();
subsystem_restart("gss");
}
}
diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h
index 17ac3ac..5e2eaf1 100644
--- a/arch/arm/mach-msm/include/mach/board.h
+++ b/arch/arm/mach-msm/include/mach/board.h
@@ -556,6 +556,7 @@
void msm_map_fsm9xxx_io(void);
void msm_map_copper_io(void);
void msm_map_msm8625_io(void);
+void msm_map_msm9625_io(void);
void msm_init_irq(void);
void msm_copper_init_irq(void);
void vic_handle_irq(struct pt_regs *regs);
diff --git a/arch/arm/mach-msm/include/mach/iommu.h b/arch/arm/mach-msm/include/mach/iommu.h
index a6f27d7..b57ae10 100644
--- a/arch/arm/mach-msm/include/mach/iommu.h
+++ b/arch/arm/mach-msm/include/mach/iommu.h
@@ -18,6 +18,7 @@
#include <mach/socinfo.h>
extern pgprot_t pgprot_kernel;
+extern struct platform_device *msm_iommu_root_dev;
/* Domain attributes */
#define MSM_IOMMU_DOMAIN_PT_CACHEABLE 0x1
@@ -104,6 +105,7 @@
* message and dump useful IOMMU registers.
*/
irqreturn_t msm_iommu_fault_handler(int irq, void *dev_id);
+irqreturn_t msm_iommu_fault_handler_v2(int irq, void *dev_id);
#ifdef CONFIG_MSM_IOMMU
/*
@@ -121,8 +123,17 @@
#endif
-static inline int msm_soc_version_supports_iommu(void)
+static inline int msm_soc_version_supports_iommu_v1(void)
{
+#ifdef CONFIG_OF
+ struct device_node *node;
+
+ node = of_find_compatible_node(NULL, NULL, "qcom,msm-smmu-v2");
+ if (node) {
+ of_node_put(node);
+ return 0;
+ }
+#endif
if (cpu_is_msm8960() &&
SOCINFO_VERSION_MAJOR(socinfo_get_version()) < 2)
return 0;
diff --git a/arch/arm/mach-msm/include/mach/iommu_hw-v2.h b/arch/arm/mach-msm/include/mach/iommu_hw-v2.h
new file mode 100644
index 0000000..fac13b3
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/iommu_hw-v2.h
@@ -0,0 +1,2111 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_IOMMU_HW_V2_H
+#define __ARCH_ARM_MACH_MSM_IOMMU_HW_V2_H
+
+#define CTX_SHIFT 12
+#define CTX_OFFSET 0x8000
+
+#define MAX_NUM_SMR 128
+
+#define GET_GLOBAL_REG(reg, base) (readl_relaxed((base) + (reg)))
+#define GET_CTX_REG(reg, base, ctx) \
+ (readl_relaxed((base) + CTX_OFFSET + (reg) + ((ctx) << CTX_SHIFT)))
+
+#define SET_GLOBAL_REG(reg, base, val) writel_relaxed((val), ((base) + (reg)))
+
+#define SET_CTX_REG(reg, base, ctx, val) \
+ writel_relaxed((val), \
+ ((base) + CTX_OFFSET + (reg) + ((ctx) << CTX_SHIFT)))
+
+/* Wrappers for numbered registers */
+#define SET_GLOBAL_REG_N(b, n, r, v) SET_GLOBAL_REG((b), ((r) + (n << 2)), (v))
+#define GET_GLOBAL_REG_N(b, n, r) GET_GLOBAL_REG((b), ((r) + (n << 2)))
+
+/* Field wrappers */
+#define GET_GLOBAL_FIELD(b, r, F) \
+ GET_FIELD(((b) + (r)), r##_##F##_MASK, r##_##F##_SHIFT)
+#define GET_CONTEXT_FIELD(b, c, r, F) \
+ GET_FIELD(((b) + CTX_OFFSET + (r) + ((c) << CTX_SHIFT)), \
+ r##_##F##_MASK, r##_##F##_SHIFT)
+
+#define SET_GLOBAL_FIELD(b, r, F, v) \
+ SET_FIELD(((b) + (r)), r##_##F##_MASK, r##_##F##_SHIFT, (v))
+#define SET_CONTEXT_FIELD(b, c, r, F, v) \
+ SET_FIELD(((b) + CTX_OFFSET + (r) + ((c) << CTX_SHIFT)), \
+ r##_##F##_MASK, r##_##F##_SHIFT, (v))
+
+/* Wrappers for numbered field registers */
+#define SET_GLOBAL_FIELD_N(b, n, r, F, v) \
+ SET_FIELD(((b) + ((n) << 2) + (r)), r##_##F##_MASK, r##_##F##_SHIFT, v)
+#define GET_GLOBAL_FIELD_N(b, n, r, F) \
+ GET_FIELD(((b) + ((n) << 2) + (r)), r##_##F##_MASK, r##_##F##_SHIFT)
+
+#define GET_FIELD(addr, mask, shift) ((readl_relaxed(addr) >> (shift)) & (mask))
+
+#define SET_FIELD(addr, mask, shift, v) \
+do { \
+ int t = readl_relaxed(addr); \
+ writel_relaxed((t & ~((mask) << (shift))) + (((v) & \
+ (mask)) << (shift)), addr); \
+} while (0)
+
+
+/* Global register space 0 setters / getters */
+#define SET_CR0(b, v) SET_GLOBAL_REG(CR0, (b), (v))
+#define SET_SCR1(b, v) SET_GLOBAL_REG(SCR1, (b), (v))
+#define SET_CR2(b, v) SET_GLOBAL_REG(CR2, (b), (v))
+#define SET_ACR(b, v) SET_GLOBAL_REG(ACR, (b), (v))
+#define SET_IDR0(b, N, v) SET_GLOBAL_REG(IDR0, (b), (v))
+#define SET_IDR1(b, N, v) SET_GLOBAL_REG(IDR1, (b), (v))
+#define SET_IDR2(b, N, v) SET_GLOBAL_REG(IDR2, (b), (v))
+#define SET_IDR7(b, N, v) SET_GLOBAL_REG(IDR7, (b), (v))
+#define SET_GFAR(b, v) SET_GLOBAL_REG(GFAR, (b), (v))
+#define SET_GFSR(b, v) SET_GLOBAL_REG(GFSR, (b), (v))
+#define SET_GFSRRESTORE(b, v) SET_GLOBAL_REG(GFSRRESTORE, (b), (v))
+#define SET_GFSYNR0(b, v) SET_GLOBAL_REG(GFSYNR0, (b), (v))
+#define SET_GFSYNR1(b, v) SET_GLOBAL_REG(GFSYNR1, (b), (v))
+#define SET_GFSYNR2(b, v) SET_GLOBAL_REG(GFSYNR2, (b), (v))
+#define SET_TLBIVMID(b, v) SET_GLOBAL_REG(TLBIVMID, (b), (v))
+#define SET_TLBIALLNSNH(b, v) SET_GLOBAL_REG(TLBIALLNSNH, (b), (v))
+#define SET_TLBIALLH(b, v) SET_GLOBAL_REG(TLBIALLH, (b), (v))
+#define SET_TLBGSYNC(b, v) SET_GLOBAL_REG(TLBGSYNC, (b), (v))
+#define SET_TLBGSTATUS(b, v) SET_GLOBAL_REG(TLBSTATUS, (b), (v))
+#define SET_TLBIVAH(b, v) SET_GLOBAL_REG(TLBIVAH, (b), (v))
+#define SET_GATS1UR(b, v) SET_GLOBAL_REG(GATS1UR, (b), (v))
+#define SET_GATS1UW(b, v) SET_GLOBAL_REG(GATS1UW, (b), (v))
+#define SET_GATS1PR(b, v) SET_GLOBAL_REG(GATS1PR, (b), (v))
+#define SET_GATS1PW(b, v) SET_GLOBAL_REG(GATS1PW, (b), (v))
+#define SET_GATS12UR(b, v) SET_GLOBAL_REG(GATS12UR, (b), (v))
+#define SET_GATS12UW(b, v) SET_GLOBAL_REG(GATS12UW, (b), (v))
+#define SET_GATS12PR(b, v) SET_GLOBAL_REG(GATS12PR, (b), (v))
+#define SET_GATS12PW(b, v) SET_GLOBAL_REG(GATS12PW, (b), (v))
+#define SET_GPAR(b, v) SET_GLOBAL_REG(GPAR, (b), (v))
+#define SET_GATSR(b, v) SET_GLOBAL_REG(GATSR, (b), (v))
+#define SET_NSCR0(b, v) SET_GLOBAL_REG(NSCR0, (b), (v))
+#define SET_NSCR2(b, v) SET_GLOBAL_REG(NSCR2, (b), (v))
+#define SET_NSACR(b, v) SET_GLOBAL_REG(NSACR, (b), (v))
+#define SET_PMCR(b, v) SET_GLOBAL_REG(PMCR, (b), (v))
+#define SET_SMR_N(b, N, v) SET_GLOBAL_REG_N(SMR, N, (b), (v))
+#define SET_S2CR_N(b, N, v) SET_GLOBAL_REG_N(S2CR, N, (b), (v))
+
+#define GET_CR0(b) GET_GLOBAL_REG(CR0, (b))
+#define GET_SCR1(b) GET_GLOBAL_REG(SCR1, (b))
+#define GET_CR2(b) GET_GLOBAL_REG(CR2, (b))
+#define GET_ACR(b) GET_GLOBAL_REG(ACR, (b))
+#define GET_IDR0(b, N) GET_GLOBAL_REG(IDR0, (b))
+#define GET_IDR1(b, N) GET_GLOBAL_REG(IDR1, (b))
+#define GET_IDR2(b, N) GET_GLOBAL_REG(IDR2, (b))
+#define GET_IDR7(b, N) GET_GLOBAL_REG(IDR7, (b))
+#define GET_GFAR(b) GET_GLOBAL_REG(GFAR, (b))
+#define GET_GFSR(b) GET_GLOBAL_REG(GFSR, (b))
+#define GET_GFSRRESTORE(b) GET_GLOBAL_REG(GFSRRESTORE, (b))
+#define GET_GFSYNR0(b) GET_GLOBAL_REG(GFSYNR0, (b))
+#define GET_GFSYNR1(b) GET_GLOBAL_REG(GFSYNR1, (b))
+#define GET_GFSYNR2(b) GET_GLOBAL_REG(GFSYNR2, (b))
+#define GET_TLBIVMID(b) GET_GLOBAL_REG(TLBIVMID, (b))
+#define GET_TLBIALLNSNH(b) GET_GLOBAL_REG(TLBIALLNSNH, (b))
+#define GET_TLBIALLH(b) GET_GLOBAL_REG(TLBIALLH, (b))
+#define GET_TLBGSYNC(b) GET_GLOBAL_REG(TLBGSYNC, (b))
+#define GET_TLBGSTATUS(b) GET_GLOBAL_REG(TLBSTATUS, (b))
+#define GET_TLBIVAH(b) GET_GLOBAL_REG(TLBIVAH, (b))
+#define GET_GATS1UR(b) GET_GLOBAL_REG(GATS1UR, (b))
+#define GET_GATS1UW(b) GET_GLOBAL_REG(GATS1UW, (b))
+#define GET_GATS1PR(b) GET_GLOBAL_REG(GATS1PR, (b))
+#define GET_GATS1PW(b) GET_GLOBAL_REG(GATS1PW, (b))
+#define GET_GATS12UR(b) GET_GLOBAL_REG(GATS12UR, (b))
+#define GET_GATS12UW(b) GET_GLOBAL_REG(GATS12UW, (b))
+#define GET_GATS12PR(b) GET_GLOBAL_REG(GATS12PR, (b))
+#define GET_GATS12PW(b) GET_GLOBAL_REG(GATS12PW, (b))
+#define GET_GPAR(b) GET_GLOBAL_REG(GPAR, (b))
+#define GET_GATSR(b) GET_GLOBAL_REG(GATSR, (b))
+#define GET_NSCR0(b) GET_GLOBAL_REG(NSCR0, (b))
+#define GET_NSCR2(b) GET_GLOBAL_REG(NSCR2, (b))
+#define GET_NSACR(b) GET_GLOBAL_REG(NSACR, (b))
+#define GET_PMCR(b, v) GET_GLOBAL_REG(PMCR, (b))
+#define GET_SMR_N(b, N) GET_GLOBAL_REG_N(SMR, N, (b))
+#define GET_S2CR_N(b, N) GET_GLOBAL_REG_N(S2CR, N, (b))
+
+/* Global register space 1 setters / getters */
+#define SET_CBAR_N(b, N, v) SET_GLOBAL_REG_N(CBAR, N, (b), (v))
+#define SET_CBFRSYNRA_N(b, N, v) SET_GLOBAL_REG_N(CBFRSYNRA, N, (b), (v))
+
+#define GET_CBAR_N(b, N) GET_GLOBAL_REG_N(CBAR, N, (b))
+#define GET_CBFRSYNRA_N(b, N) GET_GLOBAL_REG_N(CBFRSYNRA, N, (b))
+
+/* Implementation defined register setters/getters */
+#define SET_PREDICTIONDIS0(b, v) SET_GLOBAL_REG(PREDICTIONDIS0, (b), (v))
+#define SET_PREDICTIONDIS1(b, v) SET_GLOBAL_REG(PREDICTIONDIS1, (b), (v))
+#define SET_S1L1BFBLP0(b, v) SET_GLOBAL_REG(S1L1BFBLP0, (b), (v))
+
+/* SSD register setters/getters */
+#define SET_SSDR_N(b, N, v) SET_GLOBAL_REG_N(SSDR_N, N, (b), (v))
+
+#define GET_SSDR_N(b, N) GET_GLOBAL_REG_N(SSDR_N, N, (b))
+
+/* Context bank register setters/getters */
+#define SET_SCTLR(b, c, v) SET_CTX_REG(CB_SCTLR, (b), (c), (v))
+#define SET_ACTLR(b, c, v) SET_CTX_REG(CB_ACTLR, (b), (c), (v))
+#define SET_RESUME(b, c, v) SET_CTX_REG(CB_RESUME, (b), (c), (v))
+#define SET_TTBR0(b, c, v) SET_CTX_REG(CB_TTBR0, (b), (c), (v))
+#define SET_TTBR1(b, c, v) SET_CTX_REG(CB_TTBR1, (b), (c), (v))
+#define SET_TTBCR(b, c, v) SET_CTX_REG(CB_TTBCR, (b), (c), (v))
+#define SET_CONTEXTIDR(b, c, v) SET_CTX_REG(CB_CONTEXTIDR, (b), (c), (v))
+#define SET_PRRR(b, c, v) SET_CTX_REG(CB_PRRR, (b), (c), (v))
+#define SET_NMRR(b, c, v) SET_CTX_REG(CB_NMRR, (b), (c), (v))
+#define SET_PAR(b, c, v) SET_CTX_REG(CB_PAR, (b), (c), (v))
+#define SET_FSR(b, c, v) SET_CTX_REG(CB_FSR, (b), (c), (v))
+#define SET_FSRRESTORE(b, c, v) SET_CTX_REG(CB_FSRRESTORE, (b), (c), (v))
+#define SET_FAR(b, c, v) SET_CTX_REG(CB_FAR, (b), (c), (v))
+#define SET_FSYNR0(b, c, v) SET_CTX_REG(CB_FSYNR0, (b), (c), (v))
+#define SET_FSYNR1(b, c, v) SET_CTX_REG(CB_FSYNR1, (b), (c), (v))
+#define SET_TLBIVA(b, c, v) SET_CTX_REG(CB_TLBIVA, (b), (c), (v))
+#define SET_TLBIVAA(b, c, v) SET_CTX_REG(CB_TLBIVAA, (b), (c), (v))
+#define SET_TLBIASID(b, c, v) SET_CTX_REG(CB_TLBIASID, (b), (c), (v))
+#define SET_TLBIALL(b, c, v) SET_CTX_REG(CB_TLBIALL, (b), (c), (v))
+#define SET_TLBIVAL(b, c, v) SET_CTX_REG(CB_TLBIVAL, (b), (c), (v))
+#define SET_TLBIVAAL(b, c, v) SET_CTX_REG(CB_TLBIVAAL, (b), (c), (v))
+#define SET_TLBSYNC(b, c, v) SET_CTX_REG(CB_TLBSYNC, (b), (c), (v))
+#define SET_TLBSTATUS(b, c, v) SET_CTX_REG(CB_TLBSTATUS, (b), (c), (v))
+#define SET_ATS1PR(b, c, v) SET_CTX_REG(CB_ATS1PR, (b), (c), (v))
+#define SET_ATS1PW(b, c, v) SET_CTX_REG(CB_ATS1PW, (b), (c), (v))
+#define SET_ATS1UR(b, c, v) SET_CTX_REG(CB_ATS1UR, (b), (c), (v))
+#define SET_ATS1UW(b, c, v) SET_CTX_REG(CB_ATS1UW, (b), (c), (v))
+#define SET_ATSR(b, c, v) SET_CTX_REG(CB_ATSR, (b), (c), (v))
+
+#define GET_SCTLR(b, c) GET_CTX_REG(CB_SCTLR, (b), (c))
+#define GET_ACTLR(b, c) GET_CTX_REG(CB_ACTLR, (b), (c))
+#define GET_RESUME(b, c) GET_CTX_REG(CB_RESUME, (b), (c))
+#define GET_TTBR0(b, c) GET_CTX_REG(CB_TTBR0, (b), (c))
+#define GET_TTBR1(b, c) GET_CTX_REG(CB_TTBR1, (b), (c))
+#define GET_TTBCR(b, c) GET_CTX_REG(CB_TTBCR, (b), (c))
+#define GET_CONTEXTIDR(b, c) GET_CTX_REG(CB_CONTEXTIDR, (b), (c))
+#define GET_PRRR(b, c) GET_CTX_REG(CB_PRRR, (b), (c))
+#define GET_NMRR(b, c) GET_CTX_REG(CB_NMRR, (b), (c))
+#define GET_PAR(b, c) GET_CTX_REG(CB_PAR, (b), (c))
+#define GET_FSR(b, c) GET_CTX_REG(CB_FSR, (b), (c))
+#define GET_FSRRESTORE(b, c) GET_CTX_REG(CB_FSRRESTORE, (b), (c))
+#define GET_FAR(b, c) GET_CTX_REG(CB_FAR, (b), (c))
+#define GET_FSYNR0(b, c) GET_CTX_REG(CB_FSYNR0, (b), (c))
+#define GET_FSYNR1(b, c) GET_CTX_REG(CB_FSYNR1, (b), (c))
+#define GET_TLBIVA(b, c) GET_CTX_REG(CB_TLBIVA, (b), (c))
+#define GET_TLBIVAA(b, c) GET_CTX_REG(CB_TLBIVAA, (b), (c))
+#define GET_TLBIASID(b, c) GET_CTX_REG(CB_TLBIASID, (b), (c))
+#define GET_TLBIALL(b, c) GET_CTX_REG(CB_TLBIALL, (b), (c))
+#define GET_TLBIVAL(b, c) GET_CTX_REG(CB_TLBIVAL, (b), (c))
+#define GET_TLBIVAAL(b, c) GET_CTX_REG(CB_TLBIVAAL, (b), (c))
+#define GET_TLBSYNC(b, c) GET_CTX_REG(CB_TLBSYNC, (b), (c))
+#define GET_TLBSTATUS(b, c) GET_CTX_REG(CB_TLBSTATUS, (b), (c))
+#define GET_ATS1PR(b, c) GET_CTX_REG(CB_ATS1PR, (b), (c))
+#define GET_ATS1PW(b, c) GET_CTX_REG(CB_ATS1PW, (b), (c))
+#define GET_ATS1UR(b, c) GET_CTX_REG(CB_ATS1UR, (b), (c))
+#define GET_ATS1UW(b, c) GET_CTX_REG(CB_ATS1UW, (b), (c))
+#define GET_ATSR(b, c) GET_CTX_REG(CB_ATSR, (b), (c))
+
+/* Global Register field setters / getters */
+/* Configuration Register: CR0 */
+#define SET_CR0_NSCFG(b, v) SET_GLOBAL_FIELD(b, CR0, NSCFG, v)
+#define SET_CR0_WACFG(b, v) SET_GLOBAL_FIELD(b, CR0, WACFG, v)
+#define SET_CR0_RACFG(b, v) SET_GLOBAL_FIELD(b, CR0, RACFG, v)
+#define SET_CR0_SHCFG(b, v) SET_GLOBAL_FIELD(b, CR0, SHCFG, v)
+#define SET_CR0_SMCFCFG(b, v) SET_GLOBAL_FIELD(b, CR0, SMCFCFG, v)
+#define SET_CR0_MTCFG(b, v) SET_GLOBAL_FIELD(b, CR0, MTCFG, v)
+#define SET_CR0_BSU(b, v) SET_GLOBAL_FIELD(b, CR0, BSU, v)
+#define SET_CR0_FB(b, v) SET_GLOBAL_FIELD(b, CR0, FB, v)
+#define SET_CR0_PTM(b, v) SET_GLOBAL_FIELD(b, CR0, PTM, v)
+#define SET_CR0_VMIDPNE(b, v) SET_GLOBAL_FIELD(b, CR0, VMIDPNE, v)
+#define SET_CR0_USFCFG(b, v) SET_GLOBAL_FIELD(b, CR0, USFCFG, v)
+#define SET_CR0_GSE(b, v) SET_GLOBAL_FIELD(b, CR0, GSE, v)
+#define SET_CR0_STALLD(b, v) SET_GLOBAL_FIELD(b, CR0, STALLD, v)
+#define SET_CR0_TRANSIENTCFG(b, v) SET_GLOBAL_FIELD(b, CR0, TRANSIENTCFG, v)
+#define SET_CR0_GCFGFIE(b, v) SET_GLOBAL_FIELD(b, CR0, GCFGFIE, v)
+#define SET_CR0_GCFGFRE(b, v) SET_GLOBAL_FIELD(b, CR0, GCFGFRE, v)
+#define SET_CR0_GFIE(b, v) SET_GLOBAL_FIELD(b, CR0, GFIE, v)
+#define SET_CR0_GFRE(b, v) SET_GLOBAL_FIELD(b, CR0, GFRE, v)
+#define SET_CR0_CLIENTPD(b, v) SET_GLOBAL_FIELD(b, CR0, CLIENTPD, v)
+
+#define GET_CR0_NSCFG(b) GET_GLOBAL_FIELD(b, CR0, NSCFG)
+#define GET_CR0_WACFG(b) GET_GLOBAL_FIELD(b, CR0, WACFG)
+#define GET_CR0_RACFG(b) GET_GLOBAL_FIELD(b, CR0, RACFG)
+#define GET_CR0_SHCFG(b) GET_GLOBAL_FIELD(b, CR0, SHCFG)
+#define GET_CR0_SMCFCFG(b) GET_GLOBAL_FIELD(b, CR0, SMCFCFG)
+#define GET_CR0_MTCFG(b) GET_GLOBAL_FIELD(b, CR0, MTCFG)
+#define GET_CR0_BSU(b) GET_GLOBAL_FIELD(b, CR0, BSU)
+#define GET_CR0_FB(b) GET_GLOBAL_FIELD(b, CR0, FB)
+#define GET_CR0_PTM(b) GET_GLOBAL_FIELD(b, CR0, PTM)
+#define GET_CR0_VMIDPNE(b) GET_GLOBAL_FIELD(b, CR0, VMIDPNE)
+#define GET_CR0_USFCFG(b) GET_GLOBAL_FIELD(b, CR0, USFCFG)
+#define GET_CR0_GSE(b) GET_GLOBAL_FIELD(b, CR0, GSE)
+#define GET_CR0_STALLD(b) GET_GLOBAL_FIELD(b, CR0, STALLD)
+#define GET_CR0_TRANSIENTCFG(b) GET_GLOBAL_FIELD(b, CR0, TRANSIENTCFG)
+#define GET_CR0_GCFGFIE(b) GET_GLOBAL_FIELD(b, CR0, GCFGFIE)
+#define GET_CR0_GCFGFRE(b) GET_GLOBAL_FIELD(b, CR0, GCFGFRE)
+#define GET_CR0_GFIE(b) GET_GLOBAL_FIELD(b, CR0, GFIE)
+#define GET_CR0_GFRE(b) GET_GLOBAL_FIELD(b, CR0, GFRE)
+#define GET_CR0_CLIENTPD(b) GET_GLOBAL_FIELD(b, CR0, CLIENTPD)
+
+/* Configuration Register: CR2 */
+#define SET_CR2_BPVMID(b, v) SET_GLOBAL_FIELD(b, CR2, BPVMID, v)
+
+#define GET_CR2_BPVMID(b) GET_GLOBAL_FIELD(b, CR2, BPVMID)
+
+/* Global Address Translation, Stage 1, Privileged Read: GATS1PR */
+#define SET_GATS1PR_ADDR(b, v) SET_GLOBAL_FIELD(b, GATS1PR, ADDR, v)
+#define SET_GATS1PR_NDX(b, v) SET_GLOBAL_FIELD(b, GATS1PR, NDX, v)
+
+#define GET_GATS1PR_ADDR(b) GET_GLOBAL_FIELD(b, GATS1PR, ADDR)
+#define GET_GATS1PR_NDX(b) GET_GLOBAL_FIELD(b, GATS1PR, NDX)
+
+/* Global Address Translation, Stage 1, Privileged Write: GATS1PW */
+#define SET_GATS1PW_ADDR(b, v) SET_GLOBAL_FIELD(b, GATS1PW, ADDR, v)
+#define SET_GATS1PW_NDX(b, v) SET_GLOBAL_FIELD(b, GATS1PW, NDX, v)
+
+#define GET_GATS1PW_ADDR(b) GET_GLOBAL_FIELD(b, GATS1PW, ADDR)
+#define GET_GATS1PW_NDX(b) GET_GLOBAL_FIELD(b, GATS1PW, NDX)
+
+/* Global Address Translation, Stage 1, User Read: GATS1UR */
+#define SET_GATS1UR_ADDR(b, v) SET_GLOBAL_FIELD(b, GATS1UR, ADDR, v)
+#define SET_GATS1UR_NDX(b, v) SET_GLOBAL_FIELD(b, GATS1UR, NDX, v)
+
+#define GET_GATS1UR_ADDR(b) GET_GLOBAL_FIELD(b, GATS1UR, ADDR)
+#define GET_GATS1UR_NDX(b) GET_GLOBAL_FIELD(b, GATS1UR, NDX)
+
+/* Global Address Translation, Stage 1, User Read: GATS1UW */
+#define SET_GATS1UW_ADDR(b, v) SET_GLOBAL_FIELD(b, GATS1UW, ADDR, v)
+#define SET_GATS1UW_NDX(b, v) SET_GLOBAL_FIELD(b, GATS1UW, NDX, v)
+
+#define GET_GATS1UW_ADDR(b) GET_GLOBAL_FIELD(b, GATS1UW, ADDR)
+#define GET_GATS1UW_NDX(b) GET_GLOBAL_FIELD(b, GATS1UW, NDX)
+
+/* Global Address Translation, Stage 1 and 2, Privileged Read: GATS12PR */
+#define SET_GATS12PR_ADDR(b, v) SET_GLOBAL_FIELD(b, GATS12PR, ADDR, v)
+#define SET_GATS12PR_NDX(b, v) SET_GLOBAL_FIELD(b, GATS12PR, NDX, v)
+
+#define GET_GATS12PR_ADDR(b) GET_GLOBAL_FIELD(b, GATS12PR, ADDR)
+#define GET_GATS12PR_NDX(b) GET_GLOBAL_FIELD(b, GATS12PR, NDX)
+
+/* Global Address Translation, Stage 1, Privileged Write: GATS1PW */
+#define SET_GATS12PW_ADDR(b, v) SET_GLOBAL_FIELD(b, GATS12PW, ADDR, v)
+#define SET_GATS12PW_NDX(b, v) SET_GLOBAL_FIELD(b, GATS12PW, NDX, v)
+
+#define GET_GATS12PW_ADDR(b) GET_GLOBAL_FIELD(b, GATS12PW, ADDR)
+#define GET_GATS12PW_NDX(b) GET_GLOBAL_FIELD(b, GATS12PW, NDX)
+
+/* Global Address Translation, Stage 1, User Read: GATS1UR */
+#define SET_GATS12UR_ADDR(b, v) SET_GLOBAL_FIELD(b, GATS12UR, ADDR, v)
+#define SET_GATS12UR_NDX(b, v) SET_GLOBAL_FIELD(b, GATS12UR, NDX, v)
+
+#define GET_GATS12UR_ADDR(b) GET_GLOBAL_FIELD(b, GATS12UR, ADDR)
+#define GET_GATS12UR_NDX(b) GET_GLOBAL_FIELD(b, GATS12UR, NDX)
+
+/* Global Address Translation, Stage 1, User Read: GATS1UW */
+#define SET_GATS12UW_ADDR(b, v) SET_GLOBAL_FIELD(b, GATS12UW, ADDR, v)
+#define SET_GATS12UW_NDX(b, v) SET_GLOBAL_FIELD(b, GATS12UW, NDX, v)
+
+#define GET_GATS12UW_ADDR(b) GET_GLOBAL_FIELD(b, GATS12UW, ADDR)
+#define GET_GATS12UW_NDX(b) GET_GLOBAL_FIELD(b, GATS12UW, NDX)
+
+/* Global Address Translation Status Register: GATSR */
+#define SET_GATSR_ACTIVE(b, v) SET_GLOBAL_FIELD(b, GATSR, ACTIVE, v)
+
+#define GET_GATSR_ACTIVE(b) GET_GLOBAL_FIELD(b, GATSR, ACTIVE)
+
+/* Global Fault Address Register: GFAR */
+#define SET_GFAR_FADDR(b, v) SET_GLOBAL_FIELD(b, GFAR, FADDR, v)
+
+#define GET_GFAR_FADDR(b) GET_GLOBAL_FIELD(b, GFAR, FADDR)
+
+/* Global Fault Status Register: GFSR */
+#define SET_GFSR_ICF(b, v) SET_GLOBAL_FIELD(b, GFSR, ICF, v)
+#define SET_GFSR_USF(b, v) SET_GLOBAL_FIELD(b, GFSR, USF, v)
+#define SET_GFSR_SMCF(b, v) SET_GLOBAL_FIELD(b, GFSR, SMCF, v)
+#define SET_GFSR_UCBF(b, v) SET_GLOBAL_FIELD(b, GFSR, UCBF, v)
+#define SET_GFSR_UCIF(b, v) SET_GLOBAL_FIELD(b, GFSR, UCIF, v)
+#define SET_GFSR_CAF(b, v) SET_GLOBAL_FIELD(b, GFSR, CAF, v)
+#define SET_GFSR_EF(b, v) SET_GLOBAL_FIELD(b, GFSR, EF, v)
+#define SET_GFSR_PF(b, v) SET_GLOBAL_FIELD(b, GFSR, PF, v)
+#define SET_GFSR_MULTI(b, v) SET_GLOBAL_FIELD(b, GFSR, MULTI, v)
+
+#define GET_GFSR_ICF(b) GET_GLOBAL_FIELD(b, GFSR, ICF)
+#define GET_GFSR_USF(b) GET_GLOBAL_FIELD(b, GFSR, USF)
+#define GET_GFSR_SMCF(b) GET_GLOBAL_FIELD(b, GFSR, SMCF)
+#define GET_GFSR_UCBF(b) GET_GLOBAL_FIELD(b, GFSR, UCBF)
+#define GET_GFSR_UCIF(b) GET_GLOBAL_FIELD(b, GFSR, UCIF)
+#define GET_GFSR_CAF(b) GET_GLOBAL_FIELD(b, GFSR, CAF)
+#define GET_GFSR_EF(b) GET_GLOBAL_FIELD(b, GFSR, EF)
+#define GET_GFSR_PF(b) GET_GLOBAL_FIELD(b, GFSR, PF)
+#define GET_GFSR_MULTI(b) GET_GLOBAL_FIELD(b, GFSR, MULTI)
+
+/* Global Fault Syndrome Register 0: GFSYNR0 */
+#define SET_GFSYNR0_NESTED(b, v) SET_GLOBAL_FIELD(b, GFSYNR0, NESTED, v)
+#define SET_GFSYNR0_WNR(b, v) SET_GLOBAL_FIELD(b, GFSYNR0, WNR, v)
+#define SET_GFSYNR0_PNU(b, v) SET_GLOBAL_FIELD(b, GFSYNR0, PNU, v)
+#define SET_GFSYNR0_IND(b, v) SET_GLOBAL_FIELD(b, GFSYNR0, IND, v)
+#define SET_GFSYNR0_NSSTATE(b, v) SET_GLOBAL_FIELD(b, GFSYNR0, NSSTATE, v)
+#define SET_GFSYNR0_NSATTR(b, v) SET_GLOBAL_FIELD(b, GFSYNR0, NSATTR, v)
+
+#define GET_GFSYNR0_NESTED(b) GET_GLOBAL_FIELD(b, GFSYNR0, NESTED)
+#define GET_GFSYNR0_WNR(b) GET_GLOBAL_FIELD(b, GFSYNR0, WNR)
+#define GET_GFSYNR0_PNU(b) GET_GLOBAL_FIELD(b, GFSYNR0, PNU)
+#define GET_GFSYNR0_IND(b) GET_GLOBAL_FIELD(b, GFSYNR0, IND)
+#define GET_GFSYNR0_NSSTATE(b) GET_GLOBAL_FIELD(b, GFSYNR0, NSSTATE)
+#define GET_GFSYNR0_NSATTR(b) GET_GLOBAL_FIELD(b, GFSYNR0, NSATTR)
+
+/* Global Fault Syndrome Register 1: GFSYNR1 */
+#define SET_GFSYNR1_SID(b, v) SET_GLOBAL_FIELD(b, GFSYNR1, SID, v)
+
+#define GET_GFSYNR1_SID(b) GET_GLOBAL_FIELD(b, GFSYNR1, SID)
+
+/* Global Physical Address Register: GPAR */
+#define SET_GPAR_F(b, v) SET_GLOBAL_FIELD(b, GPAR, F, v)
+#define SET_GPAR_SS(b, v) SET_GLOBAL_FIELD(b, GPAR, SS, v)
+#define SET_GPAR_OUTER(b, v) SET_GLOBAL_FIELD(b, GPAR, OUTER, v)
+#define SET_GPAR_INNER(b, v) SET_GLOBAL_FIELD(b, GPAR, INNER, v)
+#define SET_GPAR_SH(b, v) SET_GLOBAL_FIELD(b, GPAR, SH, v)
+#define SET_GPAR_NS(b, v) SET_GLOBAL_FIELD(b, GPAR, NS, v)
+#define SET_GPAR_NOS(b, v) SET_GLOBAL_FIELD(b, GPAR, NOS, v)
+#define SET_GPAR_PA(b, v) SET_GLOBAL_FIELD(b, GPAR, PA, v)
+#define SET_GPAR_TF(b, v) SET_GLOBAL_FIELD(b, GPAR, TF, v)
+#define SET_GPAR_AFF(b, v) SET_GLOBAL_FIELD(b, GPAR, AFF, v)
+#define SET_GPAR_PF(b, v) SET_GLOBAL_FIELD(b, GPAR, PF, v)
+#define SET_GPAR_EF(b, v) SET_GLOBAL_FIELD(b, GPAR, EF, v)
+#define SET_GPAR_TLCMCF(b, v) SET_GLOBAL_FIELD(b, GPAR, TLCMCF, v)
+#define SET_GPAR_TLBLKF(b, v) SET_GLOBAL_FIELD(b, GPAR, TLBLKF, v)
+#define SET_GPAR_UCBF(b, v) SET_GLOBAL_FIELD(b, GPAR, UCBF, v)
+
+#define GET_GPAR_F(b) GET_GLOBAL_FIELD(b, GPAR, F)
+#define GET_GPAR_SS(b) GET_GLOBAL_FIELD(b, GPAR, SS)
+#define GET_GPAR_OUTER(b) GET_GLOBAL_FIELD(b, GPAR, OUTER)
+#define GET_GPAR_INNER(b) GET_GLOBAL_FIELD(b, GPAR, INNER)
+#define GET_GPAR_SH(b) GET_GLOBAL_FIELD(b, GPAR, SH)
+#define GET_GPAR_NS(b) GET_GLOBAL_FIELD(b, GPAR, NS)
+#define GET_GPAR_NOS(b) GET_GLOBAL_FIELD(b, GPAR, NOS)
+#define GET_GPAR_PA(b) GET_GLOBAL_FIELD(b, GPAR, PA)
+#define GET_GPAR_TF(b) GET_GLOBAL_FIELD(b, GPAR, TF)
+#define GET_GPAR_AFF(b) GET_GLOBAL_FIELD(b, GPAR, AFF)
+#define GET_GPAR_PF(b) GET_GLOBAL_FIELD(b, GPAR, PF)
+#define GET_GPAR_EF(b) GET_GLOBAL_FIELD(b, GPAR, EF)
+#define GET_GPAR_TLCMCF(b) GET_GLOBAL_FIELD(b, GPAR, TLCMCF)
+#define GET_GPAR_TLBLKF(b) GET_GLOBAL_FIELD(b, GPAR, TLBLKF)
+#define GET_GPAR_UCBF(b) GET_GLOBAL_FIELD(b, GPAR, UCBF)
+
+/* Identification Register: IDR0 */
+#define SET_IDR0_NUMSMRG(b, v) SET_GLOBAL_FIELD(b, IDR0, NUMSMRG, v)
+#define SET_IDR0_NUMSIDB(b, v) SET_GLOBAL_FIELD(b, IDR0, NUMSIDB, v)
+#define SET_IDR0_BTM(b, v) SET_GLOBAL_FIELD(b, IDR0, BTM, v)
+#define SET_IDR0_CTTW(b, v) SET_GLOBAL_FIELD(b, IDR0, CTTW, v)
+#define SET_IDR0_NUMIRPT(b, v) SET_GLOBAL_FIELD(b, IDR0, NUMIRPT, v)
+#define SET_IDR0_PTFS(b, v) SET_GLOBAL_FIELD(b, IDR0, PTFS, v)
+#define SET_IDR0_SMS(b, v) SET_GLOBAL_FIELD(b, IDR0, SMS, v)
+#define SET_IDR0_NTS(b, v) SET_GLOBAL_FIELD(b, IDR0, NTS, v)
+#define SET_IDR0_S2TS(b, v) SET_GLOBAL_FIELD(b, IDR0, S2TS, v)
+#define SET_IDR0_S1TS(b, v) SET_GLOBAL_FIELD(b, IDR0, S1TS, v)
+#define SET_IDR0_SES(b, v) SET_GLOBAL_FIELD(b, IDR0, SES, v)
+
+#define GET_IDR0_NUMSMRG(b) GET_GLOBAL_FIELD(b, IDR0, NUMSMRG)
+#define GET_IDR0_NUMSIDB(b) GET_GLOBAL_FIELD(b, IDR0, NUMSIDB)
+#define GET_IDR0_BTM(b) GET_GLOBAL_FIELD(b, IDR0, BTM)
+#define GET_IDR0_CTTW(b) GET_GLOBAL_FIELD(b, IDR0, CTTW)
+#define GET_IDR0_NUMIRPT(b) GET_GLOBAL_FIELD(b, IDR0, NUMIRPT)
+#define GET_IDR0_PTFS(b) GET_GLOBAL_FIELD(b, IDR0, PTFS)
+#define GET_IDR0_SMS(b) GET_GLOBAL_FIELD(b, IDR0, SMS)
+#define GET_IDR0_NTS(b) GET_GLOBAL_FIELD(b, IDR0, NTS)
+#define GET_IDR0_S2TS(b) GET_GLOBAL_FIELD(b, IDR0, S2TS)
+#define GET_IDR0_S1TS(b) GET_GLOBAL_FIELD(b, IDR0, S1TS)
+#define GET_IDR0_SES(b) GET_GLOBAL_FIELD(b, IDR0, SES)
+
+/* Identification Register: IDR1 */
+#define SET_IDR1_NUMCB(b, v) SET_GLOBAL_FIELD(b, IDR1, NUMCB, v)
+#define SET_IDR1_NUMSSDNDXB(b, v) SET_GLOBAL_FIELD(b, IDR1, NUMSSDNDXB, v)
+#define SET_IDR1_SSDTP(b, v) SET_GLOBAL_FIELD(b, IDR1, SSDTP, v)
+#define SET_IDR1_SMCD(b, v) SET_GLOBAL_FIELD(b, IDR1, SMCD, v)
+#define SET_IDR1_NUMS2CB(b, v) SET_GLOBAL_FIELD(b, IDR1, NUMS2CB, v)
+#define SET_IDR1_NUMPAGENDXB(b, v) SET_GLOBAL_FIELD(b, IDR1, NUMPAGENDXB, v)
+#define SET_IDR1_PAGESIZE(b, v) SET_GLOBAL_FIELD(b, IDR1, PAGESIZE, v)
+
+#define GET_IDR1_NUMCB(b) GET_GLOBAL_FIELD(b, IDR1, NUMCB)
+#define GET_IDR1_NUMSSDNDXB(b) GET_GLOBAL_FIELD(b, IDR1, NUMSSDNDXB)
+#define GET_IDR1_SSDTP(b) GET_GLOBAL_FIELD(b, IDR1, SSDTP)
+#define GET_IDR1_SMCD(b) GET_GLOBAL_FIELD(b, IDR1, SMCD)
+#define GET_IDR1_NUMS2CB(b) GET_GLOBAL_FIELD(b, IDR1, NUMS2CB)
+#define GET_IDR1_NUMPAGENDXB(b) GET_GLOBAL_FIELD(b, IDR1, NUMPAGENDXB)
+#define GET_IDR1_PAGESIZE(b) GET_GLOBAL_FIELD(b, IDR1, PAGESIZE)
+
+/* Identification Register: IDR2 */
+#define SET_IDR2_IAS(b, v) SET_GLOBAL_FIELD(b, IDR2, IAS, v)
+#define SET_IDR2_OAS(b, v) SET_GLOBAL_FIELD(b, IDR2, OAS, v)
+
+#define GET_IDR2_IAS(b) GET_GLOBAL_FIELD(b, IDR2, IAS)
+#define GET_IDR2_OAS(b) GET_GLOBAL_FIELD(b, IDR2, OAS)
+
+/* Identification Register: IDR7 */
+#define SET_IDR7_MINOR(b, v) SET_GLOBAL_FIELD(b, IDR7, MINOR, v)
+#define SET_IDR7_MAJOR(b, v) SET_GLOBAL_FIELD(b, IDR7, MAJOR, v)
+
+#define GET_IDR7_MINOR(b) GET_GLOBAL_FIELD(b, IDR7, MINOR)
+#define GET_IDR7_MAJOR(b) GET_GLOBAL_FIELD(b, IDR7, MAJOR)
+
+/* Stream to Context Register: S2CR_N */
+#define SET_S2CR_CBNDX(b, n, v) SET_GLOBAL_FIELD_N(b, n, S2CR, CBNDX, v)
+#define SET_S2CR_SHCFG(b, n, v) SET_GLOBAL_FIELD_N(b, n, S2CR, SHCFG, v)
+#define SET_S2CR_MTCFG(b, n, v) SET_GLOBAL_FIELD_N(b, n, S2CR, MTCFG, v)
+#define SET_S2CR_MEMATTR(b, n, v) SET_GLOBAL_FIELD_N(b, n, S2CR, MEMATTR, v)
+#define SET_S2CR_TYPE(b, n, v) SET_GLOBAL_FIELD_N(b, n, S2CR, TYPE, v)
+#define SET_S2CR_NSCFG(b, n, v) SET_GLOBAL_FIELD_N(b, n, S2CR, NSCFG, v)
+#define SET_S2CR_RACFG(b, n, v) SET_GLOBAL_FIELD_N(b, n, S2CR, RACFG, v)
+#define SET_S2CR_WACFG(b, n, v) SET_GLOBAL_FIELD_N(b, n, S2CR, WACFG, v)
+#define SET_S2CR_PRIVCFG(b, n, v) SET_GLOBAL_FIELD_N(b, n, S2CR, PRIVCFG, v)
+#define SET_S2CR_INSTCFG(b, n, v) SET_GLOBAL_FIELD_N(b, n, S2CR, INSTCFG, v)
+#define SET_S2CR_TRANSIENTCFG(b, n, v) \
+ SET_GLOBAL_FIELD_N(b, n, S2CR, TRANSIENTCFG, v)
+#define SET_S2CR_VMID(b, n, v) SET_GLOBAL_FIELD_N(b, n, S2CR, VMID, v)
+#define SET_S2CR_BSU(b, n, v) SET_GLOBAL_FIELD_N(b, n, S2CR, BSU, v)
+#define SET_S2CR_FB(b, n, v) SET_GLOBAL_FIELD_N(b, n, S2CR, FB, v)
+
+#define GET_S2CR_CBNDX(b, n) GET_GLOBAL_FIELD_N(b, n, S2CR, CBNDX)
+#define GET_S2CR_SHCFG(b, n) GET_GLOBAL_FIELD_N(b, n, S2CR, SHCFG)
+#define GET_S2CR_MTCFG(b, n) GET_GLOBAL_FIELD_N(b, n, S2CR, MTCFG)
+#define GET_S2CR_MEMATTR(b, n) GET_GLOBAL_FIELD_N(b, n, S2CR, MEMATTR)
+#define GET_S2CR_TYPE(b, n) GET_GLOBAL_FIELD_N(b, n, S2CR, TYPE)
+#define GET_S2CR_NSCFG(b, n) GET_GLOBAL_FIELD_N(b, n, S2CR, NSCFG)
+#define GET_S2CR_RACFG(b, n) GET_GLOBAL_FIELD_N(b, n, S2CR, RACFG)
+#define GET_S2CR_WACFG(b, n) GET_GLOBAL_FIELD_N(b, n, S2CR, WACFG)
+#define GET_S2CR_PRIVCFG(b, n) GET_GLOBAL_FIELD_N(b, n, S2CR, PRIVCFG)
+#define GET_S2CR_INSTCFG(b, n) GET_GLOBAL_FIELD_N(b, n, S2CR, INSTCFG)
+#define GET_S2CR_TRANSIENTCFG(b, n) \
+ GET_GLOBAL_FIELD_N(b, n, S2CR, TRANSIENTCFG)
+#define GET_S2CR_VMID(b, n) GET_GLOBAL_FIELD_N(b, n, S2CR, VMID)
+#define GET_S2CR_BSU(b, n) GET_GLOBAL_FIELD_N(b, n, S2CR, BSU)
+#define GET_S2CR_FB(b, n) GET_GLOBAL_FIELD_N(b, n, S2CR, FB)
+
+/* Stream Match Register: SMR_N */
+#define SET_SMR_ID(b, n, v) SET_GLOBAL_FIELD_N(b, n, SMR, ID, v)
+#define SET_SMR_MASK(b, n, v) SET_GLOBAL_FIELD_N(b, n, SMR, MASK, v)
+#define SET_SMR_VALID(b, n, v) SET_GLOBAL_FIELD_N(b, n, SMR, VALID, v)
+
+#define GET_SMR_ID(b, n) GET_GLOBAL_FIELD_N(b, n, SMR, ID)
+#define GET_SMR_MASK(b, n) GET_GLOBAL_FIELD_N(b, n, SMR, MASK)
+#define GET_SMR_VALID(b, n) GET_GLOBAL_FIELD_N(b, n, SMR, VALID)
+
+/* Global TLB Status: TLBGSTATUS */
+#define SET_TLBGSTATUS_GSACTIVE(b, v) \
+ SET_GLOBAL_FIELD(b, TLBGSTATUS, GSACTIVE, v)
+
+#define GET_TLBGSTATUS_GSACTIVE(b) \
+ GET_GLOBAL_FIELD(b, TLBGSTATUS, GSACTIVE)
+
+/* Invalidate Hyp TLB by VA: TLBIVAH */
+#define SET_TLBIVAH_ADDR(b, v) SET_GLOBAL_FIELD(b, TLBIVAH, ADDR, v)
+
+#define GET_TLBIVAH_ADDR(b) GET_GLOBAL_FIELD(b, TLBIVAH, ADDR)
+
+/* Invalidate TLB by VMID: TLBIVMID */
+#define SET_TLBIVMID_VMID(b, v) SET_GLOBAL_FIELD(b, TLBIVMID, VMID, v)
+
+#define GET_TLBIVMID_VMID(b) GET_GLOBAL_FIELD(b, TLBIVMID, VMID)
+
+/* Global Register Space 1 Field setters/getters*/
+/* Context Bank Attribute Register: CBAR_N */
+#define SET_CBAR_VMID(b, n, v) SET_GLOBAL_FIELD_N(b, n, CBAR, VMID, v)
+#define SET_CBAR_CBNDX(b, n, v) SET_GLOBAL_FIELD_N(b, n, CBAR, CBNDX, v)
+#define SET_CBAR_BPSHCFG(b, n, v) SET_GLOBAL_FIELD_N(b, n, CBAR, BPSHCFG, v)
+#define SET_CBAR_HYPC(b, n, v) SET_GLOBAL_FIELD_N(b, n, CBAR, HYPC, v)
+#define SET_CBAR_FB(b, n, v) SET_GLOBAL_FIELD_N(b, n, CBAR, FB, v)
+#define SET_CBAR_MEMATTR(b, n, v) SET_GLOBAL_FIELD_N(b, n, CBAR, MEMATTR, v)
+#define SET_CBAR_TYPE(b, n, v) SET_GLOBAL_FIELD_N(b, n, CBAR, TYPE, v)
+#define SET_CBAR_BSU(b, n, v) SET_GLOBAL_FIELD_N(b, n, CBAR, BSU, v)
+#define SET_CBAR_RACFG(b, n, v) SET_GLOBAL_FIELD_N(b, n, CBAR, RACFG, v)
+#define SET_CBAR_WACFG(b, n, v) SET_GLOBAL_FIELD_N(b, n, CBAR, WACFG, v)
+#define SET_CBAR_IRPTNDX(b, n, v) SET_GLOBAL_FIELD_N(b, n, CBAR, IRPTNDX, v)
+
+#define GET_CBAR_VMID(b, n) GET_GLOBAL_FIELD_N(b, n, CBAR, VMID)
+#define GET_CBAR_CBNDX(b, n) GET_GLOBAL_FIELD_N(b, n, CBAR, CBNDX)
+#define GET_CBAR_BPSHCFG(b, n) GET_GLOBAL_FIELD_N(b, n, CBAR, BPSHCFG)
+#define GET_CBAR_HYPC(b, n) GET_GLOBAL_FIELD_N(b, n, CBAR, HYPC)
+#define GET_CBAR_FB(b, n) GET_GLOBAL_FIELD_N(b, n, CBAR, FB)
+#define GET_CBAR_MEMATTR(b, n) GET_GLOBAL_FIELD_N(b, n, CBAR, MEMATTR)
+#define GET_CBAR_TYPE(b, n) GET_GLOBAL_FIELD_N(b, n, CBAR, TYPE)
+#define GET_CBAR_BSU(b, n) GET_GLOBAL_FIELD_N(b, n, CBAR, BSU)
+#define GET_CBAR_RACFG(b, n) GET_GLOBAL_FIELD_N(b, n, CBAR, RACFG)
+#define GET_CBAR_WACFG(b, n) GET_GLOBAL_FIELD_N(b, n, CBAR, WACFG)
+#define GET_CBAR_IRPTNDX(b, n) GET_GLOBAL_FIELD_N(b, n, CBAR, IRPTNDX)
+
+/* Context Bank Fault Restricted Syndrome Register A: CBFRSYNRA_N */
+#define SET_CBFRSYNRA_SID(b, n, v) SET_GLOBAL_FIELD_N(b, n, CBFRSYNRA, SID, v)
+
+#define GET_CBFRSYNRA_SID(b, n) GET_GLOBAL_FIELD_N(b, n, CBFRSYNRA, SID)
+
+/* Stage 1 Context Bank Format Fields */
+#define SET_CB_ACTLR_REQPRIORITY (b, c, v) \
+ SET_CONTEXT_FIELD(b, c, CB_ACTLR, REQPRIORITY, v)
+#define SET_CB_ACTLR_REQPRIORITYCFG(b, c, v) \
+ SET_CONTEXT_FIELD(b, c, CB_ACTLR, REQPRIORITYCFG, v)
+#define SET_CB_ACTLR_PRIVCFG(b, c, v) \
+ SET_CONTEXT_FIELD(b, c, CB_ACTLR, PRIVCFG, v)
+#define SET_CB_ACTLR_BPRCOSH(b, c, v) \
+ SET_CONTEXT_FIELD(b, c, CB_ACTLR, BPRCOSH, v)
+#define SET_CB_ACTLR_BPRCISH(b, c, v) \
+ SET_CONTEXT_FIELD(b, c, CB_ACTLR, BPRCISH, v)
+#define SET_CB_ACTLR_BPRCNSH(b, c, v) \
+ SET_CONTEXT_FIELD(b, c, CB_ACTLR, BPRCNSH, v)
+
+#define GET_CB_ACTLR_REQPRIORITY (b, c) \
+ GET_CONTEXT_FIELD(b, c, CB_ACTLR, REQPRIORITY)
+#define GET_CB_ACTLR_REQPRIORITYCFG(b, c) \
+ GET_CONTEXT_FIELD(b, c, CB_ACTLR, REQPRIORITYCFG)
+#define GET_CB_ACTLR_PRIVCFG(b, c) GET_CONTEXT_FIELD(b, c, CB_ACTLR, PRIVCFG)
+#define GET_CB_ACTLR_BPRCOSH(b, c) GET_CONTEXT_FIELD(b, c, CB_ACTLR, BPRCOSH)
+#define GET_CB_ACTLR_BPRCISH(b, c) GET_CONTEXT_FIELD(b, c, CB_ACTLR, BPRCISH)
+#define GET_CB_ACTLR_BPRCNSH(b, c) GET_CONTEXT_FIELD(b, c, CB_ACTLR, BPRCNSH)
+
+/* Address Translation, Stage 1, Privileged Read: CB_ATS1PR */
+#define SET_CB_ATS1PR_ADDR(b, c, v) SET_CONTEXT_FIELD(b, c, CB_ATS1PR, ADDR, v)
+
+#define GET_CB_ATS1PR_ADDR(b, c) GET_CONTEXT_FIELD(b, c, CB_ATS1PR, ADDR)
+
+/* Address Translation, Stage 1, Privileged Write: CB_ATS1PW */
+#define SET_CB_ATS1PW_ADDR(b, c, v) SET_CONTEXT_FIELD(b, c, CB_ATS1PW, ADDR, v)
+
+#define GET_CB_ATS1PW_ADDR(b, c) GET_CONTEXT_FIELD(b, c, CB_ATS1PW, ADDR)
+
+/* Address Translation, Stage 1, User Read: CB_ATS1UR */
+#define SET_CB_ATS1UR_ADDR(b, c, v) SET_CONTEXT_FIELD(b, c, CB_ATS1UR, ADDR, v)
+
+#define GET_CB_ATS1UR_ADDR(b, c) GET_CONTEXT_FIELD(b, c, CB_ATS1UR, ADDR)
+
+/* Address Translation, Stage 1, User Write: CB_ATS1UW */
+#define SET_CB_ATS1UW_ADDR(b, c, v) SET_CONTEXT_FIELD(b, c, CB_ATS1UW, ADDR, v)
+
+#define GET_CB_ATS1UW_ADDR(b, c) GET_CONTEXT_FIELD(b, c, CB_ATS1UW, ADDR)
+
+/* Address Translation Status Register: CB_ATSR */
+#define SET_CB_ATSR_ACTIVE(b, c, v) SET_CONTEXT_FIELD(b, c, CB_ATSR, ACTIVE, v)
+
+#define GET_CB_ATSR_ACTIVE(b, c) GET_CONTEXT_FIELD(b, c, CB_ATSR, ACTIVE)
+
+/* Context ID Register: CB_CONTEXTIDR */
+#define SET_CB_CONTEXTIDR_ASID(b, c, v) \
+ SET_CONTEXT_FIELD(b, c, CB_CONTEXTIDR, ASID, v)
+#define SET_CB_CONTEXTIDR_PROCID(b, c, v) \
+ SET_CONTEXT_FIELD(b, c, CB_CONTEXTIDR, PROCID, v)
+
+#define GET_CB_CONTEXTIDR_ASID(b, c) \
+ GET_CONTEXT_FIELD(b, c, CB_CONTEXTIDR, ASID)
+#define GET_CB_CONTEXTIDR_PROCID(b, c) \
+ GET_CONTEXT_FIELD(b, c, CB_CONTEXTIDR, PROCID)
+
+/* Fault Address Register: CB_FAR */
+#define SET_CB_FAR_FADDR(b, c, v) SET_CONTEXT_FIELD(b, c, CB_FAR, FADDR, v)
+
+#define GET_CB_FAR_FADDR(b, c) GET_CONTEXT_FIELD(b, c, CB_FAR, FADDR)
+
+/* Fault Status Register: CB_FSR */
+#define SET_CB_FSR_TF(b, c, v) SET_CONTEXT_FIELD(b, c, CB_FSR, TF, v)
+#define SET_CB_FSR_AFF(b, c, v) SET_CONTEXT_FIELD(b, c, CB_FSR, AFF, v)
+#define SET_CB_FSR_PF(b, c, v) SET_CONTEXT_FIELD(b, c, CB_FSR, PF, v)
+#define SET_CB_FSR_EF(b, c, v) SET_CONTEXT_FIELD(b, c, CB_FSR, EF, v)
+#define SET_CB_FSR_TLBMCF(b, c, v) SET_CONTEXT_FIELD(b, c, CB_FSR, TLBMCF, v)
+#define SET_CB_FSR_TLBLKF(b, c, v) SET_CONTEXT_FIELD(b, c, CB_FSR, TLBLKF, v)
+#define SET_CB_FSR_SS(b, c, v) SET_CONTEXT_FIELD(b, c, CB_FSR, SS, v)
+#define SET_CB_FSR_MULTI(b, c, v) SET_CONTEXT_FIELD(b, c, CB_FSR, MULTI, v)
+
+#define GET_CB_FSR_TF(b, c) GET_CONTEXT_FIELD(b, c, CB_FSR, TF)
+#define GET_CB_FSR_AFF(b, c) GET_CONTEXT_FIELD(b, c, CB_FSR, AFF)
+#define GET_CB_FSR_PF(b, c) GET_CONTEXT_FIELD(b, c, CB_FSR, PF)
+#define GET_CB_FSR_EF(b, c) GET_CONTEXT_FIELD(b, c, CB_FSR, EF)
+#define GET_CB_FSR_TLBMCF(b, c) GET_CONTEXT_FIELD(b, c, CB_FSR, TLBMCF)
+#define GET_CB_FSR_TLBLKF(b, c) GET_CONTEXT_FIELD(b, c, CB_FSR, TLBLKF)
+#define GET_CB_FSR_SS(b, c) GET_CONTEXT_FIELD(b, c, CB_FSR, SS)
+#define GET_CB_FSR_MULTI(b, c) GET_CONTEXT_FIELD(b, c, CB_FSR, MULTI)
+
+/* Fault Syndrome Register 0: CB_FSYNR0 */
+#define SET_CB_FSYNR0_PLVL(b, c, v) SET_CONTEXT_FIELD(b, c, CB_FSYNR0, PLVL, v)
+#define SET_CB_FSYNR0_S1PTWF(b, c, v) \
+ SET_CONTEXT_FIELD(b, c, CB_FSYNR0, S1PTWF, v)
+#define SET_CB_FSYNR0_WNR(b, c, v) SET_CONTEXT_FIELD(b, c, CB_FSYNR0, WNR, v)
+#define SET_CB_FSYNR0_PNU(b, c, v) SET_CONTEXT_FIELD(b, c, CB_FSYNR0, PNU, v)
+#define SET_CB_FSYNR0_IND(b, c, v) SET_CONTEXT_FIELD(b, c, CB_FSYNR0, IND, v)
+#define SET_CB_FSYNR0_NSSTATE(b, c, v) \
+ SET_CONTEXT_FIELD(b, c, CB_FSYNR0, NSSTATE, v)
+#define SET_CB_FSYNR0_NSATTR(b, c, v) \
+ SET_CONTEXT_FIELD(b, c, CB_FSYNR0, NSATTR, v)
+#define SET_CB_FSYNR0_ATOF(b, c, v) SET_CONTEXT_FIELD(b, c, CB_FSYNR0, ATOF, v)
+#define SET_CB_FSYNR0_PTWF(b, c, v) SET_CONTEXT_FIELD(b, c, CB_FSYNR0, PTWF, v)
+#define SET_CB_FSYNR0_AFR(b, c, v) SET_CONTEXT_FIELD(b, c, CB_FSYNR0, AFR, v)
+#define SET_CB_FSYNR0_S1CBNDX(b, c, v) \
+ SET_CONTEXT_FIELD(b, c, CB_FSYNR0, S1CBNDX, v)
+
+#define GET_CB_FSYNR0_PLVL(b, c) GET_CONTEXT_FIELD(b, c, CB_FSYNR0, PLVL)
+#define GET_CB_FSYNR0_S1PTWF(b, c) \
+ GET_CONTEXT_FIELD(b, c, CB_FSYNR0, S1PTWF)
+#define GET_CB_FSYNR0_WNR(b, c) GET_CONTEXT_FIELD(b, c, CB_FSYNR0, WNR)
+#define GET_CB_FSYNR0_PNU(b, c) GET_CONTEXT_FIELD(b, c, CB_FSYNR0, PNU)
+#define GET_CB_FSYNR0_IND(b, c) GET_CONTEXT_FIELD(b, c, CB_FSYNR0, IND)
+#define GET_CB_FSYNR0_NSSTATE(b, c) \
+ GET_CONTEXT_FIELD(b, c, CB_FSYNR0, NSSTATE)
+#define GET_CB_FSYNR0_NSATTR(b, c) \
+ GET_CONTEXT_FIELD(b, c, CB_FSYNR0, NSATTR)
+#define GET_CB_FSYNR0_ATOF(b, c) GET_CONTEXT_FIELD(b, c, CB_FSYNR0, ATOF)
+#define GET_CB_FSYNR0_PTWF(b, c) GET_CONTEXT_FIELD(b, c, CB_FSYNR0, PTWF)
+#define GET_CB_FSYNR0_AFR(b, c) GET_CONTEXT_FIELD(b, c, CB_FSYNR0, AFR)
+#define GET_CB_FSYNR0_S1CBNDX(b, c) \
+ GET_CONTEXT_FIELD(b, c, CB_FSYNR0, S1CBNDX)
+
+/* Normal Memory Remap Register: CB_NMRR */
+#define SET_CB_NMRR_IR0(b, c, v) SET_CONTEXT_FIELD(b, c, CB_NMRR, IR0, v)
+#define SET_CB_NMRR_IR1(b, c, v) SET_CONTEXT_FIELD(b, c, CB_NMRR, IR1, v)
+#define SET_CB_NMRR_IR2(b, c, v) SET_CONTEXT_FIELD(b, c, CB_NMRR, IR2, v)
+#define SET_CB_NMRR_IR3(b, c, v) SET_CONTEXT_FIELD(b, c, CB_NMRR, IR3, v)
+#define SET_CB_NMRR_IR4(b, c, v) SET_CONTEXT_FIELD(b, c, CB_NMRR, IR4, v)
+#define SET_CB_NMRR_IR5(b, c, v) SET_CONTEXT_FIELD(b, c, CB_NMRR, IR5, v)
+#define SET_CB_NMRR_IR6(b, c, v) SET_CONTEXT_FIELD(b, c, CB_NMRR, IR6, v)
+#define SET_CB_NMRR_IR7(b, c, v) SET_CONTEXT_FIELD(b, c, CB_NMRR, IR7, v)
+#define SET_CB_NMRR_OR0(b, c, v) SET_CONTEXT_FIELD(b, c, CB_NMRR, OR0, v)
+#define SET_CB_NMRR_OR1(b, c, v) SET_CONTEXT_FIELD(b, c, CB_NMRR, OR1, v)
+#define SET_CB_NMRR_OR2(b, c, v) SET_CONTEXT_FIELD(b, c, CB_NMRR, OR2, v)
+#define SET_CB_NMRR_OR3(b, c, v) SET_CONTEXT_FIELD(b, c, CB_NMRR, OR3, v)
+#define SET_CB_NMRR_OR4(b, c, v) SET_CONTEXT_FIELD(b, c, CB_NMRR, OR4, v)
+#define SET_CB_NMRR_OR5(b, c, v) SET_CONTEXT_FIELD(b, c, CB_NMRR, OR5, v)
+#define SET_CB_NMRR_OR6(b, c, v) SET_CONTEXT_FIELD(b, c, CB_NMRR, OR6, v)
+#define SET_CB_NMRR_OR7(b, c, v) SET_CONTEXT_FIELD(b, c, CB_NMRR, OR7, v)
+
+#define GET_CB_NMRR_IR0(b, c) GET_CONTEXT_FIELD(b, c, CB_NMRR, IR0)
+#define GET_CB_NMRR_IR1(b, c) GET_CONTEXT_FIELD(b, c, CB_NMRR, IR1)
+#define GET_CB_NMRR_IR2(b, c) GET_CONTEXT_FIELD(b, c, CB_NMRR, IR2)
+#define GET_CB_NMRR_IR3(b, c) GET_CONTEXT_FIELD(b, c, CB_NMRR, IR3)
+#define GET_CB_NMRR_IR4(b, c) GET_CONTEXT_FIELD(b, c, CB_NMRR, IR4)
+#define GET_CB_NMRR_IR5(b, c) GET_CONTEXT_FIELD(b, c, CB_NMRR, IR5)
+#define GET_CB_NMRR_IR6(b, c) GET_CONTEXT_FIELD(b, c, CB_NMRR, IR6)
+#define GET_CB_NMRR_IR7(b, c) GET_CONTEXT_FIELD(b, c, CB_NMRR, IR7)
+#define GET_CB_NMRR_OR0(b, c) GET_CONTEXT_FIELD(b, c, CB_NMRR, OR0)
+#define GET_CB_NMRR_OR1(b, c) GET_CONTEXT_FIELD(b, c, CB_NMRR, OR1)
+#define GET_CB_NMRR_OR2(b, c) GET_CONTEXT_FIELD(b, c, CB_NMRR, OR2)
+#define GET_CB_NMRR_OR3(b, c) GET_CONTEXT_FIELD(b, c, CB_NMRR, OR3)
+#define GET_CB_NMRR_OR4(b, c) GET_CONTEXT_FIELD(b, c, CB_NMRR, OR4)
+#define GET_CB_NMRR_OR5(b, c) GET_CONTEXT_FIELD(b, c, CB_NMRR, OR5)
+
+/* Physical Address Register: CB_PAR */
+#define SET_CB_PAR_F(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PAR, F, v)
+#define SET_CB_PAR_SS(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PAR, SS, v)
+#define SET_CB_PAR_OUTER(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PAR, OUTER, v)
+#define SET_CB_PAR_INNER(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PAR, INNER, v)
+#define SET_CB_PAR_SH(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PAR, SH, v)
+#define SET_CB_PAR_NS(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PAR, NS, v)
+#define SET_CB_PAR_NOS(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PAR, NOS, v)
+#define SET_CB_PAR_PA(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PAR, PA, v)
+#define SET_CB_PAR_TF(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PAR, TF, v)
+#define SET_CB_PAR_AFF(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PAR, AFF, v)
+#define SET_CB_PAR_PF(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PAR, PF, v)
+#define SET_CB_PAR_TLBMCF(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PAR, TLBMCF, v)
+#define SET_CB_PAR_TLBLKF(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PAR, TLBLKF, v)
+#define SET_CB_PAR_ATOT(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PAR, ATOT, v)
+#define SET_CB_PAR_PLVL(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PAR, PLVL, v)
+#define SET_CB_PAR_STAGE(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PAR, STAGE, v)
+
+#define GET_CB_PAR_F(b, c) GET_CONTEXT_FIELD(b, c, CB_PAR, F)
+#define GET_CB_PAR_SS(b, c) GET_CONTEXT_FIELD(b, c, CB_PAR, SS)
+#define GET_CB_PAR_OUTER(b, c) GET_CONTEXT_FIELD(b, c, CB_PAR, OUTER)
+#define GET_CB_PAR_INNER(b, c) GET_CONTEXT_FIELD(b, c, CB_PAR, INNER)
+#define GET_CB_PAR_SH(b, c) GET_CONTEXT_FIELD(b, c, CB_PAR, SH)
+#define GET_CB_PAR_NS(b, c) GET_CONTEXT_FIELD(b, c, CB_PAR, NS)
+#define GET_CB_PAR_NOS(b, c) GET_CONTEXT_FIELD(b, c, CB_PAR, NOS)
+#define GET_CB_PAR_PA(b, c) GET_CONTEXT_FIELD(b, c, CB_PAR, PA)
+#define GET_CB_PAR_TF(b, c) GET_CONTEXT_FIELD(b, c, CB_PAR, TF)
+#define GET_CB_PAR_AFF(b, c) GET_CONTEXT_FIELD(b, c, CB_PAR, AFF)
+#define GET_CB_PAR_PF(b, c) GET_CONTEXT_FIELD(b, c, CB_PAR, PF)
+#define GET_CB_PAR_TLBMCF(b, c) GET_CONTEXT_FIELD(b, c, CB_PAR, TLBMCF)
+#define GET_CB_PAR_TLBLKF(b, c) GET_CONTEXT_FIELD(b, c, CB_PAR, TLBLKF)
+#define GET_CB_PAR_ATOT(b, c) GET_CONTEXT_FIELD(b, c, CB_PAR, ATOT)
+#define GET_CB_PAR_PLVL(b, c) GET_CONTEXT_FIELD(b, c, CB_PAR, PLVL)
+#define GET_CB_PAR_STAGE(b, c) GET_CONTEXT_FIELD(b, c, CB_PAR, STAGE)
+
+/* Primary Region Remap Register: CB_PRRR */
+#define SET_CB_PRRR_TR0(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PRRR, TR0, v)
+#define SET_CB_PRRR_TR1(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PRRR, TR1, v)
+#define SET_CB_PRRR_TR2(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PRRR, TR2, v)
+#define SET_CB_PRRR_TR3(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PRRR, TR3, v)
+#define SET_CB_PRRR_TR4(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PRRR, TR4, v)
+#define SET_CB_PRRR_TR5(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PRRR, TR5, v)
+#define SET_CB_PRRR_TR6(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PRRR, TR6, v)
+#define SET_CB_PRRR_TR7(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PRRR, TR7, v)
+#define SET_CB_PRRR_DS0(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PRRR, DS0, v)
+#define SET_CB_PRRR_DS1(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PRRR, DS1, v)
+#define SET_CB_PRRR_NS0(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PRRR, NS0, v)
+#define SET_CB_PRRR_NS1(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PRRR, NS1, v)
+#define SET_CB_PRRR_NOS0(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PRRR, NOS0, v)
+#define SET_CB_PRRR_NOS1(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PRRR, NOS1, v)
+#define SET_CB_PRRR_NOS2(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PRRR, NOS2, v)
+#define SET_CB_PRRR_NOS3(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PRRR, NOS3, v)
+#define SET_CB_PRRR_NOS4(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PRRR, NOS4, v)
+#define SET_CB_PRRR_NOS5(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PRRR, NOS5, v)
+#define SET_CB_PRRR_NOS6(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PRRR, NOS6, v)
+#define SET_CB_PRRR_NOS7(b, c, v) SET_CONTEXT_FIELD(b, c, CB_PRRR, NOS7, v)
+
+#define GET_CB_PRRR_TR0(b, c) GET_CONTEXT_FIELD(b, c, CB_PRRR, TR0)
+#define GET_CB_PRRR_TR1(b, c) GET_CONTEXT_FIELD(b, c, CB_PRRR, TR1)
+#define GET_CB_PRRR_TR2(b, c) GET_CONTEXT_FIELD(b, c, CB_PRRR, TR2)
+#define GET_CB_PRRR_TR3(b, c) GET_CONTEXT_FIELD(b, c, CB_PRRR, TR3)
+#define GET_CB_PRRR_TR4(b, c) GET_CONTEXT_FIELD(b, c, CB_PRRR, TR4)
+#define GET_CB_PRRR_TR5(b, c) GET_CONTEXT_FIELD(b, c, CB_PRRR, TR5)
+#define GET_CB_PRRR_TR6(b, c) GET_CONTEXT_FIELD(b, c, CB_PRRR, TR6)
+#define GET_CB_PRRR_TR7(b, c) GET_CONTEXT_FIELD(b, c, CB_PRRR, TR7)
+#define GET_CB_PRRR_DS0(b, c) GET_CONTEXT_FIELD(b, c, CB_PRRR, DS0)
+#define GET_CB_PRRR_DS1(b, c) GET_CONTEXT_FIELD(b, c, CB_PRRR, DS1)
+#define GET_CB_PRRR_NS0(b, c) GET_CONTEXT_FIELD(b, c, CB_PRRR, NS0)
+#define GET_CB_PRRR_NS1(b, c) GET_CONTEXT_FIELD(b, c, CB_PRRR, NS1)
+#define GET_CB_PRRR_NOS0(b, c) GET_CONTEXT_FIELD(b, c, CB_PRRR, NOS0)
+#define GET_CB_PRRR_NOS1(b, c) GET_CONTEXT_FIELD(b, c, CB_PRRR, NOS1)
+#define GET_CB_PRRR_NOS2(b, c) GET_CONTEXT_FIELD(b, c, CB_PRRR, NOS2)
+#define GET_CB_PRRR_NOS3(b, c) GET_CONTEXT_FIELD(b, c, CB_PRRR, NOS3)
+#define GET_CB_PRRR_NOS4(b, c) GET_CONTEXT_FIELD(b, c, CB_PRRR, NOS4)
+#define GET_CB_PRRR_NOS5(b, c) GET_CONTEXT_FIELD(b, c, CB_PRRR, NOS5)
+#define GET_CB_PRRR_NOS6(b, c) GET_CONTEXT_FIELD(b, c, CB_PRRR, NOS6)
+#define GET_CB_PRRR_NOS7(b, c) GET_CONTEXT_FIELD(b, c, CB_PRRR, NOS7)
+
+/* Transaction Resume: CB_RESUME */
+#define SET_CB_RESUME_TNR(b, c, v) SET_CONTEXT_FIELD(b, c, CB_RESUME, TNR, v)
+
+#define GET_CB_RESUME_TNR(b, c) GET_CONTEXT_FIELD(b, c, CB_RESUME, TNR)
+
+/* System Control Register: CB_SCTLR */
+#define SET_CB_SCTLR_M(b, c, v) SET_CONTEXT_FIELD(b, c, CB_SCTLR, M, v)
+#define SET_CB_SCTLR_TRE(b, c, v) SET_CONTEXT_FIELD(b, c, CB_SCTLR, TRE, v)
+#define SET_CB_SCTLR_AFE(b, c, v) SET_CONTEXT_FIELD(b, c, CB_SCTLR, AFE, v)
+#define SET_CB_SCTLR_AFFD(b, c, v) SET_CONTEXT_FIELD(b, c, CB_SCTLR, AFFD, v)
+#define SET_CB_SCTLR_E(b, c, v) SET_CONTEXT_FIELD(b, c, CB_SCTLR, E, v)
+#define SET_CB_SCTLR_CFRE(b, c, v) SET_CONTEXT_FIELD(b, c, CB_SCTLR, CFRE, v)
+#define SET_CB_SCTLR_CFIE(b, c, v) SET_CONTEXT_FIELD(b, c, CB_SCTLR, CFIE, v)
+#define SET_CB_SCTLR_CFCFG(b, c, v) SET_CONTEXT_FIELD(b, c, CB_SCTLR, CFCFG, v)
+#define SET_CB_SCTLR_HUPCF(b, c, v) SET_CONTEXT_FIELD(b, c, CB_SCTLR, HUPCF, v)
+#define SET_CB_SCTLR_WXN(b, c, v) SET_CONTEXT_FIELD(b, c, CB_SCTLR, WXN, v)
+#define SET_CB_SCTLR_UWXN(b, c, v) SET_CONTEXT_FIELD(b, c, CB_SCTLR, UWXN, v)
+#define SET_CB_SCTLR_ASIDPNE(b, c, v) \
+ SET_CONTEXT_FIELD(b, c, CB_SCTLR, ASIDPNE, v)
+#define SET_CB_SCTLR_TRANSIENTCFG(b, c, v) \
+ SET_CONTEXT_FIELD(b, c, CB_SCTLR, TRANSIENTCFG, v)
+#define SET_CB_SCTLR_MEMATTR(b, c, v) \
+ SET_CONTEXT_FIELD(b, c, CB_SCTLR, MEMATTR, v)
+#define SET_CB_SCTLR_MTCFG(b, c, v) SET_CONTEXT_FIELD(b, c, CB_SCTLR, MTCFG, v)
+#define SET_CB_SCTLR_SHCFG(b, c, v) SET_CONTEXT_FIELD(b, c, CB_SCTLR, SHCFG, v)
+#define SET_CB_SCTLR_RACFG(b, c, v) SET_CONTEXT_FIELD(b, c, CB_SCTLR, RACFG, v)
+#define SET_CB_SCTLR_WACFG(b, c, v) SET_CONTEXT_FIELD(b, c, CB_SCTLR, WACFG, v)
+#define SET_CB_SCTLR_NSCFG(b, c, v) SET_CONTEXT_FIELD(b, c, CB_SCTLR, NSCFG, v)
+
+#define GET_CB_SCTLR_M(b, c) GET_CONTEXT_FIELD(b, c, CB_SCTLR, M)
+#define GET_CB_SCTLR_TRE(b, c) GET_CONTEXT_FIELD(b, c, CB_SCTLR, TRE)
+#define GET_CB_SCTLR_AFE(b, c) GET_CONTEXT_FIELD(b, c, CB_SCTLR, AFE)
+#define GET_CB_SCTLR_AFFD(b, c) GET_CONTEXT_FIELD(b, c, CB_SCTLR, AFFD)
+#define GET_CB_SCTLR_E(b, c) GET_CONTEXT_FIELD(b, c, CB_SCTLR, E)
+#define GET_CB_SCTLR_CFRE(b, c) GET_CONTEXT_FIELD(b, c, CB_SCTLR, CFRE)
+#define GET_CB_SCTLR_CFIE(b, c) GET_CONTEXT_FIELD(b, c, CB_SCTLR, CFIE)
+#define GET_CB_SCTLR_CFCFG(b, c) GET_CONTEXT_FIELD(b, c, CB_SCTLR, CFCFG)
+#define GET_CB_SCTLR_HUPCF(b, c) GET_CONTEXT_FIELD(b, c, CB_SCTLR, HUPCF)
+#define GET_CB_SCTLR_WXN(b, c) GET_CONTEXT_FIELD(b, c, CB_SCTLR, WXN)
+#define GET_CB_SCTLR_UWXN(b, c) GET_CONTEXT_FIELD(b, c, CB_SCTLR, UWXN)
+#define GET_CB_SCTLR_ASIDPNE(b, c) \
+ GET_CONTEXT_FIELD(b, c, CB_SCTLR, ASIDPNE)
+#define GET_CB_SCTLR_TRANSIENTCFG(b, c) \
+ GET_CONTEXT_FIELD(b, c, CB_SCTLR, TRANSIENTCFG)
+#define GET_CB_SCTLR_MEMATTR(b, c) \
+ GET_CONTEXT_FIELD(b, c, CB_SCTLR, MEMATTR)
+#define GET_CB_SCTLR_MTCFG(b, c) GET_CONTEXT_FIELD(b, c, CB_SCTLR, MTCFG)
+#define GET_CB_SCTLR_SHCFG(b, c) GET_CONTEXT_FIELD(b, c, CB_SCTLR, SHCFG)
+#define GET_CB_SCTLR_RACFG(b, c) GET_CONTEXT_FIELD(b, c, CB_SCTLR, RACFG)
+#define GET_CB_SCTLR_WACFG(b, c) GET_CONTEXT_FIELD(b, c, CB_SCTLR, WACFG)
+#define GET_CB_SCTLR_NSCFG(b, c) GET_CONTEXT_FIELD(b, c, CB_SCTLR, NSCFG)
+
+/* Invalidate TLB by ASID: CB_TLBIASID */
+#define SET_CB_TLBIASID_ASID(b, c, v) \
+ SET_CONTEXT_FIELD(b, c, CB_TLBIASID, ASID, v)
+
+#define GET_CB_TLBIASID_ASID(b, c) \
+ GET_CONTEXT_FIELD(b, c, CB_TLBIASID, ASID)
+
+/* Invalidate TLB by VA: CB_TLBIVA */
+#define SET_CB_TLBIVA_ASID(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TLBIVA, ASID, v)
+#define SET_CB_TLBIVA_VA(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TLBIVA, VA, v)
+
+#define GET_CB_TLBIVA_ASID(b, c) GET_CONTEXT_FIELD(b, c, CB_TLBIVA, ASID)
+#define GET_CB_TLBIVA_VA(b, c) GET_CONTEXT_FIELD(b, c, CB_TLBIVA, VA)
+
+/* Invalidate TLB by VA, All ASID: CB_TLBIVAA */
+#define SET_CB_TLBIVAA_VA(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TLBIVAA, VA, v)
+
+#define GET_CB_TLBIVAA_VA(b, c) GET_CONTEXT_FIELD(b, c, CB_TLBIVAA, VA)
+
+/* Invalidate TLB by VA, All ASID, Last Level: CB_TLBIVAAL */
+#define SET_CB_TLBIVAAL_VA(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TLBIVAAL, VA, v)
+
+#define GET_CB_TLBIVAAL_VA(b, c) GET_CONTEXT_FIELD(b, c, CB_TLBIVAAL, VA)
+
+/* Invalidate TLB by VA, Last Level: CB_TLBIVAL */
+#define SET_CB_TLBIVAL_ASID(b, c, v) \
+ SET_CONTEXT_FIELD(b, c, CB_TLBIVAL, ASID, v)
+#define SET_CB_TLBIVAL_VA(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TLBIVAL, VA, v)
+
+#define GET_CB_TLBIVAL_ASID(b, c) \
+ GET_CONTEXT_FIELD(b, c, CB_TLBIVAL, ASID)
+#define GET_CB_TLBIVAL_VA(b, c) GET_CONTEXT_FIELD(b, c, CB_TLBIVAL, VA)
+
+/* TLB Status: CB_TLBSTATUS */
+#define SET_CB_TLBSTATUS_SACTIVE(b, c, v) \
+ SET_CONTEXT_FIELD(b, c, CB_TLBSTATUS, SACTIVE, v)
+
+#define GET_CB_TLBSTATUS_SACTIVE(b, c) \
+ GET_CONTEXT_FIELD(b, c, CB_TLBSTATUS, SACTIVE)
+
+/* Translation Table Base Control Register: CB_TTBCR */
+#define SET_CB_TTBCR_T0SZ(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TTBCR, T0SZ, v)
+#define SET_CB_TTBCR_PD0(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TTBCR, PD0, v)
+#define SET_CB_TTBCR_PD1(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TTBCR, PD1, v)
+#define SET_CB_TTBCR_NSCFG0(b, c, v) \
+ SET_CONTEXT_FIELD(b, c, CB_TTBCR, NSCFG0, v)
+#define SET_CB_TTBCR_NSCFG1(b, c, v) \
+ SET_CONTEXT_FIELD(b, c, CB_TTBCR, NSCFG1, v)
+#define SET_CB_TTBCR_EAE(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TTBCR, EAE, v)
+
+#define GET_CB_TTBCR_T0SZ(b, c) GET_CONTEXT_FIELD(b, c, CB_TTBCR, T0SZ)
+#define GET_CB_TTBCR_PD0(b, c) GET_CONTEXT_FIELD(b, c, CB_TTBCR, PD0)
+#define GET_CB_TTBCR_PD1(b, c) GET_CONTEXT_FIELD(b, c, CB_TTBCR, PD1)
+#define GET_CB_TTBCR_NSCFG0(b, c) \
+ GET_CONTEXT_FIELD(b, c, CB_TTBCR, NSCFG0)
+#define GET_CB_TTBCR_NSCFG1(b, c) \
+ GET_CONTEXT_FIELD(b, c, CB_TTBCR, NSCFG1)
+#define GET_CB_TTBCR_EAE(b, c) GET_CONTEXT_FIELD(b, c, CB_TTBCR, EAE)
+
+/* Translation Table Base Register 0: CB_TTBR */
+#define SET_CB_TTBR0_IRGN1(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TTBR0, IRGN1, v)
+#define SET_CB_TTBR0_S(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TTBR0, S, v)
+#define SET_CB_TTBR0_RGN(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TTBR0, RGN, v)
+#define SET_CB_TTBR0_NOS(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TTBR0, NOS, v)
+#define SET_CB_TTBR0_IRGN0(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TTBR0, IRGN0, v)
+#define SET_CB_TTBR0_ADDR(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TTBR0, ADDR, v)
+
+#define GET_CB_TTBR0_IRGN1(b, c) GET_CONTEXT_FIELD(b, c, CB_TTBR0, IRGN1)
+#define GET_CB_TTBR0_S(b, c) GET_CONTEXT_FIELD(b, c, CB_TTBR0, S)
+#define GET_CB_TTBR0_RGN(b, c) GET_CONTEXT_FIELD(b, c, CB_TTBR0, RGN)
+#define GET_CB_TTBR0_NOS(b, c) GET_CONTEXT_FIELD(b, c, CB_TTBR0, NOS)
+#define GET_CB_TTBR0_IRGN0(b, c) GET_CONTEXT_FIELD(b, c, CB_TTBR0, IRGN0)
+#define GET_CB_TTBR0_ADDR(b, c) GET_CONTEXT_FIELD(b, c, CB_TTBR0, ADDR)
+
+/* Translation Table Base Register 1: CB_TTBR1 */
+#define SET_CB_TTBR1_IRGN1(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TTBR1, IRGN1, v)
+#define SET_CB_TTBR1_0S(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TTBR1, S, v)
+#define SET_CB_TTBR1_RGN(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TTBR1, RGN, v)
+#define SET_CB_TTBR1_NOS(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TTBR1, NOS, v)
+#define SET_CB_TTBR1_IRGN0(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TTBR1, IRGN0, v)
+#define SET_CB_TTBR1_ADDR(b, c, v) SET_CONTEXT_FIELD(b, c, CB_TTBR1, ADDR, v)
+
+#define GET_CB_TTBR1_IRGN1(b, c) GET_CONTEXT_FIELD(b, c, CB_TTBR1, IRGN1)
+#define GET_CB_TTBR1_0S(b, c) GET_CONTEXT_FIELD(b, c, CB_TTBR1, S)
+#define GET_CB_TTBR1_RGN(b, c) GET_CONTEXT_FIELD(b, c, CB_TTBR1, RGN)
+#define GET_CB_TTBR1_NOS(b, c) GET_CONTEXT_FIELD(b, c, CB_TTBR1, NOS)
+#define GET_CB_TTBR1_IRGN0(b, c) GET_CONTEXT_FIELD(b, c, CB_TTBR1, IRGN0)
+#define GET_CB_TTBR1_ADDR(b, c) GET_CONTEXT_FIELD(b, c, CB_TTBR1, ADDR)
+
+/* Global Register Space 0 */
+#define CR0 (0x0000)
+#define SCR1 (0x0004)
+#define CR2 (0x0008)
+#define ACR (0x0010)
+#define IDR0 (0x0020)
+#define IDR1 (0x0024)
+#define IDR2 (0x0028)
+#define IDR7 (0x003C)
+#define GFAR (0x0040)
+#define GFSR (0x0044)
+#define GFSRRESTORE (0x004C)
+#define GFSYNR0 (0x0050)
+#define GFSYNR1 (0x0054)
+#define GFSYNR2 (0x0058)
+#define TLBIVMID (0x0064)
+#define TLBIALLNSNH (0x0068)
+#define TLBIALLH (0x006C)
+#define TLBGSYNC (0x0070)
+#define TLBGSTATUS (0x0074)
+#define TLBIVAH (0x0078)
+#define GATS1UR (0x0100)
+#define GATS1UW (0x0108)
+#define GATS1PR (0x0110)
+#define GATS1PW (0x0118)
+#define GATS12UR (0x0120)
+#define GATS12UW (0x0128)
+#define GATS12PR (0x0130)
+#define GATS12PW (0x0138)
+#define GPAR (0x0180)
+#define GATSR (0x0188)
+#define NSCR0 (0x0400)
+#define NSCR2 (0x0408)
+#define NSACR (0x0410)
+#define SMR (0x0800)
+#define S2CR (0x0C00)
+
+/* Global Register Space 1 */
+#define CBAR (0x1000)
+#define CBFRSYNRA (0x1400)
+
+/* Implementation defined Register Space */
+#define PREDICTIONDIS0 (0x204C)
+#define PREDICTIONDIS1 (0x2050)
+#define S1L1BFBLP0 (0x215C)
+
+/* Performance Monitoring Register Space */
+#define PMEVCNTR_N (0x3000)
+#define PMEVTYPER_N (0x3400)
+#define PMCGCR_N (0x3800)
+#define PMCGSMR_N (0x3A00)
+#define PMCNTENSET_N (0x3C00)
+#define PMCNTENCLR_N (0x3C20)
+#define PMINTENSET_N (0x3C40)
+#define PMINTENCLR_N (0x3C60)
+#define PMOVSCLR_N (0x3C80)
+#define PMOVSSET_N (0x3CC0)
+#define PMCFGR (0x3E00)
+#define PMCR (0x3E04)
+#define PMCEID0 (0x3E20)
+#define PMCEID1 (0x3E24)
+#define PMAUTHSTATUS (0x3FB8)
+#define PMDEVTYPE (0x3FCC)
+
+/* Secure Status Determination Address Space */
+#define SSDR_N (0x4000)
+
+/* Stage 1 Context Bank Format */
+#define CB_SCTLR (0x000)
+#define CB_ACTLR (0x004)
+#define CB_RESUME (0x008)
+#define CB_TTBR0 (0x020)
+#define CB_TTBR1 (0x028)
+#define CB_TTBCR (0x030)
+#define CB_CONTEXTIDR (0x034)
+#define CB_PRRR (0x038)
+#define CB_NMRR (0x03C)
+#define CB_PAR (0x050)
+#define CB_FSR (0x058)
+#define CB_FSRRESTORE (0x05C)
+#define CB_FAR (0x060)
+#define CB_FSYNR0 (0x068)
+#define CB_FSYNR1 (0x06C)
+#define CB_TLBIVA (0x600)
+#define CB_TLBIVAA (0x608)
+#define CB_TLBIASID (0x610)
+#define CB_TLBIALL (0x618)
+#define CB_TLBIVAL (0x620)
+#define CB_TLBIVAAL (0x628)
+#define CB_TLBSYNC (0x7F0)
+#define CB_TLBSTATUS (0x7F4)
+#define CB_ATS1PR (0x800)
+#define CB_ATS1PW (0x808)
+#define CB_ATS1UR (0x810)
+#define CB_ATS1UW (0x818)
+#define CB_ATSR (0x8F0)
+#define CB_PMXEVCNTR_N (0xE00)
+#define CB_PMXEVTYPER_N (0xE80)
+#define CB_PMCFGR (0xF00)
+#define CB_PMCR (0xF04)
+#define CB_PMCEID0 (0xF20)
+#define CB_PMCEID1 (0xF24)
+#define CB_PMCNTENSET (0xF40)
+#define CB_PMCNTENCLR (0xF44)
+#define CB_PMINTENSET (0xF48)
+#define CB_PMINTENCLR (0xF4C)
+#define CB_PMOVSCLR (0xF50)
+#define CB_PMOVSSET (0xF58)
+#define CB_PMAUTHSTATUS (0xFB8)
+
+/* Global Register Fields */
+/* Configuration Register: CR0 */
+#define CR0_NSCFG (CR0_NSCFG_MASK << CR0_NSCFG_SHIFT)
+#define CR0_WACFG (CR0_WACFG_MASK << CR0_WACFG_SHIFT)
+#define CR0_RACFG (CR0_RACFG_MASK << CR0_RACFG_SHIFT)
+#define CR0_SHCFG (CR0_SHCFG_MASK << CR0_SHCFG_SHIFT)
+#define CR0_SMCFCFG (CR0_SMCFCFG_MASK << CR0_SMCFCFG_SHIFT)
+#define CR0_MTCFG (CR0_MTCFG_MASK << CR0_MTCFG_SHIFT)
+#define CR0_MEMATTR (CR0_MEMATTR_MASK << CR0_MEMATTR_SHIFT)
+#define CR0_BSU (CR0_BSU_MASK << CR0_BSU_SHIFT)
+#define CR0_FB (CR0_FB_MASK << CR0_FB_SHIFT)
+#define CR0_PTM (CR0_PTM_MASK << CR0_PTM_SHIFT)
+#define CR0_VMIDPNE (CR0_VMIDPNE_MASK << CR0_VMIDPNE_SHIFT)
+#define CR0_USFCFG (CR0_USFCFG_MASK << CR0_USFCFG_SHIFT)
+#define CR0_GSE (CR0_GSE_MASK << CR0_GSE_SHIFT)
+#define CR0_STALLD (CR0_STALLD_MASK << CR0_STALLD_SHIFT)
+#define CR0_TRANSIENTCFG (CR0_TRANSIENTCFG_MASK << CR0_TRANSIENTCFG_SHIFT)
+#define CR0_GCFGFIE (CR0_GCFGFIE_MASK << CR0_GCFGFIE_SHIFT)
+#define CR0_GCFGFRE (CR0_GCFGFRE_MASK << CR0_GCFGFRE_SHIFT)
+#define CR0_GFIE (CR0_GFIE_MASK << CR0_GFIE_SHIFT)
+#define CR0_GFRE (CR0_GFRE_MASK << CR0_GFRE_SHIFT)
+#define CR0_CLIENTPD (CR0_CLIENTPD_MASK << CR0_CLIENTPD_SHIFT)
+
+/* Configuration Register: CR2 */
+#define CR2_BPVMID (CR2_BPVMID_MASK << CR2_BPVMID_SHIFT)
+
+/* Global Address Translation, Stage 1, Privileged Read: GATS1PR */
+#define GATS1PR_ADDR (GATS1PR_ADDR_MASK << GATS1PR_ADDR_SHIFT)
+#define GATS1PR_NDX (GATS1PR_NDX_MASK << GATS1PR_NDX_SHIFT)
+
+/* Global Address Translation, Stage 1, Privileged Write: GATS1PW */
+#define GATS1PW_ADDR (GATS1PW_ADDR_MASK << GATS1PW_ADDR_SHIFT)
+#define GATS1PW_NDX (GATS1PW_NDX_MASK << GATS1PW_NDX_SHIFT)
+
+/* Global Address Translation, Stage 1, User Read: GATS1UR */
+#define GATS1UR_ADDR (GATS1UR_ADDR_MASK << GATS1UR_ADDR_SHIFT)
+#define GATS1UR_NDX (GATS1UR_NDX_MASK << GATS1UR_NDX_SHIFT)
+
+/* Global Address Translation, Stage 1, User Write: GATS1UW */
+#define GATS1UW_ADDR (GATS1UW_ADDR_MASK << GATS1UW_ADDR_SHIFT)
+#define GATS1UW_NDX (GATS1UW_NDX_MASK << GATS1UW_NDX_SHIFT)
+
+/* Global Address Translation, Stage 1 and 2, Privileged Read: GATS1PR */
+#define GATS12PR_ADDR (GATS12PR_ADDR_MASK << GATS12PR_ADDR_SHIFT)
+#define GATS12PR_NDX (GATS12PR_NDX_MASK << GATS12PR_NDX_SHIFT)
+
+/* Global Address Translation, Stage 1 and 2, Privileged Write: GATS1PW */
+#define GATS12PW_ADDR (GATS12PW_ADDR_MASK << GATS12PW_ADDR_SHIFT)
+#define GATS12PW_NDX (GATS12PW_NDX_MASK << GATS12PW_NDX_SHIFT)
+
+/* Global Address Translation, Stage 1 and 2, User Read: GATS1UR */
+#define GATS12UR_ADDR (GATS12UR_ADDR_MASK << GATS12UR_ADDR_SHIFT)
+#define GATS12UR_NDX (GATS12UR_NDX_MASK << GATS12UR_NDX_SHIFT)
+
+/* Global Address Translation, Stage 1 and 2, User Write: GATS1UW */
+#define GATS12UW_ADDR (GATS12UW_ADDR_MASK << GATS12UW_ADDR_SHIFT)
+#define GATS12UW_NDX (GATS12UW_NDX_MASK << GATS12UW_NDX_SHIFT)
+
+/* Global Address Translation Status Register: GATSR */
+#define GATSR_ACTIVE (GATSR_ACTIVE_MASK << GATSR_ACTIVE_SHIFT)
+
+/* Global Fault Address Register: GFAR */
+#define GFAR_FADDR (GFAR_FADDR_MASK << GFAR_FADDR_SHIFT)
+
+/* Global Fault Status Register: GFSR */
+#define GFSR_ICF (GFSR_ICF_MASK << GFSR_ICF_SHIFT)
+#define GFSR_USF (GFSR_USF_MASK << GFSR_USF_SHIFT)
+#define GFSR_SMCF (GFSR_SMCF_MASK << GFSR_SMCF_SHIFT)
+#define GFSR_UCBF (GFSR_UCBF_MASK << GFSR_UCBF_SHIFT)
+#define GFSR_UCIF (GFSR_UCIF_MASK << GFSR_UCIF_SHIFT)
+#define GFSR_CAF (GFSR_CAF_MASK << GFSR_CAF_SHIFT)
+#define GFSR_EF (GFSR_EF_MASK << GFSR_EF_SHIFT)
+#define GFSR_PF (GFSR_PF_MASK << GFSR_PF_SHIFT)
+#define GFSR_MULTI (GFSR_MULTI_MASK << GFSR_MULTI_SHIFT)
+
+/* Global Fault Syndrome Register 0: GFSYNR0 */
+#define GFSYNR0_NESTED (GFSYNR0_NESTED_MASK << GFSYNR0_NESTED_SHIFT)
+#define GFSYNR0_WNR (GFSYNR0_WNR_MASK << GFSYNR0_WNR_SHIFT)
+#define GFSYNR0_PNU (GFSYNR0_PNU_MASK << GFSYNR0_PNU_SHIFT)
+#define GFSYNR0_IND (GFSYNR0_IND_MASK << GFSYNR0_IND_SHIFT)
+#define GFSYNR0_NSSTATE (GFSYNR0_NSSTATE_MASK << GFSYNR0_NSSTATE_SHIFT)
+#define GFSYNR0_NSATTR (GFSYNR0_NSATTR_MASK << GFSYNR0_NSATTR_SHIFT)
+
+/* Global Fault Syndrome Register 1: GFSYNR1 */
+#define GFSYNR1_SID (GFSYNR1_SID_MASK << GFSYNR1_SID_SHIFT)
+
+/* Global Physical Address Register: GPAR */
+#define GPAR_F (GPAR_F_MASK << GPAR_F_SHIFT)
+#define GPAR_SS (GPAR_SS_MASK << GPAR_SS_SHIFT)
+#define GPAR_OUTER (GPAR_OUTER_MASK << GPAR_OUTER_SHIFT)
+#define GPAR_INNER (GPAR_INNER_MASK << GPAR_INNER_SHIFT)
+#define GPAR_SH (GPAR_SH_MASK << GPAR_SH_SHIFT)
+#define GPAR_NS (GPAR_NS_MASK << GPAR_NS_SHIFT)
+#define GPAR_NOS (GPAR_NOS_MASK << GPAR_NOS_SHIFT)
+#define GPAR_PA (GPAR_PA_MASK << GPAR_PA_SHIFT)
+#define GPAR_TF (GPAR_TF_MASK << GPAR_TF_SHIFT)
+#define GPAR_AFF (GPAR_AFF_MASK << GPAR_AFF_SHIFT)
+#define GPAR_PF (GPAR_PF_MASK << GPAR_PF_SHIFT)
+#define GPAR_EF (GPAR_EF_MASK << GPAR_EF_SHIFT)
+#define GPAR_TLCMCF (GPAR_TLBMCF_MASK << GPAR_TLCMCF_SHIFT)
+#define GPAR_TLBLKF (GPAR_TLBLKF_MASK << GPAR_TLBLKF_SHIFT)
+#define GPAR_UCBF (GPAR_UCBF_MASK << GFAR_UCBF_SHIFT)
+
+/* Identification Register: IDR0 */
+#define IDR0_NUMSMRG (IDR0_NUMSMRG_MASK << IDR0_NUMSMGR_SHIFT)
+#define IDR0_NUMSIDB (IDR0_NUMSIDB_MASK << IDR0_NUMSIDB_SHIFT)
+#define IDR0_BTM (IDR0_BTM_MASK << IDR0_BTM_SHIFT)
+#define IDR0_CTTW (IDR0_CTTW_MASK << IDR0_CTTW_SHIFT)
+#define IDR0_NUMIRPT (IDR0_NUMIPRT_MASK << IDR0_NUMIRPT_SHIFT)
+#define IDR0_PTFS (IDR0_PTFS_MASK << IDR0_PTFS_SHIFT)
+#define IDR0_SMS (IDR0_SMS_MASK << IDR0_SMS_SHIFT)
+#define IDR0_NTS (IDR0_NTS_MASK << IDR0_NTS_SHIFT)
+#define IDR0_S2TS (IDR0_S2TS_MASK << IDR0_S2TS_SHIFT)
+#define IDR0_S1TS (IDR0_S1TS_MASK << IDR0_S1TS_SHIFT)
+#define IDR0_SES (IDR0_SES_MASK << IDR0_SES_SHIFT)
+
+/* Identification Register: IDR1 */
+#define IDR1_NUMCB (IDR1_NUMCB_MASK << IDR1_NUMCB_SHIFT)
+#define IDR1_NUMSSDNDXB (IDR1_NUMSSDNDXB_MASK << IDR1_NUMSSDNDXB_SHIFT)
+#define IDR1_SSDTP (IDR1_SSDTP_MASK << IDR1_SSDTP_SHIFT)
+#define IDR1_SMCD (IDR1_SMCD_MASK << IDR1_SMCD_SHIFT)
+#define IDR1_NUMS2CB (IDR1_NUMS2CB_MASK << IDR1_NUMS2CB_SHIFT)
+#define IDR1_NUMPAGENDXB (IDR1_NUMPAGENDXB_MASK << IDR1_NUMPAGENDXB_SHIFT)
+#define IDR1_PAGESIZE (IDR1_PAGESIZE_MASK << IDR1_PAGESIZE_SHIFT)
+
+/* Identification Register: IDR2 */
+#define IDR2_IAS (IDR2_IAS_MASK << IDR2_IAS_SHIFT)
+#define IDR1_OAS (IDR2_OAS_MASK << IDR2_OAS_SHIFT)
+
+/* Identification Register: IDR7 */
+#define IDR7_MINOR (IDR7_MINOR_MASK << IDR7_MINOR_SHIFT)
+#define IDR7_MAJOR (IDR7_MAJOR_MASK << IDR7_MAJOR_SHIFT)
+
+/* Stream to Context Register: S2CR */
+#define S2CR_CBNDX (S2CR_CBNDX_MASK << S2cR_CBNDX_SHIFT)
+#define S2CR_SHCFG (S2CR_SHCFG_MASK << s2CR_SHCFG_SHIFT)
+#define S2CR_MTCFG (S2CR_MTCFG_MASK << S2CR_MTCFG_SHIFT)
+#define S2CR_MEMATTR (S2CR_MEMATTR_MASK << S2CR_MEMATTR_SHIFT)
+#define S2CR_TYPE (S2CR_TYPE_MASK << S2CR_TYPE_SHIFT)
+#define S2CR_NSCFG (S2CR_NSCFG_MASK << S2CR_NSCFG_SHIFT)
+#define S2CR_RACFG (S2CR_RACFG_MASK << S2CR_RACFG_SHIFT)
+#define S2CR_WACFG (S2CR_WACFG_MASK << S2CR_WACFG_SHIFT)
+#define S2CR_PRIVCFG (S2CR_PRIVCFG_MASK << S2CR_PRIVCFG_SHIFT)
+#define S2CR_INSTCFG (S2CR_INSTCFG_MASK << S2CR_INSTCFG_SHIFT)
+#define S2CR_TRANSIENTCFG (S2CR_TRANSIENTCFG_MASK << S2CR_TRANSIENTCFG_SHIFT)
+#define S2CR_VMID (S2CR_VMID_MASK << S2CR_VMID_SHIFT)
+#define S2CR_BSU (S2CR_BSU_MASK << S2CR_BSU_SHIFT)
+#define S2CR_FB (S2CR_FB_MASK << S2CR_FB_SHIFT)
+
+/* Stream Match Register: SMR */
+#define SMR_ID (SMR_ID_MASK << SMR_ID_SHIFT)
+#define SMR_MASK (SMR_MASK_MASK << SMR_MASK_SHIFT)
+#define SMR_VALID (SMR_VALID_MASK << SMR_VALID_SHIFT)
+
+/* Global TLB Status: TLBGSTATUS */
+#define TLBGSTATUS_GSACTIVE (TLBGSTATUS_GSACTIVE_MASK << \
+ TLBGSTATUS_GSACTIVE_SHIFT)
+/* Invalidate Hyp TLB by VA: TLBIVAH */
+#define TLBIVAH_ADDR (TLBIVAH_ADDR_MASK << TLBIVAH_ADDR_SHIFT)
+
+/* Invalidate TLB by VMID: TLBIVMID */
+#define TLBIVMID_VMID (TLBIVMID_VMID_MASK << TLBIVMID_VMID_SHIFT)
+
+/* Context Bank Attribute Register: CBAR */
+#define CBAR_VMID (CBAR_VMID_MASK << CBAR_VMID_SHIFT)
+#define CBAR_CBNDX (CBAR_CBNDX_MASK << CBAR_CBNDX_SHIFT)
+#define CBAR_BPSHCFG (CBAR_BPSHCFG_MASK << CBAR_BPSHCFG_SHIFT)
+#define CBAR_HYPC (CBAR_HYPC_MASK << CBAR_HYPC_SHIFT)
+#define CBAR_FB (CBAR_FB_MASK << CBAR_FB_SHIFT)
+#define CBAR_MEMATTR (CBAR_MEMATTR_MASK << CBAR_MEMATTR_SHIFT)
+#define CBAR_TYPE (CBAR_TYPE_MASK << CBAR_TYPE_SHIFT)
+#define CBAR_BSU (CBAR_BSU_MASK << CBAR_BSU_SHIFT)
+#define CBAR_RACFG (CBAR_RACFG_MASK << CBAR_RACFG_SHIFT)
+#define CBAR_WACFG (CBAR_WACFG_MASK << CBAR_WACFG_SHIFT)
+#define CBAR_IRPTNDX (CBAR_IRPTNDX_MASK << CBAR_IRPTNDX_SHIFT)
+
+/* Context Bank Fault Restricted Syndrome Register A: CBFRSYNRA */
+#define CBFRSYNRA_SID (CBFRSYNRA_SID_MASK << CBFRSYNRA_SID_SHIFT)
+
+/* Performance Monitoring Register Fields */
+
+/* Stage 1 Context Bank Format Fields */
+/* Auxiliary Control Register: CB_ACTLR */
+#define CB_ACTLR_REQPRIORITY \
+ (CB_ACTLR_REQPRIORITY_MASK << CB_ACTLR_REQPRIORITY_SHIFT)
+#define CB_ACTLR_REQPRIORITYCFG \
+ (CB_ACTLR_REQPRIORITYCFG_MASK << CB_ACTLR_REQPRIORITYCFG_SHIFT)
+#define CB_ACTLR_PRIVCFG (CB_ACTLR_PRIVCFG_MASK << CB_ACTLR_PRIVCFG_SHIFT)
+#define CB_ACTLR_BPRCOSH (CB_ACTLR_BPRCOSH_MASK << CB_ACTLR_BPRCOSH_SHIFT)
+#define CB_ACTLR_BPRCISH (CB_ACTLR_BPRCISH_MASK << CB_ACTLR_BPRCISH_SHIFT)
+#define CB_ACTLR_BPRCNSH (CB_ACTLR_BPRCNSH_MASK << CB_ACTLR_BPRCNSH_SHIFT)
+
+/* Address Translation, Stage 1, Privileged Read: CB_ATS1PR */
+#define CB_ATS1PR_ADDR (CB_ATS1PR_ADDR_MASK << CB_ATS1PR_ADDR_SHIFT)
+
+/* Address Translation, Stage 1, Privileged Write: CB_ATS1PW */
+#define CB_ATS1PW_ADDR (CB_ATS1PW_ADDR_MASK << CB_ATS1PW_ADDR_SHIFT)
+
+/* Address Translation, Stage 1, User Read: CB_ATS1UR */
+#define CB_ATS1UR_ADDR (CB_ATS1UR_ADDR_MASK << CB_ATS1UR_ADDR_SHIFT)
+
+/* Address Translation, Stage 1, User Write: CB_ATS1UW */
+#define CB_ATS1UW_ADDR (CB_ATS1UW_ADDR_MASK << CB_ATS1UW_ADDR_SHIFT)
+
+/* Address Translation Status Register: CB_ATSR */
+#define CB_ATSR_ACTIVE (CB_ATSR_ACTIVE_MASK << CB_ATSR_ACTIVE_SHIFT)
+
+/* Context ID Register: CB_CONTEXTIDR */
+#define CB_CONTEXTIDR_ASID (CB_CONTEXTIDR_ASID_MASK << \
+ CB_CONTEXTIDR_ASID_SHIFT)
+#define CB_CONTEXTIDR_PROCID (CB_CONTEXTIDR_PROCID_MASK << \
+ CB_CONTEXTIDR_PROCID_SHIFT)
+
+/* Fault Address Register: CB_FAR */
+#define CB_FAR_FADDR (CB_FAR_FADDR_MASK << CB_FAR_FADDR_SHIFT)
+
+/* Fault Status Register: CB_FSR */
+#define CB_FSR_TF (CB_FSR_TF_MASK << CB_FSR_TF_SHIFT)
+#define CB_FSR_AFF (CB_FSR_AFF_MASK << CB_FSR_AFF_SHIFT)
+#define CB_FSR_PF (CB_FSR_PF_MASK << CB_FSR_PF_SHIFT)
+#define CB_FSR_EF (CB_FSR_EF_MASK << CB_FSR_EF_SHIFT)
+#define CB_FSR_TLBMCF (CB_FSR_TLBMCF_MASK << CB_FSR_TLBMCF_SHIFT)
+#define CB_FSR_TLBLKF (CB_FSR_TLBLKF_MASK << CB_FSR_TLBLKF_SHIFT)
+#define CB_FSR_SS (CB_FSR_SS_MASK << CB_FSR_SS_SHIFT)
+#define CB_FSR_MULTI (CB_FSR_MULTI_MASK << CB_FSR_MULTI_SHIFT)
+
+/* Fault Syndrome Register 0: CB_FSYNR0 */
+#define CB_FSYNR0_PLVL (CB_FSYNR0_PLVL_MASK << CB_FSYNR0_PLVL_SHIFT)
+#define CB_FSYNR0_S1PTWF (CB_FSYNR0_S1PTWF_MASK << CB_FSYNR0_S1PTWF_SHIFT)
+#define CB_FSYNR0_WNR (CB_FSYNR0_WNR_MASK << CB_FSYNR0_WNR_SHIFT)
+#define CB_FSYNR0_PNU (CB_FSYNR0_PNU_MASK << CB_FSYNR0_PNU_SHIFT)
+#define CB_FSYNR0_IND (CB_FSYNR0_IND_MASK << CB_FSYNR0_IND_SHIFT)
+#define CB_FSYNR0_NSSTATE (CB_FSYNR0_NSSTATE_MASK << CB_FSYNR0_NSSTATE_SHIFT)
+#define CB_FSYNR0_NSATTR (CB_FSYNR0_NSATTR_MASK << CB_FSYNR0_NSATTR_SHIFT)
+#define CB_FSYNR0_ATOF (CB_FSYNR0_ATOF_MASK << CB_FSYNR0_ATOF_SHIFT)
+#define CB_FSYNR0_PTWF (CB_FSYNR0_PTWF_MASK << CB_FSYNR0_PTWF_SHIFT)
+#define CB_FSYNR0_AFR (CB_FSYNR0_AFR_MASK << CB_FSYNR0_AFR_SHIFT)
+#define CB_FSYNR0_S1CBNDX (CB_FSYNR0_S1CBNDX_MASK << CB_FSYNR0_S1CBNDX_SHIFT)
+
+/* Normal Memory Remap Register: CB_NMRR */
+#define CB_NMRR_IR0 (CB_NMRR_IR0_MASK << CB_NMRR_IR0_SHIFT)
+#define CB_NMRR_IR1 (CB_NMRR_IR1_MASK << CB_NMRR_IR1_SHIFT)
+#define CB_NMRR_IR2 (CB_NMRR_IR2_MASK << CB_NMRR_IR2_SHIFT)
+#define CB_NMRR_IR3 (CB_NMRR_IR3_MASK << CB_NMRR_IR3_SHIFT)
+#define CB_NMRR_IR4 (CB_NMRR_IR4_MASK << CB_NMRR_IR4_SHIFT)
+#define CB_NMRR_IR5 (CB_NMRR_IR5_MASK << CB_NMRR_IR5_SHIFT)
+#define CB_NMRR_IR6 (CB_NMRR_IR6_MASK << CB_NMRR_IR6_SHIFT)
+#define CB_NMRR_IR7 (CB_NMRR_IR7_MASK << CB_NMRR_IR7_SHIFT)
+#define CB_NMRR_OR0 (CB_NMRR_OR0_MASK << CB_NMRR_OR0_SHIFT)
+#define CB_NMRR_OR1 (CB_NMRR_OR1_MASK << CB_NMRR_OR1_SHIFT)
+#define CB_NMRR_OR2 (CB_NMRR_OR2_MASK << CB_NMRR_OR2_SHIFT)
+#define CB_NMRR_OR3 (CB_NMRR_OR3_MASK << CB_NMRR_OR3_SHIFT)
+#define CB_NMRR_OR4 (CB_NMRR_OR4_MASK << CB_NMRR_OR4_SHIFT)
+#define CB_NMRR_OR5 (CB_NMRR_OR5_MASK << CB_NMRR_OR5_SHIFT)
+#define CB_NMRR_OR6 (CB_NMRR_OR6_MASK << CB_NMRR_OR6_SHIFT)
+#define CB_NMRR_OR7 (CB_NMRR_OR7_MASK << CB_NMRR_OR7_SHIFT)
+
+/* Physical Address Register: CB_PAR */
+#define CB_PAR_F (CB_PAR_F_MASK << CB_PAR_F_SHIFT)
+#define CB_PAR_SS (CB_PAR_SS_MASK << CB_PAR_SS_SHIFT)
+#define CB_PAR_OUTER (CB_PAR_OUTER_MASK << CB_PAR_OUTER_SHIFT)
+#define CB_PAR_INNER (CB_PAR_INNER_MASK << CB_PAR_INNER_SHIFT)
+#define CB_PAR_SH (CB_PAR_SH_MASK << CB_PAR_SH_SHIFT)
+#define CB_PAR_NS (CB_PAR_NS_MASK << CB_PAR_NS_SHIFT)
+#define CB_PAR_NOS (CB_PAR_NOS_MASK << CB_PAR_NOS_SHIFT)
+#define CB_PAR_PA (CB_PAR_PA_MASK << CB_PAR_PA_SHIFT)
+#define CB_PAR_TF (CB_PAR_TF_MASK << CB_PAR_TF_SHIFT)
+#define CB_PAR_AFF (CB_PAR_AFF_MASK << CB_PAR_AFF_SHIFT)
+#define CB_PAR_PF (CB_PAR_PF_MASK << CB_PAR_PF_SHIFT)
+#define CB_PAR_TLBMCF (CB_PAR_TLBMCF_MASK << CB_PAR_TLBMCF_SHIFT)
+#define CB_PAR_TLBLKF (CB_PAR_TLBLKF_MASK << CB_PAR_TLBLKF_SHIFT)
+#define CB_PAR_ATOT (CB_PAR_ATOT_MASK << CB_PAR_ATOT_SHIFT)
+#define CB_PAR_PLVL (CB_PAR_PLVL_MASK << CB_PAR_PLVL_SHIFT)
+#define CB_PAR_STAGE (CB_PAR_STAGE_MASK << CB_PAR_STAGE_SHIFT)
+
+/* Primary Region Remap Register: CB_PRRR */
+#define CB_PRRR_TR0 (CB_PRRR_TR0_MASK << CB_PRRR_TR0_SHIFT)
+#define CB_PRRR_TR1 (CB_PRRR_TR1_MASK << CB_PRRR_TR1_SHIFT)
+#define CB_PRRR_TR2 (CB_PRRR_TR2_MASK << CB_PRRR_TR2_SHIFT)
+#define CB_PRRR_TR3 (CB_PRRR_TR3_MASK << CB_PRRR_TR3_SHIFT)
+#define CB_PRRR_TR4 (CB_PRRR_TR4_MASK << CB_PRRR_TR4_SHIFT)
+#define CB_PRRR_TR5 (CB_PRRR_TR5_MASK << CB_PRRR_TR5_SHIFT)
+#define CB_PRRR_TR6 (CB_PRRR_TR6_MASK << CB_PRRR_TR6_SHIFT)
+#define CB_PRRR_TR7 (CB_PRRR_TR7_MASK << CB_PRRR_TR7_SHIFT)
+#define CB_PRRR_DS0 (CB_PRRR_DS0_MASK << CB_PRRR_DS0_SHIFT)
+#define CB_PRRR_DS1 (CB_PRRR_DS1_MASK << CB_PRRR_DS1_SHIFT)
+#define CB_PRRR_NS0 (CB_PRRR_NS0_MASK << CB_PRRR_NS0_SHIFT)
+#define CB_PRRR_NS1 (CB_PRRR_NS1_MASK << CB_PRRR_NS1_SHIFT)
+#define CB_PRRR_NOS0 (CB_PRRR_NOS0_MASK << CB_PRRR_NOS0_SHIFT)
+#define CB_PRRR_NOS1 (CB_PRRR_NOS1_MASK << CB_PRRR_NOS1_SHIFT)
+#define CB_PRRR_NOS2 (CB_PRRR_NOS2_MASK << CB_PRRR_NOS2_SHIFT)
+#define CB_PRRR_NOS3 (CB_PRRR_NOS3_MASK << CB_PRRR_NOS3_SHIFT)
+#define CB_PRRR_NOS4 (CB_PRRR_NOS4_MASK << CB_PRRR_NOS4_SHIFT)
+#define CB_PRRR_NOS5 (CB_PRRR_NOS5_MASK << CB_PRRR_NOS5_SHIFT)
+#define CB_PRRR_NOS6 (CB_PRRR_NOS6_MASK << CB_PRRR_NOS6_SHIFT)
+#define CB_PRRR_NOS7 (CB_PRRR_NOS7_MASK << CB_PRRR_NOS7_SHIFT)
+
+/* Transaction Resume: CB_RESUME */
+#define CB_RESUME_TNR (CB_RESUME_TNR_MASK << CB_RESUME_TNR_SHIFT)
+
+/* System Control Register: CB_SCTLR */
+#define CB_SCTLR_M (CB_SCTLR_M_MASK << CB_SCTLR_M_SHIFT)
+#define CB_SCTLR_TRE (CB_SCTLR_TRE_MASK << CB_SCTLR_TRE_SHIFT)
+#define CB_SCTLR_AFE (CB_SCTLR_AFE_MASK << CB_SCTLR_AFE_SHIFT)
+#define CB_SCTLR_AFFD (CB_SCTLR_AFFD_MASK << CB_SCTLR_AFFD_SHIFT)
+#define CB_SCTLR_E (CB_SCTLR_E_MASK << CB_SCTLR_E_SHIFT)
+#define CB_SCTLR_CFRE (CB_SCTLR_CFRE_MASK << CB_SCTLR_CFRE_SHIFT)
+#define CB_SCTLR_CFIE (CB_SCTLR_CFIE_MASK << CB_SCTLR_CFIE_SHIFT)
+#define CB_SCTLR_CFCFG (CB_SCTLR_CFCFG_MASK << CB_SCTLR_CFCFG_SHIFT)
+#define CB_SCTLR_HUPCF (CB_SCTLR_HUPCF_MASK << CB_SCTLR_HUPCF_SHIFT)
+#define CB_SCTLR_WXN (CB_SCTLR_WXN_MASK << CB_SCTLR_WXN_SHIFT)
+#define CB_SCTLR_UWXN (CB_SCTLR_UWXN_MASK << CB_SCTLR_UWXN_SHIFT)
+#define CB_SCTLR_ASIDPNE (CB_SCTLR_ASIDPNE_MASK << CB_SCTLR_ASIDPNE_SHIFT)
+#define CB_SCTLR_TRANSIENTCFG (CB_SCTLR_TRANSIENTCFG_MASK << \
+ CB_SCTLR_TRANSIENTCFG_SHIFT)
+#define CB_SCTLR_MEMATTR (CB_SCTLR_MEMATTR_MASK << CB_SCTLR_MEMATTR_SHIFT)
+#define CB_SCTLR_MTCFG (CB_SCTLR_MTCFG_MASK << CB_SCTLR_MTCFG_SHIFT)
+#define CB_SCTLR_SHCFG (CB_SCTLR_SHCFG_MASK << CB_SCTLR_SHCFG_SHIFT)
+#define CB_SCTLR_RACFG (CB_SCTLR_RACFG_MASK << CB_SCTLR_RACFG_SHIFT)
+#define CB_SCTLR_WACFG (CB_SCTLR_WACFG_MASK << CB_SCTLR_WACFG_SHIFT)
+#define CB_SCTLR_NSCFG (CB_SCTLR_NSCFG_MASK << CB_SCTLR_NSCFG_SHIFT)
+
+/* Invalidate TLB by ASID: CB_TLBIASID */
+#define CB_TLBIASID_ASID (CB_TLBIASID_ASID_MASK << CB_TLBIASID_ASID_SHIFT)
+
+/* Invalidate TLB by VA: CB_TLBIVA */
+#define CB_TLBIVA_ASID (CB_TLBIVA_ASID_MASK << CB_TLBIVA_ASID_SHIFT)
+#define CB_TLBIVA_VA (CB_TLBIVA_VA_MASK << CB_TLBIVA_VA_SHIFT)
+
+/* Invalidate TLB by VA, All ASID: CB_TLBIVAA */
+#define CB_TLBIVAA_VA (CB_TLBIVAA_VA_MASK << CB_TLBIVAA_VA_SHIFT)
+
+/* Invalidate TLB by VA, All ASID, Last Level: CB_TLBIVAAL */
+#define CB_TLBIVAAL_VA (CB_TLBIVAAL_VA_MASK << CB_TLBIVAAL_VA_SHIFT)
+
+/* Invalidate TLB by VA, Last Level: CB_TLBIVAL */
+#define CB_TLBIVAL_ASID (CB_TLBIVAL_ASID_MASK << CB_TLBIVAL_ASID_SHIFT)
+#define CB_TLBIVAL_VA (CB_TLBIVAL_VA_MASK << CB_TLBIVAL_VA_SHIFT)
+
+/* TLB Status: CB_TLBSTATUS */
+#define CB_TLBSTATUS_SACTIVE (CB_TLBSTATUS_SACTIVE_MASK << \
+ CB_TLBSTATUS_SACTIVE_SHIFT)
+
+/* Translation Table Base Control Register: CB_TTBCR */
+#define CB_TTBCR_T0SZ (CB_TTBCR_T0SZ_MASK << CB_TTBCR_T0SZ_SHIFT)
+#define CB_TTBCR_PD0 (CB_TTBCR_PD0_MASK << CB_TTBCR_PD0_SHIFT)
+#define CB_TTBCR_PD1 (CB_TTBCR_PD1_MASK << CB_TTBCR_PD1_SHIFT)
+#define CB_TTBCR_NSCFG0 (CB_TTBCR_NSCFG0_MASK << CB_TTBCR_NSCFG0_SHIFT)
+#define CB_TTBCR_NSCFG1 (CB_TTBCR_NSCFG1_MASK << CB_TTBCR_NSCFG1_SHIFT)
+#define CB_TTBCR_EAE (CB_TTBCR_EAE_MASK << CB_TTBCR_EAE_SHIFT)
+
+/* Translation Table Base Register 0: CB_TTBR0 */
+#define CB_TTBR0_IRGN1 (CB_TTBR0_IRGN1_MASK << CB_TTBR0_IRGN1_SHIFT)
+#define CB_TTBR0_S (CB_TTBR0_S_MASK << CB_TTBR0_S_SHIFT)
+#define CB_TTBR0_RGN (CB_TTBR0_RGN_MASK << CB_TTBR0_RGN_SHIFT)
+#define CB_TTBR0_NOS (CB_TTBR0_NOS_MASK << CB_TTBR0_NOS_SHIFT)
+#define CB_TTBR0_IRGN0 (CB_TTBR0_IRGN0_MASK << CB_TTBR0_IRGN0_SHIFT)
+#define CB_TTBR0_ADDR (CB_TTBR0_ADDR_MASK << CB_TTBR0_ADDR_SHIFT)
+
+/* Translation Table Base Register 1: CB_TTBR1 */
+#define CB_TTBR1_IRGN1 (CB_TTBR1_IRGN1_MASK << CB_TTBR1_IRGN1_SHIFT)
+#define CB_TTBR1_S (CB_TTBR1_S_MASK << CB_TTBR1_S_SHIFT)
+#define CB_TTBR1_RGN (CB_TTBR1_RGN_MASK << CB_TTBR1_RGN_SHIFT)
+#define CB_TTBR1_NOS (CB_TTBR1_NOS_MASK << CB_TTBR1_NOS_SHIFT)
+#define CB_TTBR1_IRGN0 (CB_TTBR1_IRGN0_MASK << CB_TTBR1_IRGN0_SHIFT)
+#define CB_TTBR1_ADDR (CB_TTBR1_ADDR_MASK << CB_TTBR1_ADDR_SHIFT)
+
+/* Global Register Masks */
+/* Configuration Register 0 */
+#define CR0_NSCFG_MASK 0x03
+#define CR0_WACFG_MASK 0x03
+#define CR0_RACFG_MASK 0x03
+#define CR0_SHCFG_MASK 0x03
+#define CR0_SMCFCFG_MASK 0x01
+#define CR0_MTCFG_MASK 0x01
+#define CR0_MEMATTR_MASK 0x0F
+#define CR0_BSU_MASK 0x03
+#define CR0_FB_MASK 0x01
+#define CR0_PTM_MASK 0x01
+#define CR0_VMIDPNE_MASK 0x01
+#define CR0_USFCFG_MASK 0x01
+#define CR0_GSE_MASK 0x01
+#define CR0_STALLD_MASK 0x01
+#define CR0_TRANSIENTCFG_MASK 0x03
+#define CR0_GCFGFIE_MASK 0x01
+#define CR0_GCFGFRE_MASK 0x01
+#define CR0_GFIE_MASK 0x01
+#define CR0_GFRE_MASK 0x01
+#define CR0_CLIENTPD_MASK 0x01
+
+/* Configuration Register 2 */
+#define CR2_BPVMID_MASK 0xFF
+
+/* Global Address Translation, Stage 1, Privileged Read: GATS1PR */
+#define GATS1PR_ADDR_MASK 0xFFFFF
+#define GATS1PR_NDX_MASK 0xFF
+
+/* Global Address Translation, Stage 1, Privileged Write: GATS1PW */
+#define GATS1PW_ADDR_MASK 0xFFFFF
+#define GATS1PW_NDX_MASK 0xFF
+
+/* Global Address Translation, Stage 1, User Read: GATS1UR */
+#define GATS1UR_ADDR_MASK 0xFFFFF
+#define GATS1UR_NDX_MASK 0xFF
+
+/* Global Address Translation, Stage 1, User Write: GATS1UW */
+#define GATS1UW_ADDR_MASK 0xFFFFF
+#define GATS1UW_NDX_MASK 0xFF
+
+/* Global Address Translation, Stage 1 and 2, Privileged Read: GATS1PR */
+#define GATS12PR_ADDR_MASK 0xFFFFF
+#define GATS12PR_NDX_MASK 0xFF
+
+/* Global Address Translation, Stage 1 and 2, Privileged Write: GATS1PW */
+#define GATS12PW_ADDR_MASK 0xFFFFF
+#define GATS12PW_NDX_MASK 0xFF
+
+/* Global Address Translation, Stage 1 and 2, User Read: GATS1UR */
+#define GATS12UR_ADDR_MASK 0xFFFFF
+#define GATS12UR_NDX_MASK 0xFF
+
+/* Global Address Translation, Stage 1 and 2, User Write: GATS1UW */
+#define GATS12UW_ADDR_MASK 0xFFFFF
+#define GATS12UW_NDX_MASK 0xFF
+
+/* Global Address Translation Status Register: GATSR */
+#define GATSR_ACTIVE_MASK 0x01
+
+/* Global Fault Address Register: GFAR */
+#define GFAR_FADDR_MASK 0xFFFFFFFF
+
+/* Global Fault Status Register: GFSR */
+#define GFSR_ICF_MASK 0x01
+#define GFSR_USF_MASK 0x01
+#define GFSR_SMCF_MASK 0x01
+#define GFSR_UCBF_MASK 0x01
+#define GFSR_UCIF_MASK 0x01
+#define GFSR_CAF_MASK 0x01
+#define GFSR_EF_MASK 0x01
+#define GFSR_PF_MASK 0x01
+#define GFSR_MULTI_MASK 0x01
+
+/* Global Fault Syndrome Register 0: GFSYNR0 */
+#define GFSYNR0_NESTED_MASK 0x01
+#define GFSYNR0_WNR_MASK 0x01
+#define GFSYNR0_PNU_MASK 0x01
+#define GFSYNR0_IND_MASK 0x01
+#define GFSYNR0_NSSTATE_MASK 0x01
+#define GFSYNR0_NSATTR_MASK 0x01
+
+/* Global Fault Syndrome Register 1: GFSYNR1 */
+#define GFSYNR1_SID_MASK 0x7FFF
+#define GFSYNr1_SSD_IDX_MASK 0x7FFF
+
+/* Global Physical Address Register: GPAR */
+#define GPAR_F_MASK 0x01
+#define GPAR_SS_MASK 0x01
+#define GPAR_OUTER_MASK 0x03
+#define GPAR_INNER_MASK 0x03
+#define GPAR_SH_MASK 0x01
+#define GPAR_NS_MASK 0x01
+#define GPAR_NOS_MASK 0x01
+#define GPAR_PA_MASK 0xFFFFF
+#define GPAR_TF_MASK 0x01
+#define GPAR_AFF_MASK 0x01
+#define GPAR_PF_MASK 0x01
+#define GPAR_EF_MASK 0x01
+#define GPAR_TLBMCF_MASK 0x01
+#define GPAR_TLBLKF_MASK 0x01
+#define GPAR_UCBF_MASK 0x01
+
+/* Identification Register: IDR0 */
+#define IDR0_NUMSMRG_MASK 0xFF
+#define IDR0_NUMSIDB_MASK 0x0F
+#define IDR0_BTM_MASK 0x01
+#define IDR0_CTTW_MASK 0x01
+#define IDR0_NUMIPRT_MASK 0xFF
+#define IDR0_PTFS_MASK 0x01
+#define IDR0_SMS_MASK 0x01
+#define IDR0_NTS_MASK 0x01
+#define IDR0_S2TS_MASK 0x01
+#define IDR0_S1TS_MASK 0x01
+#define IDR0_SES_MASK 0x01
+
+/* Identification Register: IDR1 */
+#define IDR1_NUMCB_MASK 0xFF
+#define IDR1_NUMSSDNDXB_MASK 0x0F
+#define IDR1_SSDTP_MASK 0x01
+#define IDR1_SMCD_MASK 0x01
+#define IDR1_NUMS2CB_MASK 0xFF
+#define IDR1_NUMPAGENDXB_MASK 0x07
+#define IDR1_PAGESIZE_MASK 0x01
+
+/* Identification Register: IDR2 */
+#define IDR2_IAS_MASK 0x0F
+#define IDR2_OAS_MASK 0x0F
+
+/* Identification Register: IDR7 */
+#define IDR7_MINOR_MASK 0x0F
+#define IDR7_MAJOR_MASK 0x0F
+
+/* Stream to Context Register: S2CR */
+#define S2CR_CBNDX_MASK 0xFF
+#define S2CR_SHCFG_MASK 0x03
+#define S2CR_MTCFG_MASK 0x01
+#define S2CR_MEMATTR_MASK 0x0F
+#define S2CR_TYPE_MASK 0x03
+#define S2CR_NSCFG_MASK 0x03
+#define S2CR_RACFG_MASK 0x03
+#define S2CR_WACFG_MASK 0x03
+#define S2CR_PRIVCFG_MASK 0x03
+#define S2CR_INSTCFG_MASK 0x03
+#define S2CR_TRANSIENTCFG_MASK 0x03
+#define S2CR_VMID_MASK 0xFF
+#define S2CR_BSU_MASK 0x03
+#define S2CR_FB_MASK 0x01
+
+/* Stream Match Register: SMR */
+#define SMR_ID_MASK 0x7FFF
+#define SMR_MASK_MASK 0x7FFF
+#define SMR_VALID_MASK 0x01
+
+/* Global TLB Status: TLBGSTATUS */
+#define TLBGSTATUS_GSACTIVE_MASK 0x01
+
+/* Invalidate Hyp TLB by VA: TLBIVAH */
+#define TLBIVAH_ADDR_MASK 0xFFFFF
+
+/* Invalidate TLB by VMID: TLBIVMID */
+#define TLBIVMID_VMID_MASK 0xFF
+
+/* Global Register Space 1 Mask */
+/* Context Bank Attribute Register: CBAR */
+#define CBAR_VMID_MASK 0xFF
+#define CBAR_CBNDX_MASK 0x03
+#define CBAR_BPSHCFG_MASK 0x03
+#define CBAR_HYPC_MASK 0x01
+#define CBAR_FB_MASK 0x01
+#define CBAR_MEMATTR_MASK 0x0F
+#define CBAR_TYPE_MASK 0x03
+#define CBAR_BSU_MASK 0x03
+#define CBAR_RACFG_MASK 0x03
+#define CBAR_WACFG_MASK 0x03
+#define CBAR_IRPTNDX_MASK 0xFF
+
+/* Context Bank Fault Restricted Syndrome Register A: CBFRSYNRA */
+#define CBFRSYNRA_SID_MASK 0x7FFF
+
+/* Stage 1 Context Bank Format Masks */
+/* Auxiliary Control Register: CB_ACTLR */
+#define CB_ACTLR_REQPRIORITY_MASK 0x3
+#define CB_ACTLR_REQPRIORITYCFG_MASK 0x1
+#define CB_ACTLR_PRIVCFG_MASK 0x3
+#define CB_ACTLR_BPRCOSH_MASK 0x1
+#define CB_ACTLR_BPRCISH_MASK 0x1
+#define CB_ACTLR_BPRCNSH_MASK 0x1
+
+/* Address Translation, Stage 1, Privileged Read: CB_ATS1PR */
+#define CB_ATS1PR_ADDR_MASK 0xFFFFF
+
+/* Address Translation, Stage 1, Privileged Write: CB_ATS1PW */
+#define CB_ATS1PW_ADDR_MASK 0xFFFFF
+
+/* Address Translation, Stage 1, User Read: CB_ATS1UR */
+#define CB_ATS1UR_ADDR_MASK 0xFFFFF
+
+/* Address Translation, Stage 1, User Write: CB_ATS1UW */
+#define CB_ATS1UW_ADDR_MASK 0xFFFFF
+
+/* Address Translation Status Register: CB_ATSR */
+#define CB_ATSR_ACTIVE_MASK 0x01
+
+/* Context ID Register: CB_CONTEXTIDR */
+#define CB_CONTEXTIDR_ASID_MASK 0xFF
+#define CB_CONTEXTIDR_PROCID_MASK 0xFFFFFF
+
+/* Fault Address Register: CB_FAR */
+#define CB_FAR_FADDR_MASK 0xFFFFFFFF
+
+/* Fault Status Register: CB_FSR */
+#define CB_FSR_TF_MASK 0x01
+#define CB_FSR_AFF_MASK 0x01
+#define CB_FSR_PF_MASK 0x01
+#define CB_FSR_EF_MASK 0x01
+#define CB_FSR_TLBMCF_MASK 0x01
+#define CB_FSR_TLBLKF_MASK 0x01
+#define CB_FSR_SS_MASK 0x01
+#define CB_FSR_MULTI_MASK 0x01
+
+/* Fault Syndrome Register 0: CB_FSYNR0 */
+#define CB_FSYNR0_PLVL_MASK 0x03
+#define CB_FSYNR0_S1PTWF_MASK 0x01
+#define CB_FSYNR0_WNR_MASK 0x01
+#define CB_FSYNR0_PNU_MASK 0x01
+#define CB_FSYNR0_IND_MASK 0x01
+#define CB_FSYNR0_NSSTATE_MASK 0x01
+#define CB_FSYNR0_NSATTR_MASK 0x01
+#define CB_FSYNR0_ATOF_MASK 0x01
+#define CB_FSYNR0_PTWF_MASK 0x01
+#define CB_FSYNR0_AFR_MASK 0x01
+#define CB_FSYNR0_S1CBNDX_MASK 0xFF
+
+/* Normal Memory Remap Register: CB_NMRR */
+#define CB_NMRR_IR0_MASK 0x03
+#define CB_NMRR_IR1_MASK 0x03
+#define CB_NMRR_IR2_MASK 0x03
+#define CB_NMRR_IR3_MASK 0x03
+#define CB_NMRR_IR4_MASK 0x03
+#define CB_NMRR_IR5_MASK 0x03
+#define CB_NMRR_IR6_MASK 0x03
+#define CB_NMRR_IR7_MASK 0x03
+#define CB_NMRR_OR0_MASK 0x03
+#define CB_NMRR_OR1_MASK 0x03
+#define CB_NMRR_OR2_MASK 0x03
+#define CB_NMRR_OR3_MASK 0x03
+#define CB_NMRR_OR4_MASK 0x03
+#define CB_NMRR_OR5_MASK 0x03
+#define CB_NMRR_OR6_MASK 0x03
+#define CB_NMRR_OR7_MASK 0x03
+
+/* Physical Address Register: CB_PAR */
+#define CB_PAR_F_MASK 0x01
+#define CB_PAR_SS_MASK 0x01
+#define CB_PAR_OUTER_MASK 0x03
+#define CB_PAR_INNER_MASK 0x07
+#define CB_PAR_SH_MASK 0x01
+#define CB_PAR_NS_MASK 0x01
+#define CB_PAR_NOS_MASK 0x01
+#define CB_PAR_PA_MASK 0xFFFFF
+#define CB_PAR_TF_MASK 0x01
+#define CB_PAR_AFF_MASK 0x01
+#define CB_PAR_PF_MASK 0x01
+#define CB_PAR_TLBMCF_MASK 0x01
+#define CB_PAR_TLBLKF_MASK 0x01
+#define CB_PAR_ATOT_MASK 0x01
+#define CB_PAR_PLVL_MASK 0x03
+#define CB_PAR_STAGE_MASK 0x01
+
+/* Primary Region Remap Register: CB_PRRR */
+#define CB_PRRR_TR0_MASK 0x03
+#define CB_PRRR_TR1_MASK 0x03
+#define CB_PRRR_TR2_MASK 0x03
+#define CB_PRRR_TR3_MASK 0x03
+#define CB_PRRR_TR4_MASK 0x03
+#define CB_PRRR_TR5_MASK 0x03
+#define CB_PRRR_TR6_MASK 0x03
+#define CB_PRRR_TR7_MASK 0x03
+#define CB_PRRR_DS0_MASK 0x01
+#define CB_PRRR_DS1_MASK 0x01
+#define CB_PRRR_NS0_MASK 0x01
+#define CB_PRRR_NS1_MASK 0x01
+#define CB_PRRR_NOS0_MASK 0x01
+#define CB_PRRR_NOS1_MASK 0x01
+#define CB_PRRR_NOS2_MASK 0x01
+#define CB_PRRR_NOS3_MASK 0x01
+#define CB_PRRR_NOS4_MASK 0x01
+#define CB_PRRR_NOS5_MASK 0x01
+#define CB_PRRR_NOS6_MASK 0x01
+#define CB_PRRR_NOS7_MASK 0x01
+
+/* Transaction Resume: CB_RESUME */
+#define CB_RESUME_TNR_MASK 0x01
+
+/* System Control Register: CB_SCTLR */
+#define CB_SCTLR_M_MASK 0x01
+#define CB_SCTLR_TRE_MASK 0x01
+#define CB_SCTLR_AFE_MASK 0x01
+#define CB_SCTLR_AFFD_MASK 0x01
+#define CB_SCTLR_E_MASK 0x01
+#define CB_SCTLR_CFRE_MASK 0x01
+#define CB_SCTLR_CFIE_MASK 0x01
+#define CB_SCTLR_CFCFG_MASK 0x01
+#define CB_SCTLR_HUPCF_MASK 0x01
+#define CB_SCTLR_WXN_MASK 0x01
+#define CB_SCTLR_UWXN_MASK 0x01
+#define CB_SCTLR_ASIDPNE_MASK 0x01
+#define CB_SCTLR_TRANSIENTCFG_MASK 0x03
+#define CB_SCTLR_MEMATTR_MASK 0x0F
+#define CB_SCTLR_MTCFG_MASK 0x01
+#define CB_SCTLR_SHCFG_MASK 0x03
+#define CB_SCTLR_RACFG_MASK 0x03
+#define CB_SCTLR_WACFG_MASK 0x03
+#define CB_SCTLR_NSCFG_MASK 0x03
+
+/* Invalidate TLB by ASID: CB_TLBIASID */
+#define CB_TLBIASID_ASID_MASK 0xFF
+
+/* Invalidate TLB by VA: CB_TLBIVA */
+#define CB_TLBIVA_ASID_MASK 0xFF
+#define CB_TLBIVA_VA_MASK 0xFFFFF
+
+/* Invalidate TLB by VA, All ASID: CB_TLBIVAA */
+#define CB_TLBIVAA_VA_MASK 0xFFFFF
+
+/* Invalidate TLB by VA, All ASID, Last Level: CB_TLBIVAAL */
+#define CB_TLBIVAAL_VA_MASK 0xFFFFF
+
+/* Invalidate TLB by VA, Last Level: CB_TLBIVAL */
+#define CB_TLBIVAL_ASID_MASK 0xFF
+#define CB_TLBIVAL_VA_MASK 0xFFFFF
+
+/* TLB Status: CB_TLBSTATUS */
+#define CB_TLBSTATUS_SACTIVE_MASK 0x01
+
+/* Translation Table Base Control Register: CB_TTBCR */
+#define CB_TTBCR_T0SZ_MASK 0x07
+#define CB_TTBCR_PD0_MASK 0x01
+#define CB_TTBCR_PD1_MASK 0x01
+#define CB_TTBCR_NSCFG0_MASK 0x01
+#define CB_TTBCR_NSCFG1_MASK 0x01
+#define CB_TTBCR_EAE_MASK 0x01
+
+/* Translation Table Base Register 0/1: CB_TTBR */
+#define CB_TTBR0_IRGN1_MASK 0x01
+#define CB_TTBR0_S_MASK 0x01
+#define CB_TTBR0_RGN_MASK 0x01
+#define CB_TTBR0_NOS_MASK 0x01
+#define CB_TTBR0_IRGN0_MASK 0x01
+#define CB_TTBR0_ADDR_MASK 0xFFFFFF
+
+#define CB_TTBR1_IRGN1_MASK 0x1
+#define CB_TTBR1_S_MASK 0x1
+#define CB_TTBR1_RGN_MASK 0x1
+#define CB_TTBR1_NOS_MASK 0X1
+#define CB_TTBR1_IRGN0_MASK 0X1
+#define CB_TTBR1_ADDR_MASK 0xFFFFFF
+
+/* Global Register Shifts */
+/* Configuration Register: CR0 */
+#define CR0_NSCFG_SHIFT 28
+#define CR0_WACFG_SHIFT 26
+#define CR0_RACFG_SHIFT 24
+#define CR0_SHCFG_SHIFT 22
+#define CR0_SMCFCFG_SHIFT 21
+#define CR0_MTCFG_SHIFT 20
+#define CR0_MEMATTR_SHIFT 16
+#define CR0_BSU_SHIFT 14
+#define CR0_FB_SHIFT 13
+#define CR0_PTM_SHIFT 12
+#define CR0_VMIDPNE_SHIFT 11
+#define CR0_USFCFG_SHIFT 10
+#define CR0_GSE_SHIFT 9
+#define CR0_STALLD_SHIFT 8
+#define CR0_TRANSIENTCFG_SHIFT 6
+#define CR0_GCFGFIE_SHIFT 5
+#define CR0_GCFGFRE_SHIFT 4
+#define CR0_GFIE_SHIFT 2
+#define CR0_GFRE_SHIFT 1
+#define CR0_CLIENTPD_SHIFT 0
+
+/* Configuration Register: CR2 */
+#define CR2_BPVMID_SHIFT 0
+
+/* Global Address Translation, Stage 1, Privileged Read: GATS1PR */
+#define GATS1PR_ADDR_SHIFT 12
+#define GATS1PR_NDX_SHIFT 0
+
+/* Global Address Translation, Stage 1, Privileged Write: GATS1PW */
+#define GATS1PW_ADDR_SHIFT 12
+#define GATS1PW_NDX_SHIFT 0
+
+/* Global Address Translation, Stage 1, User Read: GATS1UR */
+#define GATS1UR_ADDR_SHIFT 12
+#define GATS1UR_NDX_SHIFT 0
+
+/* Global Address Translation, Stage 1, User Write: GATS1UW */
+#define GATS1UW_ADDR_SHIFT 12
+#define GATS1UW_NDX_SHIFT 0
+
+/* Global Address Translation, Stage 1 and 2, Privileged Read: GATS12PR */
+#define GATS12PR_ADDR_SHIFT 12
+#define GATS12PR_NDX_SHIFT 0
+
+/* Global Address Translation, Stage 1 and 2, Privileged Write: GATS12PW */
+#define GATS12PW_ADDR_SHIFT 12
+#define GATS12PW_NDX_SHIFT 0
+
+/* Global Address Translation, Stage 1 and 2, User Read: GATS12UR */
+#define GATS12UR_ADDR_SHIFT 12
+#define GATS12UR_NDX_SHIFT 0
+
+/* Global Address Translation, Stage 1 and 2, User Write: GATS12UW */
+#define GATS12UW_ADDR_SHIFT 12
+#define GATS12UW_NDX_SHIFT 0
+
+/* Global Address Translation Status Register: GATSR */
+#define GATSR_ACTIVE_SHIFT 0
+
+/* Global Fault Address Register: GFAR */
+#define GFAR_FADDR_SHIFT 0
+
+/* Global Fault Status Register: GFSR */
+#define GFSR_ICF_SHIFT 0
+#define GFSR_USF_SHIFT 1
+#define GFSR_SMCF_SHIFT 2
+#define GFSR_UCBF_SHIFT 3
+#define GFSR_UCIF_SHIFT 4
+#define GFSR_CAF_SHIFT 5
+#define GFSR_EF_SHIFT 6
+#define GFSR_PF_SHIFT 7
+#define GFSR_MULTI_SHIFT 31
+
+/* Global Fault Syndrome Register 0: GFSYNR0 */
+#define GFSYNR0_NESTED_SHIFT 0
+#define GFSYNR0_WNR_SHIFT 1
+#define GFSYNR0_PNU_SHIFT 2
+#define GFSYNR0_IND_SHIFT 3
+#define GFSYNR0_NSSTATE_SHIFT 4
+#define GFSYNR0_NSATTR_SHIFT 5
+
+/* Global Fault Syndrome Register 1: GFSYNR1 */
+#define GFSYNR1_SID_SHIFT 0
+
+/* Global Physical Address Register: GPAR */
+#define GPAR_F_SHIFT 0
+#define GPAR_SS_SHIFT 1
+#define GPAR_OUTER_SHIFT 2
+#define GPAR_INNER_SHIFT 4
+#define GPAR_SH_SHIFT 7
+#define GPAR_NS_SHIFT 9
+#define GPAR_NOS_SHIFT 10
+#define GPAR_PA_SHIFT 12
+#define GPAR_TF_SHIFT 1
+#define GPAR_AFF_SHIFT 2
+#define GPAR_PF_SHIFT 3
+#define GPAR_EF_SHIFT 4
+#define GPAR_TLCMCF_SHIFT 5
+#define GPAR_TLBLKF_SHIFT 6
+#define GFAR_UCBF_SHIFT 30
+
+/* Identification Register: IDR0 */
+#define IDR0_NUMSMRG_SHIFT 0
+#define IDR0_NUMSIDB_SHIFT 9
+#define IDR0_BTM_SHIFT 13
+#define IDR0_CTTW_SHIFT 14
+#define IDR0_NUMIRPT_SHIFT 16
+#define IDR0_PTFS_SHIFT 24
+#define IDR0_SMS_SHIFT 27
+#define IDR0_NTS_SHIFT 28
+#define IDR0_S2TS_SHIFT 29
+#define IDR0_S1TS_SHIFT 30
+#define IDR0_SES_SHIFT 31
+
+/* Identification Register: IDR1 */
+#define IDR1_NUMCB_SHIFT 0
+#define IDR1_NUMSSDNDXB_SHIFT 8
+#define IDR1_SSDTP_SHIFT 12
+#define IDR1_SMCD_SHIFT 15
+#define IDR1_NUMS2CB_SHIFT 16
+#define IDR1_NUMPAGENDXB_SHIFT 28
+#define IDR1_PAGESIZE_SHIFT 31
+
+/* Identification Register: IDR2 */
+#define IDR2_IAS_SHIFT 0
+#define IDR2_OAS_SHIFT 4
+
+/* Identification Register: IDR7 */
+#define IDR7_MINOR_SHIFT 0
+#define IDR7_MAJOR_SHIFT 4
+
+/* Stream to Context Register: S2CR */
+#define S2CR_CBNDX_SHIFT 0
+#define s2CR_SHCFG_SHIFT 8
+#define S2CR_MTCFG_SHIFT 11
+#define S2CR_MEMATTR_SHIFT 12
+#define S2CR_TYPE_SHIFT 16
+#define S2CR_NSCFG_SHIFT 18
+#define S2CR_RACFG_SHIFT 20
+#define S2CR_WACFG_SHIFT 22
+#define S2CR_PRIVCFG_SHIFT 24
+#define S2CR_INSTCFG_SHIFT 26
+#define S2CR_TRANSIENTCFG_SHIFT 28
+#define S2CR_VMID_SHIFT 0
+#define S2CR_BSU_SHIFT 24
+#define S2CR_FB_SHIFT 26
+
+/* Stream Match Register: SMR */
+#define SMR_ID_SHIFT 0
+#define SMR_MASK_SHIFT 16
+#define SMR_VALID_SHIFT 31
+
+/* Global TLB Status: TLBGSTATUS */
+#define TLBGSTATUS_GSACTIVE_SHIFT 0
+
+/* Invalidate Hyp TLB by VA: TLBIVAH */
+#define TLBIVAH_ADDR_SHIFT 12
+
+/* Invalidate TLB by VMID: TLBIVMID */
+#define TLBIVMID_VMID_SHIFT 0
+
+/* Context Bank Attribute Register: CBAR */
+#define CBAR_VMID_SHIFT 0
+#define CBAR_CBNDX_SHIFT 8
+#define CBAR_BPSHCFG_SHIFT 8
+#define CBAR_HYPC_SHIFT 10
+#define CBAR_FB_SHIFT 11
+#define CBAR_MEMATTR_SHIFT 12
+#define CBAR_TYPE_SHIFT 16
+#define CBAR_BSU_SHIFT 18
+#define CBAR_RACFG_SHIFT 20
+#define CBAR_WACFG_SHIFT 22
+#define CBAR_IRPTNDX_SHIFT 24
+
+/* Context Bank Fault Restricted Syndrome Register A: CBFRSYNRA */
+#define CBFRSYNRA_SID_SHIFT 0
+
+/* Stage 1 Context Bank Format Shifts */
+/* Auxiliary Control Register: CB_ACTLR */
+#define CB_ACTLR_REQPRIORITY_SHIFT 0
+#define CB_ACTLR_REQPRIORITYCFG_SHIFT 4
+#define CB_ACTLR_PRIVCFG_SHIFT 8
+#define CB_ACTLR_BPRCOSH_SHIFT 28
+#define CB_ACTLR_BPRCISH_SHIFT 29
+#define CB_ACTLR_BPRCNSH_SHIFT 30
+
+/* Address Translation, Stage 1, Privileged Read: CB_ATS1PR */
+#define CB_ATS1PR_ADDR_SHIFT 12
+
+/* Address Translation, Stage 1, Privileged Write: CB_ATS1PW */
+#define CB_ATS1PW_ADDR_SHIFT 12
+
+/* Address Translation, Stage 1, User Read: CB_ATS1UR */
+#define CB_ATS1UR_ADDR_SHIFT 12
+
+/* Address Translation, Stage 1, User Write: CB_ATS1UW */
+#define CB_ATS1UW_ADDR_SHIFT 12
+
+/* Address Translation Status Register: CB_ATSR */
+#define CB_ATSR_ACTIVE_SHIFT 0
+
+/* Context ID Register: CB_CONTEXTIDR */
+#define CB_CONTEXTIDR_ASID_SHIFT 0
+#define CB_CONTEXTIDR_PROCID_SHIFT 8
+
+/* Fault Address Register: CB_FAR */
+#define CB_FAR_FADDR_SHIFT 0
+
+/* Fault Status Register: CB_FSR */
+#define CB_FSR_TF_SHIFT 1
+#define CB_FSR_AFF_SHIFT 2
+#define CB_FSR_PF_SHIFT 3
+#define CB_FSR_EF_SHIFT 4
+#define CB_FSR_TLBMCF_SHIFT 5
+#define CB_FSR_TLBLKF_SHIFT 6
+#define CB_FSR_SS_SHIFT 30
+#define CB_FSR_MULTI_SHIFT 31
+
+/* Fault Syndrome Register 0: CB_FSYNR0 */
+#define CB_FSYNR0_PLVL_SHIFT 0
+#define CB_FSYNR0_S1PTWF_SHIFT 3
+#define CB_FSYNR0_WNR_SHIFT 4
+#define CB_FSYNR0_PNU_SHIFT 5
+#define CB_FSYNR0_IND_SHIFT 6
+#define CB_FSYNR0_NSSTATE_SHIFT 7
+#define CB_FSYNR0_NSATTR_SHIFT 8
+#define CB_FSYNR0_ATOF_SHIFT 9
+#define CB_FSYNR0_PTWF_SHIFT 10
+#define CB_FSYNR0_AFR_SHIFT 11
+#define CB_FSYNR0_S1CBNDX_SHIFT 16
+
+/* Normal Memory Remap Register: CB_NMRR */
+#define CB_NMRR_IR0_SHIFT 0
+#define CB_NMRR_IR1_SHIFT 2
+#define CB_NMRR_IR2_SHIFT 4
+#define CB_NMRR_IR3_SHIFT 6
+#define CB_NMRR_IR4_SHIFT 8
+#define CB_NMRR_IR5_SHIFT 10
+#define CB_NMRR_IR6_SHIFT 12
+#define CB_NMRR_IR7_SHIFT 14
+#define CB_NMRR_OR0_SHIFT 16
+#define CB_NMRR_OR1_SHIFT 18
+#define CB_NMRR_OR2_SHIFT 20
+#define CB_NMRR_OR3_SHIFT 22
+#define CB_NMRR_OR4_SHIFT 24
+#define CB_NMRR_OR5_SHIFT 26
+#define CB_NMRR_OR6_SHIFT 28
+#define CB_NMRR_OR7_SHIFT 30
+
+/* Physical Address Register: CB_PAR */
+#define CB_PAR_F_SHIFT 0
+#define CB_PAR_SS_SHIFT 1
+#define CB_PAR_OUTER_SHIFT 2
+#define CB_PAR_INNER_SHIFT 4
+#define CB_PAR_SH_SHIFT 7
+#define CB_PAR_NS_SHIFT 9
+#define CB_PAR_NOS_SHIFT 10
+#define CB_PAR_PA_SHIFT 12
+#define CB_PAR_TF_SHIFT 1
+#define CB_PAR_AFF_SHIFT 2
+#define CB_PAR_PF_SHIFT 3
+#define CB_PAR_TLBMCF_SHIFT 5
+#define CB_PAR_TLBLKF_SHIFT 6
+#define CB_PAR_ATOT_SHIFT 31
+#define CB_PAR_PLVL_SHIFT 0
+#define CB_PAR_STAGE_SHIFT 3
+
+/* Primary Region Remap Register: CB_PRRR */
+#define CB_PRRR_TR0_SHIFT 0
+#define CB_PRRR_TR1_SHIFT 2
+#define CB_PRRR_TR2_SHIFT 4
+#define CB_PRRR_TR3_SHIFT 6
+#define CB_PRRR_TR4_SHIFT 8
+#define CB_PRRR_TR5_SHIFT 10
+#define CB_PRRR_TR6_SHIFT 12
+#define CB_PRRR_TR7_SHIFT 14
+#define CB_PRRR_DS0_SHIFT 16
+#define CB_PRRR_DS1_SHIFT 17
+#define CB_PRRR_NS0_SHIFT 18
+#define CB_PRRR_NS1_SHIFT 19
+#define CB_PRRR_NOS0_SHIFT 24
+#define CB_PRRR_NOS1_SHIFT 25
+#define CB_PRRR_NOS2_SHIFT 26
+#define CB_PRRR_NOS3_SHIFT 27
+#define CB_PRRR_NOS4_SHIFT 28
+#define CB_PRRR_NOS5_SHIFT 29
+#define CB_PRRR_NOS6_SHIFT 30
+#define CB_PRRR_NOS7_SHIFT 31
+
+/* Transaction Resume: CB_RESUME */
+#define CB_RESUME_TNR_SHIFT 0
+
+/* System Control Register: CB_SCTLR */
+#define CB_SCTLR_M_SHIFT 0
+#define CB_SCTLR_TRE_SHIFT 1
+#define CB_SCTLR_AFE_SHIFT 2
+#define CB_SCTLR_AFFD_SHIFT 3
+#define CB_SCTLR_E_SHIFT 4
+#define CB_SCTLR_CFRE_SHIFT 5
+#define CB_SCTLR_CFIE_SHIFT 6
+#define CB_SCTLR_CFCFG_SHIFT 7
+#define CB_SCTLR_HUPCF_SHIFT 8
+#define CB_SCTLR_WXN_SHIFT 9
+#define CB_SCTLR_UWXN_SHIFT 10
+#define CB_SCTLR_ASIDPNE_SHIFT 12
+#define CB_SCTLR_TRANSIENTCFG_SHIFT 14
+#define CB_SCTLR_MEMATTR_SHIFT 16
+#define CB_SCTLR_MTCFG_SHIFT 20
+#define CB_SCTLR_SHCFG_SHIFT 22
+#define CB_SCTLR_RACFG_SHIFT 24
+#define CB_SCTLR_WACFG_SHIFT 26
+#define CB_SCTLR_NSCFG_SHIFT 28
+
+/* Invalidate TLB by ASID: CB_TLBIASID */
+#define CB_TLBIASID_ASID_SHIFT 0
+
+/* Invalidate TLB by VA: CB_TLBIVA */
+#define CB_TLBIVA_ASID_SHIFT 0
+#define CB_TLBIVA_VA_SHIFT 12
+
+/* Invalidate TLB by VA, All ASID: CB_TLBIVAA */
+#define CB_TLBIVAA_VA_SHIFT 12
+
+/* Invalidate TLB by VA, All ASID, Last Level: CB_TLBIVAAL */
+#define CB_TLBIVAAL_VA_SHIFT 12
+
+/* Invalidate TLB by VA, Last Level: CB_TLBIVAL */
+#define CB_TLBIVAL_ASID_SHIFT 0
+#define CB_TLBIVAL_VA_SHIFT 12
+
+/* TLB Status: CB_TLBSTATUS */
+#define CB_TLBSTATUS_SACTIVE_SHIFT 0
+
+/* Translation Table Base Control Register: CB_TTBCR */
+#define CB_TTBCR_T0SZ_SHIFT 0
+#define CB_TTBCR_PD0_SHIFT 4
+#define CB_TTBCR_PD1_SHIFT 5
+#define CB_TTBCR_NSCFG0_SHIFT 14
+#define CB_TTBCR_NSCFG1_SHIFT 30
+#define CB_TTBCR_EAE_SHIFT 31
+
+/* Translation Table Base Register 0/1: CB_TTBR */
+#define CB_TTBR0_IRGN1_SHIFT 0
+#define CB_TTBR0_S_SHIFT 1
+#define CB_TTBR0_RGN_SHIFT 3
+#define CB_TTBR0_NOS_SHIFT 5
+#define CB_TTBR0_IRGN0_SHIFT 6
+#define CB_TTBR0_ADDR_SHIFT 14
+
+#define CB_TTBR1_IRGN1_SHIFT 0
+#define CB_TTBR1_S_SHIFT 1
+#define CB_TTBR1_RGN_SHIFT 3
+#define CB_TTBR1_NOS_SHIFT 5
+#define CB_TTBR1_IRGN0_SHIFT 6
+#define CB_TTBR1_ADDR_SHIFT 14
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_battery.h b/arch/arm/mach-msm/include/mach/msm_battery.h
index c54e7d8..fe496d5 100644
--- a/arch/arm/mach-msm/include/mach/msm_battery.h
+++ b/arch/arm/mach-msm/include/mach/msm_battery.h
@@ -21,6 +21,7 @@
struct msm_psy_batt_pdata {
u32 voltage_max_design;
u32 voltage_min_design;
+ u32 voltage_fail_safe;
u32 avail_chg_sources;
u32 batt_technology;
u32 (*calculate_capacity)(u32 voltage);
diff --git a/arch/arm/mach-msm/include/mach/msm_cache_dump.h b/arch/arm/mach-msm/include/mach/msm_cache_dump.h
index 6e4f628..80f4159 100644
--- a/arch/arm/mach-msm/include/mach/msm_cache_dump.h
+++ b/arch/arm/mach-msm/include/mach/msm_cache_dump.h
@@ -57,9 +57,6 @@
unsigned int l2_size;
};
-#define L1_BUFFER_SIZE SZ_1M
-#define L2_BUFFER_SIZE SZ_4M
-
#define CACHE_BUFFER_DUMP_SIZE (L1_BUFFER_SIZE + L2_BUFFER_SIZE)
#define L1C_SERVICE_ID 3
diff --git a/arch/arm/mach-msm/include/mach/msm_dsps.h b/arch/arm/mach-msm/include/mach/msm_dsps.h
index cfb2024..32a4f15 100644
--- a/arch/arm/mach-msm/include/mach/msm_dsps.h
+++ b/arch/arm/mach-msm/include/mach/msm_dsps.h
@@ -75,6 +75,16 @@
* @regs_num - number of regulators.
* @dsps_pwr_ctl_en - to enable DSPS to do power control if set 1
* otherwise the apps will do power control
+ * @tcm_code_start - start of the TCM code region as physical address
+ * @tcm_code_size - size of the TCM code region in bytes
+ * @tcm_buf_start - start of the TCM buf region as physical address
+ * @tcm_buf_size - size of the TCM buf region in bytes
+ * @pipe_start - start of the PIPE region as physical address
+ * @pipe_size - size of the PIPE region in bytes
+ * @ddr_start - start of the DDR region as physical address
+ * @ddr_size - size of the DDR region in bytes
+ * @smem_start - start of the smem region as physical address
+ * @smem_size - size of the smem region in bytes
* @signature - signature for validity check.
*/
struct msm_dsps_platform_data {
@@ -87,6 +97,16 @@
int regs_num;
int dsps_pwr_ctl_en;
void (*init)(struct msm_dsps_platform_data *data);
+ int tcm_code_start;
+ int tcm_code_size;
+ int tcm_buf_start;
+ int tcm_buf_size;
+ int pipe_start;
+ int pipe_size;
+ int ddr_start;
+ int ddr_size;
+ int smem_start;
+ int smem_size;
u32 signature;
};
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-copper.h b/arch/arm/mach-msm/include/mach/msm_iomap-copper.h
index b560276..441f82a 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-copper.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-copper.h
@@ -23,7 +23,7 @@
*
*/
-#define COPPER_MSM_SHARED_RAM_PHYS 0x18D00000
+#define COPPER_MSM_SHARED_RAM_PHYS 0x0FA00000
#define COPPER_QGIC_DIST_PHYS 0xF9000000
#define COPPER_QGIC_DIST_SIZE SZ_4K
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/usf.h b/arch/arm/mach-msm/include/mach/qdsp6v2/usf.h
index bd303b2..f747a80 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/usf.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/usf.h
@@ -68,12 +68,23 @@
#define USF_POINT_EPOS_FORMAT 0
#define USF_RAW_FORMAT 1
+/* Indexes of event types, produced by the calculators */
+#define USF_TSC_EVENT_IND 0
+#define USF_TSC_PTR_EVENT_IND 1
+#define USF_MOUSE_EVENT_IND 2
+#define USF_KEYBOARD_EVENT_IND 3
+#define USF_MAX_EVENT_IND 4
+
/* Types of events, produced by the calculators */
#define USF_NO_EVENT 0
-#define USF_TSC_EVENT 1
-#define USF_MOUSE_EVENT 2
-#define USF_KEYBOARD_EVENT 4
-#define USF_ALL_EVENTS (USF_TSC_EVENT | USF_MOUSE_EVENT | USF_KEYBOARD_EVENT)
+#define USF_TSC_EVENT (1 << USF_TSC_EVENT_IND)
+#define USF_TSC_PTR_EVENT (1 << USF_TSC_PTR_EVENT_IND)
+#define USF_MOUSE_EVENT (1 << USF_MOUSE_EVENT_IND)
+#define USF_KEYBOARD_EVENT (1 << USF_KEYBOARD_EVENT_IND)
+#define USF_ALL_EVENTS (USF_TSC_EVENT |\
+ USF_TSC_PTR_EVENT |\
+ USF_MOUSE_EVENT |\
+ USF_KEYBOARD_EVENT)
/* min, max array dimension */
#define MIN_MAX_DIM 2
@@ -152,9 +163,6 @@
/* Info specific for RX*/
};
-
-#define USF_PIX_COORDINATE 0 /* unit is pixel */
-#define USF_CMM_COORDINATE 1 /* unit is 0.01 mm */
struct point_event_type {
/* Pen coordinates (x, y, z) in units, defined by <coordinates_type> */
int coordinates[COORDINATES_DIM];
@@ -162,8 +170,6 @@
int inclinations[TILTS_DIM];
/* [0-1023] (10bits); 0 - pen up */
uint32_t pressure;
-/* 0 - mapped in the display pixel. 1 - raw in 0.01 mm (only for log); */
- uint8_t coordinates_type;
};
/* Mouse buttons, supported by USF */
@@ -189,8 +195,8 @@
uint32_t seq_num;
/* Event generation system time */
uint32_t timestamp;
-/* Destination input event type (e.g. touch screen, mouse, key) */
- uint16_t event_type;
+/* Destination input event type index (e.g. touch screen, mouse, key) */
+ uint16_t event_type_ind;
union {
struct point_event_type point_event;
struct mouse_event_type mouse_event;
diff --git a/arch/arm/mach-msm/include/mach/rpm-8930.h b/arch/arm/mach-msm/include/mach/rpm-8930.h
index 0211b67..5ec3a74 100644
--- a/arch/arm/mach-msm/include/mach/rpm-8930.h
+++ b/arch/arm/mach-msm/include/mach/rpm-8930.h
@@ -100,6 +100,7 @@
MSM_RPM_8930_SEL_CXO_BUFFERS = 81,
MSM_RPM_8930_SEL_USB_OTG_SWITCH = 82,
MSM_RPM_8930_SEL_HDMI_SWITCH = 83,
+ MSM_RPM_8930_SEL_DDR_DMM = 84,
MSM_RPM_8930_SEL_VOLTAGE_CORNER = 87,
MSM_RPM_8930_SEL_LAST = MSM_RPM_8930_SEL_VOLTAGE_CORNER,
};
@@ -239,8 +240,10 @@
MSM_RPM_8930_ID_CXO_BUFFERS = 164,
MSM_RPM_8930_ID_USB_OTG_SWITCH = 165,
MSM_RPM_8930_ID_HDMI_SWITCH = 166,
- MSM_RPM_8930_ID_QDSS_CLK = 167,
- MSM_RPM_8930_ID_VOLTAGE_CORNER = 168,
+ MSM_RPM_8930_ID_DDR_DMM_0 = 167,
+ MSM_RPM_8930_ID_DDR_DMM_1 = 168,
+ MSM_RPM_8930_ID_QDSS_CLK = 168,
+ MSM_RPM_8930_ID_VOLTAGE_CORNER = 169,
MSM_RPM_8930_ID_LAST = MSM_RPM_8930_ID_VOLTAGE_CORNER,
};
@@ -350,8 +353,10 @@
MSM_RPM_8930_STATUS_ID_CXO_BUFFERS = 105,
MSM_RPM_8930_STATUS_ID_USB_OTG_SWITCH = 106,
MSM_RPM_8930_STATUS_ID_HDMI_SWITCH = 107,
- MSM_RPM_8930_STATUS_ID_QDSS_CLK = 108,
- MSM_RPM_8930_STATUS_ID_VOLTAGE_CORNER = 109,
+ MSM_RPM_8930_STATUS_ID_DDR_DMM_0 = 108,
+ MSM_RPM_8930_STATUS_ID_DDR_DMM_1 = 109,
+ MSM_RPM_8930_STATUS_ID_QDSS_CLK = 110,
+ MSM_RPM_8930_STATUS_ID_VOLTAGE_CORNER = 111,
MSM_RPM_8930_STATUS_ID_LAST = MSM_RPM_8930_STATUS_ID_VOLTAGE_CORNER,
};
diff --git a/arch/arm/mach-msm/include/mach/rpm-regulator-smd.h b/arch/arm/mach-msm/include/mach/rpm-regulator-smd.h
index 333f5af..2eb59f5 100644
--- a/arch/arm/mach-msm/include/mach/rpm-regulator-smd.h
+++ b/arch/arm/mach-msm/include/mach/rpm-regulator-smd.h
@@ -17,7 +17,7 @@
struct rpm_regulator;
-#ifdef CONFIG_MSM_RPM_REGULATOR_SMD
+#if defined(CONFIG_MSM_RPM_REGULATOR_SMD) || defined(CONFIG_MSM_RPM_REGULATOR)
struct rpm_regulator *rpm_regulator_get(struct device *dev, const char *supply);
diff --git a/arch/arm/mach-msm/include/mach/rpm-regulator.h b/arch/arm/mach-msm/include/mach/rpm-regulator.h
index f9fc487..a010257 100644
--- a/arch/arm/mach-msm/include/mach/rpm-regulator.h
+++ b/arch/arm/mach-msm/include/mach/rpm-regulator.h
@@ -81,6 +81,20 @@
};
/**
+ * enum rpm_vreg_voter - RPM regulator voter IDs for private APIs
+ */
+enum rpm_vreg_voter {
+ RPM_VREG_VOTER_REG_FRAMEWORK, /* for internal use only */
+ RPM_VREG_VOTER1, /* for use by the acpu-clock driver */
+ RPM_VREG_VOTER2, /* for use by the acpu-clock driver */
+ RPM_VREG_VOTER3, /* for use by other drivers */
+ RPM_VREG_VOTER4, /* for use by the acpu-clock driver */
+ RPM_VREG_VOTER5, /* for use by the acpu-clock driver */
+ RPM_VREG_VOTER6, /* for use by the acpu-clock driver */
+ RPM_VREG_VOTER_COUNT,
+};
+
+/**
* struct rpm_regulator_init_data - RPM regulator initialization data
* @init_data: regulator constraints
* @id: regulator id; from enum rpm_vreg_id
@@ -132,28 +146,27 @@
};
/**
- * struct rpm_regulator_platform_data - RPM regulator platform data
+ * struct rpm_regulator_consumer_mapping - mapping used by private consumers
*/
-struct rpm_regulator_platform_data {
- struct rpm_regulator_init_data *init_data;
- int num_regulators;
- enum rpm_vreg_version version;
- int vreg_id_vdd_mem;
- int vreg_id_vdd_dig;
+struct rpm_regulator_consumer_mapping {
+ const char *dev_name;
+ const char *supply;
+ int vreg_id;
+ enum rpm_vreg_voter voter;
+ int sleep_also;
};
/**
- * enum rpm_vreg_voter - RPM regulator voter IDs for private APIs
+ * struct rpm_regulator_platform_data - RPM regulator platform data
*/
-enum rpm_vreg_voter {
- RPM_VREG_VOTER_REG_FRAMEWORK, /* for internal use only */
- RPM_VREG_VOTER1, /* for use by the acpu-clock driver */
- RPM_VREG_VOTER2, /* for use by the acpu-clock driver */
- RPM_VREG_VOTER3, /* for use by other drivers */
- RPM_VREG_VOTER4, /* for use by the acpu-clock driver */
- RPM_VREG_VOTER5, /* for use by the acpu-clock driver */
- RPM_VREG_VOTER6, /* for use by the acpu-clock driver */
- RPM_VREG_VOTER_COUNT,
+struct rpm_regulator_platform_data {
+ struct rpm_regulator_init_data *init_data;
+ int num_regulators;
+ enum rpm_vreg_version version;
+ int vreg_id_vdd_mem;
+ int vreg_id_vdd_dig;
+ struct rpm_regulator_consumer_mapping *consumer_map;
+ int consumer_map_len;
};
#ifdef CONFIG_MSM_RPM_REGULATOR
diff --git a/arch/arm/mach-msm/iommu-v2.c b/arch/arm/mach-msm/iommu-v2.c
new file mode 100644
index 0000000..41f5043
--- /dev/null
+++ b/arch/arm/mach-msm/iommu-v2.c
@@ -0,0 +1,570 @@
+/* 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/iommu.h>
+#include <linux/clk.h>
+#include <linux/scatterlist.h>
+
+#include <asm/sizes.h>
+
+#include <mach/iommu_hw-v2.h>
+#include <mach/iommu.h>
+
+#include "iommu_pagetable.h"
+
+static DEFINE_MUTEX(msm_iommu_lock);
+
+struct msm_priv {
+ struct iommu_pt pt;
+ struct list_head list_attached;
+};
+
+static int __enable_clocks(struct msm_iommu_drvdata *drvdata)
+{
+ int ret;
+
+ ret = clk_prepare_enable(drvdata->pclk);
+ if (ret)
+ goto fail;
+
+ if (drvdata->clk) {
+ ret = clk_prepare_enable(drvdata->clk);
+ if (ret)
+ clk_disable_unprepare(drvdata->pclk);
+ }
+fail:
+ return ret;
+}
+
+static void __disable_clocks(struct msm_iommu_drvdata *drvdata)
+{
+ if (drvdata->clk)
+ clk_disable_unprepare(drvdata->clk);
+ clk_disable_unprepare(drvdata->pclk);
+}
+
+static int __flush_iotlb_va(struct iommu_domain *domain, unsigned int va)
+{
+ struct msm_priv *priv = domain->priv;
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+ int asid;
+
+ list_for_each_entry(ctx_drvdata, &priv->list_attached, attached_elm) {
+ BUG_ON(!ctx_drvdata->pdev || !ctx_drvdata->pdev->dev.parent);
+
+ iommu_drvdata = dev_get_drvdata(ctx_drvdata->pdev->dev.parent);
+ BUG_ON(!iommu_drvdata);
+
+ asid = GET_CB_CONTEXTIDR_ASID(iommu_drvdata->base,
+ ctx_drvdata->num);
+
+ SET_TLBIVA(iommu_drvdata->base, ctx_drvdata->num,
+ asid | (va & CB_TLBIVA_VA));
+ mb();
+ }
+
+ return 0;
+}
+
+static int __flush_iotlb(struct iommu_domain *domain)
+{
+ struct msm_priv *priv = domain->priv;
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+ int asid;
+
+ list_for_each_entry(ctx_drvdata, &priv->list_attached, attached_elm) {
+ BUG_ON(!ctx_drvdata->pdev || !ctx_drvdata->pdev->dev.parent);
+
+ iommu_drvdata = dev_get_drvdata(ctx_drvdata->pdev->dev.parent);
+ BUG_ON(!iommu_drvdata);
+
+ asid = GET_CB_CONTEXTIDR_ASID(iommu_drvdata->base,
+ ctx_drvdata->num);
+
+ SET_TLBIASID(iommu_drvdata->base, ctx_drvdata->num, asid);
+ mb();
+ }
+
+ return 0;
+}
+
+static void __reset_context(void __iomem *base, int ctx)
+{
+ SET_ACTLR(base, ctx, 0);
+ SET_FAR(base, ctx, 0);
+ SET_FSRRESTORE(base, ctx, 0);
+ SET_NMRR(base, ctx, 0);
+ SET_PAR(base, ctx, 0);
+ SET_PRRR(base, ctx, 0);
+ SET_SCTLR(base, ctx, 0);
+ SET_TLBIALL(base, ctx, 0);
+ SET_TTBCR(base, ctx, 0);
+ SET_TTBR0(base, ctx, 0);
+ SET_TTBR1(base, ctx, 0);
+ mb();
+}
+
+static void __program_context(void __iomem *base, int ctx, int ncb,
+ phys_addr_t pgtable, int redirect)
+{
+ unsigned int prrr, nmrr;
+ unsigned int pn;
+ int i, j, found;
+
+ __reset_context(base, ctx);
+
+ pn = pgtable >> CB_TTBR0_ADDR_SHIFT;
+ SET_TTBCR(base, ctx, 0);
+ SET_CB_TTBR0_ADDR(base, ctx, pn);
+
+ /* Enable context fault interrupt */
+ SET_CB_SCTLR_CFIE(base, ctx, 1);
+
+ /* Redirect all cacheable requests to L2 slave port. */
+ SET_CB_ACTLR_BPRCISH(base, ctx, 1);
+ SET_CB_ACTLR_BPRCOSH(base, ctx, 1);
+ SET_CB_ACTLR_BPRCNSH(base, ctx, 1);
+
+ /* Turn on TEX Remap */
+ SET_CB_SCTLR_TRE(base, ctx, 1);
+
+ /* Enable private ASID namespace */
+ SET_CB_SCTLR_ASIDPNE(base, ctx, 1);
+
+ /* Set TEX remap attributes */
+ RCP15_PRRR(prrr);
+ RCP15_NMRR(nmrr);
+ SET_PRRR(base, ctx, prrr);
+ SET_NMRR(base, ctx, nmrr);
+
+ /* Configure page tables as inner-cacheable and shareable to reduce
+ * the TLB miss penalty.
+ */
+ if (redirect) {
+ SET_CB_TTBR0_S(base, ctx, 1);
+ SET_CB_TTBR0_NOS(base, ctx, 1);
+ SET_CB_TTBR0_IRGN1(base, ctx, 0); /* WB, WA */
+ SET_CB_TTBR0_IRGN0(base, ctx, 1);
+ SET_CB_TTBR0_RGN(base, ctx, 1); /* WB, WA */
+ }
+
+ /* Find if this page table is used elsewhere, and re-use ASID */
+ found = 0;
+ for (i = 0; i < ncb; i++)
+ if ((GET_CB_TTBR0_ADDR(base, i) == pn) && (i != ctx)) {
+ SET_CB_CONTEXTIDR_ASID(base, ctx, \
+ GET_CB_CONTEXTIDR_ASID(base, i));
+ found = 1;
+ break;
+ }
+
+ /* If page table is new, find an unused ASID */
+ if (!found) {
+ for (i = 0; i < ncb; i++) {
+ found = 0;
+ for (j = 0; j < ncb; j++) {
+ if (GET_CB_CONTEXTIDR_ASID(base, j) == i &&
+ j != ctx)
+ found = 1;
+ }
+
+ if (!found) {
+ SET_CB_CONTEXTIDR_ASID(base, ctx, i);
+ break;
+ }
+ }
+ BUG_ON(found);
+ }
+
+ /* Enable the MMU */
+ SET_CB_SCTLR_M(base, ctx, 1);
+ mb();
+}
+
+static int msm_iommu_domain_init(struct iommu_domain *domain, int flags)
+{
+ struct msm_priv *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ goto fail_nomem;
+
+#ifdef CONFIG_IOMMU_PGTABLES_L2
+ priv->pt.redirect = flags & MSM_IOMMU_DOMAIN_PT_CACHEABLE;
+#endif
+
+ INIT_LIST_HEAD(&priv->list_attached);
+ if (msm_iommu_pagetable_alloc(&priv->pt))
+ goto fail_nomem;
+
+ domain->priv = priv;
+ return 0;
+
+fail_nomem:
+ kfree(priv);
+ return -ENOMEM;
+}
+
+static void msm_iommu_domain_destroy(struct iommu_domain *domain)
+{
+ struct msm_priv *priv;
+
+ mutex_lock(&msm_iommu_lock);
+ priv = domain->priv;
+ domain->priv = NULL;
+
+ if (priv)
+ msm_iommu_pagetable_free(&priv->pt);
+
+ kfree(priv);
+ mutex_unlock(&msm_iommu_lock);
+}
+
+static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
+{
+ struct msm_priv *priv;
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+ struct msm_iommu_ctx_drvdata *tmp_drvdata;
+ int ret = 0;
+
+ mutex_lock(&msm_iommu_lock);
+
+ priv = domain->priv;
+ if (!priv || !dev) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ iommu_drvdata = dev_get_drvdata(dev->parent);
+ ctx_drvdata = dev_get_drvdata(dev);
+ if (!iommu_drvdata || !ctx_drvdata) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (!list_empty(&ctx_drvdata->attached_elm)) {
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ list_for_each_entry(tmp_drvdata, &priv->list_attached, attached_elm)
+ if (tmp_drvdata == ctx_drvdata) {
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ ret = __enable_clocks(iommu_drvdata);
+ if (ret)
+ goto fail;
+
+ __program_context(iommu_drvdata->base, ctx_drvdata->num,
+ iommu_drvdata->ncb, __pa(priv->pt.fl_table),
+ priv->pt.redirect);
+
+ __disable_clocks(iommu_drvdata);
+ list_add(&(ctx_drvdata->attached_elm), &priv->list_attached);
+ ctx_drvdata->attached_domain = domain;
+
+fail:
+ mutex_unlock(&msm_iommu_lock);
+ return ret;
+}
+
+static void msm_iommu_detach_dev(struct iommu_domain *domain,
+ struct device *dev)
+{
+ struct msm_priv *priv;
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+ int ret;
+
+ mutex_lock(&msm_iommu_lock);
+ priv = domain->priv;
+ if (!priv || !dev)
+ goto fail;
+
+ iommu_drvdata = dev_get_drvdata(dev->parent);
+ ctx_drvdata = dev_get_drvdata(dev);
+ if (!iommu_drvdata || !ctx_drvdata || !ctx_drvdata->attached_domain)
+ goto fail;
+
+ ret = __enable_clocks(iommu_drvdata);
+ if (ret)
+ goto fail;
+
+ SET_TLBIASID(iommu_drvdata->base, ctx_drvdata->num,
+ GET_CB_CONTEXTIDR_ASID(iommu_drvdata->base, ctx_drvdata->num));
+
+ __reset_context(iommu_drvdata->base, ctx_drvdata->num);
+ __disable_clocks(iommu_drvdata);
+ list_del_init(&ctx_drvdata->attached_elm);
+ ctx_drvdata->attached_domain = NULL;
+
+fail:
+ mutex_unlock(&msm_iommu_lock);
+}
+
+static int msm_iommu_map(struct iommu_domain *domain, unsigned long va,
+ phys_addr_t pa, int order, int prot)
+{
+ struct msm_priv *priv;
+ int ret = 0;
+
+ mutex_lock(&msm_iommu_lock);
+
+ priv = domain->priv;
+ if (!priv) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ ret = msm_iommu_pagetable_map(&priv->pt, va, pa, order, prot);
+ if (ret)
+ goto fail;
+
+ ret = __flush_iotlb_va(domain, va);
+fail:
+ mutex_unlock(&msm_iommu_lock);
+ return ret;
+}
+
+static int msm_iommu_unmap(struct iommu_domain *domain, unsigned long va,
+ int order)
+{
+ struct msm_priv *priv;
+ int ret = 0;
+
+ mutex_lock(&msm_iommu_lock);
+
+ priv = domain->priv;
+ if (!priv) {
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ ret = msm_iommu_pagetable_unmap(&priv->pt, va, order);
+ if (ret < 0)
+ goto fail;
+
+ ret = __flush_iotlb_va(domain, va);
+fail:
+ mutex_unlock(&msm_iommu_lock);
+ return ret;
+}
+
+static int msm_iommu_map_range(struct iommu_domain *domain, unsigned int va,
+ struct scatterlist *sg, unsigned int len,
+ int prot)
+{
+ int ret;
+ struct msm_priv *priv;
+
+ mutex_lock(&msm_iommu_lock);
+
+ priv = domain->priv;
+ if (!priv) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ ret = msm_iommu_pagetable_map_range(&priv->pt, va, sg, len, prot);
+ if (ret)
+ goto fail;
+
+ __flush_iotlb(domain);
+fail:
+ mutex_unlock(&msm_iommu_lock);
+ return ret;
+}
+
+
+static int msm_iommu_unmap_range(struct iommu_domain *domain, unsigned int va,
+ unsigned int len)
+{
+ struct msm_priv *priv;
+
+ mutex_lock(&msm_iommu_lock);
+
+ priv = domain->priv;
+ msm_iommu_pagetable_unmap_range(&priv->pt, va, len);
+
+ __flush_iotlb(domain);
+ mutex_unlock(&msm_iommu_lock);
+ return 0;
+}
+
+static phys_addr_t msm_iommu_iova_to_phys(struct iommu_domain *domain,
+ unsigned long va)
+{
+ struct msm_priv *priv;
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+ unsigned int par;
+ void __iomem *base;
+ phys_addr_t pa = 0;
+ int ctx;
+
+ mutex_lock(&msm_iommu_lock);
+
+ priv = domain->priv;
+ if (list_empty(&priv->list_attached))
+ goto fail;
+
+ ctx_drvdata = list_entry(priv->list_attached.next,
+ struct msm_iommu_ctx_drvdata, attached_elm);
+ iommu_drvdata = dev_get_drvdata(ctx_drvdata->pdev->dev.parent);
+
+ base = iommu_drvdata->base;
+ ctx = ctx_drvdata->num;
+
+ SET_ATS1PR(base, ctx, va & CB_ATS1PR_ADDR);
+ mb();
+ while (GET_CB_ATSR_ACTIVE(base, ctx))
+ cpu_relax();
+
+ par = GET_PAR(base, ctx);
+ if (par & CB_PAR_F) {
+ pa = 0;
+ } else {
+ /* We are dealing with a supersection */
+ if (par & CB_PAR_SS)
+ pa = (par & 0xFF000000) | (va & 0x00FFFFFF);
+ else /* Upper 20 bits from PAR, lower 12 from VA */
+ pa = (par & 0xFFFFF000) | (va & 0x00000FFF);
+ }
+
+fail:
+ mutex_unlock(&msm_iommu_lock);
+ return pa;
+}
+
+static int msm_iommu_domain_has_cap(struct iommu_domain *domain,
+ unsigned long cap)
+{
+ return 0;
+}
+
+static void print_ctx_regs(void __iomem *base, int ctx, unsigned int fsr)
+{
+ pr_err("FAR = %08x PAR = %08x\n",
+ GET_FAR(base, ctx), GET_PAR(base, ctx));
+ pr_err("FSR = %08x [%s%s%s%s%s%s%s%s%s]\n", fsr,
+ (fsr & 0x02) ? "TF " : "",
+ (fsr & 0x04) ? "AFF " : "",
+ (fsr & 0x08) ? "PF " : "",
+ (fsr & 0x10) ? "EF " : "",
+ (fsr & 0x20) ? "TLBMCF " : "",
+ (fsr & 0x40) ? "TLBLKF " : "",
+ (fsr & 0x80) ? "MHF " : "",
+ (fsr & 0x40000000) ? "SS " : "",
+ (fsr & 0x80000000) ? "MULTI " : "");
+
+ pr_err("FSYNR0 = %08x FSYNR1 = %08x\n",
+ GET_FSYNR0(base, ctx), GET_FSYNR1(base, ctx));
+ pr_err("TTBR0 = %08x TTBR1 = %08x\n",
+ GET_TTBR0(base, ctx), GET_TTBR1(base, ctx));
+ pr_err("SCTLR = %08x ACTLR = %08x\n",
+ GET_SCTLR(base, ctx), GET_ACTLR(base, ctx));
+ pr_err("PRRR = %08x NMRR = %08x\n",
+ GET_PRRR(base, ctx), GET_NMRR(base, ctx));
+}
+
+irqreturn_t msm_iommu_fault_handler_v2(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct msm_iommu_drvdata *drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+ unsigned int fsr;
+ int ret = IRQ_NONE;
+
+ mutex_lock(&msm_iommu_lock);
+
+ BUG_ON(!pdev);
+
+ drvdata = dev_get_drvdata(pdev->dev.parent);
+ BUG_ON(!drvdata);
+
+ ctx_drvdata = dev_get_drvdata(&pdev->dev);
+ BUG_ON(!ctx_drvdata);
+
+ fsr = GET_FSR(drvdata->base, ctx_drvdata->num);
+ if (fsr) {
+ if (!ctx_drvdata->attached_domain) {
+ pr_err("Bad domain in interrupt handler\n");
+ ret = -ENOSYS;
+ } else
+ ret = report_iommu_fault(ctx_drvdata->attached_domain,
+ &ctx_drvdata->pdev->dev,
+ GET_FAR(drvdata->base, ctx_drvdata->num), 0);
+
+ if (ret == -ENOSYS) {
+ pr_err("Unexpected IOMMU page fault!\n");
+ pr_err("name = %s\n", drvdata->name);
+ pr_err("context = %s (%d)\n", ctx_drvdata->name,
+ ctx_drvdata->num);
+ pr_err("Interesting registers:\n");
+ print_ctx_regs(drvdata->base, ctx_drvdata->num, fsr);
+ }
+
+ SET_FSR(drvdata->base, ctx_drvdata->num, fsr);
+ ret = IRQ_HANDLED;
+ } else
+ ret = IRQ_NONE;
+
+ mutex_unlock(&msm_iommu_lock);
+ return ret;
+}
+
+static phys_addr_t msm_iommu_get_pt_base_addr(struct iommu_domain *domain)
+{
+ struct msm_priv *priv = domain->priv;
+ return __pa(priv->pt.fl_table);
+}
+
+static struct iommu_ops msm_iommu_ops = {
+ .domain_init = msm_iommu_domain_init,
+ .domain_destroy = msm_iommu_domain_destroy,
+ .attach_dev = msm_iommu_attach_dev,
+ .detach_dev = msm_iommu_detach_dev,
+ .map = msm_iommu_map,
+ .unmap = msm_iommu_unmap,
+ .map_range = msm_iommu_map_range,
+ .unmap_range = msm_iommu_unmap_range,
+ .iova_to_phys = msm_iommu_iova_to_phys,
+ .domain_has_cap = msm_iommu_domain_has_cap,
+ .get_pt_base_addr = msm_iommu_get_pt_base_addr
+};
+
+static int __init msm_iommu_init(void)
+{
+ msm_iommu_pagetable_init();
+ register_iommu(&msm_iommu_ops);
+ return 0;
+}
+
+subsys_initcall(msm_iommu_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM SMMU v2 Driver");
diff --git a/arch/arm/mach-msm/iommu.c b/arch/arm/mach-msm/iommu.c
index 49a3e6f..89eef57 100644
--- a/arch/arm/mach-msm/iommu.c
+++ b/arch/arm/mach-msm/iommu.c
@@ -1083,7 +1083,7 @@
static int __init msm_iommu_init(void)
{
- if (!msm_soc_version_supports_iommu())
+ if (!msm_soc_version_supports_iommu_v1())
return -ENODEV;
setup_iommu_tex_classes();
diff --git a/arch/arm/mach-msm/iommu_dev-v2.c b/arch/arm/mach-msm/iommu_dev-v2.c
new file mode 100644
index 0000000..e690ada
--- /dev/null
+++ b/arch/arm/mach-msm/iommu_dev-v2.c
@@ -0,0 +1,376 @@
+/* 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/iommu.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+
+#include <mach/iommu_hw-v2.h>
+#include <mach/iommu.h>
+
+static void msm_iommu_reset(void __iomem *base)
+{
+ int i;
+
+ SET_ACR(base, 0);
+ SET_NSACR(base, 0);
+ SET_CR2(base, 0);
+ SET_NSCR2(base, 0);
+ SET_GFAR(base, 0);
+ SET_GFSRRESTORE(base, 0);
+ SET_TLBIALLNSNH(base, 0);
+ SET_PMCR(base, 0);
+ SET_SCR1(base, 0);
+ SET_SSDR_N(base, 0, 0);
+
+ for (i = 0; i < MAX_NUM_SMR; i++)
+ SET_SMR_VALID(base, i, 0);
+
+ mb();
+}
+
+static int msm_iommu_parse_dt(struct platform_device *pdev,
+ struct msm_iommu_drvdata *drvdata)
+{
+ struct device_node *child;
+ int ret;
+
+ ret = device_move(&pdev->dev, &msm_iommu_root_dev->dev, DPM_ORDER_NONE);
+ if (ret)
+ return ret;
+
+ for_each_child_of_node(pdev->dev.of_node, child) {
+ drvdata->ncb++;
+ if (!of_platform_device_create(child, NULL, &pdev->dev))
+ pr_err("Failed to create %s device\n", child->name);
+ }
+
+ drvdata->name = dev_name(&pdev->dev);
+ return 0;
+}
+
+static atomic_t msm_iommu_next_id = ATOMIC_INIT(-1);
+
+static int __devinit msm_iommu_probe(struct platform_device *pdev)
+{
+ struct msm_iommu_drvdata *drvdata;
+ struct resource *r;
+ int ret;
+
+ if (msm_iommu_root_dev == pdev)
+ return 0;
+
+ if (pdev->id == -1)
+ pdev->id = atomic_inc_return(&msm_iommu_next_id) - 1;
+
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r)
+ return -EINVAL;
+
+ drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
+ if (!drvdata->base)
+ return -ENOMEM;
+
+ drvdata->pclk = clk_get(&pdev->dev, "iface_clk");
+ if (IS_ERR(drvdata->pclk))
+ return PTR_ERR(drvdata->pclk);
+
+ ret = clk_prepare_enable(drvdata->pclk);
+ if (ret)
+ goto fail_enable;
+
+ drvdata->clk = clk_get(&pdev->dev, "core_clk");
+ if (!IS_ERR(drvdata->clk)) {
+ if (clk_get_rate(drvdata->clk) == 0) {
+ ret = clk_round_rate(drvdata->clk, 1);
+ clk_set_rate(drvdata->clk, ret);
+ }
+
+ ret = clk_prepare_enable(drvdata->clk);
+ if (ret) {
+ clk_put(drvdata->clk);
+ goto fail_pclk;
+ }
+ } else
+ drvdata->clk = NULL;
+
+ msm_iommu_reset(drvdata->base);
+
+ SET_CR0_SMCFCFG(drvdata->base, 1);
+ SET_CR0_USFCFG(drvdata->base, 1);
+ SET_CR0_STALLD(drvdata->base, 1);
+ SET_CR0_GCFGFIE(drvdata->base, 1);
+ SET_CR0_GCFGFRE(drvdata->base, 1);
+ SET_CR0_GFIE(drvdata->base, 1);
+ SET_CR0_GFRE(drvdata->base, 1);
+ SET_CR0_CLIENTPD(drvdata->base, 0);
+
+ ret = msm_iommu_parse_dt(pdev, drvdata);
+ if (ret)
+ goto fail_clk;
+
+ pr_info("device %s mapped at %p, with %d ctx banks\n",
+ drvdata->name, drvdata->base, drvdata->ncb);
+
+ platform_set_drvdata(pdev, drvdata);
+
+ if (drvdata->clk)
+ clk_disable_unprepare(drvdata->clk);
+
+ clk_disable_unprepare(drvdata->pclk);
+
+ return 0;
+
+fail_clk:
+ if (drvdata->clk) {
+ clk_disable_unprepare(drvdata->clk);
+ clk_put(drvdata->clk);
+ }
+fail_pclk:
+ clk_disable_unprepare(drvdata->pclk);
+fail_enable:
+ clk_put(drvdata->pclk);
+ return ret;
+}
+
+static int __devexit msm_iommu_remove(struct platform_device *pdev)
+{
+ struct msm_iommu_drvdata *drv = NULL;
+
+ drv = platform_get_drvdata(pdev);
+ if (drv) {
+ if (drv->clk)
+ clk_put(drv->clk);
+ clk_put(drv->pclk);
+ platform_set_drvdata(pdev, NULL);
+ }
+ return 0;
+}
+
+static int msm_iommu_ctx_parse_dt(struct platform_device *pdev,
+ struct msm_iommu_drvdata *drvdata,
+ struct msm_iommu_ctx_drvdata *ctx_drvdata)
+{
+ struct resource *r, rp;
+ u32 sids[MAX_NUM_SMR];
+ int num = 0;
+ int irq, i, ret, len = 0;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq > 0) {
+ ret = request_threaded_irq(irq, NULL,
+ msm_iommu_fault_handler_v2,
+ IRQF_ONESHOT | IRQF_SHARED,
+ "msm_iommu_nonsecure_irq", pdev);
+ if (ret) {
+ pr_err("Request IRQ %d failed with ret=%d\n", irq, ret);
+ return ret;
+ }
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r)
+ return -EINVAL;
+
+ ret = of_address_to_resource(pdev->dev.parent->of_node, 0, &rp);
+ if (ret)
+ return -EINVAL;
+
+ /* Calculate the context bank number using the base addresses. The
+ * first 8 pages belong to the global address space which is followed
+ * by the context banks, hence subtract by 8 to get the context bank
+ * number.
+ */
+ ctx_drvdata->num = ((r->start - rp.start) >> CTX_SHIFT) - 8;
+
+ if (of_property_read_string(pdev->dev.of_node, "qcom,iommu-ctx-name",
+ &ctx_drvdata->name))
+ ctx_drvdata->name = dev_name(&pdev->dev);
+
+ of_get_property(pdev->dev.of_node, "qcom,iommu-ctx-sids", &len);
+ BUG_ON(len >= sizeof(sids));
+ if (of_property_read_u32_array(pdev->dev.of_node, "qcom,iommu-ctx-sids",
+ sids, len / sizeof(*sids)))
+ return -EINVAL;
+
+ /* Program the M2V tables for this context */
+ for (i = 0; i < len / sizeof(*sids); i++) {
+ for (; num < MAX_NUM_SMR; num++)
+ if (GET_SMR_VALID(drvdata->base, num) == 0)
+ break;
+ BUG_ON(num >= MAX_NUM_SMR);
+
+ SET_SMR_VALID(drvdata->base, num, 1);
+ SET_SMR_MASK(drvdata->base, num, 0);
+ SET_SMR_ID(drvdata->base, num, sids[i]);
+
+ /* Set VMID = 0 */
+ SET_S2CR_N(drvdata->base, num, 0);
+ SET_S2CR_CBNDX(drvdata->base, num, ctx_drvdata->num);
+ /* Set security bit override to be Non-secure */
+ SET_S2CR_NSCFG(drvdata->base, sids[i], 3);
+
+ SET_CBAR_N(drvdata->base, ctx_drvdata->num, 0);
+ /* Stage 1 Context with Stage 2 bypass */
+ SET_CBAR_TYPE(drvdata->base, ctx_drvdata->num, 1);
+ /* Route page faults to the non-secure interrupt */
+ SET_CBAR_IRPTNDX(drvdata->base, ctx_drvdata->num, 1);
+ }
+ mb();
+
+ return 0;
+}
+
+static int __devinit msm_iommu_ctx_probe(struct platform_device *pdev)
+{
+ struct msm_iommu_drvdata *drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata = NULL;
+ int ret;
+
+ if (!pdev->dev.parent)
+ return -EINVAL;
+
+ drvdata = dev_get_drvdata(pdev->dev.parent);
+ if (!drvdata)
+ return -ENODEV;
+
+ ctx_drvdata = devm_kzalloc(&pdev->dev, sizeof(*ctx_drvdata),
+ GFP_KERNEL);
+ if (!ctx_drvdata)
+ return -ENOMEM;
+
+ ctx_drvdata->pdev = pdev;
+ INIT_LIST_HEAD(&ctx_drvdata->attached_elm);
+ platform_set_drvdata(pdev, ctx_drvdata);
+
+ ret = clk_prepare_enable(drvdata->pclk);
+ if (ret)
+ return ret;
+
+ if (drvdata->clk) {
+ ret = clk_prepare_enable(drvdata->clk);
+ if (ret) {
+ clk_disable_unprepare(drvdata->pclk);
+ return ret;
+ }
+ }
+
+ ret = msm_iommu_ctx_parse_dt(pdev, drvdata, ctx_drvdata);
+ if (!ret)
+ dev_info(&pdev->dev, "context %s using bank %d\n",
+ dev_name(&pdev->dev), ctx_drvdata->num);
+
+ if (drvdata->clk)
+ clk_disable_unprepare(drvdata->clk);
+ clk_disable_unprepare(drvdata->pclk);
+
+ return ret;
+}
+
+static int __devexit msm_iommu_ctx_remove(struct platform_device *pdev)
+{
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static struct of_device_id msm_iommu_match_table[] = {
+ { .compatible = "qcom,msm-smmu-v2", },
+ {}
+};
+
+static struct platform_driver msm_iommu_driver = {
+ .driver = {
+ .name = "msm_iommu_v2",
+ .of_match_table = msm_iommu_match_table,
+ },
+ .probe = msm_iommu_probe,
+ .remove = __devexit_p(msm_iommu_remove),
+};
+
+static struct of_device_id msm_iommu_ctx_match_table[] = {
+ { .name = "qcom,iommu-ctx", },
+ {}
+};
+
+static struct platform_driver msm_iommu_ctx_driver = {
+ .driver = {
+ .name = "msm_iommu_ctx_v2",
+ .of_match_table = msm_iommu_ctx_match_table,
+ },
+ .probe = msm_iommu_ctx_probe,
+ .remove = __devexit_p(msm_iommu_ctx_remove),
+};
+
+static int __init msm_iommu_driver_init(void)
+{
+ struct device_node *node;
+ int ret;
+
+ node = of_find_compatible_node(NULL, NULL, "qcom,msm-smmu-v2");
+ if (!node)
+ return -ENODEV;
+
+ of_node_put(node);
+
+ msm_iommu_root_dev = platform_device_register_simple(
+ "msm_iommu", -1, 0, 0);
+ if (!msm_iommu_root_dev) {
+ pr_err("Failed to create root IOMMU device\n");
+ ret = -ENODEV;
+ goto error;
+ }
+
+ atomic_inc(&msm_iommu_next_id);
+
+ ret = platform_driver_register(&msm_iommu_driver);
+ if (ret != 0) {
+ pr_err("Failed to register IOMMU driver\n");
+ goto error;
+ }
+
+ ret = platform_driver_register(&msm_iommu_ctx_driver);
+ if (ret != 0) {
+ pr_err("Failed to register IOMMU context driver\n");
+ goto error;
+ }
+
+error:
+ return ret;
+}
+
+static void __exit msm_iommu_driver_exit(void)
+{
+ platform_driver_unregister(&msm_iommu_ctx_driver);
+ platform_driver_unregister(&msm_iommu_driver);
+ platform_device_unregister(msm_iommu_root_dev);
+}
+
+subsys_initcall(msm_iommu_driver_init);
+module_exit(msm_iommu_driver_exit);
+
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/iommu_dev.c b/arch/arm/mach-msm/iommu_dev.c
index b8b5aa3..967283d 100644
--- a/arch/arm/mach-msm/iommu_dev.c
+++ b/arch/arm/mach-msm/iommu_dev.c
@@ -33,13 +33,14 @@
struct device *dev;
};
-static struct platform_device *msm_iommu_root_dev;
+struct platform_device *msm_iommu_root_dev;
static int each_iommu_ctx(struct device *dev, void *data)
{
struct iommu_ctx_iter_data *res = data;
- struct msm_iommu_ctx_dev *c = dev->platform_data;
+ struct msm_iommu_ctx_drvdata *c;
+ c = dev_get_drvdata(dev);
if (!res || !c || !c->name || !res->name)
return -EINVAL;
@@ -68,7 +69,7 @@
r.name = ctx_name;
found = device_for_each_child(&msm_iommu_root_dev->dev, &r, each_iommu);
- if (!found || !dev_get_drvdata(r.dev)) {
+ if (found <= 0 || !dev_get_drvdata(r.dev)) {
pr_err("Could not find context <%s>\n", ctx_name);
goto fail;
}
diff --git a/arch/arm/mach-msm/iommu_domains.c b/arch/arm/mach-msm/iommu_domains.c
index 271e252b..9cf16b6 100644
--- a/arch/arm/mach-msm/iommu_domains.c
+++ b/arch/arm/mach-msm/iommu_domains.c
@@ -173,7 +173,7 @@
node = rb_entry(p, struct msm_iova_data, node);
if (domain_num < node->domain_num)
p = p->rb_left;
- else if (domain_num > domain_num)
+ else if (domain_num > node->domain_num)
p = p->rb_right;
else {
mutex_unlock(&domain_mutex);
diff --git a/arch/arm/mach-msm/iommu_pagetable.c b/arch/arm/mach-msm/iommu_pagetable.c
new file mode 100644
index 0000000..b485605
--- /dev/null
+++ b/arch/arm/mach-msm/iommu_pagetable.c
@@ -0,0 +1,520 @@
+/* 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/module.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/iommu.h>
+#include <linux/scatterlist.h>
+
+#include <asm/cacheflush.h>
+
+#include <mach/iommu.h>
+#include "iommu_pagetable.h"
+
+/* Sharability attributes of MSM IOMMU mappings */
+#define MSM_IOMMU_ATTR_NON_SH 0x0
+#define MSM_IOMMU_ATTR_SH 0x4
+
+/* Cacheability attributes of MSM IOMMU mappings */
+#define MSM_IOMMU_ATTR_NONCACHED 0x0
+#define MSM_IOMMU_ATTR_CACHED_WB_WA 0x1
+#define MSM_IOMMU_ATTR_CACHED_WB_NWA 0x2
+#define MSM_IOMMU_ATTR_CACHED_WT 0x3
+
+static int msm_iommu_tex_class[4];
+
+static inline void clean_pte(unsigned long *start, unsigned long *end,
+ int redirect)
+{
+ if (!redirect)
+ dmac_flush_range(start, end);
+}
+
+int msm_iommu_pagetable_alloc(struct iommu_pt *pt)
+{
+ pt->fl_table = (unsigned long *)__get_free_pages(GFP_KERNEL,
+ get_order(SZ_16K));
+ if (!pt->fl_table)
+ return -ENOMEM;
+
+ memset(pt->fl_table, 0, SZ_16K);
+ clean_pte(pt->fl_table, pt->fl_table + NUM_FL_PTE, pt->redirect);
+
+ return 0;
+}
+
+void msm_iommu_pagetable_free(struct iommu_pt *pt)
+{
+ unsigned long *fl_table;
+ int i;
+
+ fl_table = pt->fl_table;
+ for (i = 0; i < NUM_FL_PTE; i++)
+ if ((fl_table[i] & 0x03) == FL_TYPE_TABLE)
+ free_page((unsigned long) __va(((fl_table[i]) &
+ FL_BASE_MASK)));
+ free_pages((unsigned long)fl_table, get_order(SZ_16K));
+ pt->fl_table = 0;
+}
+
+static int __get_pgprot(int prot, int len)
+{
+ unsigned int pgprot;
+ int tex;
+
+ if (!(prot & (IOMMU_READ | IOMMU_WRITE))) {
+ prot |= IOMMU_READ | IOMMU_WRITE;
+ WARN_ONCE(1, "No attributes in iommu mapping; assuming RW\n");
+ }
+
+ if ((prot & IOMMU_WRITE) && !(prot & IOMMU_READ)) {
+ prot |= IOMMU_READ;
+ WARN_ONCE(1, "Write-only unsupported; falling back to RW\n");
+ }
+
+ if (prot & IOMMU_CACHE)
+ tex = (pgprot_kernel >> 2) & 0x07;
+ else
+ tex = msm_iommu_tex_class[MSM_IOMMU_ATTR_NONCACHED];
+
+ if (tex < 0 || tex > NUM_TEX_CLASS - 1)
+ return 0;
+
+ if (len == SZ_16M || len == SZ_1M) {
+ pgprot = FL_SHARED;
+ pgprot |= tex & 0x01 ? FL_BUFFERABLE : 0;
+ pgprot |= tex & 0x02 ? FL_CACHEABLE : 0;
+ pgprot |= tex & 0x04 ? FL_TEX0 : 0;
+ pgprot |= FL_AP0 | FL_AP1;
+ pgprot |= prot & IOMMU_WRITE ? 0 : FL_AP2;
+ } else {
+ pgprot = SL_SHARED;
+ pgprot |= tex & 0x01 ? SL_BUFFERABLE : 0;
+ pgprot |= tex & 0x02 ? SL_CACHEABLE : 0;
+ pgprot |= tex & 0x04 ? SL_TEX0 : 0;
+ pgprot |= SL_AP0 | SL_AP1;
+ pgprot |= prot & IOMMU_WRITE ? 0 : SL_AP2;
+ }
+
+ return pgprot;
+}
+
+int msm_iommu_pagetable_map(struct iommu_pt *pt, unsigned long va,
+ phys_addr_t pa, int order, int prot)
+{
+ unsigned long *fl_pte;
+ unsigned long fl_offset;
+ unsigned long *sl_table;
+ unsigned long *sl_pte;
+ unsigned long sl_offset;
+ unsigned int pgprot;
+ size_t len = 0x1000UL << order;
+ int ret = 0;
+
+ if (len != SZ_16M && len != SZ_1M &&
+ len != SZ_64K && len != SZ_4K) {
+ pr_debug("Bad size: %d\n", len);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (!pt->fl_table) {
+ pr_debug("Null page table\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ pgprot = __get_pgprot(prot, len);
+ if (!pgprot) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ fl_offset = FL_OFFSET(va); /* Upper 12 bits */
+ fl_pte = pt->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;
+ clean_pte(fl_pte, fl_pte + 16, pt->redirect);
+ }
+
+ if (len == SZ_1M) {
+ if (*fl_pte) {
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ *fl_pte = (pa & 0xFFF00000) | FL_NG | FL_TYPE_SECT
+ | FL_SHARED | pgprot;
+ clean_pte(fl_pte, fl_pte + 1, pt->redirect);
+ }
+
+ /* Need a 2nd level table */
+ 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");
+ ret = -ENOMEM;
+ goto fail;
+ }
+ memset(sl, 0, SZ_4K);
+ clean_pte(sl, sl + NUM_SL_PTE, pt->redirect);
+
+ *fl_pte = ((((int)__pa(sl)) & FL_BASE_MASK) | \
+ FL_TYPE_TABLE);
+ clean_pte(fl_pte, fl_pte + 1, pt->redirect);
+ }
+
+ if (!(*fl_pte & FL_TYPE_TABLE)) {
+ ret = -EBUSY;
+ goto fail;
+ }
+ }
+
+ sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
+ sl_offset = SL_OFFSET(va);
+ sl_pte = sl_table + sl_offset;
+
+ if (len == SZ_4K) {
+ if (*sl_pte) {
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ *sl_pte = (pa & SL_BASE_MASK_SMALL) | SL_NG | SL_SHARED
+ | SL_TYPE_SMALL | pgprot;
+ clean_pte(sl_pte, sl_pte + 1, pt->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;
+
+ clean_pte(sl_pte, sl_pte + 16, pt->redirect);
+ }
+
+fail:
+ return ret;
+}
+
+int msm_iommu_pagetable_unmap(struct iommu_pt *pt, unsigned long va, int order)
+{
+ unsigned long *fl_pte;
+ unsigned long fl_offset;
+ unsigned long *sl_table;
+ unsigned long *sl_pte;
+ unsigned long sl_offset;
+ size_t len = 0x1000UL << order;
+ int i, ret = 0;
+
+ if (len != SZ_16M && len != SZ_1M &&
+ len != SZ_64K && len != SZ_4K) {
+ pr_debug("Bad length: %d\n", len);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (!pt->fl_table) {
+ pr_debug("Null page table\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ fl_offset = FL_OFFSET(va); /* Upper 12 bits */
+ fl_pte = pt->fl_table + fl_offset; /* int pointers, 4 bytes */
+
+ if (*fl_pte == 0) {
+ pr_debug("First level PTE is 0\n");
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ /* Unmap supersection */
+ if (len == SZ_16M) {
+ for (i = 0; i < 16; i++)
+ *(fl_pte+i) = 0;
+
+ clean_pte(fl_pte, fl_pte + 16, pt->redirect);
+ }
+
+ if (len == SZ_1M) {
+ *fl_pte = 0;
+ clean_pte(fl_pte, fl_pte + 1, pt->redirect);
+ }
+
+ sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
+ sl_offset = SL_OFFSET(va);
+ sl_pte = sl_table + sl_offset;
+
+ if (len == SZ_64K) {
+ for (i = 0; i < 16; i++)
+ *(sl_pte+i) = 0;
+
+ clean_pte(sl_pte, sl_pte + 16, pt->redirect);
+ }
+
+ if (len == SZ_4K) {
+ *sl_pte = 0;
+ clean_pte(sl_pte, sl_pte + 1, pt->redirect);
+ }
+
+ if (len == SZ_4K || len == SZ_64K) {
+ int used = 0;
+
+ for (i = 0; i < NUM_SL_PTE; i++)
+ if (sl_table[i])
+ used = 1;
+ if (!used) {
+ free_page((unsigned long)sl_table);
+ *fl_pte = 0;
+ clean_pte(fl_pte, fl_pte + 1, pt->redirect);
+ }
+ }
+
+fail:
+ return ret;
+}
+
+static unsigned int get_phys_addr(struct scatterlist *sg)
+{
+ /*
+ * Try sg_dma_address first so that we can
+ * map carveout regions that do not have a
+ * struct page associated with them.
+ */
+ unsigned int pa = sg_dma_address(sg);
+ if (pa == 0)
+ pa = sg_phys(sg);
+ return pa;
+}
+
+int msm_iommu_pagetable_map_range(struct iommu_pt *pt, unsigned int va,
+ struct scatterlist *sg, unsigned int len, int prot)
+{
+ unsigned int pa;
+ unsigned int offset = 0;
+ unsigned int pgprot;
+ unsigned long *fl_pte;
+ unsigned long fl_offset;
+ unsigned long *sl_table;
+ unsigned long sl_offset, sl_start;
+ unsigned int chunk_offset = 0;
+ unsigned int chunk_pa;
+ int ret = 0;
+
+ BUG_ON(len & (SZ_4K - 1));
+
+ pgprot = __get_pgprot(prot, SZ_4K);
+ if (!pgprot) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ fl_offset = FL_OFFSET(va); /* Upper 12 bits */
+ fl_pte = pt->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;
+ }
+
+ 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));
+
+ if (!sl_table) {
+ pr_debug("Could not allocate second level table\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ memset(sl_table, 0, SZ_4K);
+ clean_pte(sl_table, sl_table + NUM_SL_PTE,
+ pt->redirect);
+
+ *fl_pte = ((((int)__pa(sl_table)) & FL_BASE_MASK) |
+ FL_TYPE_TABLE);
+ clean_pte(fl_pte, fl_pte + 1, pt->redirect);
+ } else
+ sl_table = (unsigned long *)
+ __va(((*fl_pte) & FL_BASE_MASK));
+
+ /* Keep track of initial position so we
+ * don't clean more than we have to
+ */
+ sl_start = sl_offset;
+
+ /* 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;
+
+ if (chunk_offset >= sg->length && offset < len) {
+ chunk_offset = 0;
+ sg = sg_next(sg);
+ chunk_pa = get_phys_addr(sg);
+ if (chunk_pa == 0) {
+ pr_debug("No dma address for sg %p\n",
+ sg);
+ ret = -EINVAL;
+ goto fail;
+ }
+ }
+ }
+
+ clean_pte(sl_table + sl_start, sl_table + sl_offset,
+ pt->redirect);
+ fl_pte++;
+ sl_offset = 0;
+ }
+
+fail:
+ return ret;
+}
+
+void msm_iommu_pagetable_unmap_range(struct iommu_pt *pt, unsigned int va,
+ unsigned int len)
+{
+ unsigned int offset = 0;
+ unsigned long *fl_pte;
+ unsigned long fl_offset;
+ unsigned long *sl_table;
+ unsigned long sl_start, sl_end;
+ int used, i;
+
+ BUG_ON(len & (SZ_4K - 1));
+
+ fl_offset = FL_OFFSET(va); /* Upper 12 bits */
+ fl_pte = pt->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 (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,
+ pt->redirect);
+
+ offset += (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;
+
+ /* 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, pt->redirect);
+ }
+
+ sl_start = 0;
+ fl_pte++;
+ }
+}
+
+static int __init get_tex_class(int icp, int ocp, int mt, int nos)
+{
+ int i = 0;
+ unsigned int prrr = 0;
+ unsigned int nmrr = 0;
+ int c_icp, c_ocp, c_mt, c_nos;
+
+ RCP15_PRRR(prrr);
+ RCP15_NMRR(nmrr);
+
+ for (i = 0; i < NUM_TEX_CLASS; i++) {
+ c_nos = PRRR_NOS(prrr, i);
+ c_mt = PRRR_MT(prrr, i);
+ c_icp = NMRR_ICP(nmrr, i);
+ c_ocp = NMRR_OCP(nmrr, i);
+
+ if (icp == c_icp && ocp == c_ocp && c_mt == mt && c_nos == nos)
+ return i;
+ }
+
+ return -ENODEV;
+}
+
+static void __init setup_iommu_tex_classes(void)
+{
+ msm_iommu_tex_class[MSM_IOMMU_ATTR_NONCACHED] =
+ get_tex_class(CP_NONCACHED, CP_NONCACHED, MT_NORMAL, 1);
+
+ msm_iommu_tex_class[MSM_IOMMU_ATTR_CACHED_WB_WA] =
+ get_tex_class(CP_WB_WA, CP_WB_WA, MT_NORMAL, 1);
+
+ msm_iommu_tex_class[MSM_IOMMU_ATTR_CACHED_WB_NWA] =
+ get_tex_class(CP_WB_NWA, CP_WB_NWA, MT_NORMAL, 1);
+
+ msm_iommu_tex_class[MSM_IOMMU_ATTR_CACHED_WT] =
+ get_tex_class(CP_WT, CP_WT, MT_NORMAL, 1);
+}
+
+void __init msm_iommu_pagetable_init(void)
+{
+ setup_iommu_tex_classes();
+}
diff --git a/arch/arm/mach-msm/iommu_pagetable.h b/arch/arm/mach-msm/iommu_pagetable.h
new file mode 100644
index 0000000..39f1d3d
--- /dev/null
+++ b/arch/arm/mach-msm/iommu_pagetable.h
@@ -0,0 +1,90 @@
+/* Copyright (c) 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_IOMMU_PAGETABLE_H
+#define __ARCH_ARM_MACH_MSM_IOMMU_PAGETABLE_H
+
+#define NUM_FL_PTE 4096
+#define NUM_SL_PTE 256
+#define NUM_TEX_CLASS 8
+
+/* First-level page table bits */
+#define FL_BASE_MASK 0xFFFFFC00
+#define FL_TYPE_TABLE (1 << 0)
+#define FL_TYPE_SECT (2 << 0)
+#define FL_SUPERSECTION (1 << 18)
+#define FL_AP0 (1 << 10)
+#define FL_AP1 (1 << 11)
+#define FL_AP2 (1 << 15)
+#define FL_SHARED (1 << 16)
+#define FL_BUFFERABLE (1 << 2)
+#define FL_CACHEABLE (1 << 3)
+#define FL_TEX0 (1 << 12)
+#define FL_OFFSET(va) (((va) & 0xFFF00000) >> 20)
+#define FL_NG (1 << 17)
+
+/* Second-level page table bits */
+#define SL_BASE_MASK_LARGE 0xFFFF0000
+#define SL_BASE_MASK_SMALL 0xFFFFF000
+#define SL_TYPE_LARGE (1 << 0)
+#define SL_TYPE_SMALL (2 << 0)
+#define SL_AP0 (1 << 4)
+#define SL_AP1 (2 << 4)
+#define SL_AP2 (1 << 9)
+#define SL_SHARED (1 << 10)
+#define SL_BUFFERABLE (1 << 2)
+#define SL_CACHEABLE (1 << 3)
+#define SL_TEX0 (1 << 6)
+#define SL_OFFSET(va) (((va) & 0xFF000) >> 12)
+#define SL_NG (1 << 11)
+
+/* Memory type and cache policy attributes */
+#define MT_SO 0
+#define MT_DEV 1
+#define MT_NORMAL 2
+#define CP_NONCACHED 0
+#define CP_WB_WA 1
+#define CP_WT 2
+#define CP_WB_NWA 3
+
+/* TEX Remap Registers */
+#define NMRR_ICP(nmrr, n) (((nmrr) & (3 << ((n) * 2))) >> ((n) * 2))
+#define NMRR_OCP(nmrr, n) (((nmrr) & (3 << ((n) * 2 + 16))) >> ((n) * 2 + 16))
+
+#define PRRR_NOS(prrr, n) ((prrr) & (1 << ((n) + 24)) ? 1 : 0)
+#define PRRR_MT(prrr, n) ((((prrr) & (3 << ((n) * 2))) >> ((n) * 2)))
+
+#define MRC(reg, processor, op1, crn, crm, op2) \
+__asm__ __volatile__ ( \
+" mrc " #processor "," #op1 ", %0," #crn "," #crm "," #op2 "\n" \
+: "=r" (reg))
+
+#define RCP15_PRRR(reg) MRC(reg, p15, 0, c10, c2, 0)
+#define RCP15_NMRR(reg) MRC(reg, p15, 0, c10, c2, 1)
+
+struct iommu_pt {
+ unsigned long *fl_table;
+ int redirect;
+};
+
+void msm_iommu_pagetable_init(void);
+int msm_iommu_pagetable_alloc(struct iommu_pt *pt);
+void msm_iommu_pagetable_free(struct iommu_pt *pt);
+int msm_iommu_pagetable_map(struct iommu_pt *pt, unsigned long va,
+ phys_addr_t pa, int order, int prot);
+int msm_iommu_pagetable_unmap(struct iommu_pt *pt, unsigned long va,
+ int order);
+int msm_iommu_pagetable_map_range(struct iommu_pt *pt, unsigned int va,
+ struct scatterlist *sg, unsigned int len, int prot);
+void msm_iommu_pagetable_unmap_range(struct iommu_pt *pt, unsigned int va,
+ unsigned int len);
+#endif
diff --git a/arch/arm/mach-msm/ipc_router_smd_xprt.c b/arch/arm/mach-msm/ipc_router_smd_xprt.c
index 307b6ae..a5721c4 100644
--- a/arch/arm/mach-msm/ipc_router_smd_xprt.c
+++ b/arch/arm/mach-msm/ipc_router_smd_xprt.c
@@ -52,6 +52,7 @@
struct delayed_work read_work;
spinlock_t ss_reset_lock; /*Subsystem reset lock*/
int ss_reset;
+ void *pil;
};
struct msm_ipc_router_smd_xprt_work {
@@ -202,10 +203,16 @@
static int msm_ipc_router_smd_remote_close(struct msm_ipc_router_xprt *xprt)
{
+ int rc;
struct msm_ipc_router_smd_xprt *smd_xprtp =
container_of(xprt, struct msm_ipc_router_smd_xprt, xprt);
- return smd_close(smd_xprtp->channel);
+ rc = smd_close(smd_xprtp->channel);
+ if (smd_xprtp->pil) {
+ pil_put(smd_xprtp->pil);
+ smd_xprtp->pil = NULL;
+ }
+ return rc;
}
static void smd_xprt_read_data(struct work_struct *work)
@@ -387,6 +394,23 @@
}
}
+static void *msm_ipc_load_subsystem(uint32_t edge)
+{
+ void *pil = NULL;
+ const char *peripheral;
+
+ peripheral = smd_edge_to_subsystem(edge);
+ if (peripheral) {
+ pil = pil_get(peripheral);
+ if (IS_ERR(pil)) {
+ pr_err("%s: Failed to load %s\n",
+ __func__, peripheral);
+ pil = NULL;
+ }
+ }
+ return pil;
+}
+
static int msm_ipc_router_smd_remote_probe(struct platform_device *pdev)
{
int rc;
@@ -424,6 +448,8 @@
spin_lock_init(&smd_remote_xprt[id].ss_reset_lock);
smd_remote_xprt[id].ss_reset = 0;
+ smd_remote_xprt[id].pil = msm_ipc_load_subsystem(
+ smd_xprt_cfg[id].edge);
rc = smd_named_open_on_edge(smd_xprt_cfg[id].ch_name,
smd_xprt_cfg[id].edge,
&smd_remote_xprt[id].channel,
@@ -432,6 +458,10 @@
if (rc < 0) {
pr_err("%s: Channel open failed for %s\n",
__func__, smd_xprt_cfg[id].ch_name);
+ if (smd_remote_xprt[id].pil) {
+ pil_put(smd_remote_xprt[id].pil);
+ smd_remote_xprt[id].pil = NULL;
+ }
destroy_workqueue(smd_remote_xprt[id].smd_xprt_wq);
return rc;
}
diff --git a/arch/arm/mach-msm/lpm_levels.c b/arch/arm/mach-msm/lpm_levels.c
index 48cf3f7..80b82cb 100644
--- a/arch/arm/mach-msm/lpm_levels.c
+++ b/arch/arm/mach-msm/lpm_levels.c
@@ -19,18 +19,19 @@
#include <linux/of.h>
#include <mach/mpm.h>
#include "rpm_resources.h"
+#include "pm.h"
static struct msm_rpmrs_level *msm_lpm_levels;
static int msm_lpm_level_count;
-int msm_rpmrs_enter_sleep(uint32_t sclk_count, struct msm_rpmrs_limits *limits,
+static int msm_lpm_enter_sleep(uint32_t sclk_count, void *limits,
bool from_idle, bool notify_rpm)
{
/* TODO */
return 0;
}
-void msm_rpmrs_exit_sleep(struct msm_rpmrs_limits *limits, bool from_idle,
+static void msm_lpm_exit_sleep(void *limits, bool from_idle,
bool notify_rpm, bool collapsed)
{
/* TODO */
@@ -50,14 +51,15 @@
return;
}
-struct msm_rpmrs_limits *msm_rpmrs_lowest_limits(
- bool from_idle, enum msm_pm_sleep_mode sleep_mode, uint32_t latency_us,
- uint32_t sleep_us)
+static void *msm_lpm_lowest_limits(bool from_idle,
+ enum msm_pm_sleep_mode sleep_mode, uint32_t latency_us,
+ uint32_t sleep_us, uint32_t *power)
{
unsigned int cpu = smp_processor_id();
struct msm_rpmrs_level *best_level = NULL;
bool irqs_detectable = false;
bool gpio_detectable = false;
+ uint32_t pwr;
int i;
if (!msm_lpm_levels)
@@ -70,7 +72,6 @@
for (i = 0; i < msm_lpm_level_count; i++) {
struct msm_rpmrs_level *level = &msm_lpm_levels[i];
- uint32_t power;
if (!level->available)
continue;
@@ -86,28 +87,36 @@
continue;
if (sleep_us <= 1) {
- power = level->energy_overhead;
+ pwr = level->energy_overhead;
} else if (sleep_us <= level->time_overhead_us) {
- power = level->energy_overhead / sleep_us;
+ pwr = level->energy_overhead / sleep_us;
} else if ((sleep_us >> 10) > level->time_overhead_us) {
- power = level->steady_state_power;
+ pwr = level->steady_state_power;
} else {
- power = level->steady_state_power;
- power -= (level->time_overhead_us *
+ pwr = level->steady_state_power;
+ pwr -= (level->time_overhead_us *
level->steady_state_power)/sleep_us;
- power += level->energy_overhead / sleep_us;
+ pwr += level->energy_overhead / sleep_us;
}
- if (!best_level ||
- best_level->rs_limits.power[cpu] >= power) {
+ if (!best_level || best_level->rs_limits.power[cpu] >= pwr) {
+
level->rs_limits.latency_us[cpu] = level->latency_us;
- level->rs_limits.power[cpu] = power;
+ level->rs_limits.power[cpu] = pwr;
best_level = level;
+
+ if (power)
+ *power = pwr;
}
}
return best_level ? &best_level->rs_limits : NULL;
}
+static struct msm_pm_sleep_ops msm_lpm_ops = {
+ .lowest_limits = msm_lpm_lowest_limits,
+ .enter_sleep = msm_lpm_enter_sleep,
+ .exit_sleep = msm_lpm_exit_sleep,
+};
static int __devinit msm_lpm_levels_probe(struct platform_device *pdev)
{
@@ -204,6 +213,8 @@
msm_lpm_levels = levels;
msm_lpm_level_count = idx;
+ msm_pm_set_sleep_ops(&msm_lpm_ops);
+
return 0;
fail:
pr_err("%s: Error in name %s key %s\n", __func__, node->full_name, key);
diff --git a/arch/arm/mach-msm/msm_cache_dump.c b/arch/arm/mach-msm/msm_cache_dump.c
index b21412f..9759d5a 100644
--- a/arch/arm/mach-msm/msm_cache_dump.c
+++ b/arch/arm/mach-msm/msm_cache_dump.c
@@ -31,13 +31,12 @@
static unsigned long msm_cache_dump_addr;
/*
- * These are dummy pointers so the defintion of l1_cache_dump
- * and l2_cache_dump don't get optimized away. If they aren't
- * referenced, the structure definitions don't show up in the
- * debugging information which is needed for post processing.
+ * These should not actually be dereferenced. There's no
+ * need for a virtual mapping, but the physical address is
+ * necessary.
*/
-static struct l1_cache_dump __used *l1_dump;
-static struct l2_cache_dump __used *l2_dump;
+static struct l1_cache_dump *l1_dump;
+static struct l2_cache_dump *l2_dump;
static int msm_cache_dump_panic(struct notifier_block *this,
unsigned long event, void *ptr)
@@ -96,6 +95,8 @@
pr_err("%s: could not register L1 buffer ret = %d.\n",
__func__, ret);
+ l1_dump = (struct l1_cache_dump *)msm_cache_dump_addr;
+
#if defined(CONFIG_MSM_CACHE_DUMP_ON_PANIC)
l1_cache_data.buf = msm_cache_dump_addr + d->l1_size;
l1_cache_data.size = d->l2_size;
@@ -110,6 +111,9 @@
__raw_writel(msm_cache_dump_addr + d->l1_size,
MSM_IMEM_BASE + L2_DUMP_OFFSET);
+
+ l2_dump = (struct l2_cache_dump *)(msm_cache_dump_addr + d->l1_size);
+
atomic_notifier_chain_register(&panic_notifier_list,
&msm_cache_dump_blk);
return 0;
diff --git a/arch/arm/mach-msm/msm_dsps.c b/arch/arm/mach-msm/msm_dsps.c
index 057665b..3b52a9f 100644
--- a/arch/arm/mach-msm/msm_dsps.c
+++ b/arch/arm/mach-msm/msm_dsps.c
@@ -15,6 +15,8 @@
*
*/
+#include <asm/atomic.h>
+
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/module.h>
@@ -39,10 +41,11 @@
#include <mach/subsystem_restart.h>
#include <mach/subsystem_notif.h>
+#include "ramdump.h"
#include "timer.h"
#define DRV_NAME "msm_dsps"
-#define DRV_VERSION "3.02"
+#define DRV_VERSION "4.00"
#define PPSS_PAUSE_REG 0x1804
@@ -58,8 +61,15 @@
* @cdev - character device for user interface.
* @pdata - platform data.
* @pil - handle to DSPS Firmware loader.
+ * @dspsfw_ramdump_dev - handle to ramdump device for DSPS
+ * @dspsfw_ramdump_segments - Ramdump segment information for DSPS
+ * @smem_ramdump_dev - handle to ramdump device for smem
+ * @smem_ramdump_segments - Ramdump segment information for smem
* @is_on - DSPS is on.
* @ref_count - open/close reference count.
+ * @wdog_irq - DSPS Watchdog IRQ
+ * @wd_crash - Watchdog ISR fired
+ * @crash_in_progress - 1 if crash recovery is in progress
* @ppss_base - ppss registers virtual base address.
*/
struct dsps_drv {
@@ -73,9 +83,18 @@
void *pil;
+ void *dspsfw_ramdump_dev;
+ struct ramdump_segment dspsfw_ramdump_segments[4];
+
+ void *smem_ramdump_dev;
+ struct ramdump_segment smem_ramdump_segments[1];
+
int is_on;
int ref_count;
+ int wdog_irq;
+ atomic_t wd_crash;
+ atomic_t crash_in_progress;
void __iomem *ppss_base;
};
@@ -89,8 +108,7 @@
*/
static int dsps_crash_shutdown_g;
-
-static void dsps_fatal_handler(struct work_struct *work);
+static void dsps_restart_handler(struct work_struct *work);
/**
* Load DSPS Firmware.
@@ -360,7 +378,7 @@
return 0;
}
-static DECLARE_WORK(dsps_fatal_work, dsps_fatal_handler);
+static DECLARE_WORK(dsps_fatal_work, dsps_restart_handler);
/**
* Watchdog interrupt handler
@@ -369,6 +387,7 @@
static irqreturn_t dsps_wdog_bite_irq(int irq, void *dev_id)
{
pr_debug("%s\n", __func__);
+ atomic_set(&drv->wd_crash, 1);
(void)schedule_work(&dsps_fatal_work);
disable_irq_nosync(irq);
return IRQ_HANDLED;
@@ -406,7 +425,7 @@
ret = put_user(val, (u32 __user *) arg);
break;
case DSPS_IOCTL_RESET:
- dsps_fatal_handler(NULL);
+ dsps_restart_handler(NULL);
ret = 0;
break;
default:
@@ -498,21 +517,52 @@
ppss_wdog = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
"ppss_wdog");
if (ppss_wdog) {
- ret = request_irq(ppss_wdog->start, dsps_wdog_bite_irq,
+ drv->wdog_irq = ppss_wdog->start;
+ ret = request_irq(drv->wdog_irq, dsps_wdog_bite_irq,
IRQF_TRIGGER_RISING, "dsps_wdog", NULL);
if (ret) {
pr_err("%s: request_irq fail %d\n", __func__, ret);
goto request_irq_err;
}
} else {
+ drv->wdog_irq = -1;
pr_debug("%s: ppss_wdog not supported.\n", __func__);
}
+ drv->dspsfw_ramdump_segments[0].address = drv->pdata->tcm_code_start;
+ drv->dspsfw_ramdump_segments[0].size = drv->pdata->tcm_code_size;
+ drv->dspsfw_ramdump_segments[1].address = drv->pdata->tcm_buf_start;
+ drv->dspsfw_ramdump_segments[1].size = drv->pdata->tcm_buf_size;
+ drv->dspsfw_ramdump_segments[2].address = drv->pdata->pipe_start;
+ drv->dspsfw_ramdump_segments[2].size = drv->pdata->pipe_size;
+ drv->dspsfw_ramdump_segments[3].address = drv->pdata->ddr_start;
+ drv->dspsfw_ramdump_segments[3].size = drv->pdata->ddr_size;
+
+ drv->dspsfw_ramdump_dev = create_ramdump_device("dsps");
+ if (!drv->dspsfw_ramdump_dev) {
+ pr_err("%s: create_ramdump_device(\"dsps\") fail\n",
+ __func__);
+ goto create_ramdump_err;
+ }
+
+ drv->smem_ramdump_segments[0].address = drv->pdata->smem_start;
+ drv->smem_ramdump_segments[0].size = drv->pdata->smem_size;
+ drv->smem_ramdump_dev = create_ramdump_device("smem");
+ if (!drv->smem_ramdump_dev) {
+ pr_err("%s: create_ramdump_device(\"smem\") fail\n",
+ __func__);
+ goto create_ramdump_err;
+ }
+
if (drv->pdata->init)
drv->pdata->init(drv->pdata);
return 0;
+create_ramdump_err:
+ disable_irq_nosync(drv->wdog_irq);
+ free_irq(drv->wdog_irq, NULL);
+
request_irq_err:
iounmap(drv->ppss_base);
@@ -600,6 +650,8 @@
}
}
+ free_irq(drv->wdog_irq, NULL);
+
iounmap(drv->ppss_base);
}
@@ -646,22 +698,42 @@
* Fatal error handler
* Resets DSPS.
*/
-static void dsps_fatal_handler(struct work_struct *work)
+static void dsps_restart_handler(struct work_struct *work)
{
uint32_t dsps_state;
+ int restart_level;
+ char *smem_reset_reason;
+ unsigned smem_reset_size;
+ const char dflt_reason[] = "Died too early due to unknown reason";
dsps_state = smsm_get_state(SMSM_DSPS_STATE);
+ restart_level = get_restart_level();
- pr_debug("%s: DSPS state 0x%x\n", __func__, dsps_state);
+ pr_debug("%s: DSPS state 0x%x. Restart lvl %d\n",
+ __func__, dsps_state, restart_level);
- if (dsps_state & SMSM_RESET) {
- pr_err("%s: DSPS fatal error detected. Resetting\n",
+ if ((dsps_state & SMSM_RESET) ||
+ (atomic_read(&drv->wd_crash) == 1)) {
+ smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_DSPS0,
+ &smem_reset_size);
+ if (smem_reset_reason != NULL && smem_reset_reason[0] != 0) {
+ smem_reset_reason[smem_reset_size-1] = 0;
+ pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
+ __func__, smem_reset_reason);
+ memset(smem_reset_reason, 0, smem_reset_size);
+ wmb();
+ } else
+ pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
+ __func__, dflt_reason);
+ } else
+ pr_err("%s: User-initiated DSPS reset.\nResetting DSPS\n",
__func__);
- panic("DSPS fatal error detected.");
+
+ if (atomic_add_return(1, &drv->crash_in_progress) > 1) {
+ pr_err("%s: DSPS already resetting. Count %d\n", __func__,
+ atomic_read(&drv->crash_in_progress));
} else {
- pr_debug("%s: User-initiated DSPS reset. Resetting\n",
- __func__);
- panic("User-initiated DSPS reset.");
+ subsystem_restart("dsps");
}
}
@@ -680,13 +752,8 @@
dsps_crash_shutdown_g = 0;
return;
}
-
- if (new_state & SMSM_RESET) {
- pr_err
- ("%s: SMSM_RESET state detected. restarting the DSPS\n",
- __func__);
- panic("SMSM_RESET state detected.");
- }
+ if (new_state & SMSM_RESET)
+ dsps_restart_handler(NULL);
}
/**
@@ -697,7 +764,9 @@
static int dsps_shutdown(const struct subsys_data *subsys)
{
pr_debug("%s\n", __func__);
- dsps_unload();
+ dsps_suspend();
+ pil_force_shutdown(drv->pdata->pil_name);
+ dsps_power_off_handler();
return 0;
}
@@ -709,11 +778,14 @@
static int dsps_powerup(const struct subsys_data *subsys)
{
pr_debug("%s\n", __func__);
- if (dsps_load(drv->pdata->pil_name) != 0) {
- pr_err("%s: fail to restart DSPS after reboot\n",
- __func__);
- return 1;
+ dsps_power_on_handler();
+ pil_force_boot(drv->pdata->pil_name);
+ atomic_set(&drv->crash_in_progress, 0);
+ if (atomic_read(&drv->wd_crash) > 0) {
+ atomic_set(&drv->wd_crash, 0);
+ enable_irq(drv->wdog_irq);
}
+ dsps_resume();
return 0;
}
@@ -736,8 +808,34 @@
*/
static int dsps_ramdump(int enable, const struct subsys_data *subsys)
{
+ int ret = 0;
pr_debug("%s\n", __func__);
- return 0;
+
+ if (enable) {
+ if (drv->dspsfw_ramdump_dev != NULL) {
+ ret = do_ramdump(drv->dspsfw_ramdump_dev,
+ drv->dspsfw_ramdump_segments,
+ ARRAY_SIZE(drv->dspsfw_ramdump_segments));
+ if (ret < 0) {
+ pr_err("%s: Unable to dump DSPS memory (rc = %d).\n",
+ __func__, ret);
+ goto dsps_ramdump_out;
+ }
+ }
+ if (drv->smem_ramdump_dev != NULL) {
+ ret = do_ramdump(drv->smem_ramdump_dev,
+ drv->smem_ramdump_segments,
+ ARRAY_SIZE(drv->smem_ramdump_segments));
+ if (ret < 0) {
+ pr_err("%s: Unable to dump smem memory (rc = %d).\n",
+ __func__, ret);
+ goto dsps_ramdump_out;
+ }
+ }
+ }
+
+dsps_ramdump_out:
+ return ret;
}
static struct subsys_data dsps_ssrops = {
@@ -768,6 +866,9 @@
pr_err("%s: kzalloc fail.\n", __func__);
goto alloc_err;
}
+ atomic_set(&drv->wd_crash, 0);
+ atomic_set(&drv->crash_in_progress, 0);
+
drv->pdata = pdev->dev.platform_data;
drv->dev_class = class_create(THIS_MODULE, DRV_NAME);
diff --git a/arch/arm/mach-msm/peripheral-loader.c b/arch/arm/mach-msm/peripheral-loader.c
index 9d0ce0d..bfbf4bc 100644
--- a/arch/arm/mach-msm/peripheral-loader.c
+++ b/arch/arm/mach-msm/peripheral-loader.c
@@ -138,8 +138,8 @@
const u8 *data;
if (memblock_overlaps_memory(phdr->p_paddr, phdr->p_memsz)) {
- dev_err(&pil->dev,
- "kernel memory would be overwritten [%#08lx, %#08lx)\n",
+ dev_err(&pil->dev, "%s: kernel memory would be overwritten "
+ "[%#08lx, %#08lx)\n", pil->desc->name,
(unsigned long)phdr->p_paddr,
(unsigned long)(phdr->p_paddr + phdr->p_memsz));
return -EPERM;
@@ -150,14 +150,15 @@
pil->desc->name, num);
ret = request_firmware(&fw, fw_name, &pil->dev);
if (ret) {
- dev_err(&pil->dev, "Failed to locate blob %s\n",
- fw_name);
+ dev_err(&pil->dev, "%s: Failed to locate blob %s\n",
+ pil->desc->name, fw_name);
return ret;
}
if (fw->size != phdr->p_filesz) {
- dev_err(&pil->dev, "Blob size %u doesn't match %u\n",
- fw->size, phdr->p_filesz);
+ dev_err(&pil->dev, "%s: Blob size %u doesn't match "
+ "%u\n", pil->desc->name, fw->size,
+ phdr->p_filesz);
ret = -EPERM;
goto release_fw;
}
@@ -174,7 +175,8 @@
size = min_t(size_t, IOMAP_SIZE, count);
buf = ioremap(paddr, size);
if (!buf) {
- dev_err(&pil->dev, "Failed to map memory\n");
+ dev_err(&pil->dev, "%s: Failed to map memory\n",
+ pil->desc->name);
ret = -ENOMEM;
goto release_fw;
}
@@ -195,7 +197,8 @@
size = min_t(size_t, IOMAP_SIZE, count);
buf = ioremap(paddr, size);
if (!buf) {
- dev_err(&pil->dev, "Failed to map memory\n");
+ dev_err(&pil->dev, "%s: Failed to map memory\n",
+ pil->desc->name);
ret = -ENOMEM;
goto release_fw;
}
@@ -210,7 +213,8 @@
ret = pil->desc->ops->verify_blob(pil->desc, phdr->p_paddr,
phdr->p_memsz);
if (ret)
- dev_err(&pil->dev, "Blob%u failed verification\n", num);
+ dev_err(&pil->dev, "%s: Blob%u failed verification\n",
+ pil->desc->name, num);
}
release_fw:
@@ -241,38 +245,43 @@
snprintf(fw_name, sizeof(fw_name), "%s.mdt", pil->desc->name);
ret = request_firmware(&fw, fw_name, &pil->dev);
if (ret) {
- dev_err(&pil->dev, "Failed to locate %s\n", fw_name);
+ dev_err(&pil->dev, "%s: Failed to locate %s\n",
+ pil->desc->name, fw_name);
goto out;
}
if (fw->size < sizeof(*ehdr)) {
- dev_err(&pil->dev, "Not big enough to be an elf header\n");
+ dev_err(&pil->dev, "%s: Not big enough to be an elf header\n",
+ pil->desc->name);
ret = -EIO;
goto release_fw;
}
ehdr = (struct elf32_hdr *)fw->data;
if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
- dev_err(&pil->dev, "Not an elf header\n");
+ dev_err(&pil->dev, "%s: Not an elf header\n", pil->desc->name);
ret = -EIO;
goto release_fw;
}
if (ehdr->e_phnum == 0) {
- dev_err(&pil->dev, "No loadable segments\n");
+ dev_err(&pil->dev, "%s: No loadable segments\n",
+ pil->desc->name);
ret = -EIO;
goto release_fw;
}
if (sizeof(struct elf32_phdr) * ehdr->e_phnum +
sizeof(struct elf32_hdr) > fw->size) {
- dev_err(&pil->dev, "Program headers not within mdt\n");
+ dev_err(&pil->dev, "%s: Program headers not within mdt\n",
+ pil->desc->name);
ret = -EIO;
goto release_fw;
}
ret = pil->desc->ops->init_image(pil->desc, fw->data, fw->size);
if (ret) {
- dev_err(&pil->dev, "Invalid firmware metadata\n");
+ dev_err(&pil->dev, "%s: Invalid firmware metadata\n",
+ pil->desc->name);
goto release_fw;
}
@@ -283,25 +292,27 @@
ret = load_segment(phdr, i, pil);
if (ret) {
- dev_err(&pil->dev, "Failed to load segment %d\n",
- i);
+ dev_err(&pil->dev, "%s: Failed to load segment %d\n",
+ pil->desc->name, i);
goto release_fw;
}
}
ret = pil_proxy_vote(pil);
if (ret) {
- dev_err(&pil->dev, "Failed to proxy vote\n");
+ dev_err(&pil->dev, "%s: Failed to proxy vote\n",
+ pil->desc->name);
goto release_fw;
}
ret = pil->desc->ops->auth_and_reset(pil->desc);
if (ret) {
- dev_err(&pil->dev, "Failed to bring out of reset\n");
+ dev_err(&pil->dev, "%s: Failed to bring out of reset\n",
+ pil->desc->name);
proxy_timeout = 0; /* Remove proxy vote immediately on error */
goto err_boot;
}
- dev_info(&pil->dev, "brought %s out of reset\n", pil->desc->name);
+ dev_info(&pil->dev, "%s: Brought out of reset\n", pil->desc->name);
err_boot:
pil_proxy_unvote(pil, proxy_timeout);
release_fw:
@@ -397,7 +408,8 @@
return;
mutex_lock(&pil->lock);
- if (WARN(!pil->count, "%s: Reference count mismatch\n", __func__))
+ if (WARN(!pil->count, "%s: %s: Reference count mismatch\n",
+ pil->desc->name, __func__))
goto err_out;
if (!--pil->count)
pil_shutdown(pil);
@@ -428,7 +440,8 @@
}
mutex_lock(&pil->lock);
- if (!WARN(!pil->count, "%s: Reference count mismatch\n", __func__))
+ if (!WARN(!pil->count, "%s: %s: Reference count mismatch\n",
+ pil->desc->name, __func__))
pil_shutdown(pil);
mutex_unlock(&pil->lock);
@@ -448,7 +461,8 @@
}
mutex_lock(&pil->lock);
- if (!WARN(!pil->count, "%s: Reference count mismatch\n", __func__))
+ if (!WARN(!pil->count, "%s: %s: Reference count mismatch\n",
+ pil->desc->name, __func__))
ret = load_image(pil);
if (!ret)
pil_set_state(pil, PIL_ONLINE);
diff --git a/arch/arm/mach-msm/pil-mba.c b/arch/arm/mach-msm/pil-mba.c
new file mode 100644
index 0000000..7405ab9
--- /dev/null
+++ b/arch/arm/mach-msm/pil-mba.c
@@ -0,0 +1,242 @@
+/*
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/ioport.h>
+#include <linux/elf.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/of.h>
+
+#include "peripheral-loader.h"
+
+#define RMB_MBA_COMMAND 0x08
+#define RMB_MBA_STATUS 0x0C
+#define RMB_PMI_META_DATA 0x10
+#define RMB_PMI_CODE_START 0x14
+#define RMB_PMI_CODE_LENGTH 0x18
+
+#define CMD_META_DATA_READY 0x1
+#define CMD_LOAD_READY 0x2
+
+#define STATUS_META_DATA_AUTH_SUCCESS 0x3
+#define STATUS_AUTH_COMPLETE 0x4
+#define STATUS_ERROR_MASK BIT(31)
+
+#define AUTH_TIMEOUT_US 10000000
+#define PROXY_TIMEOUT_MS 10000
+#define POLL_INTERVAL_US 50
+
+struct mba_data {
+ void __iomem *reg_base;
+ void __iomem *metadata_base;
+ unsigned long metadata_phys;
+ struct pil_device *pil;
+ struct clk *xo;
+ u32 img_length;
+};
+
+static int pil_mba_make_proxy_votes(struct pil_desc *pil)
+{
+ int ret;
+ struct mba_data *drv = dev_get_drvdata(pil->dev);
+
+ ret = clk_prepare_enable(drv->xo);
+ if (ret) {
+ dev_err(pil->dev, "Failed to enable XO\n");
+ return ret;
+ }
+ return 0;
+}
+
+static void pil_mba_remove_proxy_votes(struct pil_desc *pil)
+{
+ struct mba_data *drv = dev_get_drvdata(pil->dev);
+ clk_disable_unprepare(drv->xo);
+}
+
+static int pil_mba_init_image(struct pil_desc *pil,
+ const u8 *metadata, size_t size)
+{
+ struct mba_data *drv = dev_get_drvdata(pil->dev);
+ u32 status;
+ int ret;
+
+ /* Copy metadata to assigned shared buffer location */
+ memcpy(drv->metadata_base, metadata, size);
+
+ /* Initialize length counter to 0 */
+ writel_relaxed(0, drv->reg_base + RMB_PMI_CODE_LENGTH);
+ drv->img_length = 0;
+
+ /* Pass address of meta-data to the MBA and perform authentication */
+ writel_relaxed(drv->metadata_phys, drv->reg_base + RMB_PMI_META_DATA);
+ writel_relaxed(CMD_META_DATA_READY, drv->reg_base + RMB_MBA_COMMAND);
+ ret = readl_poll_timeout(drv->reg_base + RMB_MBA_STATUS, status,
+ status == STATUS_META_DATA_AUTH_SUCCESS,
+ POLL_INTERVAL_US, AUTH_TIMEOUT_US);
+ if (ret)
+ dev_err(pil->dev, "MBA authentication timed out\n");
+
+ return ret;
+}
+
+static int pil_mba_verify_blob(struct pil_desc *pil, u32 phy_addr,
+ size_t size)
+{
+ struct mba_data *drv = dev_get_drvdata(pil->dev);
+
+ /* Begin image authentication */
+ if (drv->img_length == 0) {
+ writel_relaxed(phy_addr, drv->reg_base + RMB_PMI_CODE_START);
+ writel_relaxed(CMD_LOAD_READY, drv->reg_base + RMB_MBA_COMMAND);
+ }
+ /* Increment length counter */
+ drv->img_length += size;
+ writel_relaxed(drv->img_length, drv->reg_base + RMB_PMI_CODE_LENGTH);
+
+ return readl_relaxed(drv->reg_base + RMB_MBA_STATUS)
+ & STATUS_ERROR_MASK;
+}
+
+static int pil_mba_auth(struct pil_desc *pil)
+{
+ struct mba_data *drv = dev_get_drvdata(pil->dev);
+ int ret;
+ u32 status;
+
+ /* Wait for all segments to be authenticated or an error to occur */
+ ret = readl_poll_timeout(drv->reg_base + RMB_MBA_STATUS, status,
+ status == STATUS_AUTH_COMPLETE ||
+ status & STATUS_ERROR_MASK,
+ 50, AUTH_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ if (status & STATUS_ERROR_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int pil_mba_shutdown(struct pil_desc *pil)
+{
+ return 0;
+}
+
+static struct pil_reset_ops pil_mba_ops = {
+ .init_image = pil_mba_init_image,
+ .proxy_vote = pil_mba_make_proxy_votes,
+ .proxy_unvote = pil_mba_remove_proxy_votes,
+ .verify_blob = pil_mba_verify_blob,
+ .auth_and_reset = pil_mba_auth,
+ .shutdown = pil_mba_shutdown,
+};
+
+static int __devinit pil_mba_driver_probe(struct platform_device *pdev)
+{
+ struct mba_data *drv;
+ struct resource *res;
+ struct pil_desc *desc;
+ int ret;
+
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, drv);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+ drv->reg_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!drv->reg_base)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res) {
+ drv->metadata_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!drv->metadata_base)
+ return -ENOMEM;
+ drv->metadata_phys = res->start;
+ }
+
+ desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+
+ ret = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name",
+ &desc->name);
+ if (ret)
+ return ret;
+
+ of_property_read_string(pdev->dev.of_node, "qcom,depends-on",
+ &desc->depends_on);
+
+ drv->xo = devm_clk_get(&pdev->dev, "xo");
+ if (IS_ERR(drv->xo))
+ return PTR_ERR(drv->xo);
+
+ desc->dev = &pdev->dev;
+ desc->ops = &pil_mba_ops;
+ desc->owner = THIS_MODULE;
+ desc->proxy_timeout = PROXY_TIMEOUT_MS;
+
+ drv->pil = msm_pil_register(desc);
+ if (IS_ERR(drv->pil))
+ return PTR_ERR(drv->pil);
+
+ return 0;
+}
+
+static int __devexit pil_mba_driver_exit(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct of_device_id mba_match_table[] = {
+ { .compatible = "qcom,pil-mba" },
+ {}
+};
+
+struct platform_driver pil_mba_driver = {
+ .probe = pil_mba_driver_probe,
+ .remove = __devexit_p(pil_mba_driver_exit),
+ .driver = {
+ .name = "pil-mba",
+ .of_match_table = mba_match_table,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pil_mba_init(void)
+{
+ return platform_driver_register(&pil_mba_driver);
+}
+module_init(pil_mba_init);
+
+static void __exit pil_mba_exit(void)
+{
+ platform_driver_unregister(&pil_mba_driver);
+}
+module_exit(pil_mba_exit);
+
+MODULE_DESCRIPTION("Support for modem boot using the Modem Boot Authenticator");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/pil-q6v5-mss.c b/arch/arm/mach-msm/pil-q6v5-mss.c
new file mode 100644
index 0000000..e279f99
--- /dev/null
+++ b/arch/arm/mach-msm/pil-q6v5-mss.c
@@ -0,0 +1,291 @@
+/*
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/ioport.h>
+#include <linux/elf.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <mach/clk.h>
+
+#include "peripheral-loader.h"
+#include "pil-q6v5.h"
+
+/* Q6 Register Offsets */
+#define QDSP6SS_RST_EVB 0x010
+
+/* AXI Halting Registers */
+#define MSS_Q6_HALT_BASE 0x180
+#define MSS_MODEM_HALT_BASE 0x200
+#define MSS_NC_HALT_BASE 0x280
+
+/* RMB Status Register Values */
+#define STATUS_PBL_SUCCESS 0x1
+#define STATUS_XPU_UNLOCKED 0x1
+#define STATUS_XPU_UNLOCKED_SCRIBBLED 0x2
+
+/* PBL/MBA interface registers */
+#define RMB_MBA_IMAGE 0x00
+#define RMB_PBL_STATUS 0x04
+#define RMB_MBA_STATUS 0x0C
+
+#define PBL_MBA_WAIT_TIMEOUT_US 100000
+#define PROXY_TIMEOUT_MS 10000
+#define POLL_INTERVAL_US 50
+
+static int pil_mss_power_up(struct device *dev)
+{
+ int ret;
+ struct q6v5_data *drv = dev_get_drvdata(dev);
+
+ ret = regulator_enable(drv->vreg);
+ if (ret)
+ dev_err(dev, "Failed to enable regulator.\n");
+
+ return ret;
+}
+
+static int pil_mss_power_down(struct device *dev)
+{
+ struct q6v5_data *drv = dev_get_drvdata(dev);
+
+ return regulator_disable(drv->vreg);
+}
+
+static int wait_for_mba_ready(struct device *dev)
+{
+ struct q6v5_data *drv = dev_get_drvdata(dev);
+ int ret;
+ u32 status;
+
+ /* Wait for PBL completion. */
+ ret = readl_poll_timeout(drv->rmb_base + RMB_PBL_STATUS, status,
+ status != 0, POLL_INTERVAL_US, PBL_MBA_WAIT_TIMEOUT_US);
+ if (ret) {
+ dev_err(dev, "PBL boot timed out\n");
+ return ret;
+ }
+ if (status != STATUS_PBL_SUCCESS) {
+ dev_err(dev, "PBL returned unexpected status %d\n", status);
+ return -EINVAL;
+ }
+
+ /* Wait for MBA completion. */
+ ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status,
+ status != 0, POLL_INTERVAL_US, PBL_MBA_WAIT_TIMEOUT_US);
+ if (ret) {
+ dev_err(dev, "MBA boot timed out\n");
+ return ret;
+ }
+ if (status != STATUS_XPU_UNLOCKED &&
+ status != STATUS_XPU_UNLOCKED_SCRIBBLED) {
+ dev_err(dev, "MBA returned unexpected status %d\n", status);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int pil_mss_shutdown(struct pil_desc *pil)
+{
+ struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+
+ pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_Q6_HALT_BASE);
+ pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_MODEM_HALT_BASE);
+ pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_NC_HALT_BASE);
+
+ /*
+ * If the shutdown function is called before the reset function, clocks
+ * and power will not be enabled yet. Enable them here so that register
+ * writes performed during the shutdown succeed.
+ */
+ if (drv->is_booted == false) {
+ pil_mss_power_up(pil->dev);
+ pil_q6v5_enable_clks(pil);
+ }
+ pil_q6v5_shutdown(pil);
+
+ pil_q6v5_disable_clks(pil);
+ pil_mss_power_down(pil->dev);
+
+ writel_relaxed(1, drv->restart_reg);
+
+ drv->is_booted = false;
+
+ return 0;
+}
+
+static int pil_mss_reset(struct pil_desc *pil)
+{
+ struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ int ret;
+
+ writel_relaxed(0, drv->restart_reg);
+ mb();
+
+ /*
+ * Bring subsystem out of reset and enable required
+ * regulators and clocks.
+ */
+ ret = pil_mss_power_up(pil->dev);
+ if (ret)
+ goto err_power;
+
+ ret = pil_q6v5_enable_clks(pil);
+ if (ret)
+ goto err_clks;
+
+ /* Program Image Address */
+ if (drv->self_auth)
+ writel_relaxed(drv->start_addr, drv->rmb_base + RMB_MBA_IMAGE);
+ else
+ writel_relaxed((drv->start_addr >> 4) & 0x0FFFFFF0,
+ drv->reg_base + QDSP6SS_RST_EVB);
+
+ ret = pil_q6v5_reset(pil);
+ if (ret)
+ goto err_q6v5_reset;
+
+ /* Wait for MBA to start. Check for PBL and MBA errors while waiting. */
+ if (drv->self_auth) {
+ ret = wait_for_mba_ready(pil->dev);
+ if (ret)
+ goto err_auth;
+ }
+
+ drv->is_booted = true;
+
+ return 0;
+
+err_auth:
+ pil_q6v5_shutdown(pil);
+err_q6v5_reset:
+ pil_q6v5_disable_clks(pil);
+err_clks:
+ pil_mss_power_down(pil->dev);
+err_power:
+ return ret;
+}
+
+static struct pil_reset_ops pil_mss_ops = {
+ .init_image = pil_q6v5_init_image,
+ .proxy_vote = pil_q6v5_make_proxy_votes,
+ .proxy_unvote = pil_q6v5_remove_proxy_votes,
+ .auth_and_reset = pil_mss_reset,
+ .shutdown = pil_mss_shutdown,
+};
+
+static int __devinit pil_mss_driver_probe(struct platform_device *pdev)
+{
+ struct q6v5_data *drv;
+ struct pil_desc *desc;
+ struct resource *res;
+ int ret;
+
+ desc = pil_q6v5_init(pdev);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+ drv = platform_get_drvdata(pdev);
+ if (drv == NULL)
+ return -ENODEV;
+
+ desc->ops = &pil_mss_ops;
+ desc->owner = THIS_MODULE;
+ desc->proxy_timeout = PROXY_TIMEOUT_MS;
+
+ of_property_read_u32(pdev->dev.of_node, "qcom,pil-self-auth",
+ &drv->self_auth);
+ if (drv->self_auth) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ drv->rmb_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!drv->rmb_base)
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
+ drv->restart_reg = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!drv->restart_reg)
+ return -ENOMEM;
+
+ drv->vreg = devm_regulator_get(&pdev->dev, "vdd_mss");
+ if (IS_ERR(drv->vreg))
+ return PTR_ERR(drv->vreg);
+
+ ret = regulator_set_voltage(drv->vreg, 1150000, 1150000);
+ if (ret)
+ dev_err(&pdev->dev, "Failed to set regulator's voltage.\n");
+
+ 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->mem_clk = devm_clk_get(&pdev->dev, "mem_clk");
+ if (IS_ERR(drv->mem_clk))
+ return PTR_ERR(drv->mem_clk);
+
+ drv->pil = msm_pil_register(desc);
+ if (IS_ERR(drv->pil))
+ return PTR_ERR(drv->pil);
+
+ return 0;
+}
+
+static int __devexit pil_mss_driver_exit(struct platform_device *pdev)
+{
+ struct q6v5_data *drv = platform_get_drvdata(pdev);
+ msm_pil_unregister(drv->pil);
+ return 0;
+}
+
+static struct of_device_id mss_match_table[] = {
+ { .compatible = "qcom,pil-q6v5-mss" },
+ {}
+};
+
+static struct platform_driver pil_mss_driver = {
+ .probe = pil_mss_driver_probe,
+ .remove = __devexit_p(pil_mss_driver_exit),
+ .driver = {
+ .name = "pil-q6v5-mss",
+ .of_match_table = mss_match_table,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pil_mss_init(void)
+{
+ return platform_driver_register(&pil_mss_driver);
+}
+module_init(pil_mss_init);
+
+static void __exit pil_mss_exit(void)
+{
+ platform_driver_unregister(&pil_mss_driver);
+}
+module_exit(pil_mss_exit);
+
+MODULE_DESCRIPTION("Support for booting modem subsystems with QDSP6v5 Hexagon processors");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/pil-q6v5.c b/arch/arm/mach-msm/pil-q6v5.c
index 6a96990..a362a7e3 100644
--- a/arch/arm/mach-msm/pil-q6v5.c
+++ b/arch/arm/mach-msm/pil-q6v5.c
@@ -121,9 +121,16 @@
ret = clk_prepare_enable(drv->bus_clk);
if (ret)
goto err_bus_clk;
+ if (drv->mem_clk) {
+ ret = clk_prepare_enable(drv->mem_clk);
+ if (ret)
+ goto err_mem_clk;
+ }
return 0;
+err_mem_clk:
+ clk_disable_unprepare(drv->bus_clk);
err_bus_clk:
clk_disable_unprepare(drv->core_clk);
err_core_clk:
@@ -139,6 +146,7 @@
clk_disable_unprepare(drv->bus_clk);
clk_disable_unprepare(drv->core_clk);
+ clk_disable_unprepare(drv->mem_clk);
clk_reset(drv->core_clk, CLK_RESET_ASSERT);
}
EXPORT_SYMBOL(pil_q6v5_disable_clks);
diff --git a/arch/arm/mach-msm/pil-q6v5.h b/arch/arm/mach-msm/pil-q6v5.h
index a9a8d07..e0d7a20 100644
--- a/arch/arm/mach-msm/pil-q6v5.h
+++ b/arch/arm/mach-msm/pil-q6v5.h
@@ -24,8 +24,10 @@
struct clk *xo;
struct clk *bus_clk;
struct clk *core_clk;
+ struct clk *mem_clk;
void __iomem *axi_halt_base;
void __iomem *rmb_base;
+ void __iomem *restart_reg;
unsigned long start_addr;
struct regulator *vreg;
bool is_booted;
diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index 070e2c5..6f68c8b 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.c
@@ -44,7 +44,6 @@
#include <mach/cpuidle.h>
#include "idle.h"
#include "pm.h"
-#include "rpm_resources.h"
#include "scm-boot.h"
#include "spm.h"
#include "timer.h"
@@ -109,6 +108,7 @@
"standalone_power_collapse",
};
+static struct msm_pm_sleep_ops pm_sleep_ops;
/*
* Write out the attribute.
*/
@@ -396,7 +396,7 @@
*
*****************************************************************************/
-static struct msm_rpmrs_limits *msm_pm_idle_rs_limits;
+static void *msm_pm_idle_rs_limits;
static bool msm_pm_use_qtimer;
static void msm_pm_swfi(void)
@@ -644,7 +644,8 @@
struct cpuidle_state *state = &dev->states[i];
enum msm_pm_sleep_mode mode;
bool allow;
- struct msm_rpmrs_limits *rs_limits = NULL;
+ void *rs_limits = NULL;
+ uint32_t power;
int idx;
mode = (enum msm_pm_sleep_mode) state->driver_data;
@@ -673,12 +674,6 @@
case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
if (!allow)
break;
-
- if (!dev->cpu &&
- msm_rpm_local_request_is_outstanding()) {
- allow = false;
- break;
- }
/* fall through */
case MSM_PM_SLEEP_MODE_RETENTION:
@@ -690,8 +685,10 @@
if (!allow)
break;
- rs_limits = msm_rpmrs_lowest_limits(true,
- mode, latency_us, sleep_us);
+ if (pm_sleep_ops.lowest_limits)
+ rs_limits = pm_sleep_ops.lowest_limits(true,
+ mode, latency_us, sleep_us,
+ &power);
if (MSM_PM_DEBUG_IDLE & msm_pm_debug_mask)
pr_info("CPU%u: %s: %s, latency %uus, "
@@ -699,17 +696,6 @@
dev->cpu, __func__, state->desc,
latency_us, sleep_us, rs_limits);
- if ((MSM_PM_DEBUG_IDLE_LIMITS & msm_pm_debug_mask) &&
- rs_limits)
- pr_info("CPU%u: %s: limit %p: "
- "pxo %d, l2_cache %d, "
- "vdd_mem %d, vdd_dig %d\n",
- dev->cpu, __func__, rs_limits,
- rs_limits->pxo,
- rs_limits->l2_cache,
- rs_limits->vdd_mem,
- rs_limits->vdd_dig);
-
if (!rs_limits)
allow = false;
break;
@@ -727,7 +713,7 @@
state->flags &= ~CPUIDLE_FLAG_IGNORE;
state->target_residency = 0;
state->exit_latency = 0;
- state->power_usage = rs_limits->power[dev->cpu];
+ state->power_usage = power;
if (MSM_PM_SLEEP_MODE_POWER_COLLAPSE == mode)
msm_pm_idle_rs_limits = rs_limits;
@@ -770,7 +756,7 @@
int64_t timer_expiration = 0;
bool timer_halted = false;
uint32_t sleep_delay;
- int ret;
+ int ret = -ENODEV;
int notify_rpm =
(sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE);
int collapsed;
@@ -785,14 +771,17 @@
if (MSM_PM_DEBUG_IDLE_CLK & msm_pm_debug_mask)
clock_debug_print_enabled();
- ret = msm_rpmrs_enter_sleep(
- sleep_delay, msm_pm_idle_rs_limits, true, notify_rpm);
+ if (pm_sleep_ops.enter_sleep)
+ ret = pm_sleep_ops.enter_sleep(sleep_delay,
+ msm_pm_idle_rs_limits,
+ true, notify_rpm);
if (!ret) {
collapsed = msm_pm_power_collapse(true);
timer_halted = true;
- msm_rpmrs_exit_sleep(msm_pm_idle_rs_limits, true,
- notify_rpm, collapsed);
+ if (pm_sleep_ops.exit_sleep)
+ pm_sleep_ops.exit_sleep(msm_pm_idle_rs_limits,
+ true, notify_rpm, collapsed);
}
msm_pm_timer_exit_idle(timer_halted);
exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE;
@@ -920,8 +909,9 @@
}
if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]) {
- struct msm_rpmrs_limits *rs_limits;
- int ret;
+ void *rs_limits = NULL;
+ int ret = -ENODEV;
+ uint32_t power;
if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
pr_info("%s: power collapse\n", __func__);
@@ -936,28 +926,22 @@
msm_pm_sleep_time_override = 0;
}
#endif /* CONFIG_MSM_SLEEP_TIME_OVERRIDE */
-
- if (MSM_PM_DEBUG_SUSPEND_LIMITS & msm_pm_debug_mask)
- msm_rpmrs_show_resources();
-
- rs_limits = msm_rpmrs_lowest_limits(false,
- MSM_PM_SLEEP_MODE_POWER_COLLAPSE, -1, -1);
-
- if ((MSM_PM_DEBUG_SUSPEND_LIMITS & msm_pm_debug_mask) &&
- rs_limits)
- pr_info("%s: limit %p: pxo %d, l2_cache %d, "
- "vdd_mem %d, vdd_dig %d\n",
- __func__, rs_limits,
- rs_limits->pxo, rs_limits->l2_cache,
- rs_limits->vdd_mem, rs_limits->vdd_dig);
+ if (pm_sleep_ops.lowest_limits)
+ rs_limits = pm_sleep_ops.lowest_limits(false,
+ MSM_PM_SLEEP_MODE_POWER_COLLAPSE, -1,
+ -1, &power);
if (rs_limits) {
- ret = msm_rpmrs_enter_sleep(
- msm_pm_max_sleep_time, rs_limits, false, true);
+ if (pm_sleep_ops.enter_sleep)
+ ret = pm_sleep_ops.enter_sleep(
+ msm_pm_max_sleep_time,
+ rs_limits, false, true);
if (!ret) {
int collapsed = msm_pm_power_collapse(false);
- msm_rpmrs_exit_sleep(rs_limits, false, true,
- collapsed);
+ if (pm_sleep_ops.exit_sleep) {
+ pm_sleep_ops.exit_sleep(rs_limits,
+ false, true, collapsed);
+ }
}
} else {
pr_err("%s: cannot find the lowest power limit\n",
@@ -1001,6 +985,12 @@
msm_pm_slp_sts = data;
}
+void msm_pm_set_sleep_ops(struct msm_pm_sleep_ops *ops)
+{
+ if (ops)
+ pm_sleep_ops = *ops;
+}
+
static int __init msm_pm_init(void)
{
pgd_t *pc_pgd;
diff --git a/arch/arm/mach-msm/pm.h b/arch/arm/mach-msm/pm.h
index ce0d747..0d5101d 100644
--- a/arch/arm/mach-msm/pm.h
+++ b/arch/arm/mach-msm/pm.h
@@ -76,6 +76,16 @@
uint32_t mask;
};
+struct msm_pm_sleep_ops {
+ void *(*lowest_limits)(bool from_idle,
+ enum msm_pm_sleep_mode sleep_mode, uint32_t latency_us,
+ uint32_t sleep_us, uint32_t *power);
+ int (*enter_sleep)(uint32_t sclk_count, void *limits,
+ bool from_idle, bool notify_rpm);
+ void (*exit_sleep)(void *limits, bool from_idle,
+ bool notify_rpm, bool collapsed);
+};
+
void msm_pm_set_platform_data(struct msm_pm_platform_data *data, int count);
void msm_pm_set_irq_extns(struct msm_pm_irq_calls *irq_calls);
int msm_pm_idle_prepare(struct cpuidle_device *dev);
@@ -90,12 +100,13 @@
void msm_pm_set_rpm_wakeup_irq(unsigned int irq);
int msm_pm_wait_cpu_shutdown(unsigned int cpu);
bool msm_pm_verify_cpu_pc(unsigned int cpu);
+void msm_pm_set_sleep_ops(struct msm_pm_sleep_ops *ops);
#else
static inline void msm_pm_set_rpm_wakeup_irq(unsigned int irq) {}
static inline int msm_pm_wait_cpu_shutdown(unsigned int cpu) { return 0; }
static inline bool msm_pm_verify_cpu_pc(unsigned int cpu) { return true; }
+static inline void msm_pm_set_sleep_ops(struct msm_pm_sleep_ops *ops) {}
#endif
-
#ifdef CONFIG_HOTPLUG_CPU
int msm_platform_secondary_init(unsigned int cpu);
#else
diff --git a/arch/arm/mach-msm/qdsp5/adsp.c b/arch/arm/mach-msm/qdsp5/adsp.c
index 6f5ccbf..b485058 100644
--- a/arch/arm/mach-msm/qdsp5/adsp.c
+++ b/arch/arm/mach-msm/qdsp5/adsp.c
@@ -865,7 +865,8 @@
unsigned msg_id;
unsigned msg_length;
#ifdef CONFIG_DEBUG_FS
- uint16_t *ptr;
+ uint16_t *ptr16;
+ uint32_t *ptr32;
int ii;
#endif /* CONFIG_DEBUG_FS */
void (*func)(void *, size_t);
@@ -909,12 +910,20 @@
return 0;
}
#ifdef CONFIG_DEBUG_FS
- if (rdump > 0) {
- ptr = read_event_addr;
+ if (rdump > 0 &&
+ (dsp_addr >= (void *)(MSM_AD5_BASE + QDSP_RAMC_OFFSET))) {
+ ptr32 = read_event_addr;
+ pr_info("D->A\n");
+ pr_info("m_id = %x id = %x\n", module->id, msg_id);
+ for (ii = 0; ii < msg_length/4; ii++)
+ pr_info("%x ", ptr32[ii]);
+ pr_info("\n");
+ } else if (rdump > 0) {
+ ptr16 = read_event_addr;
pr_info("D->A\n");
pr_info("m_id = %x id = %x\n", module->id, msg_id);
for (ii = 0; ii < msg_length/2; ii++)
- pr_info("%x ", ptr[ii]);
+ pr_info("%x ", ptr16[ii]);
pr_info("\n");
}
#endif /* CONFIG_DEBUG_FS */
diff --git a/arch/arm/mach-msm/qdsp6v2/Makefile b/arch/arm/mach-msm/qdsp6v2/Makefile
index 083a9f1..cee8f04 100644
--- a/arch/arm/mach-msm/qdsp6v2/Makefile
+++ b/arch/arm/mach-msm/qdsp6v2/Makefile
@@ -1,5 +1,4 @@
obj-y += rtac.o
-
ifdef CONFIG_ARCH_MSM8X60
obj-y += audio_dev_ctl.o
obj-y += board-msm8x60-audio.o
@@ -17,6 +16,7 @@
ifndef CONFIG_ARCH_MSM9615
obj-y += aac_in.o qcelp_in.o evrc_in.o amrnb_in.o audio_utils.o
obj-y += audio_wma.o audio_wmapro.o audio_aac.o audio_multi_aac.o audio_utils_aio.o
+obj-$(CONFIG_MSM_QDSP6_CODECS) += q6audio_v1.o q6audio_v1_aio.o
obj-$(CONFIG_MSM_ULTRASOUND) += ultrasound/
obj-y += audio_mp3.o audio_amrnb.o audio_amrwb.o audio_evrc.o audio_qcelp.o amrwb_in.o
endif
diff --git a/arch/arm/mach-msm/qdsp6v2/aac_in.c b/arch/arm/mach-msm/qdsp6v2/aac_in.c
index 41d3ff3..6e79a75 100644
--- a/arch/arm/mach-msm/qdsp6v2/aac_in.c
+++ b/arch/arm/mach-msm/qdsp6v2/aac_in.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2010-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
@@ -22,8 +22,6 @@
#include <linux/msm_audio_aac.h>
#include <asm/atomic.h>
#include <asm/ioctls.h>
-#include <sound/q6asm.h>
-#include <sound/apr_audio.h>
#include "audio_utils.h"
@@ -35,43 +33,6 @@
#define AAC_FORMAT_ADTS 65535
-void q6asm_aac_in_cb(uint32_t opcode, uint32_t token,
- uint32_t *payload, void *priv)
-{
- struct q6audio_in * audio = (struct q6audio_in *)priv;
- unsigned long flags;
-
- pr_debug("%s:session id %d: opcode[0x%x]\n", __func__,
- audio->ac->session, opcode);
-
- spin_lock_irqsave(&audio->dsp_lock, flags);
- switch (opcode) {
- case ASM_DATA_EVENT_READ_DONE:
- audio_in_get_dsp_frames(audio, token, payload);
- break;
- case ASM_DATA_EVENT_WRITE_DONE:
- atomic_inc(&audio->in_count);
- wake_up(&audio->write_wait);
- break;
- case ASM_DATA_CMDRSP_EOS:
- audio->eos_rsp = 1;
- wake_up(&audio->read_wait);
- break;
- case ASM_STREAM_CMDRSP_GET_ENCDEC_PARAM:
- break;
- case ASM_STREAM_CMDRSP_GET_PP_PARAMS:
- break;
- case ASM_SESSION_EVENT_TX_OVERFLOW:
- pr_err("%s:session id %d: ASM_SESSION_EVENT_TX_OVERFLOW\n",
- __func__, audio->ac->session);
- break;
- default:
- pr_debug("%s:session id %d: Ignore opcode[0x%x]\n", __func__,
- audio->ac->session, opcode);
- break;
- }
- spin_unlock_irqrestore(&audio->dsp_lock, flags);
-}
/* ------------------- device --------------------- */
static long aac_in_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
@@ -121,8 +82,8 @@
aac_mode,
enc_cfg->stream_format);
if (rc < 0) {
- pr_err("%s:session id %d: cmd media format block\
- failed\n", __func__, audio->ac->session);
+ pr_err("%s:session id %d: cmd media format block"
+ "failed\n", __func__, audio->ac->session);
break;
}
if (audio->feedback == NON_TUNNEL_MODE) {
@@ -130,8 +91,8 @@
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count);
if (rc < 0) {
- pr_err("%s:session id %d: media format block\
- failed\n", __func__, audio->ac->session);
+ pr_err("%s:session id %d: media format block"
+ "failed\n", __func__, audio->ac->session);
break;
}
}
@@ -140,8 +101,8 @@
audio->enabled = 1;
} else {
audio->enabled = 0;
- pr_err("%s:session id %d: Audio Start procedure\
- failed rc=%d\n", __func__, audio->ac->session, rc);
+ pr_err("%s:session id %d: Audio Start procedure"
+ "failed rc=%d\n", __func__, audio->ac->session, rc);
break;
}
while (cnt++ < audio->str_cfg.buffer_count)
@@ -155,8 +116,8 @@
audio->ac->session);
rc = audio_in_disable(audio);
if (rc < 0) {
- pr_err("%s:session id %d: Audio Stop procedure failed\
- rc=%d\n", __func__, audio->ac->session, rc);
+ pr_err("%s:session id %d: Audio Stop procedure failed"
+ "rc=%d\n", __func__, audio->ac->session, rc);
break;
}
break;
@@ -174,8 +135,8 @@
/* ADTS(-1) to ADTS(0x00), RAW(0x00) to RAW(0x03) */
cfg.stream_format = ((enc_cfg->stream_format == \
0x00) ? AUDIO_AAC_FORMAT_ADTS : AUDIO_AAC_FORMAT_RAW);
- pr_debug("%s:session id %d: Get-aac-cfg: format=%d sr=%d\
- bitrate=%d\n", __func__, audio->ac->session,
+ pr_debug("%s:session id %d: Get-aac-cfg: format=%d sr=%d"
+ "bitrate=%d\n", __func__, audio->ac->session,
cfg.stream_format, cfg.sample_rate, cfg.bit_rate);
if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
rc = -EFAULT;
@@ -229,8 +190,8 @@
enc_cfg->stream_format =
((cfg.stream_format == AUDIO_AAC_FORMAT_RAW) ? \
0x03 : 0x00);
- pr_debug("%s:session id %d: Set-aac-cfg:SR= 0x%x ch=0x%x\
- bitrate=0x%x, format(adts/raw) = %d\n",
+ pr_debug("%s:session id %d: Set-aac-cfg:SR= 0x%x ch=0x%x"
+ "bitrate=0x%x, format(adts/raw) = %d\n",
__func__, audio->ac->session, enc_cfg->sample_rate,
enc_cfg->channels, enc_cfg->bit_rate,
enc_cfg->stream_format);
@@ -289,16 +250,16 @@
audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
if (audio == NULL) {
- pr_err("%s: Could not allocate memory for aac\
- driver\n", __func__);
+ pr_err("%s: Could not allocate memory for aac"
+ "driver\n", __func__);
return -ENOMEM;
}
/* Allocate memory for encoder config param */
audio->enc_cfg = kzalloc(sizeof(struct msm_audio_aac_enc_config),
GFP_KERNEL);
if (audio->enc_cfg == NULL) {
- pr_err("%s:session id %d: Could not allocate memory for aac\
- config param\n", __func__, audio->ac->session);
+ pr_err("%s:session id %d: Could not allocate memory for aac"
+ "config param\n", __func__, audio->ac->session);
kfree(audio);
return -ENOMEM;
}
@@ -307,8 +268,8 @@
audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config),
GFP_KERNEL);
if (audio->codec_cfg == NULL) {
- pr_err("%s:session id %d: Could not allocate memory for aac\
- config\n", __func__, audio->ac->session);
+ pr_err("%s:session id %d: Could not allocate memory for aac"
+ "config\n", __func__, audio->ac->session);
kfree(audio->enc_cfg);
kfree(audio);
return -ENOMEM;
@@ -343,12 +304,12 @@
aac_config->sbr_ps_on_flag = 0;
aac_config->channel_configuration = 1;
- audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_aac_in_cb,
+ audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
(void *)audio);
if (!audio->ac) {
- pr_err("%s: Could not allocate memory for\
- audio client\n", __func__);
+ pr_err("%s: Could not allocate memory for"
+ "audio client\n", __func__);
kfree(audio->enc_cfg);
kfree(audio->codec_cfg);
kfree(audio);
@@ -386,8 +347,8 @@
/* register for tx overflow (valid for tunnel mode only) */
rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
if (rc < 0) {
- pr_err("%s:session id %d: TX Overflow registration\
- failed rc=%d\n", __func__,
+ pr_err("%s:session id %d: TX Overflow registration"
+ "failed rc=%d\n", __func__,
audio->ac->session, rc);
rc = -ENODEV;
goto fail;
diff --git a/arch/arm/mach-msm/qdsp6v2/amrnb_in.c b/arch/arm/mach-msm/qdsp6v2/amrnb_in.c
index 6a70428..63a0774 100644
--- a/arch/arm/mach-msm/qdsp6v2/amrnb_in.c
+++ b/arch/arm/mach-msm/qdsp6v2/amrnb_in.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-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
@@ -22,8 +22,6 @@
#include <linux/msm_audio_amrnb.h>
#include <asm/atomic.h>
#include <asm/ioctls.h>
-#include <sound/q6asm.h>
-#include <sound/apr_audio.h>
#include "audio_utils.h"
/* Buffer with meta*/
@@ -32,44 +30,6 @@
/* Maximum 10 frames in buffer with meta */
#define FRAME_SIZE (1 + ((32+sizeof(struct meta_out_dsp)) * 10))
-void q6asm_amrnb_in_cb(uint32_t opcode, uint32_t token,
- uint32_t *payload, void *priv)
-{
- struct q6audio_in * audio = (struct q6audio_in *)priv;
- unsigned long flags;
-
- pr_debug("%s:session id %d: opcode - %d\n", __func__,
- audio->ac->session, opcode);
-
- spin_lock_irqsave(&audio->dsp_lock, flags);
- switch (opcode) {
- case ASM_DATA_EVENT_READ_DONE:
- audio_in_get_dsp_frames(audio, token, payload);
- break;
- case ASM_DATA_EVENT_WRITE_DONE:
- atomic_inc(&audio->in_count);
- wake_up(&audio->write_wait);
- break;
- case ASM_DATA_CMDRSP_EOS:
- audio->eos_rsp = 1;
- wake_up(&audio->read_wait);
- break;
- case ASM_STREAM_CMDRSP_GET_ENCDEC_PARAM:
- break;
- case ASM_STREAM_CMDRSP_GET_PP_PARAMS:
- break;
- case ASM_SESSION_EVENT_TX_OVERFLOW:
- pr_err("%s:session id %d: ASM_SESSION_EVENT_TX_OVERFLOW\n",
- __func__, audio->ac->session);
- break;
- default:
- pr_err("%s:session id %d: Ignore opcode[0x%x]\n", __func__,
- audio->ac->session, opcode);
- break;
- }
- spin_unlock_irqrestore(&audio->dsp_lock, flags);
-}
-
/* ------------------- device --------------------- */
static long amrnb_in_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
@@ -102,8 +62,8 @@
enc_cfg->dtx_enable);
if (rc < 0) {
- pr_err("%s:session id %d: cmd amrnb media format block\
- failed\n", __func__, audio->ac->session);
+ pr_err("%s:session id %d: cmd amrnb media format block"
+ "failed\n", __func__, audio->ac->session);
break;
}
if (audio->feedback == NON_TUNNEL_MODE) {
@@ -112,8 +72,8 @@
audio->pcm_cfg.channel_count);
if (rc < 0) {
- pr_err("%s:session id %d: media format block\
- failed\n", __func__, audio->ac->session);
+ pr_err("%s:session id %d: media format block"
+ "failed\n", __func__, audio->ac->session);
break;
}
}
@@ -125,8 +85,8 @@
audio->enabled = 1;
} else {
audio->enabled = 0;
- pr_err("%s:session id %d: Audio Start procedure failed\
- rc=%d\n", __func__,
+ pr_err("%s:session id %d: Audio Start procedure failed"
+ "rc=%d\n", __func__,
audio->ac->session, rc);
break;
}
@@ -141,8 +101,8 @@
pr_debug("%s:AUDIO_STOP\n", __func__);
rc = audio_in_disable(audio);
if (rc < 0) {
- pr_err("%s:session id %d: Audio Stop procedure failed\
- rc=%d\n", __func__,
+ pr_err("%s:session id %d: Audio Stop procedure failed"
+ "rc=%d\n", __func__,
audio->ac->session, rc);
break;
}
@@ -196,16 +156,16 @@
audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
if (audio == NULL) {
- pr_err("%s Could not allocate memory for amrnb\
- driver\n", __func__);
+ pr_err("%s Could not allocate memory for amrnb"
+ "driver\n", __func__);
return -ENOMEM;
}
/* Allocate memory for encoder config param */
audio->enc_cfg = kzalloc(sizeof(struct msm_audio_amrnb_enc_config_v2),
GFP_KERNEL);
if (audio->enc_cfg == NULL) {
- pr_err("%s:session id %d: Could not allocate memory for aac\
- config param\n", __func__, audio->ac->session);
+ pr_err("%s:session id %d: Could not allocate memory for aac"
+ "config param\n", __func__, audio->ac->session);
kfree(audio);
return -ENOMEM;
}
@@ -234,12 +194,12 @@
audio->buf_cfg.meta_info_enable = 0x01;
audio->buf_cfg.frames_per_buf = 0x01;
- audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_amrnb_in_cb,
+ audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
(void *)audio);
if (!audio->ac) {
- pr_err("%s: Could not allocate memory for audio\
- client\n", __func__);
+ pr_err("%s: Could not allocate memory for audio"
+ "client\n", __func__);
kfree(audio->enc_cfg);
kfree(audio);
return -ENOMEM;
@@ -272,8 +232,8 @@
/* register for tx overflow (valid for tunnel mode only) */
rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
if (rc < 0) {
- pr_err("%s:session id %d: TX Overflow registration\
- failed rc=%d\n", __func__, audio->ac->session,
+ pr_err("%s:session id %d: TX Overflow registration"
+ "failed rc=%d\n", __func__, audio->ac->session,
rc);
rc = -ENODEV;
goto fail;
diff --git a/arch/arm/mach-msm/qdsp6v2/amrwb_in.c b/arch/arm/mach-msm/qdsp6v2/amrwb_in.c
index 5df976d..d0462e0 100644
--- a/arch/arm/mach-msm/qdsp6v2/amrwb_in.c
+++ b/arch/arm/mach-msm/qdsp6v2/amrwb_in.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,8 +19,6 @@
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
-#include <sound/apr_audio.h>
-#include <sound/q6asm.h>
#include <asm/atomic.h>
#include <asm/ioctls.h>
#include "audio_utils.h"
@@ -31,44 +29,6 @@
/* Maximum 10 frames in buffer with meta */
#define FRAME_SIZE (1 + ((61+sizeof(struct meta_out_dsp)) * 10))
-void q6asm_amrwb_in_cb(uint32_t opcode, uint32_t token,
- uint32_t *payload, void *priv)
-{
- struct q6audio_in * audio = (struct q6audio_in *)priv;
- unsigned long flags;
-
- pr_debug("%s:session id %d: opcode - %d\n", __func__,
- audio->ac->session, opcode);
-
- spin_lock_irqsave(&audio->dsp_lock, flags);
- switch (opcode) {
- case ASM_DATA_EVENT_READ_DONE:
- audio_in_get_dsp_frames(audio, token, payload);
- break;
- case ASM_DATA_EVENT_WRITE_DONE:
- atomic_inc(&audio->in_count);
- wake_up(&audio->write_wait);
- break;
- case ASM_DATA_CMDRSP_EOS:
- audio->eos_rsp = 1;
- wake_up(&audio->read_wait);
- break;
- case ASM_STREAM_CMDRSP_GET_ENCDEC_PARAM:
- break;
- case ASM_STREAM_CMDRSP_GET_PP_PARAMS:
- break;
- case ASM_SESSION_EVENT_TX_OVERFLOW:
- pr_err("%s:session id %d: ASM_SESSION_EVENT_TX_OVERFLOW\n",
- __func__, audio->ac->session);
- break;
- default:
- pr_err("%s:session id %d: Ignore opcode[0x%x]\n", __func__,
- audio->ac->session, opcode);
- break;
- }
- spin_unlock_irqrestore(&audio->dsp_lock, flags);
-}
-
/* ------------------- device --------------------- */
static long amrwb_in_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
@@ -231,7 +191,7 @@
audio->buf_cfg.meta_info_enable = 0x01;
audio->buf_cfg.frames_per_buf = 0x01;
- audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_amrwb_in_cb,
+ audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
(void *)audio);
if (!audio->ac) {
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_aac.c b/arch/arm/mach-msm/qdsp6v2/audio_aac.c
index 88189f6..485234f 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_aac.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_aac.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -21,28 +21,6 @@
#define AUDIO_AAC_DUAL_MONO_INVALID -1
#define PCM_BUFSZ_MIN_AAC ((8*1024) + sizeof(struct dec_meta_out))
-static void q6_audio_aac_cb(uint32_t opcode, uint32_t token,
- uint32_t *payload, void *priv)
-{
- struct q6audio_aio *audio = (struct q6audio_aio *)priv;
-
- pr_debug("%s:opcode = %x token = 0x%x\n", __func__, opcode, token);
- switch (opcode) {
- case ASM_DATA_EVENT_WRITE_DONE:
- case ASM_DATA_EVENT_READ_DONE:
- case ASM_DATA_CMDRSP_EOS:
- case ASM_DATA_CMD_MEDIA_FORMAT_UPDATE:
- case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
- case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
- case ASM_DATA_EVENT_ENC_SR_CM_NOTIFY:
- audio_aio_cb(opcode, token, payload, audio);
- break;
- default:
- pr_debug("%s:Unhandled event = 0x%8x\n", __func__, opcode);
- break;
- }
-}
-
#ifdef CONFIG_DEBUG_FS
static const struct file_operations audio_aac_debug_fops = {
.read = audio_aio_debug_read,
@@ -222,8 +200,8 @@
audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config),
GFP_KERNEL);
if (audio->codec_cfg == NULL) {
- pr_err("%s:Could not allocate memory for aac\
- config\n", __func__);
+ pr_err("%s:Could not allocate memory for aac"
+ "config\n", __func__);
kfree(audio);
return -ENOMEM;
}
@@ -235,7 +213,7 @@
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN_AAC;
aac_config->dual_mono_mode = AUDIO_AAC_DUAL_MONO_INVALID;
- audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_aac_cb,
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_amrnb.c b/arch/arm/mach-msm/qdsp6v2/audio_amrnb.c
index 6768f7a..f4316d0 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_amrnb.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_amrnb.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -16,24 +16,6 @@
*/
#include "audio_utils_aio.h"
-static void q6_audio_amrnb_cb(uint32_t opcode, uint32_t token,
- uint32_t *payload, void *priv)
-{
- struct q6audio_aio *audio = (struct q6audio_aio *)priv;
-
- pr_debug("%s:opcde = %d token = 0x%x\n", __func__, opcode, token);
- switch (opcode) {
- case ASM_DATA_EVENT_WRITE_DONE:
- case ASM_DATA_EVENT_READ_DONE:
- case ASM_DATA_CMDRSP_EOS:
- audio_aio_cb(opcode, token, payload, audio);
- break;
- default:
- pr_debug("%s:Unhandled event = 0x%8x\n", __func__, opcode);
- break;
- }
-}
-
#ifdef CONFIG_DEBUG_FS
static const struct file_operations audio_amrnb_debug_fops = {
.read = audio_aio_debug_read,
@@ -101,7 +83,7 @@
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
- audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_amrnb_cb,
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_amrwb.c b/arch/arm/mach-msm/qdsp6v2/audio_amrwb.c
index f95e191..28c1732 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_amrwb.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_amrwb.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -17,24 +17,6 @@
#include "audio_utils_aio.h"
-static void q6_audio_amrwb_cb(uint32_t opcode, uint32_t token,
- uint32_t *payload, void *priv)
-{
- struct q6audio_aio *audio = (struct q6audio_aio *)priv;
-
- pr_debug("%s:opcde = %d token = 0x%x\n", __func__, opcode, token);
- switch (opcode) {
- case ASM_DATA_EVENT_WRITE_DONE:
- case ASM_DATA_EVENT_READ_DONE:
- case ASM_DATA_CMDRSP_EOS:
- audio_aio_cb(opcode, token, payload, audio);
- break;
- default:
- pr_debug("%s:Unhandled event = 0x%8x\n", __func__, opcode);
- break;
- }
-}
-
#ifdef CONFIG_DEBUG_FS
static const struct file_operations audio_amrwb_debug_fops = {
.read = audio_aio_debug_read,
@@ -103,7 +85,7 @@
}
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
- audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_amrwb_cb,
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_evrc.c b/arch/arm/mach-msm/qdsp6v2/audio_evrc.c
index 12c815d..ec5162d 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_evrc.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_evrc.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -17,23 +17,7 @@
#include "audio_utils_aio.h"
-static void q6_audio_evrc_cb(uint32_t opcode, uint32_t token,
- uint32_t *payload, void *priv)
-{
- struct q6audio_aio *audio = (struct q6audio_aio *)priv;
- pr_debug("%s:opcde = %d token = 0x%x\n", __func__, opcode, token);
- switch (opcode) {
- case ASM_DATA_EVENT_WRITE_DONE:
- case ASM_DATA_EVENT_READ_DONE:
- case ASM_DATA_CMDRSP_EOS:
- audio_aio_cb(opcode, token, payload, audio);
- break;
- default:
- pr_debug("%s:Unhandled event = 0x%8x\n", __func__, opcode);
- break;
- }
-}
#ifdef CONFIG_DEBUG_FS
static const struct file_operations audio_evrc_debug_fops = {
@@ -107,7 +91,7 @@
*/
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
- audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_evrc_cb,
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_mp3.c b/arch/arm/mach-msm/qdsp6v2/audio_mp3.c
index 22552c6..93a8739 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_mp3.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_mp3.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -17,24 +17,6 @@
#include "audio_utils_aio.h"
-static void q6_audio_mp3_cb(uint32_t opcode, uint32_t token,
- uint32_t *payload, void *priv)
-{
- struct q6audio_aio *audio = (struct q6audio_aio *)priv;
-
- pr_debug("%s:opcde = %d token = 0x%x\n", __func__, opcode, token);
- switch (opcode) {
- case ASM_DATA_EVENT_WRITE_DONE:
- case ASM_DATA_EVENT_READ_DONE:
- case ASM_DATA_CMDRSP_EOS:
- audio_aio_cb(opcode, token, payload, audio);
- break;
- default:
- pr_debug("%s:Unhandled event = 0x%8x\n", __func__, opcode);
- break;
- }
-}
-
#ifdef CONFIG_DEBUG_FS
static const struct file_operations audio_mp3_debug_fops = {
.read = audio_aio_debug_read,
@@ -103,7 +85,7 @@
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
- audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_mp3_cb,
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c b/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c
index 8a0ba3e..9253056 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c
@@ -25,28 +25,6 @@
/* Default number of pre-allocated event packets */
#define PCM_BUFSZ_MIN_AACM ((8*1024) + sizeof(struct dec_meta_out))
-static void q6_audio_aac_cb(uint32_t opcode, uint32_t token,
- uint32_t *payload, void *priv)
-{
- struct q6audio_aio *audio = (struct q6audio_aio *)priv;
-
- pr_debug("%s:opcode = %x token = 0x%x\n", __func__, opcode, token);
- switch (opcode) {
- case ASM_DATA_EVENT_WRITE_DONE:
- case ASM_DATA_EVENT_READ_DONE:
- case ASM_DATA_CMDRSP_EOS:
- case ASM_DATA_CMD_MEDIA_FORMAT_UPDATE:
- case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
- case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
- case ASM_DATA_EVENT_ENC_SR_CM_NOTIFY:
- audio_aio_cb(opcode, token, payload, audio);
- break;
- default:
- pr_debug("%s:Unhandled event = 0x%8x\n", __func__, opcode);
- break;
- }
-}
-
#ifdef CONFIG_DEBUG_FS
static const struct file_operations audio_aac_debug_fops = {
.read = audio_aio_debug_read,
@@ -234,8 +212,8 @@
audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config),
GFP_KERNEL);
if (audio->codec_cfg == NULL) {
- pr_err("%s: Could not allocate memory for aac\
- config\n", __func__);
+ pr_err("%s: Could not allocate memory for aac"
+ "config\n", __func__);
kfree(audio);
return -ENOMEM;
}
@@ -245,7 +223,7 @@
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN_AACM;
aac_config->dual_mono_mode = AUDIO_AAC_DUAL_MONO_INVALID;
- audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_aac_cb,
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_qcelp.c b/arch/arm/mach-msm/qdsp6v2/audio_qcelp.c
index 7b72c97..37f6e6b 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_qcelp.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_qcelp.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -19,23 +19,6 @@
#define FRAME_SIZE_DEC_QCELP ((32) + sizeof(struct dec_meta_in))
-static void q6_audio_qcelp_cb(uint32_t opcode, uint32_t token,
- uint32_t *payload, void *priv)
-{
- struct q6audio_aio *audio = (struct q6audio_aio *)priv;
-
- switch (opcode) {
- case ASM_DATA_EVENT_WRITE_DONE:
- case ASM_DATA_EVENT_READ_DONE:
- case ASM_DATA_CMDRSP_EOS:
- audio_aio_cb(opcode, token, payload, audio);
- break;
- default:
- pr_debug("%s:Unhandled event = 0x%8x\n", __func__, opcode);
- break;
- }
-}
-
#ifdef CONFIG_DEBUG_FS
static const struct file_operations audio_qcelp_debug_fops = {
.read = audio_aio_debug_read,
@@ -113,7 +96,7 @@
audio->pcm_cfg.sample_rate = 8000;
audio->pcm_cfg.channel_count = 1;
- audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_qcelp_cb,
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils.c b/arch/arm/mach-msm/qdsp6v2/audio_utils.c
index f9445d8..6a23e37 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_utils.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_utils.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-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
@@ -21,8 +21,6 @@
#include <linux/slab.h>
#include <asm/atomic.h>
#include <asm/ioctls.h>
-#include <sound/q6asm.h>
-#include <sound/apr_audio.h>
#include "audio_utils.h"
static int audio_in_pause(struct q6audio_in *audio)
@@ -86,35 +84,6 @@
return 0;
}
-void audio_in_get_dsp_frames(struct q6audio_in *audio,
- uint32_t token, uint32_t *payload)
-{
- uint32_t index;
-
- index = token;
- pr_debug("%s:session id %d: index=%d nr frames=%d offset[%d]\n",
- __func__, audio->ac->session, token, payload[7],
- payload[3]);
- pr_debug("%s:session id %d: timemsw=%d lsw=%d\n", __func__,
- audio->ac->session, payload[4], payload[5]);
- pr_debug("%s:session id %d: uflags=0x%8x uid=0x%8x\n", __func__,
- audio->ac->session, payload[6], payload[8]);
- pr_debug("%s:session id %d: enc frame size=0x%8x\n", __func__,
- audio->ac->session, payload[2]);
-
- audio->out_frame_info[index][0] = payload[7];
- audio->out_frame_info[index][1] = payload[3];
-
- /* statistics of read */
- atomic_add(payload[2], &audio->in_bytes);
- atomic_add(payload[7], &audio->in_samples);
-
- if (atomic_read(&audio->out_count) <= audio->str_cfg.buffer_count) {
- atomic_inc(&audio->out_count);
- wake_up(&audio->read_wait);
- }
-}
-
/* must be called with audio->lock held */
int audio_in_enable(struct q6audio_in *audio)
{
@@ -140,8 +109,8 @@
rc = q6asm_cmd(audio->ac, CMD_CLOSE);
if (rc < 0)
- pr_err("%s:session id %d: Failed to close the\
- session rc=%d\n", __func__, audio->ac->session,
+ pr_err("%s:session id %d: Failed to close the"
+ "session rc=%d\n", __func__, audio->ac->session,
rc);
audio->stopped = 1;
memset(audio->out_frame_info, 0,
@@ -166,8 +135,8 @@
ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
audio->pcm_cfg.buffer_count);
if (rc < 0) {
- pr_err("%s:session id %d: Buffer Alloc\
- failed\n", __func__,
+ pr_err("%s:session id %d: Buffer Alloc"
+ "failed\n", __func__,
audio->ac->session);
rc = -ENOMEM;
break;
@@ -203,8 +172,8 @@
ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
audio->pcm_cfg.buffer_count);
if (rc < 0) {
- pr_err("%s:session id %d: Buffer Alloc\
- failed\n", __func__,
+ pr_err("%s:session id %d: Buffer Alloc"
+ "failed\n", __func__,
audio->ac->session);
rc = -ENOMEM;
break;
@@ -334,15 +303,15 @@
}
audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
audio->buf_cfg.frames_per_buf = cfg.frames_per_buf;
- pr_debug("%s:session id %d: Set-buf-cfg: meta[%d]\
- framesperbuf[%d]\n", __func__,
+ pr_debug("%s:session id %d: Set-buf-cfg: meta[%d]"
+ "framesperbuf[%d]\n", __func__,
audio->ac->session, cfg.meta_info_enable,
cfg.frames_per_buf);
break;
}
case AUDIO_GET_BUF_CFG: {
- pr_debug("%s:session id %d: Get-buf-cfg: meta[%d]\
- framesperbuf[%d]\n", __func__,
+ pr_debug("%s:session id %d: Get-buf-cfg: meta[%d]"
+ "framesperbuf[%d]\n", __func__,
audio->ac->session, audio->buf_cfg.meta_info_enable,
audio->buf_cfg.frames_per_buf);
@@ -437,8 +406,8 @@
if ((audio->stopped && !(atomic_read(&audio->out_count))) ||
audio->rflush) {
- pr_debug("%s:session id %d: driver in stop state or\
- flush,No more buf to read", __func__,
+ pr_debug("%s:session id %d: driver in stop state or"
+ "flush,No more buf to read", __func__,
audio->ac->session);
rc = 0;/* End of File */
break;
@@ -504,8 +473,8 @@
count -= bytes_to_copy;
buf += bytes_to_copy;
} else {
- pr_err("%s:session id %d: short read data[%p]\
- bytesavail[%d]bytesrequest[%d]\n", __func__,
+ pr_err("%s:session id %d: short read data[%p]"
+ "bytesavail[%d]bytesrequest[%d]\n", __func__,
audio->ac->session,
data, size, count);
}
@@ -585,8 +554,8 @@
&nflags);
buf += mfield_size;
/* send the EOS and return */
- pr_debug("%s:session id %d: send EOS\
- 0x%8x\n", __func__,
+ pr_debug("%s:session id %d: send EOS"
+ "0x%8x\n", __func__,
audio->ac->session, nflags);
break;
}
@@ -613,8 +582,8 @@
buf += mfield_size;
count -= mfield_size;
} else {
- pr_debug("%s:session id %d: continuous\
- buffer\n", __func__, audio->ac->session);
+ pr_debug("%s:session id %d: continuous"
+ "buffer\n", __func__, audio->ac->session);
}
}
xfer = (count > (audio->pcm_cfg.buffer_size)) ?
@@ -634,8 +603,8 @@
buf += xfer;
}
mutex_unlock(&audio->write_lock);
- pr_debug("%s:session id %d: eos_condition 0x%8x buf[0x%x]\
- start[0x%x]\n", __func__, audio->ac->session,
+ pr_debug("%s:session id %d: eos_condition 0x%8x buf[0x%x]"
+ "start[0x%x]\n", __func__, audio->ac->session,
nflags, (int) buf, (int) start);
if (nflags & AUD_EOS_SET) {
rc = q6asm_cmd(audio->ac, CMD_EOS);
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils.h b/arch/arm/mach-msm/qdsp6v2/audio_utils.h
index 7a696ca..df963f9 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_utils.h
+++ b/arch/arm/mach-msm/qdsp6v2/audio_utils.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-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
@@ -11,6 +11,7 @@
*
*/
#include <linux/msm_audio.h>
+#include "q6audio_common.h"
#define FRAME_NUM (8)
@@ -26,18 +27,18 @@
#define BUF_ALLOC_INOUT 0x03
#define ALIGN_BUF_SIZE(size) ((size + 4095) & (~4095))
-struct timestamp{
+struct timestamp {
unsigned long lowpart;
unsigned long highpart;
} __attribute__ ((packed));
-struct meta_in{
+struct meta_in {
unsigned short offset;
struct timestamp ntimestamp;
unsigned int nflags;
} __attribute__ ((packed));
-struct meta_out_dsp{
+struct meta_out_dsp {
u32 offset_to_frame;
u32 frame_size;
u32 encoded_pcm_samples;
@@ -46,12 +47,12 @@
u32 nflags;
} __attribute__ ((packed));
-struct meta_out{
+struct meta_out {
unsigned char num_of_frames;
struct meta_out_dsp meta_out_dsp[];
} __attribute__ ((packed));
-struct q6audio_in{
+struct q6audio_in {
spinlock_t dsp_lock;
atomic_t in_bytes;
atomic_t in_samples;
@@ -89,8 +90,6 @@
long (*enc_ioctl)(struct file *, unsigned int, unsigned long);
};
-void audio_in_get_dsp_frames(struct q6audio_in *audio,
- uint32_t token, uint32_t *payload);
int audio_in_enable(struct q6audio_in *audio);
int audio_in_disable(struct q6audio_in *audio);
int audio_in_buf_alloc(struct q6audio_in *audio);
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
index 644df2d..cef2fae 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
@@ -23,8 +23,6 @@
#include <linux/slab.h>
#include <asm/atomic.h>
#include <asm/ioctls.h>
-#include <sound/q6asm.h>
-#include <sound/apr_audio.h>
#include <linux/debugfs.h>
#include "audio_utils_aio.h"
@@ -295,8 +293,8 @@
kfree(used_buf);
if (list_empty(&audio->out_queue) &&
(audio->drv_status & ADRV_STATUS_FSYNC)) {
- pr_debug("%s[%p]: list is empty, reached EOS in\
- Tunnel\n", __func__, audio);
+ pr_debug("%s[%p]: list is empty, reached EOS in"
+ "Tunnel\n", __func__, audio);
wake_up(&audio->write_wait);
}
} else {
@@ -424,72 +422,6 @@
}
}
-void audio_aio_cb(uint32_t opcode, uint32_t token,
- uint32_t *payload, struct q6audio_aio *audio)
-{
- union msm_audio_event_payload e_payload;
-
- switch (opcode) {
- case ASM_DATA_EVENT_WRITE_DONE:
- pr_debug("%s[%p]:ASM_DATA_EVENT_WRITE_DONE token = 0x%x\n",
- __func__, audio, token);
- audio_aio_async_write_ack(audio, token, payload);
- break;
- case ASM_DATA_EVENT_READ_DONE:
- pr_debug("%s[%p]:ASM_DATA_EVENT_READ_DONE token = 0x%x\n",
- __func__, audio, token);
- audio_aio_async_read_ack(audio, token, payload);
- break;
- case ASM_DATA_CMDRSP_EOS:
- /* EOS Handle */
- pr_debug("%s[%p]:ASM_DATA_CMDRSP_EOS\n", __func__, audio);
- if (audio->feedback) { /* Non-Tunnel mode */
- audio->eos_rsp = 1;
- /* propagate input EOS i/p buffer,
- after receiving DSP acknowledgement */
- if (audio->eos_flag &&
- (audio->eos_write_payload.aio_buf.buf_addr)) {
- audio_aio_post_event(audio,
- AUDIO_EVENT_WRITE_DONE,
- audio->eos_write_payload);
- memset(&audio->eos_write_payload , 0,
- sizeof(union msm_audio_event_payload));
- audio->eos_flag = 0;
- }
- } else { /* Tunnel mode */
- audio->eos_rsp = 1;
- wake_up(&audio->write_wait);
- wake_up(&audio->cmd_wait);
- }
- break;
- case ASM_DATA_CMD_MEDIA_FORMAT_UPDATE:
- case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
- pr_debug("%s[%p]:payload0[%x] payloa1d[%x]opcode= 0x%x\n",
- __func__, audio, payload[0], payload[1], opcode);
- break;
- case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
- case ASM_DATA_EVENT_ENC_SR_CM_NOTIFY:
- pr_debug("%s[%p]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, "
-
- "payload[0]-sr = %d, payload[1]-chl = %d, "
- "payload[2] = %d, payload[3] = %d\n", __func__,
- audio, payload[0], payload[1], payload[2],
- payload[3]);
- pr_debug("%s[%p]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, "
- "sr(prev) = %d, chl(prev) = %d,",
- __func__, audio, audio->pcm_cfg.sample_rate,
- audio->pcm_cfg.channel_count);
- audio->pcm_cfg.sample_rate = payload[0];
- audio->pcm_cfg.channel_count = payload[1] & 0xFFFF;
- e_payload.stream_info.chan_info = audio->pcm_cfg.channel_count;
- e_payload.stream_info.sample_rate = audio->pcm_cfg.sample_rate;
- audio_aio_post_event(audio, AUDIO_EVENT_STREAM_INFO, e_payload);
- break;
- default:
- break;
- }
-}
-
int audio_aio_enable(struct q6audio_aio *audio)
{
/* 2nd arg: 0 -> run immediately
@@ -1041,8 +973,8 @@
return -EFAULT;
}
- pr_debug("%s[%p]:node %p dir %x buf_addr %p buf_len %d data_len \
- %d\n", __func__, audio, buf_node, dir, buf_node->buf.buf_addr,
+ pr_debug("%s[%p]:node %p dir %x buf_addr %p buf_len %d data_len"
+ "%d\n", __func__, audio, buf_node, dir, buf_node->buf.buf_addr,
buf_node->buf.buf_len, buf_node->buf.data_len);
buf_node->paddr = audio_aio_ion_fixup(audio, buf_node->buf.buf_addr,
buf_node->buf.buf_len, 1,
@@ -1447,8 +1379,8 @@
break;
}
case AUDIO_GET_BUF_CFG: {
- pr_debug("%s[%p]:session id %d: Get-buf-cfg: meta[%d]\
- framesperbuf[%d]\n", __func__, audio,
+ pr_debug("%s[%p]:session id %d: Get-buf-cfg: meta[%d]"
+ "framesperbuf[%d]\n", __func__, audio,
audio->ac->session, audio->buf_cfg.meta_info_enable,
audio->buf_cfg.frames_per_buf);
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h
index a25ca4d..16acb06 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h
+++ b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h
@@ -27,8 +27,7 @@
#include <linux/ion.h>
#include <asm/ioctls.h>
#include <asm/atomic.h>
-#include <sound/q6asm.h>
-#include <sound/apr_audio.h>
+#include "q6audio_common.h"
#define TUNNEL_MODE 0x0000
#define NON_TUNNEL_MODE 0x0001
@@ -190,6 +189,12 @@
long (*codec_ioctl)(struct file *, unsigned int, unsigned long);
};
+void audio_aio_async_write_ack(struct q6audio_aio *audio, uint32_t token,
+ uint32_t *payload);
+
+void audio_aio_async_read_ack(struct q6audio_aio *audio, uint32_t token,
+ uint32_t *payload);
+
int audio_aio_open(struct q6audio_aio *audio, struct file *file);
int audio_aio_enable(struct q6audio_aio *audio);
void audio_aio_post_event(struct q6audio_aio *audio, int type,
@@ -197,8 +202,6 @@
int audio_aio_release(struct inode *inode, struct file *file);
long audio_aio_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
int audio_aio_fsync(struct file *file, int datasync);
-void audio_aio_cb(uint32_t opcode, uint32_t token,
- uint32_t *payload, struct q6audio_aio *audio);
void audio_aio_async_out_flush(struct q6audio_aio *audio);
void audio_aio_async_in_flush(struct q6audio_aio *audio);
#ifdef CONFIG_DEBUG_FS
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_wma.c b/arch/arm/mach-msm/qdsp6v2/audio_wma.c
index bea0485..021d58b 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_wma.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_wma.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2012, Code Aurora Forum. 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
@@ -19,24 +19,6 @@
#include <linux/msm_audio_wma.h>
#include "audio_utils_aio.h"
-static void q6_audio_wma_cb(uint32_t opcode, uint32_t token,
- uint32_t *payload, void *priv)
-{
- struct q6audio_aio *audio = (struct q6audio_aio *)priv;
-
- pr_debug("%s:opcode = %x token = 0x%x\n", __func__, opcode, token);
- switch (opcode) {
- case ASM_DATA_EVENT_WRITE_DONE:
- case ASM_DATA_EVENT_READ_DONE:
- case ASM_DATA_CMDRSP_EOS:
- audio_aio_cb(opcode, token, payload, audio);
- break;
- default:
- pr_debug("%s:Unhandled event = 0x%8x\n", __func__, opcode);
- break;
- }
-}
-
#ifdef CONFIG_DEBUG_FS
static const struct file_operations audio_wma_debug_fops = {
.read = audio_aio_debug_read,
@@ -139,15 +121,15 @@
audio->codec_cfg = kzalloc(sizeof(struct msm_audio_wma_config_v2),
GFP_KERNEL);
if (audio->codec_cfg == NULL) {
- pr_err("%s:Could not allocate memory for wma\
- config\n", __func__);
+ pr_err("%s:Could not allocate memory for wma"
+ "config\n", __func__);
kfree(audio);
return -ENOMEM;
}
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
- audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_wma_cb,
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_wmapro.c b/arch/arm/mach-msm/qdsp6v2/audio_wmapro.c
index 98d1b30..4fcdcc1 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_wmapro.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_wmapro.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2012, Code Aurora Forum. 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
@@ -19,24 +19,6 @@
#include <linux/msm_audio_wmapro.h>
#include "audio_utils_aio.h"
-static void q6_audio_wmapro_cb(uint32_t opcode, uint32_t token,
- uint32_t *payload, void *priv)
-{
- struct q6audio_aio *audio = (struct q6audio_aio *)priv;
-
- pr_debug("%s:opcode = %x token = 0x%x\n", __func__, opcode, token);
- switch (opcode) {
- case ASM_DATA_EVENT_WRITE_DONE:
- case ASM_DATA_EVENT_READ_DONE:
- case ASM_DATA_CMDRSP_EOS:
- audio_aio_cb(opcode, token, payload, audio);
- break;
- default:
- pr_debug("%s:Unhandled event = 0x%8x\n", __func__, opcode);
- break;
- }
-}
-
#ifdef CONFIG_DEBUG_FS
static const struct file_operations audio_wmapro_debug_fops = {
.read = audio_aio_debug_read,
@@ -197,8 +179,8 @@
audio->codec_cfg = kzalloc(sizeof(struct msm_audio_wmapro_config),
GFP_KERNEL);
if (audio->codec_cfg == NULL) {
- pr_err("%s: Could not allocate memory for wmapro\
- config\n", __func__);
+ pr_err("%s: Could not allocate memory for wmapro"
+ "config\n", __func__);
kfree(audio);
return -ENOMEM;
}
@@ -206,7 +188,7 @@
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
- audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_wmapro_cb,
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
diff --git a/arch/arm/mach-msm/qdsp6v2/evrc_in.c b/arch/arm/mach-msm/qdsp6v2/evrc_in.c
index ffe10bc..b95d659 100644
--- a/arch/arm/mach-msm/qdsp6v2/evrc_in.c
+++ b/arch/arm/mach-msm/qdsp6v2/evrc_in.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-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
@@ -22,8 +22,6 @@
#include <linux/msm_audio_qcp.h>
#include <asm/atomic.h>
#include <asm/ioctls.h>
-#include <sound/q6asm.h>
-#include <sound/apr_audio.h>
#include "audio_utils.h"
/* Buffer with meta*/
@@ -32,44 +30,6 @@
/* Maximum 10 frames in buffer with meta */
#define FRAME_SIZE (1 + ((23+sizeof(struct meta_out_dsp)) * 10))
-void q6asm_evrc_in_cb(uint32_t opcode, uint32_t token,
- uint32_t *payload, void *priv)
-{
- struct q6audio_in * audio = (struct q6audio_in *)priv;
- unsigned long flags;
-
- pr_debug("%s:session id %d: opcode - %d\n", __func__,
- audio->ac->session, opcode);
-
- spin_lock_irqsave(&audio->dsp_lock, flags);
- switch (opcode) {
- case ASM_DATA_EVENT_READ_DONE:
- audio_in_get_dsp_frames(audio, token, payload);
- break;
- case ASM_DATA_EVENT_WRITE_DONE:
- atomic_inc(&audio->in_count);
- wake_up(&audio->write_wait);
- break;
- case ASM_DATA_CMDRSP_EOS:
- audio->eos_rsp = 1;
- wake_up(&audio->read_wait);
- break;
- case ASM_STREAM_CMDRSP_GET_ENCDEC_PARAM:
- break;
- case ASM_STREAM_CMDRSP_GET_PP_PARAMS:
- break;
- case ASM_SESSION_EVENT_TX_OVERFLOW:
- pr_err("%s:session id %d: ASM_SESSION_EVENT_TX_OVERFLOW\n",
- __func__, audio->ac->session);
- break;
- default:
- pr_err("%s:session id %d: Ignore opcode[0x%x]\n", __func__,
- audio->ac->session, opcode);
- break;
- }
- spin_unlock_irqrestore(&audio->dsp_lock, flags);
-}
-
/* ------------------- device --------------------- */
static long evrc_in_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
@@ -104,8 +64,8 @@
enc_cfg->max_bit_rate, 0);
if (rc < 0) {
- pr_err("%s:session id %d: cmd evrc media format block\
- failed\n", __func__, audio->ac->session);
+ pr_err("%s:session id %d: cmd evrc media format block"
+ "failed\n", __func__, audio->ac->session);
break;
}
if (audio->feedback == NON_TUNNEL_MODE) {
@@ -114,8 +74,8 @@
audio->pcm_cfg.channel_count);
if (rc < 0) {
- pr_err("%s:session id %d: media format block\
- failed\n", __func__, audio->ac->session);
+ pr_err("%s:session id %d: media format block"
+ "failed\n", __func__, audio->ac->session);
break;
}
}
@@ -126,8 +86,8 @@
audio->enabled = 1;
} else {
audio->enabled = 0;
- pr_err("%s:session id %d: Audio Start procedure failed\
- rc=%d\n", __func__, audio->ac->session, rc);
+ pr_err("%s:session id %d: Audio Start procedure failed"
+ "rc=%d\n", __func__, audio->ac->session, rc);
break;
}
while (cnt++ < audio->str_cfg.buffer_count)
@@ -142,8 +102,8 @@
audio->ac->session);
rc = audio_in_disable(audio);
if (rc < 0) {
- pr_err("%s:session id %d: Audio Stop procedure failed\
- rc=%d\n", __func__, audio->ac->session, rc);
+ pr_err("%s:session id %d: Audio Stop procedure failed"
+ "rc=%d\n", __func__, audio->ac->session, rc);
break;
}
break;
@@ -183,8 +143,8 @@
}
enc_cfg->min_bit_rate = cfg.min_bit_rate;
enc_cfg->max_bit_rate = cfg.max_bit_rate;
- pr_debug("%s:session id %d: min_bit_rate= 0x%x\
- max_bit_rate=0x%x\n", __func__,
+ pr_debug("%s:session id %d: min_bit_rate= 0x%x"
+ "max_bit_rate=0x%x\n", __func__,
audio->ac->session, enc_cfg->min_bit_rate,
enc_cfg->max_bit_rate);
break;
@@ -204,16 +164,16 @@
audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
if (audio == NULL) {
- pr_err("%s: Could not allocate memory for evrc\
- driver\n", __func__);
+ pr_err("%s: Could not allocate memory for evrc"
+ "driver\n", __func__);
return -ENOMEM;
}
/* Allocate memory for encoder config param */
audio->enc_cfg = kzalloc(sizeof(struct msm_audio_evrc_enc_config),
GFP_KERNEL);
if (audio->enc_cfg == NULL) {
- pr_err("%s:session id %d: Could not allocate memory for aac\
- config param\n", __func__, audio->ac->session);
+ pr_err("%s:session id %d: Could not allocate memory for aac"
+ "config param\n", __func__, audio->ac->session);
kfree(audio);
return -ENOMEM;
}
@@ -241,12 +201,12 @@
audio->buf_cfg.meta_info_enable = 0x01;
audio->buf_cfg.frames_per_buf = 0x01;
- audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_evrc_in_cb,
+ audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
(void *)audio);
if (!audio->ac) {
- pr_err("%s: Could not allocate memory for audio\
- client\n", __func__);
+ pr_err("%s: Could not allocate memory for audio"
+ "client\n", __func__);
kfree(audio->enc_cfg);
kfree(audio);
return -ENOMEM;
@@ -279,8 +239,8 @@
/* register for tx overflow (valid for tunnel mode only) */
rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
if (rc < 0) {
- pr_err("%s:session id %d: TX Overflow registration\
- failed rc=%d\n", __func__,
+ pr_err("%s:session id %d: TX Overflow registration"
+ "failed rc=%d\n", __func__,
audio->ac->session, rc);
rc = -ENODEV;
goto fail;
diff --git a/arch/arm/mach-msm/qdsp6v2/q6audio_common.h b/arch/arm/mach-msm/qdsp6v2/q6audio_common.h
new file mode 100644
index 0000000..e108de5
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6v2/q6audio_common.h
@@ -0,0 +1,35 @@
+/* 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.
+ *
+*/
+
+/* For Decoders */
+#ifndef __Q6_AUDIO_COMMON_H__
+#define __Q6_AUDIO_COMMON_H__
+
+#include <sound/apr_audio.h>
+#include <sound/q6asm.h>
+
+void q6_audio_cb(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv);
+
+void audio_aio_cb(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *audio);
+
+
+/* For Encoders */
+void q6asm_in_cb(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv);
+
+void audio_in_get_dsp_frames(void *audio,
+ uint32_t token, uint32_t *payload);
+
+#endif /*__Q6_AUDIO_COMMON_H__*/
diff --git a/arch/arm/mach-msm/qdsp6v2/q6audio_v1.c b/arch/arm/mach-msm/qdsp6v2/q6audio_v1.c
new file mode 100644
index 0000000..f49d6e0
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6v2/q6audio_v1.c
@@ -0,0 +1,92 @@
+/*
+ * 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/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+void q6asm_in_cb(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv)
+{
+ struct q6audio_in * audio = (struct q6audio_in *)priv;
+ unsigned long flags;
+
+ pr_debug("%s:session id %d: opcode[0x%x]\n", __func__,
+ audio->ac->session, opcode);
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ switch (opcode) {
+ case ASM_DATA_EVENT_READ_DONE:
+ audio_in_get_dsp_frames(audio, token, payload);
+ break;
+ case ASM_DATA_EVENT_WRITE_DONE:
+ atomic_inc(&audio->in_count);
+ wake_up(&audio->write_wait);
+ break;
+ case ASM_DATA_CMDRSP_EOS:
+ audio->eos_rsp = 1;
+ wake_up(&audio->read_wait);
+ break;
+ case ASM_STREAM_CMDRSP_GET_ENCDEC_PARAM:
+ break;
+ case ASM_STREAM_CMDRSP_GET_PP_PARAMS:
+ break;
+ case ASM_SESSION_EVENT_TX_OVERFLOW:
+ pr_err("%s:session id %d: ASM_SESSION_EVENT_TX_OVERFLOW\n",
+ __func__, audio->ac->session);
+ break;
+ default:
+ pr_debug("%s:session id %d: Ignore opcode[0x%x]\n", __func__,
+ audio->ac->session, opcode);
+ break;
+ }
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+void audio_in_get_dsp_frames(/*struct q6audio_in *audio,*/void *aud,
+ uint32_t token, uint32_t *payload)
+{
+ struct q6audio_in *audio = (struct q6audio_in *)aud;
+ uint32_t index;
+
+ index = token;
+ pr_debug("%s:session id %d: index=%d nr frames=%d offset[%d]\n",
+ __func__, audio->ac->session, token, payload[7],
+ payload[3]);
+ pr_debug("%s:session id %d: timemsw=%d lsw=%d\n", __func__,
+ audio->ac->session, payload[4], payload[5]);
+ pr_debug("%s:session id %d: uflags=0x%8x uid=0x%8x\n", __func__,
+ audio->ac->session, payload[6], payload[8]);
+ pr_debug("%s:session id %d: enc frame size=0x%8x\n", __func__,
+ audio->ac->session, payload[2]);
+
+ audio->out_frame_info[index][0] = payload[7];
+ audio->out_frame_info[index][1] = payload[3];
+
+ /* statistics of read */
+ atomic_add(payload[2], &audio->in_bytes);
+ atomic_add(payload[7], &audio->in_samples);
+
+ if (atomic_read(&audio->out_count) <= audio->str_cfg.buffer_count) {
+ atomic_inc(&audio->out_count);
+ wake_up(&audio->read_wait);
+ }
+}
diff --git a/arch/arm/mach-msm/qdsp6v2/q6audio_v1_aio.c b/arch/arm/mach-msm/qdsp6v2/q6audio_v1_aio.c
new file mode 100644
index 0000000..112de62
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6v2/q6audio_v1_aio.c
@@ -0,0 +1,113 @@
+/*
+ * 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/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include "audio_utils_aio.h"
+
+void q6_audio_cb(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv)
+{
+ struct q6audio_aio *audio = (struct q6audio_aio *)priv;
+
+ pr_debug("%s:opcode = %x token = 0x%x\n", __func__, opcode, token);
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE:
+ case ASM_DATA_EVENT_READ_DONE:
+ case ASM_DATA_CMDRSP_EOS:
+ case ASM_DATA_CMD_MEDIA_FORMAT_UPDATE:
+ case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
+ case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
+ case ASM_DATA_EVENT_ENC_SR_CM_NOTIFY:
+ audio_aio_cb(opcode, token, payload, audio);
+ break;
+ default:
+ pr_debug("%s:Unhandled event = 0x%8x\n", __func__, opcode);
+ break;
+ }
+}
+
+void audio_aio_cb(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv)
+{
+ union msm_audio_event_payload e_payload;
+ struct q6audio_aio *audio = (struct q6audio_aio *)priv;
+
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE:
+ pr_debug("%s[%p]:ASM_DATA_EVENT_WRITE_DONE token = 0x%x\n",
+ __func__, audio, token);
+ audio_aio_async_write_ack(audio, token, payload);
+ break;
+ case ASM_DATA_EVENT_READ_DONE:
+ pr_debug("%s[%p]:ASM_DATA_EVENT_READ_DONE token = 0x%x\n",
+ __func__, audio, token);
+ audio_aio_async_read_ack(audio, token, payload);
+ break;
+ case ASM_DATA_CMDRSP_EOS:
+ /* EOS Handle */
+ pr_debug("%s[%p]:ASM_DATA_CMDRSP_EOS\n", __func__, audio);
+ if (audio->feedback) { /* Non-Tunnel mode */
+ audio->eos_rsp = 1;
+ /* propagate input EOS i/p buffer,
+ after receiving DSP acknowledgement */
+ if (audio->eos_flag &&
+ (audio->eos_write_payload.aio_buf.buf_addr)) {
+ audio_aio_post_event(audio,
+ AUDIO_EVENT_WRITE_DONE,
+ audio->eos_write_payload);
+ memset(&audio->eos_write_payload , 0,
+ sizeof(union msm_audio_event_payload));
+ audio->eos_flag = 0;
+ }
+ } else { /* Tunnel mode */
+ audio->eos_rsp = 1;
+ wake_up(&audio->write_wait);
+ wake_up(&audio->cmd_wait);
+ }
+ break;
+ case ASM_DATA_CMD_MEDIA_FORMAT_UPDATE:
+ case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
+ pr_debug("%s[%p]:payload0[%x] payloa1d[%x]opcode= 0x%x\n",
+ __func__, audio, payload[0], payload[1], opcode);
+ break;
+ case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
+ case ASM_DATA_EVENT_ENC_SR_CM_NOTIFY:
+ pr_debug("%s[%p]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, "
+
+ "payload[0]-sr = %d, payload[1]-chl = %d, "
+ "payload[2] = %d, payload[3] = %d\n", __func__,
+ audio, payload[0], payload[1], payload[2],
+ payload[3]);
+ pr_debug("%s[%p]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, "
+ "sr(prev) = %d, chl(prev) = %d,",
+ __func__, audio, audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+ audio->pcm_cfg.sample_rate = payload[0];
+ audio->pcm_cfg.channel_count = payload[1] & 0xFFFF;
+ e_payload.stream_info.chan_info = audio->pcm_cfg.channel_count;
+ e_payload.stream_info.sample_rate = audio->pcm_cfg.sample_rate;
+ audio_aio_post_event(audio, AUDIO_EVENT_STREAM_INFO, e_payload);
+ break;
+ default:
+ break;
+ }
+}
diff --git a/arch/arm/mach-msm/qdsp6v2/qcelp_in.c b/arch/arm/mach-msm/qdsp6v2/qcelp_in.c
index 3cf4e25..a48df39 100644
--- a/arch/arm/mach-msm/qdsp6v2/qcelp_in.c
+++ b/arch/arm/mach-msm/qdsp6v2/qcelp_in.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-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
@@ -22,8 +22,6 @@
#include <linux/msm_audio_qcp.h>
#include <asm/atomic.h>
#include <asm/ioctls.h>
-#include <sound/q6asm.h>
-#include <sound/apr_audio.h>
#include "audio_utils.h"
/* Buffer with meta*/
@@ -32,43 +30,6 @@
/* Maximum 10 frames in buffer with meta */
#define FRAME_SIZE (1 + ((35+sizeof(struct meta_out_dsp)) * 10))
-void q6asm_qcelp_in_cb(uint32_t opcode, uint32_t token,
- uint32_t *payload, void *priv)
-{
- struct q6audio_in * audio = (struct q6audio_in *)priv;
- unsigned long flags;
-
- pr_debug("%s:session id %d: opcode - %d\n", __func__,
- audio->ac->session, opcode);
-
- spin_lock_irqsave(&audio->dsp_lock, flags);
- switch (opcode) {
- case ASM_DATA_EVENT_READ_DONE:
- audio_in_get_dsp_frames(audio, token, payload);
- break;
- case ASM_DATA_EVENT_WRITE_DONE:
- atomic_inc(&audio->in_count);
- wake_up(&audio->write_wait);
- break;
- case ASM_DATA_CMDRSP_EOS:
- audio->eos_rsp = 1;
- wake_up(&audio->read_wait);
- break;
- case ASM_STREAM_CMDRSP_GET_ENCDEC_PARAM:
- break;
- case ASM_STREAM_CMDRSP_GET_PP_PARAMS:
- break;
- case ASM_SESSION_EVENT_TX_OVERFLOW:
- pr_err("%s:session id %d:ASM_SESSION_EVENT_TX_OVERFLOW\n",
- __func__, audio->ac->session);
- break;
- default:
- pr_err("%s:session id %d: Ignore opcode[0x%x]\n", __func__,
- audio->ac->session, opcode);
- break;
- }
- spin_unlock_irqrestore(&audio->dsp_lock, flags);
-}
/* ------------------- device --------------------- */
static long qcelp_in_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
@@ -103,8 +64,8 @@
enc_cfg->max_bit_rate, 0, 0);
if (rc < 0) {
- pr_err("%s:session id %d: cmd qcelp media format block\
- failed\n", __func__, audio->ac->session);
+ pr_err("%s:session id %d: cmd qcelp media format block"
+ "failed\n", __func__, audio->ac->session);
break;
}
if (audio->feedback == NON_TUNNEL_MODE) {
@@ -113,8 +74,8 @@
audio->pcm_cfg.channel_count);
if (rc < 0) {
- pr_err("%s:session id %d: media format block\
- failed\n", __func__, audio->ac->session);
+ pr_err("%s:session id %d: media format block"
+ "failed\n", __func__, audio->ac->session);
break;
}
}
@@ -125,8 +86,8 @@
audio->enabled = 1;
} else {
audio->enabled = 0;
- pr_err("%s:session id %d: Audio Start procedure failed\
- rc=%d\n", __func__, audio->ac->session, rc);
+ pr_err("%s:session id %d: Audio Start procedure failed"
+ "rc=%d\n", __func__, audio->ac->session, rc);
break;
}
while (cnt++ < audio->str_cfg.buffer_count)
@@ -141,8 +102,8 @@
audio->ac->session);
rc = audio_in_disable(audio);
if (rc < 0) {
- pr_err("%s:session id %d: Audio Stop procedure failed\
- rc=%d\n", __func__, audio->ac->session,
+ pr_err("%s:session id %d: Audio Stop procedure failed"
+ "rc=%d\n", __func__, audio->ac->session,
rc);
break;
}
@@ -180,8 +141,8 @@
}
enc_cfg->min_bit_rate = cfg.min_bit_rate;
enc_cfg->max_bit_rate = cfg.max_bit_rate;
- pr_debug("%s:session id %d: min_bit_rate= 0x%x\
- max_bit_rate=0x%x\n", __func__,
+ pr_debug("%s:session id %d: min_bit_rate= 0x%x"
+ "max_bit_rate=0x%x\n", __func__,
audio->ac->session, enc_cfg->min_bit_rate,
enc_cfg->max_bit_rate);
break;
@@ -201,16 +162,16 @@
audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
if (audio == NULL) {
- pr_err("%s: Could not allocate memory for qcelp\
- driver\n", __func__);
+ pr_err("%s: Could not allocate memory for qcelp"
+ "driver\n", __func__);
return -ENOMEM;
}
/* Allocate memory for encoder config param */
audio->enc_cfg = kzalloc(sizeof(struct msm_audio_qcelp_enc_config),
GFP_KERNEL);
if (audio->enc_cfg == NULL) {
- pr_err("%s:session id %d: Could not allocate memory for aac\
- config param\n", __func__, audio->ac->session);
+ pr_err("%s:session id %d: Could not allocate memory for aac"
+ "config param\n", __func__, audio->ac->session);
kfree(audio);
return -ENOMEM;
}
@@ -239,12 +200,12 @@
audio->buf_cfg.meta_info_enable = 0x01;
audio->buf_cfg.frames_per_buf = 0x01;
- audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_qcelp_in_cb,
+ audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
(void *)audio);
if (!audio->ac) {
- pr_err("%s: Could not allocate memory for audio\
- client\n", __func__);
+ pr_err("%s: Could not allocate memory for audio"
+ "client\n", __func__);
kfree(audio->enc_cfg);
kfree(audio);
return -ENOMEM;
@@ -277,8 +238,8 @@
/* register for tx overflow (valid for tunnel mode only) */
rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
if (rc < 0) {
- pr_err("%s:session id %d: TX Overflow registration\
- failed rc=%d\n", __func__, audio->ac->session, rc);
+ pr_err("%s:session id %d: TX Overflow registration"
+ "failed rc=%d\n", __func__, audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
diff --git a/arch/arm/mach-msm/qdsp6v2/snddev_ecodec.c b/arch/arm/mach-msm/qdsp6v2/snddev_ecodec.c
index f75af16..0b38ec2 100644
--- a/arch/arm/mach-msm/qdsp6v2/snddev_ecodec.c
+++ b/arch/arm/mach-msm/qdsp6v2/snddev_ecodec.c
@@ -220,7 +220,7 @@
goto err_clk;
}
- clk_enable(drv->ecodec_clk);
+ clk_prepare_enable(drv->ecodec_clk);
clk_reset(drv->ecodec_clk, CLK_RESET_DEASSERT);
@@ -260,7 +260,7 @@
pr_info("%s: closing all devices\n", __func__);
- clk_disable(drv->ecodec_clk);
+ clk_disable_unprepare(drv->ecodec_clk);
aux_pcm_gpios_free();
afe_close(PCM_RX);
diff --git a/arch/arm/mach-msm/qdsp6v2/snddev_icodec.c b/arch/arm/mach-msm/qdsp6v2/snddev_icodec.c
index ea935cc..8a92c85 100644
--- a/arch/arm/mach-msm/qdsp6v2/snddev_icodec.c
+++ b/arch/arm/mach-msm/qdsp6v2/snddev_icodec.c
@@ -319,7 +319,7 @@
goto error_invalid_freq;
}
- clk_enable(drv->rx_osrclk);
+ clk_prepare_enable(drv->rx_osrclk);
drv->rx_bitclk = clk_get_sys(NULL, "i2s_spkr_bit_clk");
if (IS_ERR(drv->rx_bitclk))
pr_err("%s clock Error\n", __func__);
@@ -339,7 +339,7 @@
pr_err("ERROR setting m clock1\n");
goto error_adie;
}
- clk_enable(drv->rx_bitclk);
+ clk_prepare_enable(drv->rx_bitclk);
if (icodec->data->voltage_on)
icodec->data->voltage_on();
@@ -406,7 +406,7 @@
error_pamp:
error_adie:
- clk_disable(drv->rx_osrclk);
+ clk_disable_unprepare(drv->rx_osrclk);
error_invalid_freq:
pr_err("%s: encounter error\n", __func__);
@@ -448,7 +448,7 @@
goto error_invalid_freq;
}
- clk_enable(drv->tx_osrclk);
+ clk_prepare_enable(drv->tx_osrclk);
drv->tx_bitclk = clk_get_sys(NULL, "i2s_mic_bit_clk");
if (IS_ERR(drv->tx_bitclk))
pr_err("%s clock Error\n", __func__);
@@ -464,7 +464,7 @@
} else
trc = clk_set_rate(drv->tx_bitclk, 8);
- clk_enable(drv->tx_bitclk);
+ clk_prepare_enable(drv->tx_bitclk);
/* Enable ADIE */
trc = adie_codec_open(icodec->data->profile, &icodec->adie_path);
@@ -572,8 +572,8 @@
if (icodec->data->voltage_off)
icodec->data->voltage_off();
- clk_disable(drv->rx_bitclk);
- clk_disable(drv->rx_osrclk);
+ clk_disable_unprepare(drv->rx_bitclk);
+ clk_disable_unprepare(drv->rx_osrclk);
msm_snddev_rx_mclk_free();
@@ -602,8 +602,8 @@
afe_close(icodec->data->copp_id);
- clk_disable(drv->tx_bitclk);
- clk_disable(drv->tx_osrclk);
+ clk_disable_unprepare(drv->tx_bitclk);
+ clk_disable_unprepare(drv->tx_osrclk);
msm_snddev_tx_mclk_free();
diff --git a/arch/arm/mach-msm/qdsp6v2/snddev_mi2s.c b/arch/arm/mach-msm/qdsp6v2/snddev_mi2s.c
index 75a7411..4cf18b3 100644
--- a/arch/arm/mach-msm/qdsp6v2/snddev_mi2s.c
+++ b/arch/arm/mach-msm/qdsp6v2/snddev_mi2s.c
@@ -194,7 +194,7 @@
pr_err("ERROR setting osr clock\n");
return -ENODEV;
}
- clk_enable(drv->tx_osrclk);
+ clk_prepare_enable(drv->tx_osrclk);
/* set up bit clk */
drv->tx_bitclk = clk_get_sys(NULL, "mi2s_bit_clk");
@@ -204,10 +204,10 @@
rc = clk_set_rate(drv->tx_bitclk, 8);
if (IS_ERR_VALUE(rc)) {
pr_err("ERROR setting bit clock\n");
- clk_disable(drv->tx_osrclk);
+ clk_disable_unprepare(drv->tx_osrclk);
return -ENODEV;
}
- clk_enable(drv->tx_bitclk);
+ clk_prepare_enable(drv->tx_bitclk);
afe_config.mi2s.bitwidth = 16;
@@ -336,8 +336,8 @@
error_invalid_data:
- clk_disable(drv->tx_bitclk);
- clk_disable(drv->tx_osrclk);
+ clk_disable_unprepare(drv->tx_bitclk);
+ clk_disable_unprepare(drv->tx_osrclk);
return -EINVAL;
}
@@ -358,8 +358,8 @@
return -EIO;
}
afe_close(snddev_mi2s_data->copp_id);
- clk_disable(mi2s_drv->tx_bitclk);
- clk_disable(mi2s_drv->tx_osrclk);
+ clk_disable_unprepare(mi2s_drv->tx_bitclk);
+ clk_disable_unprepare(mi2s_drv->tx_osrclk);
mi2s_gpios_free();
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
index 614339b..a8773ea 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
@@ -27,7 +27,8 @@
#include "usfcdev.h"
/* The driver version*/
-#define DRV_VERSION "1.3.1"
+#define DRV_VERSION "1.4.0"
+#define USF_VERSION_ID 0x0140
/* Standard timeout in the asynchronous ops */
#define USF_TIMEOUT_JIFFIES (1*HZ) /* 1 sec */
@@ -108,8 +109,8 @@
uint16_t dev_ind;
/* Event types, supported by device */
uint16_t event_types;
- /* The device is "input" module registered client */
- struct input_dev *input_if;
+ /* The input devices are "input" module registered clients */
+ struct input_dev *input_ifs[USF_MAX_EVENT_IND];
/* The event source */
int event_src;
/* Bitmap of types of events, conflicting to USF's ones */
@@ -118,6 +119,23 @@
uint16_t conflicting_event_filters;
};
+struct usf_input_dev_type {
+ /* Input event type, supported by the input device */
+ uint16_t event_type;
+ /* Input device name */
+ const char *input_dev_name;
+ /* Input device registration function */
+ int (*prepare_dev)(uint16_t, struct usf_type *,
+ struct us_input_info_type *,
+ const char *);
+ /* Input event notification function */
+ void (*notify_event)(struct usf_type *,
+ uint16_t,
+ struct usf_event_type *
+ );
+};
+
+
/* The MAX number of the supported devices */
#define MAX_DEVS_NUMBER 1
@@ -131,9 +149,197 @@
/* The opened devices container */
static int s_opened_devs[MAX_DEVS_NUMBER];
-#define USF_NAME_PREFIX "USF_"
+#define USF_NAME_PREFIX "usf_"
#define USF_NAME_PREFIX_SIZE 4
+
+static struct input_dev *allocate_dev(uint16_t ind, const char *name)
+{
+ struct input_dev *in_dev = input_allocate_device();
+
+ if (in_dev == NULL) {
+ pr_err("%s: input_allocate_device() failed\n", __func__);
+ } else {
+ /* Common part configuration */
+ in_dev->name = name;
+ in_dev->phys = NULL;
+ in_dev->id.bustype = BUS_HOST;
+ in_dev->id.vendor = 0x0001;
+ in_dev->id.product = 0x0001;
+ in_dev->id.version = USF_VERSION_ID;
+ }
+ return in_dev;
+}
+
+static int prepare_tsc_input_device(uint16_t ind,
+ struct usf_type *usf_info,
+ struct us_input_info_type *input_info,
+ const char *name)
+{
+ struct input_dev *in_dev = allocate_dev(ind, name);
+
+ if (in_dev == NULL)
+ return -ENOMEM;
+
+ usf_info->input_ifs[ind] = in_dev;
+ in_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ in_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(in_dev, ABS_X,
+ input_info->tsc_x_dim[MIN_IND],
+ input_info->tsc_x_dim[MAX_IND],
+ 0, 0);
+ input_set_abs_params(in_dev, ABS_Y,
+ input_info->tsc_y_dim[MIN_IND],
+ input_info->tsc_y_dim[MAX_IND],
+ 0, 0);
+ input_set_abs_params(in_dev, ABS_DISTANCE,
+ input_info->tsc_z_dim[MIN_IND],
+ input_info->tsc_z_dim[MAX_IND],
+ 0, 0);
+
+ input_set_abs_params(in_dev, ABS_PRESSURE,
+ input_info->tsc_pressure[MIN_IND],
+ input_info->tsc_pressure[MAX_IND],
+ 0, 0);
+
+ input_set_abs_params(in_dev, ABS_TILT_X,
+ input_info->tsc_x_tilt[MIN_IND],
+ input_info->tsc_x_tilt[MAX_IND],
+ 0, 0);
+ input_set_abs_params(in_dev, ABS_TILT_Y,
+ input_info->tsc_y_tilt[MIN_IND],
+ input_info->tsc_y_tilt[MAX_IND],
+ 0, 0);
+
+ return 0;
+}
+
+static int prepare_mouse_input_device(uint16_t ind, struct usf_type *usf_info,
+ struct us_input_info_type *input_info,
+ const char *name)
+{
+ struct input_dev *in_dev = allocate_dev(ind, name);
+
+ if (in_dev == NULL)
+ return -ENOMEM;
+
+ usf_info->input_ifs[ind] = in_dev;
+ in_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+
+ in_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_RIGHT) |
+ BIT_MASK(BTN_MIDDLE);
+ in_dev->relbit[0] = BIT_MASK(REL_X) |
+ BIT_MASK(REL_Y) |
+ BIT_MASK(REL_Z);
+
+ return 0;
+}
+
+static int prepare_keyboard_input_device(
+ uint16_t ind,
+ struct usf_type *usf_info,
+ struct us_input_info_type *input_info,
+ const char *name)
+{
+ struct input_dev *in_dev = allocate_dev(ind, name);
+
+ if (in_dev == NULL)
+ return -ENOMEM;
+
+ usf_info->input_ifs[ind] = in_dev;
+ in_dev->evbit[0] |= BIT_MASK(EV_KEY);
+ /* All keys are permitted */
+ memset(in_dev->keybit, 0xff, sizeof(in_dev->keybit));
+
+ return 0;
+}
+
+static void notify_tsc_event(struct usf_type *usf_info,
+ uint16_t if_ind,
+ struct usf_event_type *event)
+
+{
+ struct input_dev *input_if = usf_info->input_ifs[if_ind];
+ struct point_event_type *pe = &(event->event_data.point_event);
+
+ input_report_abs(input_if, ABS_X, pe->coordinates[X_IND]);
+ input_report_abs(input_if, ABS_Y, pe->coordinates[Y_IND]);
+ input_report_abs(input_if, ABS_DISTANCE, pe->coordinates[Z_IND]);
+
+ input_report_abs(input_if, ABS_TILT_X, pe->inclinations[X_IND]);
+ input_report_abs(input_if, ABS_TILT_Y, pe->inclinations[Y_IND]);
+
+ input_report_abs(input_if, ABS_PRESSURE, pe->pressure);
+ input_report_key(input_if, BTN_TOUCH, !!(pe->pressure));
+
+ if (usf_info->event_src)
+ input_report_key(input_if, usf_info->event_src, 1);
+
+ input_sync(input_if);
+
+ pr_debug("%s: TSC event: xyz[%d;%d;%d], incl[%d;%d], pressure[%d]\n",
+ __func__,
+ pe->coordinates[X_IND],
+ pe->coordinates[Y_IND],
+ pe->coordinates[Z_IND],
+ pe->inclinations[X_IND],
+ pe->inclinations[Y_IND],
+ pe->pressure);
+}
+
+static void notify_mouse_event(struct usf_type *usf_info,
+ uint16_t if_ind,
+ struct usf_event_type *event)
+{
+ struct input_dev *input_if = usf_info->input_ifs[if_ind];
+ struct mouse_event_type *me = &(event->event_data.mouse_event);
+
+ input_report_rel(input_if, REL_X, me->rels[X_IND]);
+ input_report_rel(input_if, REL_Y, me->rels[Y_IND]);
+ input_report_rel(input_if, REL_Z, me->rels[Z_IND]);
+
+ input_report_key(input_if, BTN_LEFT,
+ me->buttons_states & USF_BUTTON_LEFT_MASK);
+ input_report_key(input_if, BTN_MIDDLE,
+ me->buttons_states & USF_BUTTON_MIDDLE_MASK);
+ input_report_key(input_if, BTN_RIGHT,
+ me->buttons_states & USF_BUTTON_RIGHT_MASK);
+
+ input_sync(input_if);
+
+ pr_debug("%s: mouse event: dx[%d], dy[%d], buttons_states[%d]\n",
+ __func__, me->rels[X_IND],
+ me->rels[Y_IND], me->buttons_states);
+}
+
+static void notify_key_event(struct usf_type *usf_info,
+ uint16_t if_ind,
+ struct usf_event_type *event)
+{
+ struct input_dev *input_if = usf_info->input_ifs[if_ind];
+ struct key_event_type *ke = &(event->event_data.key_event);
+
+ input_report_key(input_if, ke->key, ke->key_state);
+ input_sync(input_if);
+ pr_debug("%s: key event: key[%d], state[%d]\n",
+ __func__,
+ ke->key,
+ ke->key_state);
+
+}
+
+static struct usf_input_dev_type s_usf_input_devs[] = {
+ {USF_TSC_EVENT, "usf_tsc",
+ prepare_tsc_input_device, notify_tsc_event},
+ {USF_TSC_PTR_EVENT, "usf_tsc_ptr",
+ prepare_tsc_input_device, notify_tsc_event},
+ {USF_MOUSE_EVENT, "usf_mouse",
+ prepare_mouse_input_device, notify_mouse_event},
+ {USF_KEYBOARD_EVENT, "usf_kb",
+ prepare_keyboard_input_device, notify_key_event},
+};
+
static void usf_rx_cb(uint32_t opcode, uint32_t token,
uint32_t *payload, void *priv)
{
@@ -386,8 +592,8 @@
struct us_input_info_type *input_info)
{
int rc = 0;
- struct input_dev *input_dev = NULL;
bool ret = true;
+ uint16_t ind = 0;
if ((usf_info == NULL) ||
(input_info == NULL) ||
@@ -396,194 +602,75 @@
return -EINVAL;
}
- if (usf_info->input_if != NULL) {
- pr_err("%s: input_if is already allocated\n", __func__);
- return -EFAULT;
- }
-
- input_dev = input_allocate_device();
- if (input_dev == NULL) {
- pr_err("%s: input_allocate_device() failed\n", __func__);
- return -ENOMEM;
- }
-
- /* Common part configuration */
- input_dev->name = (const char *)(usf_info->usf_tx.client_name);
- input_dev->phys = NULL;
- input_dev->id.bustype = BUS_HOST;
- input_dev->id.vendor = 0x0001;
- input_dev->id.product = 0x0001;
- input_dev->id.version = 0x0001;
-
- if (input_info->event_types & USF_TSC_EVENT) {
- /* TSC part configuration */
- input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
- input_set_abs_params(input_dev, ABS_X,
- input_info->tsc_x_dim[MIN_IND],
- input_info->tsc_x_dim[MAX_IND],
- 0, 0);
- input_set_abs_params(input_dev, ABS_Y,
- input_info->tsc_y_dim[MIN_IND],
- input_info->tsc_y_dim[MAX_IND],
- 0, 0);
- input_set_abs_params(input_dev, ABS_DISTANCE,
- input_info->tsc_z_dim[MIN_IND],
- input_info->tsc_z_dim[MAX_IND],
- 0, 0);
-
- input_set_abs_params(input_dev, ABS_PRESSURE,
- input_info->tsc_pressure[MIN_IND],
- input_info->tsc_pressure[MAX_IND], 0, 0);
-
- input_set_abs_params(input_dev, ABS_TILT_X,
- input_info->tsc_x_tilt[MIN_IND],
- input_info->tsc_x_tilt[MAX_IND],
- 0, 0);
- input_set_abs_params(input_dev, ABS_TILT_Y,
- input_info->tsc_y_tilt[MIN_IND],
- input_info->tsc_y_tilt[MAX_IND],
- 0, 0);
- }
-
- if (input_info->event_types & USF_MOUSE_EVENT) {
- /* Mouse part configuration */
- input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
-
- input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
- BIT_MASK(BTN_RIGHT) |
- BIT_MASK(BTN_MIDDLE);
- input_dev->relbit[0] = BIT_MASK(REL_X) |
- BIT_MASK(REL_Y) |
- BIT_MASK(REL_Z);
- }
-
- if (input_info->event_types & USF_KEYBOARD_EVENT) {
- /* Keyboard part configuration */
- input_dev->evbit[0] |= BIT_MASK(EV_KEY);
-
- /* All keys are permitted */
- memset(input_dev->keybit, 0xff, sizeof(input_dev->keybit));
- }
-
if (input_info->event_src < ARRAY_SIZE(s_event_src_map))
- usf_info->event_src = s_event_src_map[input_info->event_src];
+ usf_info->event_src =
+ s_event_src_map[input_info->event_src];
else
usf_info->event_src = 0;
- if (usf_info->event_src)
- input_set_capability(input_dev, EV_KEY, usf_info->event_src);
+ for (ind = 0; ind < USF_MAX_EVENT_IND; ++ind) {
+ if (usf_info->input_ifs[ind] != NULL) {
+ pr_err("%s: input_if[%d] is already allocated\n",
+ __func__, ind);
+ return -EFAULT;
+ }
+ if ((input_info->event_types &
+ s_usf_input_devs[ind].event_type) &&
+ s_usf_input_devs[ind].prepare_dev) {
+ rc = (*s_usf_input_devs[ind].prepare_dev)(
+ ind,
+ usf_info,
+ input_info,
+ s_usf_input_devs[ind].input_dev_name);
+ if (rc)
+ return rc;
- rc = input_register_device(input_dev);
- if (rc) {
- pr_err("%s: input_register_device() failed; rc=%d\n",
- __func__, rc);
- input_free_device(input_dev);
- } else {
- usf_info->input_if = input_dev;
- usf_info->event_types = input_info->event_types;
- pr_debug("%s: input device[%s] was registered\n",
- __func__, input_dev->name);
- ret = usf_register_conflicting_events(
- input_info->conflicting_event_types);
- if (ret)
- usf_info->conflicting_event_types =
- input_info->conflicting_event_types;
- }
- return rc;
+ if (usf_info->event_src)
+ input_set_capability(usf_info->input_ifs[ind],
+ EV_KEY,
+ usf_info->event_src);
+
+ rc = input_register_device(usf_info->input_ifs[ind]);
+ if (rc) {
+ pr_err("%s: input_reg_dev() failed; rc=%d\n",
+ __func__, rc);
+ input_free_device(usf_info->input_ifs[ind]);
+ usf_info->input_ifs[ind] = NULL;
+ } else {
+ usf_info->event_types |=
+ s_usf_input_devs[ind].event_type;
+ pr_debug("%s: input device[%s] was registered\n",
+ __func__,
+ s_usf_input_devs[ind].input_dev_name);
+ }
+ } /* supported event */
+ } /* event types loop */
+
+ ret = usf_register_conflicting_events(
+ input_info->conflicting_event_types);
+ if (ret)
+ usf_info->conflicting_event_types =
+ input_info->conflicting_event_types;
+
+ return 0;
}
-static void notify_tsc_event(struct usf_type *usf_info,
- struct point_event_type *pe)
-{
- struct input_dev *input_if = usf_info->input_if;
-
- input_report_abs(input_if, ABS_X, pe->coordinates[X_IND]);
- input_report_abs(input_if, ABS_Y, pe->coordinates[Y_IND]);
- input_report_abs(input_if, ABS_DISTANCE, pe->coordinates[Z_IND]);
-
- input_report_abs(input_if, ABS_TILT_X, pe->inclinations[X_IND]);
- input_report_abs(input_if, ABS_TILT_Y, pe->inclinations[Y_IND]);
-
- input_report_abs(input_if, ABS_PRESSURE, pe->pressure);
- input_report_key(input_if, BTN_TOUCH, !!(pe->pressure));
-
- if (usf_info->event_src)
- input_report_key(input_if, usf_info->event_src, 1);
-
- input_sync(input_if);
-
- pr_debug("%s: TSC event: xyz[%d;%d;%d], incl[%d;%d], pressure[%d]\n",
- __func__,
- pe->coordinates[X_IND],
- pe->coordinates[Y_IND],
- pe->coordinates[Z_IND],
- pe->inclinations[X_IND],
- pe->inclinations[Y_IND],
- pe->pressure);
-}
-
-static void notify_mouse_event(struct input_dev *input_if,
- struct mouse_event_type *me)
-{
- if (me == NULL) {
- pr_err("%s: mouse event is NULL\n", __func__);
- return;
- }
-
- input_report_rel(input_if, REL_X, me->rels[X_IND]);
- input_report_rel(input_if, REL_Y, me->rels[Y_IND]);
- input_report_rel(input_if, REL_Z, me->rels[Z_IND]);
-
- input_report_key(input_if, BTN_LEFT,
- me->buttons_states & USF_BUTTON_LEFT_MASK);
- input_report_key(input_if, BTN_MIDDLE,
- me->buttons_states & USF_BUTTON_MIDDLE_MASK);
- input_report_key(input_if, BTN_RIGHT,
- me->buttons_states & USF_BUTTON_RIGHT_MASK);
-
- input_sync(input_if);
-
- pr_debug("%s: mouse event: dx[%d], dy[%d], buttons_states[%d]\n",
- __func__, me->rels[X_IND],
- me->rels[Y_IND], me->buttons_states);
-}
-
-static void notify_key_event(struct input_dev *input_if,
- struct key_event_type *ke)
-{
- if (ke == NULL) {
- pr_err("%s: key event is NULL\n", __func__);
- return;
- }
-
- input_report_key(input_if, ke->key, ke->key_state);
- input_sync(input_if);
- pr_debug("%s: key event: key[%d], state[%d]\n",
- __func__,
- ke->key,
- ke->key_state);
-
-}
static void handle_input_event(struct usf_type *usf_info,
uint16_t event_counter,
struct usf_event_type *event)
{
- struct input_dev *input_if = NULL;
uint16_t ind = 0;
uint16_t events_num = 0;
struct usf_event_type usf_events[USF_EVENTS_PORTION_SIZE];
int rc = 0;
- if ((usf_info == NULL) || (usf_info->input_if == NULL) ||
+ if ((usf_info == NULL) ||
(event == NULL) || (!event_counter)) {
return;
}
- input_if = usf_info->input_if;
-
while (event_counter > 0) {
if (event_counter > USF_EVENTS_PORTION_SIZE) {
events_num = USF_EVENTS_PORTION_SIZE;
@@ -602,26 +689,17 @@
}
for (ind = 0; ind < events_num; ++ind) {
struct usf_event_type *p_event = &usf_events[ind];
- if (p_event->event_type & USF_TSC_EVENT) {
- struct point_event_type *pe =
- &(p_event->event_data.point_event);
- if (pe->coordinates_type ==
- USF_PIX_COORDINATE)
- notify_tsc_event(usf_info, pe);
- else
- pr_debug("%s: wrong coord type: %d",
- __func__,
- pe->coordinates_type);
- continue;
- }
- if (p_event->event_type & USF_MOUSE_EVENT) {
- notify_mouse_event(input_if,
- &(p_event->event_data.mouse_event));
- continue;
- }
- if (p_event->event_type & USF_KEYBOARD_EVENT)
- notify_key_event(input_if,
- &(p_event->event_data.key_event));
+ uint16_t if_ind = p_event->event_type_ind;
+
+ if ((if_ind >= USF_MAX_EVENT_IND) ||
+ (usf_info->input_ifs[if_ind] == NULL))
+ continue; /* event isn't supported */
+
+ if (s_usf_input_devs[if_ind].notify_event)
+ (*s_usf_input_devs[if_ind].notify_event)(
+ usf_info,
+ if_ind,
+ p_event);
} /* loop in the portion */
} /* all events loop */
}
@@ -1051,13 +1129,20 @@
static void usf_release_input(struct usf_type *usf)
{
- if (usf->input_if != NULL) {
+ uint16_t ind = 0;
+
+ for (ind = 0; ind < USF_MAX_EVENT_IND; ++ind) {
+ if (usf->input_ifs[ind] == NULL)
+ continue;
+
usf_unregister_conflicting_events(
usf->conflicting_event_types);
usf->conflicting_event_types = 0;
- input_unregister_device(usf->input_if);
- usf->input_if = NULL;
- pr_debug("%s input_unregister_device\n", __func__);
+ input_unregister_device(usf->input_ifs[ind]);
+ usf->input_ifs[ind] = NULL;
+ pr_debug("%s input_unregister_device[%s]\n",
+ __func__,
+ s_usf_input_devs[ind].input_dev_name);
}
} /* usf_release_input */
diff --git a/arch/arm/mach-msm/rpm-regulator.c b/arch/arm/mach-msm/rpm-regulator.c
index c708df5..f663695 100644
--- a/arch/arm/mach-msm/rpm-regulator.c
+++ b/arch/arm/mach-msm/rpm-regulator.c
@@ -19,10 +19,12 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <mach/rpm.h>
#include <mach/rpm-regulator.h>
+#include <mach/rpm-regulator-smd.h>
#include <mach/socinfo.h>
#include "rpm_resources.h"
@@ -42,6 +44,15 @@
debug_mask, msm_rpm_vreg_debug_mask, int, S_IRUSR | S_IWUSR
);
+/* Used for access via the rpm_regulator_* API. */
+struct rpm_regulator {
+ int vreg_id;
+ enum rpm_vreg_voter voter;
+ int sleep_also;
+ int min_uV;
+ int max_uV;
+};
+
struct vreg_config *(*get_config[])(void) = {
[RPM_VREG_VERSION_8660] = get_config_8660,
[RPM_VREG_VERSION_8960] = get_config_8960,
@@ -49,6 +60,9 @@
[RPM_VREG_VERSION_8930] = get_config_8930,
};
+static struct rpm_regulator_consumer_mapping *consumer_map;
+static int consumer_map_len;
+
#define SET_PART(_vreg, _part, _val) \
_vreg->req[_vreg->part->_part.word].value \
= (_vreg->req[_vreg->part->_part.word].value \
@@ -615,6 +629,243 @@
}
EXPORT_SYMBOL_GPL(rpm_vreg_set_frequency);
+#define MAX_NAME_LEN 64
+/**
+ * rpm_regulator_get() - lookup and obtain a handle to an RPM regulator
+ * @dev: device for regulator consumer
+ * @supply: supply name
+ *
+ * Returns a struct rpm_regulator corresponding to the regulator producer,
+ * or ERR_PTR() containing errno.
+ *
+ * This function may only be called from nonatomic context. The mapping between
+ * <dev, supply> tuples and rpm_regulators struct pointers is specified via
+ * rpm-regulator platform data.
+ */
+struct rpm_regulator *rpm_regulator_get(struct device *dev, const char *supply)
+{
+ struct rpm_regulator_consumer_mapping *mapping = NULL;
+ const char *devname = NULL;
+ struct rpm_regulator *regulator;
+ int i;
+
+ if (!config) {
+ pr_err("rpm-regulator driver has not probed yet.\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (consumer_map == NULL || consumer_map_len == 0) {
+ pr_err("No private consumer mapping has been specified.\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (supply == NULL) {
+ pr_err("supply name must be specified\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (dev)
+ devname = dev_name(dev);
+
+ for (i = 0; i < consumer_map_len; i++) {
+ /* If the mapping has a device set up it must match */
+ if (consumer_map[i].dev_name &&
+ (!devname || strncmp(consumer_map[i].dev_name, devname,
+ MAX_NAME_LEN)))
+ continue;
+
+ if (strncmp(consumer_map[i].supply, supply, MAX_NAME_LEN)
+ == 0) {
+ mapping = &consumer_map[i];
+ break;
+ }
+ }
+
+ if (mapping == NULL) {
+ pr_err("could not find mapping for dev=%s, supply=%s\n",
+ (devname ? devname : "(null)"), supply);
+ return ERR_PTR(-ENODEV);
+ }
+
+ regulator = kzalloc(sizeof(struct rpm_regulator), GFP_KERNEL);
+ if (regulator == NULL) {
+ pr_err("could not allocate memory for regulator\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ regulator->vreg_id = mapping->vreg_id;
+ regulator->voter = mapping->voter;
+ regulator->sleep_also = mapping->sleep_also;
+
+ return regulator;
+}
+EXPORT_SYMBOL_GPL(rpm_regulator_get);
+
+static int rpm_regulator_check_input(struct rpm_regulator *regulator)
+{
+ int rc = 0;
+
+ if (regulator == NULL) {
+ rc = -EINVAL;
+ pr_err("invalid (null) rpm_regulator pointer\n");
+ } else if (IS_ERR(regulator)) {
+ rc = PTR_ERR(regulator);
+ pr_err("invalid rpm_regulator pointer, rc=%d\n", rc);
+ }
+
+ return rc;
+}
+
+/**
+ * rpm_regulator_put() - free the RPM regulator handle
+ * @regulator: RPM regulator handle
+ *
+ * Parameter reaggregation does not take place when rpm_regulator_put is called.
+ * Therefore, regulator enable state and voltage must be configured
+ * appropriately before calling rpm_regulator_put.
+ *
+ * This function may be called from either atomic or nonatomic context.
+ */
+void rpm_regulator_put(struct rpm_regulator *regulator)
+{
+ kfree(regulator);
+}
+EXPORT_SYMBOL_GPL(rpm_regulator_put);
+
+/**
+ * rpm_regulator_enable() - enable regulator output
+ * @regulator: RPM regulator handle
+ *
+ * Returns 0 on success or errno on failure.
+ *
+ * This function may be called from either atomic or nonatomic context. This
+ * function may only be called for regulators which have the sleep_selectable
+ * flag set in their configuration data.
+ *
+ * rpm_regulator_set_voltage must be called before rpm_regulator_enable because
+ * enabling is defined by the RPM interface to be requesting the desired
+ * non-zero regulator output voltage.
+ */
+int rpm_regulator_enable(struct rpm_regulator *regulator)
+{
+ int rc = rpm_regulator_check_input(regulator);
+ struct vreg *vreg;
+
+ if (rc)
+ return rc;
+
+ if (regulator->vreg_id < config->vreg_id_min
+ || regulator->vreg_id > config->vreg_id_max) {
+ pr_err("invalid regulator id=%d\n", regulator->vreg_id);
+ return -EINVAL;
+ }
+
+ vreg = &config->vregs[regulator->vreg_id];
+
+ /*
+ * Handle voltage switches which can be enabled without
+ * rpm_regulator_set_voltage ever being called.
+ */
+ if (regulator->min_uV == 0 && regulator->max_uV == 0
+ && vreg->part->uV.mask == 0 && vreg->part->mV.mask == 0) {
+ regulator->min_uV = 1;
+ regulator->max_uV = 1;
+ }
+
+ if (regulator->min_uV == 0 && regulator->max_uV == 0) {
+ pr_err("Voltage must be set with rpm_regulator_set_voltage "
+ "before calling rpm_regulator_enable; vreg_id=%d, "
+ "voter=%d\n", regulator->vreg_id, regulator->voter);
+ return -EINVAL;
+ }
+
+ rc = rpm_vreg_set_voltage(regulator->vreg_id, regulator->voter,
+ regulator->min_uV, regulator->max_uV, regulator->sleep_also);
+
+ if (rc)
+ pr_err("rpm_vreg_set_voltage failed, rc=%d\n", rc);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(rpm_regulator_enable);
+
+/**
+ * rpm_regulator_disable() - disable regulator output
+ * @regulator: RPM regulator handle
+ *
+ * Returns 0 on success or errno on failure.
+ *
+ * The enable state of the regulator is determined by aggregating the requests
+ * of all consumers. Therefore, it is possible that the regulator will remain
+ * enabled even after rpm_regulator_disable is called.
+ *
+ * This function may be called from either atomic or nonatomic context. This
+ * function may only be called for regulators which have the sleep_selectable
+ * flag set in their configuration data.
+ */
+int rpm_regulator_disable(struct rpm_regulator *regulator)
+{
+ int rc = rpm_regulator_check_input(regulator);
+
+ if (rc)
+ return rc;
+
+ rc = rpm_vreg_set_voltage(regulator->vreg_id, regulator->voter, 0, 0,
+ regulator->sleep_also);
+
+ if (rc)
+ pr_err("rpm_vreg_set_voltage failed, rc=%d\n", rc);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(rpm_regulator_disable);
+
+/**
+ * rpm_regulator_set_voltage() - set regulator output voltage
+ * @regulator: RPM regulator handle
+ * @min_uV: minimum required voltage in uV
+ * @max_uV: maximum acceptable voltage in uV
+ *
+ * Sets a voltage regulator to the desired output voltage. This can be set
+ * while the regulator is disabled or enabled. If the regulator is disabled,
+ * then rpm_regulator_set_voltage will both enable the regulator and set it to
+ * output at the requested voltage.
+ *
+ * The min_uV to max_uV voltage range requested must intersect with the
+ * voltage constraint range configured for the regulator.
+ *
+ * Returns 0 on success or errno on failure.
+ *
+ * The final voltage value that is sent to the RPM is aggregated based upon the
+ * values requested by all consumers of the regulator. This corresponds to the
+ * maximum min_uV value.
+ *
+ * This function may be called from either atomic or nonatomic context. This
+ * function may only be called for regulators which have the sleep_selectable
+ * flag set in their configuration data.
+ */
+int rpm_regulator_set_voltage(struct rpm_regulator *regulator, int min_uV,
+ int max_uV)
+{
+ int rc = rpm_regulator_check_input(regulator);
+
+ if (rc)
+ return rc;
+
+ rc = rpm_vreg_set_voltage(regulator->vreg_id, regulator->voter, min_uV,
+ max_uV, regulator->sleep_also);
+
+ if (rc) {
+ pr_err("rpm_vreg_set_voltage failed, rc=%d\n", rc);
+ } else {
+ regulator->min_uV = min_uV;
+ regulator->max_uV = max_uV;
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(rpm_regulator_set_voltage);
+
static inline int vreg_hpm_min_uA(struct vreg *vreg)
{
return vreg->hpm_min_load;
@@ -1374,6 +1625,8 @@
static int __devinit rpm_vreg_probe(struct platform_device *pdev)
{
struct rpm_regulator_platform_data *platform_data;
+ static struct rpm_regulator_consumer_mapping *prev_consumer_map;
+ static int prev_consumer_map_len;
int rc = 0;
int i, id;
@@ -1416,6 +1669,42 @@
mutex_init(&config->vregs[i].pc_lock);
}
+ /* Copy the list of private API consumers. */
+ if (platform_data->consumer_map_len > 0) {
+ if (consumer_map_len == 0) {
+ consumer_map_len = platform_data->consumer_map_len;
+ consumer_map = kmemdup(platform_data->consumer_map,
+ sizeof(struct rpm_regulator_consumer_mapping)
+ * consumer_map_len, GFP_KERNEL);
+ if (consumer_map == NULL) {
+ pr_err("memory allocation failed\n");
+ consumer_map_len = 0;
+ return -ENOMEM;
+ }
+ } else {
+ /* Concatenate new map with the existing one. */
+ prev_consumer_map = consumer_map;
+ prev_consumer_map_len = consumer_map_len;
+ consumer_map_len += platform_data->consumer_map_len;
+ consumer_map = kmalloc(
+ sizeof(struct rpm_regulator_consumer_mapping)
+ * consumer_map_len, GFP_KERNEL);
+ if (consumer_map == NULL) {
+ pr_err("memory allocation failed\n");
+ consumer_map_len = 0;
+ return -ENOMEM;
+ }
+ memcpy(consumer_map, prev_consumer_map,
+ sizeof(struct rpm_regulator_consumer_mapping)
+ * prev_consumer_map_len);
+ memcpy(&consumer_map[prev_consumer_map_len],
+ platform_data->consumer_map,
+ sizeof(struct rpm_regulator_consumer_mapping)
+ * platform_data->consumer_map_len);
+ }
+
+ }
+
/* Initialize all of the regulators listed in the platform data. */
for (i = 0; i < platform_data->num_regulators; i++) {
rc = rpm_vreg_init_regulator(&platform_data->init_data[i],
@@ -1492,6 +1781,8 @@
platform_driver_unregister(&rpm_vreg_driver);
+ kfree(consumer_map);
+
for (i = 0; i < config->vregs_len; i++)
mutex_destroy(&config->vregs[i].pc_lock);
}
diff --git a/arch/arm/mach-msm/rpm_resources.c b/arch/arm/mach-msm/rpm_resources.c
index 7daea5c..5314cee 100644
--- a/arch/arm/mach-msm/rpm_resources.c
+++ b/arch/arm/mach-msm/rpm_resources.c
@@ -867,15 +867,16 @@
spin_unlock_irqrestore(&msm_rpmrs_lock, flags);
}
-struct msm_rpmrs_limits *msm_rpmrs_lowest_limits(
- bool from_idle, enum msm_pm_sleep_mode sleep_mode, uint32_t latency_us,
- uint32_t sleep_us)
+static void *msm_rpmrs_lowest_limits(bool from_idle,
+ enum msm_pm_sleep_mode sleep_mode, uint32_t latency_us,
+ uint32_t sleep_us, uint32_t *power)
{
unsigned int cpu = smp_processor_id();
struct msm_rpmrs_level *best_level = NULL;
bool irqs_detectable = false;
bool gpio_detectable = false;
int i;
+ uint32_t pwr;
if (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) {
irqs_detectable = msm_mpm_irqs_detectable(from_idle);
@@ -884,7 +885,6 @@
for (i = 0; i < msm_rpmrs_level_count; i++) {
struct msm_rpmrs_level *level = &msm_rpmrs_levels[i];
- uint32_t power;
if (!level->available)
continue;
@@ -902,31 +902,38 @@
irqs_detectable, gpio_detectable))
continue;
+ if (MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE == sleep_mode)
+ if (!cpu && msm_rpm_local_request_is_outstanding())
+ break;
+
+
if (sleep_us <= 1) {
- power = level->energy_overhead;
+ pwr = level->energy_overhead;
} else if (sleep_us <= level->time_overhead_us) {
- power = level->energy_overhead / sleep_us;
+ pwr = level->energy_overhead / sleep_us;
} else if ((sleep_us >> 10) > level->time_overhead_us) {
- power = level->steady_state_power;
+ pwr = level->steady_state_power;
} else {
- power = level->steady_state_power;
- power -= (level->time_overhead_us *
+ pwr = level->steady_state_power;
+ pwr -= (level->time_overhead_us *
level->steady_state_power)/sleep_us;
- power += level->energy_overhead / sleep_us;
+ pwr += level->energy_overhead / sleep_us;
}
if (!best_level ||
- best_level->rs_limits.power[cpu] >= power) {
+ best_level->rs_limits.power[cpu] >= pwr) {
level->rs_limits.latency_us[cpu] = level->latency_us;
- level->rs_limits.power[cpu] = power;
+ level->rs_limits.power[cpu] = pwr;
best_level = level;
+ if (power)
+ *power = pwr;
}
}
return best_level ? &best_level->rs_limits : NULL;
}
-int msm_rpmrs_enter_sleep(uint32_t sclk_count, struct msm_rpmrs_limits *limits,
+static int msm_rpmrs_enter_sleep(uint32_t sclk_count, void *limits,
bool from_idle, bool notify_rpm)
{
int rc = 0;
@@ -944,7 +951,7 @@
return rc;
}
-void msm_rpmrs_exit_sleep(struct msm_rpmrs_limits *limits, bool from_idle,
+static void msm_rpmrs_exit_sleep(void *limits, bool from_idle,
bool notify_rpm, bool collapsed)
{
@@ -1067,6 +1074,12 @@
}
device_initcall(msm_rpmrs_init);
+static struct msm_pm_sleep_ops msm_rpmrs_ops = {
+ .lowest_limits = msm_rpmrs_lowest_limits,
+ .enter_sleep = msm_rpmrs_enter_sleep,
+ .exit_sleep = msm_rpmrs_exit_sleep,
+};
+
static int __init msm_rpmrs_l2_init(void)
{
if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_apq8064()) {
@@ -1085,6 +1098,9 @@
msm_rpmrs_l2_cache.aggregate = NULL;
msm_rpmrs_l2_cache.restore = NULL;
}
+
+ msm_pm_set_sleep_ops(&msm_rpmrs_ops);
+
return 0;
}
early_initcall(msm_rpmrs_l2_init);
diff --git a/arch/arm/mach-msm/rpm_resources.h b/arch/arm/mach-msm/rpm_resources.h
index a5c61b2..d594405 100644
--- a/arch/arm/mach-msm/rpm_resources.h
+++ b/arch/arm/mach-msm/rpm_resources.h
@@ -137,16 +137,6 @@
}
void msm_rpmrs_show_resources(void);
-
-struct msm_rpmrs_limits *msm_rpmrs_lowest_limits(
- bool from_idle, enum msm_pm_sleep_mode sleep_mode, uint32_t latency_us,
- uint32_t sleep_us);
-
-int msm_rpmrs_enter_sleep(uint32_t sclk_count, struct msm_rpmrs_limits *limits,
- bool from_idle, bool notify_rpm);
-void msm_rpmrs_exit_sleep(struct msm_rpmrs_limits *limits, bool from_idle,
- bool notify_rpm, bool collapsed);
-
int msm_rpmrs_levels_init(struct msm_rpmrs_platform_data *data);
#endif /* __ARCH_ARM_MACH_MSM_RPM_RESOURCES_H */
diff --git a/arch/arm/mach-msm/timer.h b/arch/arm/mach-msm/timer.h
index 368dd7b..5d18bb4 100644
--- a/arch/arm/mach-msm/timer.h
+++ b/arch/arm/mach-msm/timer.h
@@ -17,12 +17,12 @@
extern struct sys_timer msm_timer;
void __iomem *msm_timer_get_timer0_base(void);
-int64_t msm_timer_get_sclk_time(int64_t *period);
uint32_t msm_timer_get_sclk_ticks(void);
int msm_timer_init_time_sync(void (*timeout)(void));
#ifndef CONFIG_ARM_ARCH_TIMER
int64_t msm_timer_enter_idle(void);
void msm_timer_exit_idle(int low_power);
+int64_t msm_timer_get_sclk_time(int64_t *period);
#else
static inline int64_t msm_timer_enter_idle(void) { return 0; }
static inline void msm_timer_exit_idle(int low_power) { return; }
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 58bf45d..aaa2086 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -132,4 +132,6 @@
source "drivers/clocksource/Kconfig"
+source "drivers/gud/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 82008ed..13dc06e 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -126,3 +126,6 @@
obj-y += clk/
obj-$(CONFIG_HWSPINLOCK) += hwspinlock/
+
+#MobiCore
+obj-$(CONFIG_MOBICORE_SUPPORT) += gud/
diff --git a/drivers/bluetooth/hci_smd.c b/drivers/bluetooth/hci_smd.c
index 8f51e2d..18cef72 100644
--- a/drivers/bluetooth/hci_smd.c
+++ b/drivers/bluetooth/hci_smd.c
@@ -22,6 +22,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/errno.h>
+#include <linux/semaphore.h>
#include <linux/string.h>
#include <linux/skbuff.h>
#include <linux/wakelock.h>
@@ -43,7 +44,9 @@
static int hcismd_set;
-static DEFINE_MUTEX(hci_smd_enable);
+static DEFINE_SEMAPHORE(hci_smd_enable);
+
+static int restart_in_progress;
static int hcismd_set_enable(const char *val, struct kernel_param *kp);
module_param_call(hcismd_set, hcismd_set_enable, NULL, &hcismd_set, 0644);
@@ -496,18 +499,24 @@
static void hci_dev_restart(struct work_struct *worker)
{
- mutex_lock(&hci_smd_enable);
+ down(&hci_smd_enable);
+ restart_in_progress = 1;
hci_smd_deregister_dev(&hs);
hci_smd_register_smd(&hs);
- mutex_unlock(&hci_smd_enable);
+ up(&hci_smd_enable);
kfree(worker);
}
static void hci_dev_smd_open(struct work_struct *worker)
{
- mutex_lock(&hci_smd_enable);
+ down(&hci_smd_enable);
+ if (restart_in_progress == 1) {
+ /* Allow wcnss to initialize */
+ restart_in_progress = 0;
+ msleep(10000);
+ }
hci_smd_hci_register_dev(&hs);
- mutex_unlock(&hci_smd_enable);
+ up(&hci_smd_enable);
kfree(worker);
}
@@ -515,7 +524,9 @@
{
int ret = 0;
- mutex_lock(&hci_smd_enable);
+ pr_err("hcismd_set_enable %d", hcismd_set);
+
+ down(&hci_smd_enable);
ret = param_set_int(val, kp);
@@ -525,7 +536,8 @@
switch (hcismd_set) {
case 1:
- hci_smd_register_smd(&hs);
+ if (hs.hdev == NULL)
+ hci_smd_register_smd(&hs);
break;
case 0:
hci_smd_deregister_dev(&hs);
@@ -535,7 +547,7 @@
}
done:
- mutex_unlock(&hci_smd_enable);
+ up(&hci_smd_enable);
return ret;
}
static int __init hci_smd_init(void)
@@ -544,6 +556,8 @@
"msm_smd_Rx");
wake_lock_init(&hs.wake_lock_tx, WAKE_LOCK_SUSPEND,
"msm_smd_Tx");
+ restart_in_progress = 0;
+ hs.hdev = NULL;
return 0;
}
module_init(hci_smd_init);
diff --git a/drivers/char/diag/Kconfig b/drivers/char/diag/Kconfig
index 53df29b..8f8707f 100644
--- a/drivers/char/diag/Kconfig
+++ b/drivers/char/diag/Kconfig
@@ -32,10 +32,10 @@
menu "HSIC support for DIAG"
-config DIAG_HSIC_PIPE
+config DIAG_BRIDGE_CODE
depends on USB_QCOM_DIAG_BRIDGE
default y
- bool "Enable 9K DIAG traffic over HSIC"
+ bool "Enable QSC/9K DIAG traffic over SMUX/HSIC"
help
- HSIC Transport Layer for DIAG Router
+ SMUX/HSIC Transport Layer for DIAG Router
endmenu
diff --git a/drivers/char/diag/Makefile b/drivers/char/diag/Makefile
index 3181d29..ea75ffd 100644
--- a/drivers/char/diag/Makefile
+++ b/drivers/char/diag/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_DIAG_CHAR) := diagchar.o
obj-$(CONFIG_DIAG_SDIO_PIPE) += diagfwd_sdio.o
-obj-$(CONFIG_DIAG_HSIC_PIPE) += diagfwd_hsic.o
+obj-$(CONFIG_DIAG_BRIDGE_CODE) += diagfwd_hsic.o
+obj-$(CONFIG_DIAG_BRIDGE_CODE) += diagfwd_smux.o
diagchar-objs := diagchar_core.o diagchar_hdlc.o diagfwd.o diagmem.o diagfwd_cntl.o diag_dci.o
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 49d687d..7e7b514 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -41,6 +41,7 @@
#define SDIO_DATA 4
#define WCNSS_DATA 5
#define HSIC_DATA 6
+#define SMUX_DATA 7
#define MODEM_PROC 0
#define APPS_PROC 1
#define QDSP_PROC 2
@@ -254,24 +255,30 @@
struct diag_request *usb_read_mdm_ptr;
struct diag_request *write_ptr_mdm;
#endif
-#ifdef CONFIG_DIAG_HSIC_PIPE
+#ifdef CONFIG_DIAG_BRIDGE_CODE
+ /* SGLTE variables */
+ int lcid;
+ unsigned char *buf_in_smux;
+ int in_busy_smux;
+ int diag_smux_enabled;
+ /* HSIC variables */
unsigned char *buf_in_hsic;
- unsigned char *usb_buf_mdm_out;
- int hsic_initialized;
int hsic_ch;
int hsic_device_enabled;
int hsic_device_opened;
int hsic_suspend;
- int read_len_mdm;
int in_busy_hsic_read_on_device;
int in_busy_hsic_write_on_device;
int in_busy_hsic_write;
int in_busy_hsic_read;
- int usb_mdm_connected;
- struct usb_diag_ch *mdm_ch;
- struct workqueue_struct *diag_hsic_wq;
- struct work_struct diag_read_mdm_work;
struct work_struct diag_read_hsic_work;
+ /* USB MDM channel variables */
+ int usb_mdm_connected;
+ int read_len_mdm;
+ unsigned char *usb_buf_mdm_out;
+ struct usb_diag_ch *mdm_ch;
+ struct workqueue_struct *diag_bridge_wq;
+ struct work_struct diag_read_mdm_work;
struct work_struct diag_disconnect_work;
struct work_struct diag_usb_read_complete_work;
struct diag_request *usb_read_mdm_ptr;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 7b7549a..8d6a607 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -32,8 +32,9 @@
#ifdef CONFIG_DIAG_SDIO_PIPE
#include "diagfwd_sdio.h"
#endif
-#ifdef CONFIG_DIAG_HSIC_PIPE
+#ifdef CONFIG_DIAG_BRIDGE_CODE
#include "diagfwd_hsic.h"
+#include "diagfwd_smux.h"
#endif
#include <linux/timer.h>
@@ -234,9 +235,9 @@
if (driver->logging_process_id == current->tgid) {
driver->logging_mode = USB_MODE;
diagfwd_connect();
-#ifdef CONFIG_DIAG_HSIC_PIPE
+#ifdef CONFIG_DIAG_BRIDGE_CODE
diagfwd_cancel_hsic();
- diagfwd_connect_hsic(0);
+ diagfwd_connect_bridge(0);
#endif
}
#endif /* DIAG over USB */
@@ -481,8 +482,8 @@
#ifdef CONFIG_DIAG_SDIO_PIPE
driver->in_busy_sdio = 1;
#endif
-#ifdef CONFIG_DIAG_HSIC_PIPE
- diagfwd_disconnect_hsic(0);
+#ifdef CONFIG_DIAG_BRIDGE_CODE
+ diagfwd_disconnect_bridge(0);
#endif
} else if (temp == NO_LOGGING_MODE && driver->logging_mode
== MEMORY_DEVICE_MODE) {
@@ -509,22 +510,22 @@
queue_work(driver->diag_sdio_wq,
&(driver->diag_read_sdio_work));
#endif
-#ifdef CONFIG_DIAG_HSIC_PIPE
- diagfwd_connect_hsic(0);
+#ifdef CONFIG_DIAG_BRIDGE_CODE
+ diagfwd_connect_bridge(0);
#endif
}
#ifdef CONFIG_DIAG_OVER_USB
else if (temp == USB_MODE && driver->logging_mode
== NO_LOGGING_MODE) {
diagfwd_disconnect();
-#ifdef CONFIG_DIAG_HSIC_PIPE
- diagfwd_disconnect_hsic(0);
+#ifdef CONFIG_DIAG_BRIDGE_CODE
+ diagfwd_disconnect_bridge(0);
#endif
} else if (temp == NO_LOGGING_MODE && driver->logging_mode
== USB_MODE) {
diagfwd_connect();
-#ifdef CONFIG_DIAG_HSIC_PIPE
- diagfwd_connect_hsic(0);
+#ifdef CONFIG_DIAG_BRIDGE_CODE
+ diagfwd_connect_bridge(0);
#endif
} else if (temp == USB_MODE && driver->logging_mode
== MEMORY_DEVICE_MODE) {
@@ -552,16 +553,16 @@
queue_work(driver->diag_sdio_wq,
&(driver->diag_read_sdio_work));
#endif
-#ifdef CONFIG_DIAG_HSIC_PIPE
+#ifdef CONFIG_DIAG_BRIDGE_CODE
diagfwd_cancel_hsic();
- diagfwd_connect_hsic(0);
+ diagfwd_connect_bridge(0);
#endif
} else if (temp == MEMORY_DEVICE_MODE &&
driver->logging_mode == USB_MODE) {
diagfwd_connect();
-#ifdef CONFIG_DIAG_HSIC_PIPE
+#ifdef CONFIG_DIAG_BRIDGE_CODE
diagfwd_cancel_hsic();
- diagfwd_connect_hsic(0);
+ diagfwd_connect_bridge(0);
#endif
}
#endif /* DIAG over USB */
@@ -720,7 +721,7 @@
driver->in_busy_sdio = 0;
}
#endif
-#ifdef CONFIG_DIAG_HSIC_PIPE
+#ifdef CONFIG_DIAG_BRIDGE_CODE
pr_debug("diag: Copy data to user space %d\n",
driver->in_busy_hsic_write_on_device);
if (driver->in_busy_hsic_write_on_device == 1) {
@@ -898,7 +899,7 @@
}
}
#endif
-#ifdef CONFIG_DIAG_HSIC_PIPE
+#ifdef CONFIG_DIAG_BRIDGE_CODE
/* send masks to 9k too */
if (driver->hsic_ch && (payload_size > 0)) {
/* wait sending mask updates if HSIC ch not ready */
@@ -1199,16 +1200,16 @@
inline void diag_sdio_fn(int type) {}
#endif
-#ifdef CONFIG_DIAG_HSIC_PIPE
-void diag_hsic_fn(int type)
+#ifdef CONFIG_DIAG_BRIDGE_CODE
+void diag_bridge_fn(int type)
{
if (type == INIT)
- diagfwd_hsic_init();
+ diagfwd_bridge_init();
else if (type == EXIT)
- diagfwd_hsic_exit();
+ diagfwd_bridge_exit();
}
#else
-inline void diag_hsic_fn(int type) {}
+inline void diag_bridge_fn(int type) {}
#endif
static int __init diagchar_init(void)
@@ -1255,7 +1256,7 @@
diagfwd_cntl_init();
driver->dci_state = diag_dci_init();
diag_sdio_fn(INIT);
- diag_hsic_fn(INIT);
+ diag_bridge_fn(INIT);
pr_debug("diagchar initializing ..\n");
driver->num = 1;
driver->name = ((void *)driver) + sizeof(struct diagchar_dev);
@@ -1289,7 +1290,7 @@
diagfwd_exit();
diagfwd_cntl_exit();
diag_sdio_fn(EXIT);
- diag_hsic_fn(EXIT);
+ diag_bridge_fn(EXIT);
return -1;
}
@@ -1302,7 +1303,7 @@
diagfwd_exit();
diagfwd_cntl_exit();
diag_sdio_fn(EXIT);
- diag_hsic_fn(EXIT);
+ diag_bridge_fn(EXIT);
diag_debugfs_cleanup();
diagchar_cleanup();
printk(KERN_INFO "done diagchar exit\n");
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 4ac2643..a920f56 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -300,12 +300,12 @@
&(driver->diag_read_sdio_work));
}
#endif
-#ifdef CONFIG_DIAG_HSIC_PIPE
+#ifdef CONFIG_DIAG_BRIDGE_CODE
else if (proc_num == HSIC_DATA) {
driver->in_busy_hsic_read = 0;
driver->in_busy_hsic_write_on_device = 0;
if (driver->hsic_ch)
- queue_work(driver->diag_hsic_wq,
+ queue_work(driver->diag_bridge_wq,
&(driver->diag_read_hsic_work));
}
#endif
@@ -352,7 +352,7 @@
"while USB write\n");
}
#endif
-#ifdef CONFIG_DIAG_HSIC_PIPE
+#ifdef CONFIG_DIAG_BRIDGE_CODE
else if (proc_num == HSIC_DATA) {
if (driver->hsic_device_enabled) {
write_ptr->buf = buf;
@@ -360,6 +360,10 @@
} else
pr_err("diag: Incorrect hsic data "
"while USB write\n");
+ } else if (proc_num == SMUX_DATA) {
+ write_ptr->buf = buf;
+ pr_debug("diag: writing SMUX data\n");
+ err = usb_diag_write(driver->mdm_ch, write_ptr);
}
#endif
APPEND_DEBUG('d');
diff --git a/drivers/char/diag/diagfwd_hsic.c b/drivers/char/diag/diagfwd_hsic.c
index a3c6f26..d54d3dc 100644
--- a/drivers/char/diag/diagfwd_hsic.c
+++ b/drivers/char/diag/diagfwd_hsic.c
@@ -19,6 +19,7 @@
#include <linux/workqueue.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
+#include <linux/smux.h>
#include <asm/current.h>
#ifdef CONFIG_DIAG_OVER_USB
#include <mach/usbdiag.h>
@@ -28,6 +29,7 @@
#include "diagchar.h"
#include "diagfwd.h"
#include "diagfwd_hsic.h"
+#include "diagfwd_smux.h"
static void diag_read_hsic_work_fn(struct work_struct *work)
{
@@ -71,7 +73,8 @@
* the next read
*/
if (!driver->in_busy_hsic_read)
- queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work);
+ queue_work(driver->diag_bridge_wq,
+ &driver->diag_read_hsic_work);
}
static void diag_hsic_read_complete_callback(void *ctxt, char *buf,
@@ -114,7 +117,8 @@
if (!driver->in_busy_hsic_write_on_device && ((driver->logging_mode
== MEMORY_DEVICE_MODE) || (driver->usb_mdm_connected &&
!driver->hsic_suspend)))
- queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work);
+ queue_work(driver->diag_bridge_wq,
+ &driver->diag_read_hsic_work);
}
static void diag_hsic_write_complete_callback(void *ctxt, char *buf,
@@ -132,7 +136,7 @@
pr_err("DIAG in %s: actual_size: %d\n", __func__, actual_size);
if (driver->usb_mdm_connected)
- queue_work(driver->diag_hsic_wq, &driver->diag_read_mdm_work);
+ queue_work(driver->diag_bridge_wq, &driver->diag_read_mdm_work);
}
static int diag_hsic_suspend(void *ctxt)
@@ -157,7 +161,8 @@
if (!driver->in_busy_hsic_write_on_device && (driver->logging_mode
== MEMORY_DEVICE_MODE || driver->usb_mdm_connected))
- queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work);
+ queue_work(driver->diag_bridge_wq,
+ &driver->diag_read_hsic_work);
}
static struct diag_bridge_ops hsic_diag_bridge_ops = {
@@ -209,42 +214,48 @@
return 0;
}
-/* diagfwd_connect_hsic is called when the USB mdm channel is connected */
-int diagfwd_connect_hsic(int process_cable)
+/* diagfwd_connect_bridge is called when the USB mdm channel is connected */
+int diagfwd_connect_bridge(int process_cable)
{
int err;
- pr_debug("DIAG in %s\n", __func__);
+ pr_debug("diag: in %s\n", __func__);
/* If the usb cable is being connected */
if (process_cable) {
err = usb_diag_alloc_req(driver->mdm_ch, N_MDM_WRITE,
N_MDM_READ);
if (err)
- pr_err("DIAG: unable to alloc USB req on mdm"
+ pr_err("diag: unable to alloc USB req on mdm"
" ch err:%d\n", err);
driver->usb_mdm_connected = 1;
}
- driver->in_busy_hsic_write_on_device = 0;
- driver->in_busy_hsic_read_on_device = 0;
- driver->in_busy_hsic_write = 0;
- driver->in_busy_hsic_read = 0;
+ if (driver->hsic_device_enabled) {
+ driver->in_busy_hsic_write_on_device = 0;
+ driver->in_busy_hsic_read_on_device = 0;
+ driver->in_busy_hsic_write = 0;
+ driver->in_busy_hsic_read = 0;
+ } else if (driver->diag_smux_enabled) {
+ driver->in_busy_smux = 0;
+ diagfwd_connect_smux();
+ return 0;
+ }
/* If the hsic (diag_bridge) platform device is not open */
if (driver->hsic_device_enabled) {
if (!driver->hsic_device_opened) {
err = diag_bridge_open(&hsic_diag_bridge_ops);
if (err) {
- pr_err("DIAG: HSIC channel open error: %d\n",
+ pr_err("diag: HSIC channel open error: %d\n",
err);
} else {
- pr_debug("DIAG: opened HSIC channel\n");
+ pr_debug("diag: opened HSIC channel\n");
driver->hsic_device_opened = 1;
}
} else {
- pr_debug("DIAG: HSIC channel already open\n");
+ pr_debug("diag: HSIC channel already open\n");
}
/*
@@ -256,24 +267,25 @@
/* Poll USB mdm channel to check for data */
if (driver->logging_mode == USB_MODE)
- queue_work(driver->diag_hsic_wq,
+ queue_work(driver->diag_bridge_wq,
&driver->diag_read_mdm_work);
/* Poll HSIC channel to check for data */
- queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work);
+ queue_work(driver->diag_bridge_wq,
+ &driver->diag_read_hsic_work);
} else {
/* The hsic device driver has not yet been enabled */
- pr_info("DIAG: HSIC channel not yet enabled\n");
+ pr_info("diag: HSIC channel not yet enabled\n");
}
return 0;
}
/*
- * diagfwd_disconnect_hsic is called when the USB mdm channel
+ * diagfwd_disconnect_bridge is called when the USB mdm channel
* is disconnected
*/
-int diagfwd_disconnect_hsic(int process_cable)
+int diagfwd_disconnect_bridge(int process_cable)
{
pr_debug("DIAG in %s\n", __func__);
@@ -284,12 +296,19 @@
}
if (driver->logging_mode != MEMORY_DEVICE_MODE) {
- driver->in_busy_hsic_write_on_device = 1;
- driver->in_busy_hsic_read_on_device = 1;
- driver->in_busy_hsic_write = 1;
- driver->in_busy_hsic_read = 1;
- /* Turn off communication over usb mdm and hsic */
- return diag_hsic_close();
+ if (driver->hsic_device_enabled) {
+ driver->in_busy_hsic_write_on_device = 1;
+ driver->in_busy_hsic_read_on_device = 1;
+ driver->in_busy_hsic_write = 1;
+ driver->in_busy_hsic_read = 1;
+ /* Turn off communication over usb mdm and hsic */
+ return diag_hsic_close();
+ } else if (driver->diag_smux_enabled) {
+ driver->in_busy_smux = 1;
+ driver->lcid = LCID_INVALID;
+ /* Turn off communication over usb mdm and smux */
+ msm_smux_close(LCID_VALID);
+ }
}
return 0;
}
@@ -313,18 +332,23 @@
APPEND_DEBUG('q');
/* Read data from the hsic */
- queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work);
+ queue_work(driver->diag_bridge_wq, &driver->diag_read_hsic_work);
return 0;
}
/* Called after the asychronous usb_diag_read() on mdm channel is complete */
-static int diagfwd_read_complete_hsic(struct diag_request *diag_read_ptr)
+static int diagfwd_read_complete_bridge(struct diag_request *diag_read_ptr)
{
/* The read of the usb driver on the mdm (not hsic) has completed */
driver->in_busy_hsic_read_on_device = 0;
driver->read_len_mdm = diag_read_ptr->actual;
+ if (driver->diag_smux_enabled) {
+ diagfwd_read_complete_smux();
+ return 0;
+ }
+ /* If SMUX not enabled, check for HSIC */
if (!driver->hsic_ch) {
pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
return 0;
@@ -366,30 +390,34 @@
* hsic channel
*/
if (!driver->in_busy_hsic_write)
- queue_work(driver->diag_hsic_wq, &driver->diag_read_mdm_work);
+ queue_work(driver->diag_bridge_wq, &driver->diag_read_mdm_work);
return 0;
}
-static void diagfwd_hsic_notifier(void *priv, unsigned event,
+static void diagfwd_bridge_notifier(void *priv, unsigned event,
struct diag_request *d_req)
{
switch (event) {
case USB_DIAG_CONNECT:
- diagfwd_connect_hsic(1);
+ diagfwd_connect_bridge(1);
break;
case USB_DIAG_DISCONNECT:
- queue_work(driver->diag_hsic_wq, &driver->diag_disconnect_work);
+ queue_work(driver->diag_bridge_wq,
+ &driver->diag_disconnect_work);
break;
case USB_DIAG_READ_DONE:
- queue_work(driver->diag_hsic_wq,
+ queue_work(driver->diag_bridge_wq,
&driver->diag_usb_read_complete_work);
break;
case USB_DIAG_WRITE_DONE:
- diagfwd_write_complete_hsic();
+ if (driver->hsic_device_enabled)
+ diagfwd_write_complete_hsic();
+ else if (driver->diag_smux_enabled)
+ diagfwd_write_complete_smux();
break;
default:
- pr_err("DIAG in %s: Unknown event from USB diag:%u\n",
+ pr_err("diag: in %s: Unknown event from USB diag:%u\n",
__func__, event);
break;
}
@@ -397,16 +425,33 @@
static void diag_usb_read_complete_fn(struct work_struct *w)
{
- diagfwd_read_complete_hsic(driver->usb_read_mdm_ptr);
+ diagfwd_read_complete_bridge(driver->usb_read_mdm_ptr);
}
static void diag_disconnect_work_fn(struct work_struct *w)
{
- diagfwd_disconnect_hsic(1);
+ diagfwd_disconnect_bridge(1);
}
static void diag_read_mdm_work_fn(struct work_struct *work)
{
+ int ret;
+ if (driver->diag_smux_enabled) {
+ if (driver->lcid && driver->usb_buf_mdm_out &&
+ (driver->read_len_mdm > 0)) {
+ ret = msm_smux_write(driver->lcid, NULL,
+ driver->usb_buf_mdm_out, driver->read_len_mdm);
+ if (ret)
+ pr_err("diag: writing to SMUX ch, r = %d,"
+ "lcid = %d\n", ret, driver->lcid);
+ }
+ driver->usb_read_mdm_ptr->buf = driver->usb_buf_mdm_out;
+ driver->usb_read_mdm_ptr->length = USB_MAX_OUT_BUF;
+ usb_diag_read(driver->mdm_ch, driver->usb_read_mdm_ptr);
+ return;
+ }
+
+ /* if SMUX not enabled, check for HSIC */
if (!driver->hsic_ch) {
pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
return;
@@ -434,7 +479,8 @@
* queue up the reading of data from the mdm channel
*/
if (!driver->in_busy_hsic_read_on_device)
- queue_work(driver->diag_hsic_wq, &driver->diag_read_mdm_work);
+ queue_work(driver->diag_bridge_wq,
+ &driver->diag_read_mdm_work);
}
static int diag_hsic_probe(struct platform_device *pdev)
@@ -442,31 +488,10 @@
int err = 0;
pr_debug("diag: in %s\n", __func__);
if (!driver->hsic_device_enabled) {
- driver->read_len_mdm = 0;
if (driver->buf_in_hsic == NULL)
driver->buf_in_hsic = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
- if (driver->buf_in_hsic == NULL)
- goto err;
- if (driver->usb_buf_mdm_out == NULL)
- driver->usb_buf_mdm_out = kzalloc(USB_MAX_OUT_BUF,
- GFP_KERNEL);
- if (driver->usb_buf_mdm_out == NULL)
- goto err;
- if (driver->write_ptr_mdm == NULL)
- driver->write_ptr_mdm = kzalloc(
- sizeof(struct diag_request), GFP_KERNEL);
- if (driver->write_ptr_mdm == NULL)
- goto err;
- if (driver->usb_read_mdm_ptr == NULL)
- driver->usb_read_mdm_ptr = kzalloc(
- sizeof(struct diag_request), GFP_KERNEL);
- if (driver->usb_read_mdm_ptr == NULL)
- goto err;
-#ifdef CONFIG_DIAG_OVER_USB
- INIT_WORK(&(driver->diag_read_mdm_work), diag_read_mdm_work_fn);
-#endif
INIT_WORK(&(driver->diag_read_hsic_work),
- diag_read_hsic_work_fn);
+ diag_read_hsic_work_fn);
driver->hsic_device_enabled = 1;
}
@@ -495,25 +520,16 @@
if (driver->usb_mdm_connected) {
/* Poll USB mdm channel to check for data */
- queue_work(driver->diag_hsic_wq,
+ queue_work(driver->diag_bridge_wq,
&driver->diag_read_mdm_work);
}
/* Poll HSIC channel to check for data */
- queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work);
+ queue_work(driver->diag_bridge_wq,
+ &driver->diag_read_hsic_work);
}
return err;
-err:
- pr_err("DIAG could not initialize buf for HSIC\n");
- kfree(driver->buf_in_hsic);
- kfree(driver->usb_buf_mdm_out);
- kfree(driver->write_ptr_mdm);
- kfree(driver->usb_read_mdm_ptr);
- if (driver->diag_hsic_wq)
- destroy_workqueue(driver->diag_hsic_wq);
-
- return -ENOMEM;
}
static int diag_hsic_remove(struct platform_device *pdev)
@@ -550,55 +566,93 @@
},
};
-void diagfwd_hsic_init(void)
+void diagfwd_bridge_init(void)
{
int ret;
- pr_debug("DIAG in %s\n", __func__);
+ pr_debug("diag: in %s\n", __func__);
+ driver->diag_bridge_wq = create_singlethread_workqueue(
+ "diag_bridge_wq");
+ driver->read_len_mdm = 0;
+ if (driver->usb_buf_mdm_out == NULL)
+ driver->usb_buf_mdm_out = kzalloc(USB_MAX_OUT_BUF,
+ GFP_KERNEL);
+ if (driver->usb_buf_mdm_out == NULL)
+ goto err;
+ if (driver->write_ptr_mdm == NULL)
+ driver->write_ptr_mdm = kzalloc(
+ sizeof(struct diag_request), GFP_KERNEL);
+ if (driver->write_ptr_mdm == NULL)
+ goto err;
+ if (driver->usb_read_mdm_ptr == NULL)
+ driver->usb_read_mdm_ptr = kzalloc(
+ sizeof(struct diag_request), GFP_KERNEL);
+ if (driver->usb_read_mdm_ptr == NULL)
+ goto err;
- driver->diag_hsic_wq = create_singlethread_workqueue("diag_hsic_wq");
+#ifdef CONFIG_DIAG_OVER_USB
+ INIT_WORK(&(driver->diag_read_mdm_work), diag_read_mdm_work_fn);
+#endif
INIT_WORK(&(driver->diag_disconnect_work), diag_disconnect_work_fn);
INIT_WORK(&(driver->diag_usb_read_complete_work),
diag_usb_read_complete_fn);
-
#ifdef CONFIG_DIAG_OVER_USB
- driver->mdm_ch = usb_diag_open(DIAG_MDM, driver, diagfwd_hsic_notifier);
+ driver->mdm_ch = usb_diag_open(DIAG_MDM, driver,
+ diagfwd_bridge_notifier);
if (IS_ERR(driver->mdm_ch)) {
- pr_err("DIAG Unable to open USB diag MDM channel\n");
+ pr_err("diag: Unable to open USB diag MDM channel\n");
goto err;
}
#endif
+#ifdef CONFIG_DIAG_BRIDGE_CODE
+ INIT_WORK(&(driver->diag_disconnect_work), diag_disconnect_work_fn);
+ INIT_WORK(&(driver->diag_usb_read_complete_work),
+ diag_usb_read_complete_fn);
+ /* register HSIC device */
ret = platform_driver_register(&msm_hsic_ch_driver);
if (ret)
- pr_err("DIAG could not register HSIC device, ret: %d\n", ret);
- else
- driver->hsic_initialized = 1;
-
+ pr_err("diag: could not register HSIC device, ret: %d\n", ret);
+ /* register SMUX device */
+ ret = platform_driver_register(&msm_diagfwd_smux_driver);
+ if (ret)
+ pr_err("diag: could not register SMUX device, ret: %d\n", ret);
+#endif
return;
err:
- pr_err("DIAG could not initialize for HSIC execution\n");
-}
-
-void diagfwd_hsic_exit(void)
-{
- pr_debug("DIAG in %s\n", __func__);
-
- if (driver->hsic_initialized)
- diag_hsic_close();
-
-#ifdef CONFIG_DIAG_OVER_USB
- if (driver->usb_mdm_connected)
- usb_diag_free_req(driver->mdm_ch);
-#endif
- platform_driver_unregister(&msm_hsic_ch_driver);
-#ifdef CONFIG_DIAG_OVER_USB
- usb_diag_close(driver->mdm_ch);
-#endif
- kfree(driver->buf_in_hsic);
+ pr_err("diag: Could not initialize for bridge forwarding\n");
kfree(driver->usb_buf_mdm_out);
kfree(driver->write_ptr_mdm);
kfree(driver->usb_read_mdm_ptr);
- destroy_workqueue(driver->diag_hsic_wq);
+ if (driver->diag_bridge_wq)
+ destroy_workqueue(driver->diag_bridge_wq);
- driver->hsic_device_enabled = 0;
+ return;
+}
+
+void diagfwd_bridge_exit(void)
+{
+ pr_debug("diag: in %s\n", __func__);
+
+ if (driver->hsic_device_enabled) {
+ diag_hsic_close();
+ kfree(driver->buf_in_hsic);
+ driver->hsic_device_enabled = 0;
+ }
+ if (driver->diag_smux_enabled) {
+ driver->lcid = LCID_INVALID;
+ kfree(driver->buf_in_smux);
+ driver->diag_smux_enabled = 0;
+ }
+ platform_driver_unregister(&msm_hsic_ch_driver);
+ platform_driver_unregister(&msm_diagfwd_smux_driver);
+ /* destroy USB MDM specific variables */
+#ifdef CONFIG_DIAG_OVER_USB
+ if (driver->usb_mdm_connected)
+ usb_diag_free_req(driver->mdm_ch);
+ usb_diag_close(driver->mdm_ch);
+#endif
+ kfree(driver->usb_buf_mdm_out);
+ kfree(driver->write_ptr_mdm);
+ kfree(driver->usb_read_mdm_ptr);
+ destroy_workqueue(driver->diag_bridge_wq);
}
diff --git a/drivers/char/diag/diagfwd_hsic.h b/drivers/char/diag/diagfwd_hsic.h
index 8785d9f..b189c94 100644
--- a/drivers/char/diag/diagfwd_hsic.h
+++ b/drivers/char/diag/diagfwd_hsic.h
@@ -17,11 +17,11 @@
#define N_MDM_WRITE 1 /* Upgrade to 2 with ping pong buffer */
#define N_MDM_READ 1
-int diagfwd_connect_hsic(int);
-int diagfwd_disconnect_hsic(int);
+int diagfwd_connect_bridge(int);
+int diagfwd_disconnect_bridge(int);
int diagfwd_write_complete_hsic(void);
int diagfwd_cancel_hsic(void);
-void diagfwd_hsic_init(void);
-void diagfwd_hsic_exit(void);
+void diagfwd_bridge_init(void);
+void diagfwd_bridge_exit(void);
#endif
diff --git a/drivers/char/diag/diagfwd_smux.c b/drivers/char/diag/diagfwd_smux.c
new file mode 100644
index 0000000..8bbc67e
--- /dev/null
+++ b/drivers/char/diag/diagfwd_smux.c
@@ -0,0 +1,156 @@
+/* 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/termios.h>
+#include <linux/slab.h>
+#include <linux/diagchar.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <mach/usbdiag.h>
+#include "diagchar.h"
+#include "diagfwd.h"
+#include "diagfwd_smux.h"
+
+void diag_smux_event(void *priv, int event_type, const void *metadata)
+{
+ unsigned char *rx_buf;
+ int len;
+
+ switch (event_type) {
+ case SMUX_CONNECTED:
+ pr_debug("diag: SMUX_CONNECTED received\n");
+ driver->in_busy_smux = 0;
+ /* read data from USB MDM channel & Initiate first write */
+ queue_work(driver->diag_bridge_wq,
+ &(driver->diag_read_mdm_work));
+ break;
+ case SMUX_DISCONNECTED:
+ pr_info("diag: SMUX_DISCONNECTED received\n");
+ break;
+ case SMUX_WRITE_DONE:
+ pr_debug("diag: SMUX Write done\n");
+ break;
+ case SMUX_WRITE_FAIL:
+ pr_info("diag: SMUX Write Failed\n");
+ break;
+ case SMUX_READ_FAIL:
+ pr_info("diag: SMUX Read Failed\n");
+ break;
+ case SMUX_READ_DONE:
+ len = ((struct smux_meta_read *)metadata)->len;
+ rx_buf = ((struct smux_meta_read *)metadata)->buffer;
+ driver->write_ptr_mdm->length = len;
+ diag_device_write(driver->buf_in_smux, SMUX_DATA,
+ driver->write_ptr_mdm);
+ break;
+ };
+}
+
+int diagfwd_write_complete_smux(void)
+{
+ pr_debug("diag: clear in_busy_smux\n");
+ driver->in_busy_smux = 0;
+ return 0;
+}
+
+int diagfwd_read_complete_smux(void)
+{
+ queue_work(driver->diag_bridge_wq, &(driver->diag_read_mdm_work));
+ return 0;
+}
+
+int diag_get_rx_buffer(void *priv, void **pkt_priv, void **buffer, int size)
+{
+ if (!driver->in_busy_smux) {
+ *pkt_priv = (void *)0x1234;
+ *buffer = driver->buf_in_smux;
+ pr_debug("diag: set in_busy_smux as 1\n");
+ driver->in_busy_smux = 1;
+ } else {
+ pr_debug("diag: read buffer for SMUX is BUSY\n");
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+static int diagfwd_smux_runtime_suspend(struct device *dev)
+{
+ dev_dbg(dev, "pm_runtime: suspending...\n");
+ return 0;
+}
+
+static int diagfwd_smux_runtime_resume(struct device *dev)
+{
+ dev_dbg(dev, "pm_runtime: resuming...\n");
+ return 0;
+}
+
+static const struct dev_pm_ops diagfwd_smux_dev_pm_ops = {
+ .runtime_suspend = diagfwd_smux_runtime_suspend,
+ .runtime_resume = diagfwd_smux_runtime_resume,
+};
+
+int diagfwd_connect_smux(void)
+{
+ void *priv = NULL;
+ int ret = 0;
+
+ if (driver->lcid == LCID_INVALID) {
+ ret = msm_smux_open(LCID_VALID, priv, diag_smux_event,
+ diag_get_rx_buffer);
+ if (!ret) {
+ driver->lcid = LCID_VALID;
+ msm_smux_tiocm_set(driver->lcid, TIOCM_DTR, 0);
+ pr_info("diag: open SMUX ch, r = %d\n", ret);
+ } else {
+ pr_err("diag: failed to open SMUX ch, r = %d\n", ret);
+ }
+ }
+ /* Poll USB channel to check for data*/
+ queue_work(driver->diag_bridge_wq, &(driver->diag_read_mdm_work));
+ return ret;
+}
+
+static int diagfwd_smux_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ pr_info("diag: SMUX probe called\n");
+ driver->lcid = LCID_INVALID;
+ driver->diag_smux_enabled = 1;
+ if (driver->buf_in_smux == NULL) {
+ driver->buf_in_smux = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+ if (driver->buf_in_smux == NULL)
+ goto err;
+ }
+ /* Only required for Local loopback test
+ * ret = msm_smux_set_ch_option(LCID_VALID,
+ SMUX_CH_OPTION_LOCAL_LOOPBACK, 0);
+ * if (ret)
+ * pr_err("diag: error setting SMUX ch option, r = %d\n", ret);
+ */
+ ret = diagfwd_connect_smux();
+ return ret;
+
+err:
+ pr_err("diag: Could not initialize SMUX buffer\n");
+ kfree(driver->buf_in_smux);
+ return ret;
+}
+
+struct platform_driver msm_diagfwd_smux_driver = {
+ .probe = diagfwd_smux_probe,
+ .driver = {
+ .name = "SMUX_DIAG",
+ .owner = THIS_MODULE,
+ .pm = &diagfwd_smux_dev_pm_ops,
+ },
+};
diff --git a/drivers/char/diag/diagfwd_smux.h b/drivers/char/diag/diagfwd_smux.h
new file mode 100644
index 0000000..e78b7ed
--- /dev/null
+++ b/drivers/char/diag/diagfwd_smux.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef DIAGFWD_SMUX_H
+#define DIAGFWD_SMUX_H
+
+#include <linux/smux.h>
+#define LCID_VALID SMUX_USB_DIAG_0
+#define LCID_INVALID 0
+
+int diagfwd_read_complete_smux(void);
+int diagfwd_write_complete_smux(void);
+int diagfwd_connect_smux(void);
+extern struct platform_driver msm_diagfwd_smux_driver;
+
+#endif
diff --git a/drivers/char/hw_random/msm_rng.c b/drivers/char/hw_random/msm_rng.c
index 974b77e..7e6670d 100644
--- a/drivers/char/hw_random/msm_rng.c
+++ b/drivers/char/hw_random/msm_rng.c
@@ -231,12 +231,19 @@
return 0;
}
+static struct of_device_id qrng_match[] = {
+ { .compatible = "qcom,msm-rng",
+ },
+ {}
+};
+
static struct platform_driver rng_driver = {
.probe = msm_rng_probe,
.remove = __devexit_p(msm_rng_remove),
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
+ .of_match_table = qrng_match,
}
};
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 36375c05..473b0ef 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -281,8 +281,23 @@
}
}
EXPORT_SYMBOL_GPL(cpufreq_notify_transition);
+/**
+ * cpufreq_notify_utilization - notify CPU userspace about CPU utilization
+ * change
+ *
+ * This function is called everytime the CPU load is evaluated by the
+ * ondemand governor. It notifies userspace of cpu load changes via sysfs.
+ */
+void cpufreq_notify_utilization(struct cpufreq_policy *policy,
+ unsigned int util)
+{
+ if (policy)
+ policy->util = util;
+ if (policy->util >= MIN_CPU_UTIL_NOTIFY)
+ sysfs_notify(&policy->kobj, NULL, "cpu_utilization");
+}
/*********************************************************************
* SYSFS INTERFACE *
@@ -370,6 +385,7 @@
show_one(scaling_min_freq, min);
show_one(scaling_max_freq, max);
show_one(scaling_cur_freq, cur);
+show_one(cpu_utilization, util);
static int __cpufreq_set_policy(struct cpufreq_policy *data,
struct cpufreq_policy *policy);
@@ -459,6 +475,8 @@
policy->user_policy.policy = policy->policy;
policy->user_policy.governor = policy->governor;
+ sysfs_notify(&policy->kobj, NULL, "scaling_governor");
+
if (ret)
return ret;
else
@@ -584,6 +602,7 @@
cpufreq_freq_attr_ro(bios_limit);
cpufreq_freq_attr_ro(related_cpus);
cpufreq_freq_attr_ro(affected_cpus);
+cpufreq_freq_attr_ro(cpu_utilization);
cpufreq_freq_attr_rw(scaling_min_freq);
cpufreq_freq_attr_rw(scaling_max_freq);
cpufreq_freq_attr_rw(scaling_governor);
@@ -596,6 +615,7 @@
&scaling_min_freq.attr,
&scaling_max_freq.attr,
&affected_cpus.attr,
+ &cpu_utilization.attr,
&related_cpus.attr,
&scaling_governor.attr,
&scaling_driver.attr,
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index 2d33096..cfc2534 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -552,7 +552,11 @@
static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
{
+ /* Extrapolated load of this CPU */
+ unsigned int load_at_max_freq = 0;
unsigned int max_load_freq;
+ /* Current load across this CPU */
+ unsigned int cur_load = 0;
struct cpufreq_policy *policy;
unsigned int j;
@@ -579,7 +583,7 @@
struct cpu_dbs_info_s *j_dbs_info;
cputime64_t cur_wall_time, cur_idle_time, cur_iowait_time;
unsigned int idle_time, wall_time, iowait_time;
- unsigned int load, load_freq;
+ unsigned int load_freq;
int freq_avg;
j_dbs_info = &per_cpu(od_cpu_dbs_info, j);
@@ -629,16 +633,20 @@
if (unlikely(!wall_time || wall_time < idle_time))
continue;
- load = 100 * (wall_time - idle_time) / wall_time;
+ cur_load = 100 * (wall_time - idle_time) / wall_time;
freq_avg = __cpufreq_driver_getavg(policy, j);
if (freq_avg <= 0)
freq_avg = policy->cur;
- load_freq = load * freq_avg;
+ load_freq = cur_load * freq_avg;
if (load_freq > max_load_freq)
max_load_freq = load_freq;
}
+ /* calculate the scaled load across CPU */
+ load_at_max_freq = (cur_load * policy->cur)/policy->cpuinfo.max_freq;
+
+ cpufreq_notify_utilization(policy, load_at_max_freq);
/* Check for frequency increase */
if (max_load_freq > dbs_tuners_ins.up_threshold * policy->cur) {
diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c
index fff494c..2a191d5 100644
--- a/drivers/crypto/msm/qcedev.c
+++ b/drivers/crypto/msm/qcedev.c
@@ -352,7 +352,7 @@
}
kzfree(handle);
file->private_data = NULL;
- if (podev->platform_support.bus_scale_table != NULL)
+ if (podev != NULL && podev->platform_support.bus_scale_table != NULL)
qcedev_ce_high_bw_req(podev, false);
return 0;
}
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index 21c3aff..63dfc2d 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -888,7 +888,7 @@
ctx->authsize, 1);
} else {
- unsigned char tmp[SHA256_DIGESTSIZE];
+ unsigned char tmp[SHA256_DIGESTSIZE] = {0};
/* compare icv from src */
scatterwalk_map_and_copy(tmp,
diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c
index c239910..83afb25 100644
--- a/drivers/gpu/ion/msm/msm_ion.c
+++ b/drivers/gpu/ion/msm/msm_ion.c
@@ -136,9 +136,11 @@
}
cp_data->virt_addr = fmem_info->virt;
- cp_data->secure_base = heap->base;
- cp_data->secure_size =
+ if (!cp_data->secure_base) {
+ cp_data->secure_base = heap->base;
+ cp_data->secure_size =
heap->size + shared_heap->size;
+ }
} else if (!heap->base) {
ion_set_base_address(heap, shared_heap,
co_heap_data, cp_data);
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 1a34e80..662a1c4 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -2595,11 +2595,14 @@
static void kgsl_core_exit(void)
{
- unregister_chrdev_region(kgsl_driver.major, KGSL_DEVICE_MAX);
-
- kgsl_mmu_ptpool_destroy(&kgsl_driver.ptpool);
+ kgsl_mmu_ptpool_destroy(kgsl_driver.ptpool);
kgsl_driver.ptpool = NULL;
+ kgsl_drm_exit();
+ kgsl_cffdump_destroy();
+ kgsl_core_debugfs_close();
+ kgsl_sharedmem_uninit_sysfs();
+
device_unregister(&kgsl_driver.virtdev);
if (kgsl_driver.class) {
@@ -2607,10 +2610,7 @@
kgsl_driver.class = NULL;
}
- kgsl_drm_exit();
- kgsl_cffdump_destroy();
- kgsl_core_debugfs_close();
- kgsl_sharedmem_uninit_sysfs();
+ unregister_chrdev_region(kgsl_driver.major, KGSL_DEVICE_MAX);
}
static int __init kgsl_core_init(void)
diff --git a/drivers/gud/Kconfig b/drivers/gud/Kconfig
new file mode 100644
index 0000000..3a241b7
--- /dev/null
+++ b/drivers/gud/Kconfig
@@ -0,0 +1,32 @@
+#
+# MobiCore configuration
+#
+config MOBICORE_SUPPORT
+ tristate "Linux MobiCore Support"
+ #depends on ARM_TRUSTZONE
+ ---help---
+ Enable Linux Kernel MobiCore Support
+
+config MOBICORE_DEBUG
+ bool "MobiCore Module debug mode"
+ depends on MOBICORE_SUPPORT
+ ---help---
+ Enable Debug mode in the MobiCore Driver.
+ It enables printing information about mobicore operations
+
+config MOBICORE_VERBOSE
+ bool "MobiCore Module verbose debug mode"
+ depends on MOBICORE_DEBUG
+ ---help---
+ Enable Verbose Debug mode in the MobiCore Driver.
+ It enables printing extra information about mobicore operations
+ Beware: this is only useful for debuging deep in the driver because
+ it prints too much logs
+
+
+config MOBICORE_API
+ tristate "Linux MobiCore API"
+ depends on MOBICORE_SUPPORT
+ ---help---
+ Enable Linux Kernel MobiCore API
+
diff --git a/drivers/gud/Makefile b/drivers/gud/Makefile
new file mode 100644
index 0000000..ea212c5
--- /dev/null
+++ b/drivers/gud/Makefile
@@ -0,0 +1,31 @@
+#
+# Makefile for the kernel mobicore drivers
+#
+GUD_ROOT_FOLDER := drivers/gud
+# add our modules to kernel.
+obj-$(CONFIG_MOBICORE_API) += mckernelapi.o
+obj-$(CONFIG_MOBICORE_SUPPORT) += mcdrvmodule.o
+
+mcdrvmodule-objs := mobicore_driver/logging.o mobicore_driver/main.o
+
+mckernelapi-objs := mobicore_kernelapi/main.o \
+ mobicore_kernelapi/clientlib.o \
+ mobicore_kernelapi/device.o \
+ mobicore_kernelapi/session.o \
+ mobicore_kernelapi/connection.o
+
+# Release mode by default
+ccflags-y := -DNDEBUG
+ccflags-y += -Wno-declaration-after-statement
+
+ccflags-$(CONFIG_MOBICORE_DEBUG) += -DDEBUG
+ccflags-$(CONFIG_MOBICORE_VERBOSE) += -DDEBUG_VERBOSE
+
+# Choose one platform from the folder
+MOBICORE_PLATFORM := $(shell (ls -1 $(PWD)/$(GUD_ROOT_FOLDER)/mobicore_driver/platforms | tail -1) )
+# Use the available platform folder
+ccflags-y += -I$(GUD_ROOT_FOLDER)/mobicore_driver/platforms/$(MOBICORE_PLATFORM)
+
+
+ccflags-y += -I$(GUD_ROOT_FOLDER)/mobicore_driver/public
+ccflags-y += -I$(GUD_ROOT_FOLDER)/mobicore_kernelapi/include
diff --git a/drivers/gud/README b/drivers/gud/README
new file mode 100644
index 0000000..c6d62a1
--- /dev/null
+++ b/drivers/gud/README
@@ -0,0 +1,6 @@
+MobiCore is an operating system being shipped with TZBSP
+on msm chipsets. MobiCore consists of several components in
+the secure world(TrustZone) and non-secure world(linux
+kernel, Android user space). The MobiCore driver
+communicates with the MobiCore kernel that exists in
+TrustZone.
diff --git a/drivers/gud/mobicore_driver/build_tag.h b/drivers/gud/mobicore_driver/build_tag.h
new file mode 100644
index 0000000..43541bb
--- /dev/null
+++ b/drivers/gud/mobicore_driver/build_tag.h
@@ -0,0 +1,29 @@
+/**
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2012-2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#define MOBICORE_COMPONENT_BUILD_TAG "*** GC_MSM8960_Release_V010 ###"
diff --git a/drivers/gud/mobicore_driver/logging.c b/drivers/gud/mobicore_driver/logging.c
new file mode 100644
index 0000000..eb44c8a
--- /dev/null
+++ b/drivers/gud/mobicore_driver/logging.c
@@ -0,0 +1,336 @@
+/** MobiCore driver module.(interface to the secure world SWD)
+ * @addtogroup MCD_MCDIMPL_KMOD_LOGGING MobiCore Driver Logging Subsystem.
+ * @ingroup MCD_MCDIMPL_KMOD
+ * @{
+ * @file
+ * MobiCore Driver Logging Subsystem.
+ * The logging subsytem provides the interface between the Mobicore trace
+ * buffer and the Linux log
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * 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
+ * published by the Free Software Foundation.
+ */
+#include "mc_drv_module.h"
+#include "mc_drv_module_linux_api.h"
+#include "mc_drv_module_fastcalls.h"
+
+/* Default len of the log ring buffer 256KB*/
+#define LOG_BUF_SIZE (64 * PAGE_SIZE)
+
+/* Max Len of a log line for printing */
+#define LOG_LINE_SIZE 256
+
+static uint32_t log_size = LOG_BUF_SIZE;
+module_param(log_size, uint, 0);
+MODULE_PARM_DESC(log_size, " Size of the MobiCore log ringbuffer "
+ "(or 256KB default).");
+
+/*----------------------------------------------------------------------------*/
+/* Definitions for log version 2 */
+#define LOG_TYPE_MASK (0x0007)
+#define LOG_TYPE_CHAR 0
+#define LOG_TYPE_INTEGER 1
+/* Field length */
+#define LOG_LENGTH_MASK (0x00F8)
+#define LOG_LENGTH_SHIFT 3
+/* Extra attributes */
+#define LOG_EOL (0x0100)
+#define LOG_INTEGER_DECIMAL (0x0200)
+#define LOG_INTEGER_SIGNED (0x0400)
+
+struct logmsg_struct {
+ /* Type and format of data */
+ uint16_t ctrl;
+ /* Unique value for each event source */
+ uint16_t source;
+ /* Value, if any */
+ uint32_t log_data;
+};
+
+/** MobiCore log previous position */
+static uint32_t log_pos;
+/** MobiCore log buffer structure */
+static struct mc_trace_buf *log_buf;
+/** Log Thread task structure */
+struct task_struct *log_thread;
+/** Log Line buffer */
+static char *log_line;
+
+static void log_msg(struct logmsg_struct *msg);
+
+/*----------------------------------------------------------------------------*/
+static void log_eol(void)
+{
+ if (!strnlen(log_line, LOG_LINE_SIZE))
+ return;
+ printk(KERN_INFO "%s\n", log_line);
+ log_line[0] = 0;
+}
+/*----------------------------------------------------------------------------*/
+/**
+ * Put a char to the log line if there is enough space if not then also
+ * output the line. Assume nobody else is updating the line! */
+static void log_char(char ch)
+{
+ uint32_t len;
+ if (ch == '\n' || ch == '\r') {
+ log_eol();
+ return;
+ }
+
+ if (strnlen(log_line, LOG_LINE_SIZE) >= LOG_LINE_SIZE - 1) {
+ printk(KERN_INFO "%s\n", log_line);
+ log_line[0] = 0;
+ }
+
+ len = strnlen(log_line, LOG_LINE_SIZE);
+ log_line[len] = ch;
+ log_line[len + 1] = 0;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Put a string to the log line if there is enough space if not then also
+ * output the line. Assume nobody else is updating the line! */
+static void log_str(const char *s)
+{
+ int i;
+ for (i = 0; i < strnlen(s, LOG_LINE_SIZE); i++)
+ log_char(s[i]);
+}
+
+/*----------------------------------------------------------------------------*/
+static uint32_t process_v1log(void)
+{
+ char *last_char = log_buf->buff + log_buf->write_pos;
+ char *buff = log_buf->buff + log_pos;
+ while (buff != last_char) {
+ log_char(*(buff++));
+ /* Wrap around */
+ if (buff - (char *)log_buf >= log_size)
+ buff = log_buf->buff;
+ }
+ return buff - log_buf->buff;
+}
+
+/*----------------------------------------------------------------------------*/
+static uint32_t process_v2log(void)
+{
+ char *last_msg = log_buf->buff + log_buf->write_pos;
+ char *buff = log_buf->buff + log_pos;
+ while (buff != last_msg) {
+ log_msg((struct logmsg_struct *)buff);
+ buff += sizeof(struct logmsg_struct);
+ /* Wrap around */
+ if (buff + sizeof(struct logmsg_struct) >
+ (char *)log_buf + log_size)
+ buff = log_buf->buff;
+ }
+ return buff - log_buf->buff;
+}
+
+static const uint8_t HEX2ASCII[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+/*----------------------------------------------------------------------------*/
+static void dbg_raw_nro(uint32_t format, uint32_t value)
+{
+ int digits = 1;
+ uint32_t base = (format & LOG_INTEGER_DECIMAL) ? 10 : 16;
+ int width = (format & LOG_LENGTH_MASK) >> LOG_LENGTH_SHIFT;
+ int negative = FALSE;
+ uint32_t digit_base = 1;
+
+ if ((format & LOG_INTEGER_SIGNED) != 0 && ((signed int)value) < 0) {
+ negative = TRUE;
+ value = (uint32_t)(-(signed int)value);
+ width--;
+ }
+
+ /* Find length and divider to get largest digit */
+ while (value / digit_base >= base) {
+ digit_base *= base;
+ digits++;
+ }
+
+ if (width > digits) {
+ char ch = (base == 10) ? ' ' : '0';
+ while (width > digits) {
+ log_char(ch);
+ width--;
+ }
+ }
+
+ if (negative)
+ log_char('-');
+
+ while (digits-- > 0) {
+ uint32_t d = value / digit_base;
+ log_char(HEX2ASCII[d]);
+ value = value - d * digit_base;
+ digit_base /= base;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+static void log_msg(struct logmsg_struct *msg)
+{
+ unsigned char msgtxt[5];
+ int mpos = 0;
+ switch (msg->ctrl & LOG_TYPE_MASK) {
+ case LOG_TYPE_CHAR: {
+ uint32_t ch;
+ ch = msg->log_data;
+ while (ch != 0) {
+ msgtxt[mpos++] = ch&0xFF;
+ ch >>= 8;
+ }
+ msgtxt[mpos] = 0;
+ log_str(msgtxt);
+ break;
+ }
+ case LOG_TYPE_INTEGER: {
+ dbg_raw_nro(msg->ctrl, msg->log_data);
+ break;
+ }
+ default:
+ break;
+ }
+ if (msg->ctrl & LOG_EOL)
+ log_eol();
+}
+
+/*----------------------------------------------------------------------------*/
+static int log_worker(void *p)
+{
+ if (log_buf == NULL)
+ return -EFAULT;
+
+ /* The thread should have never started */
+ if (log_buf == NULL)
+ return -EFAULT;
+
+ while (!kthread_should_stop()) {
+ if (log_buf->write_pos == log_pos)
+ schedule_timeout_interruptible(MAX_SCHEDULE_TIMEOUT);
+
+ switch (log_buf->version) {
+ case 1:
+ log_pos = process_v1log();
+ break;
+ case 2:
+ log_pos = process_v2log();
+ break;
+ default:
+ MCDRV_DBG_ERROR("Unknown Mobicore log data "
+ "version %d logging disabled.",
+ log_buf->version);
+ log_pos = log_buf->write_pos;
+ /* Stop the thread as we have no idea what
+ * happens next */
+ return -EFAULT;
+ }
+ }
+ MCDRV_DBG("Logging thread stopped!");
+ return 0;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Wakeup the log reader thread
+ * This should be called from the places where calls into MobiCore have
+ * generated some logs(eg, yield, SIQ...)
+ */
+void mobicore_log_read(void)
+{
+ if (log_thread == NULL || IS_ERR(log_thread))
+ return;
+
+ wake_up_process(log_thread);
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Setup mobicore kernel log. It assumes it's running on CORE 0!
+ * The fastcall will complain is that is not the case!
+ */
+long mobicore_log_setup(void *data)
+{
+ unsigned long phys_log_buf;
+ union fc_generic fc_log;
+
+ log_pos = 0;
+ log_buf = NULL;
+ log_thread = NULL;
+ log_line = NULL;
+
+ /* Sanity check for the log size */
+ if (log_size < PAGE_SIZE)
+ return -EFAULT;
+ else
+ log_size =
+ get_nr_of_pages_for_buffer(NULL, log_size) * PAGE_SIZE;
+
+ log_line = kzalloc(LOG_LINE_SIZE, GFP_KERNEL);
+ if (IS_ERR(log_line)) {
+ MCDRV_DBG_ERROR("failed to allocate log line!");
+ return -ENOMEM;
+ }
+
+ log_thread = kthread_create(log_worker, NULL, "mobicore_log");
+ if (IS_ERR(log_thread)) {
+ MCDRV_DBG_ERROR("mobicore log thread creation failed!");
+ return -EFAULT;
+ }
+
+ log_pos = 0;
+ log_buf = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
+ size_to_order(log_size));
+ if (!log_buf) {
+ MCDRV_DBG_ERROR("Failed to get page for logger!");
+ return -ENOMEM;
+ }
+ phys_log_buf = virt_to_phys(log_buf);
+
+ memset(&fc_log, 0, sizeof(fc_log));
+ fc_log.as_in.cmd = MC_FC_NWD_TRACE;
+ fc_log.as_in.param[0] = phys_log_buf;
+ fc_log.as_in.param[1] = log_size;
+
+ MCDRV_DBG("fc_log virt=%p phys=%p ", log_buf, (void *)phys_log_buf);
+ mc_fastcall(&fc_log);
+ MCDRV_DBG("fc_log out ret=0x%08x", fc_log.as_out.ret);
+ /* If the setup failed we must free the memory allocated */
+ if (fc_log.as_out.ret) {
+ MCDRV_DBG_ERROR("MobiCore shared traces setup failed!");
+ kthread_stop(log_thread);
+ free_pages((unsigned long)log_buf, size_to_order(log_size));
+
+ log_buf = NULL;
+ log_thread = NULL;
+ return -EIO;
+ }
+
+ MCDRV_DBG("fc_log Logger version %u\n", log_buf->version);
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Free kernel log componenets.
+ * ATTN: We can't free the log buffer because it's also in use by MobiCore and
+ * even if the module is unloaded MobiCore is still running.
+ */
+void mobicore_log_free(void)
+{
+ if (log_thread && !IS_ERR(log_thread)) {
+ /* We don't really care what the thread returns for exit */
+ kthread_stop(log_thread);
+ }
+
+ kfree(log_line);
+}
diff --git a/drivers/gud/mobicore_driver/main.c b/drivers/gud/mobicore_driver/main.c
new file mode 100644
index 0000000..9a99c5c
--- /dev/null
+++ b/drivers/gud/mobicore_driver/main.c
@@ -0,0 +1,2868 @@
+/** MobiCore driver module.(interface to the secure world SWD)
+ * @addtogroup MCD_MCDIMPL_KMOD_IMPL
+ * @{
+ * @file
+ * MobiCore Driver Kernel Module.
+ * This module is written as a Linux device driver.
+ * This driver represents the command proxy on the lowest layer, from the
+ * secure world to the non secure world, and vice versa.
+ * This driver is located in the non secure world (Linux).
+ * This driver offers IOCTL commands, for access to the secure world, and has
+ * the interface from the secure world to the normal world.
+ * The access to the driver is possible with a file descriptor,
+ * which has to be created by the fd = open(/dev/mobicore) command.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * 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
+ * published by the Free Software Foundation.
+ */
+
+#include "mc_drv_module.h"
+#include "mc_drv_module_linux_api.h"
+#include "mc_drv_module_android.h"
+#include "mc_drv_module_fastcalls.h"
+#include "public/mc_kernel_api.h"
+
+/* Initial value for the daemon sempahore signaling */
+#define DAEMON_SEM_VAL 0
+
+/** MobiCore interrupt context data */
+static struct mc_drv_kmod_ctx mc_drv_kmod_ctx;
+
+/** MobiCore MCI information */
+static uint32_t mci_base;
+/*
+#############################################################################
+##
+## Convenience functions for Linux API functions
+##
+#############################################################################*/
+static int goto_cpu0(void);
+static int goto_all_cpu(void) __attribute__ ((unused));
+
+
+/*----------------------------------------------------------------------------*/
+static void init_and_add_to_list(
+ struct list_head *item,
+ struct list_head *list_head
+)
+{
+ INIT_LIST_HEAD(item);
+
+ list_add(item, list_head);
+}
+
+/*----------------------------------------------------------------------------*/
+/** check if CPU supports the ARM TrustZone Security Extensions
+ * @return int TRUE or FALSE */
+static int has_security_extensions(
+ void
+)
+{
+ u32 fea = 0;
+ asm volatile(
+ "mrc p15, 0, %[fea], cr0, cr1, 0" :
+ [fea]"=r" (fea));
+
+ MCDRV_DBG_VERBOSE("CPU Features: 0x%X", fea);
+
+ /* If the CPU features ID has 0 for security features then the CPU
+ * doesn't support TrustZone at all!
+ */
+ if ((fea & ARM_SECURITY_EXTENSION_MASK) == 0)
+ return 0;
+
+ return 1;
+}
+
+/*----------------------------------------------------------------------------*/
+/** check if running in secure mode
+ * @return int TRUE or FALSE */
+static int is_secure_mode(
+ void
+)
+{
+ u32 cpsr = 0, nsacr = 0;
+ asm volatile(
+ "mrc p15, 0, %[nsacr], cr1, cr1, 2\n"
+ "mrs %[cpsr], cpsr\n" :
+ [nsacr]"=r" (nsacr),
+ [cpsr]"=r"(cpsr));
+
+ MCDRV_DBG_VERBOSE("CPRS.M = set to 0x%X\n", cpsr & ARM_CPSR_MASK);
+ MCDRV_DBG_VERBOSE("SCR.NS = set to 0x%X\n", nsacr);
+
+ /* If the NSACR contains the reset value(=0) then most likely we are
+ * running in Secure MODE.
+ * If the cpsr mode is set to monitor mode then we cannot load!
+ */
+ if (nsacr == 0 || ((cpsr & ARM_CPSR_MASK) == ARM_MONITOR_MODE))
+ return 1;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+/** check if userland caller is privileged (aka has "root" access rights).
+ @return int TRUE or FALSE */
+static int is_userland_caller_privileged(
+ void
+) {
+ /* For some platforms we cannot run the Daemon as root - for Android
+ * compliance tests it is not allowed, thus we assume the daemon is ran
+ * as the system user.
+ * In Android the system user for daemons has no particular capabilities
+ * other than a fixed UID: AID_SYSTEM 1000
+ * The actual number is guaranteed to be the same in all Android systems
+ * so we will take it for granted: see android_filesystem_config.h in
+ * the Android source tree for all UIDs and their meaning:
+ * http://android-dls.com/wiki/index.php?title=Android_UIDs_and_GIDs
+ */
+#ifdef MC_ANDROID_UID_CHECK
+ return current_euid() <= AID_SYSTEM;
+#else
+ /* capable should cover all possibilities, root or sudo, uid checking
+ * was not very reliable */
+ return capable(CAP_SYS_ADMIN);
+#endif
+}
+
+
+
+/*----------------------------------------------------------------------------*/
+static void unlock_page_from_used_l2_table(
+ struct page *page
+){
+ /* REV axh: check if we should do this. */
+ SetPageDirty(page);
+
+ /* release page, old api was page_cache_release() */
+ ClearPageReserved(page);
+ put_page(page);
+}
+
+/*----------------------------------------------------------------------------*/
+/* convert L2 PTE to page pointer */
+static struct page *l2_pte_to_page(
+ pte_t pte
+) {
+ void *phys_page_addr = (void *)((unsigned int)pte & PAGE_MASK);
+ unsigned int pfn = addr_to_pfn(phys_page_addr);
+ struct page *page = pfn_to_page(pfn);
+ return page;
+}
+
+/*----------------------------------------------------------------------------*/
+/* convert page pointer to L2 PTE */
+static pte_t page_to_l2_pte(
+ struct page *page
+)
+{
+ unsigned int pfn = page_to_pfn(page);
+ void *phys_addr = pfn_to_addr(pfn);
+ pte_t pte = (pte_t)((unsigned int)phys_addr & PAGE_MASK);
+ return pte;
+}
+
+
+/*----------------------------------------------------------------------------*/
+static inline int lock_user_pages(
+ struct task_struct *task,
+ void *virt_start_page_addr,
+ int nr_of_pages,
+ struct page **pages
+)
+{
+ int ret = 0;
+ int locked_pages = 0;
+ unsigned int i;
+
+ do {
+
+ /* lock user pages, must hold the mmap_sem to do this. */
+ down_read(&(task->mm->mmap_sem));
+ locked_pages = get_user_pages(
+ task,
+ task->mm,
+ (unsigned long)virt_start_page_addr,
+ nr_of_pages,
+ 1, /* write access */
+ 0, /* they say drivers should always
+ pass 0 here..... */
+ pages,
+ NULL); /* we don't need the VMAs */
+ up_read(&(task->mm->mmap_sem));
+
+ /* could as lock all pages? */
+ if (locked_pages != nr_of_pages) {
+ MCDRV_DBG_ERROR(
+ "get_user_pages() failed, "
+ "locked_pages=%d\n",
+ locked_pages);
+ ret = -ENOMEM;
+ /* check if an error has been returned. */
+ if (locked_pages < 0) {
+ ret = locked_pages;
+ locked_pages = 0;
+ }
+ break;
+ }
+
+ /* do cache maintenance on locked pages. */
+ for (i = 0; i < nr_of_pages; i++)
+ flush_dcache_page(pages[i]);
+
+ } while (FALSE);
+
+
+ if (ret != 0) {
+ /* release all locked pages. */
+ MCDRV_ASSERT(locked_pages >= 0);
+ for (i = 0; i < locked_pages; i++)
+ put_page(pages[i]);
+ }
+
+ return ret;
+
+}
+
+/*
+#############################################################################
+##
+## Driver implementation functions
+##
+#############################################################################*/
+/*----------------------------------------------------------------------------*/
+/* check if caller is MobiCore Daemon */
+static unsigned int is_caller_mc_daemon(
+ struct mc_instance *instance
+)
+{
+ return ((instance != NULL)
+ && (mc_drv_kmod_ctx.daemon_inst == instance));
+}
+
+
+/*----------------------------------------------------------------------------*/
+/* Get process context from file pointer */
+static struct mc_instance *get_instance(
+ struct file *file
+) {
+ MCDRV_ASSERT(file != NULL);
+
+ return (struct mc_instance *)(file->private_data);
+}
+
+
+/*----------------------------------------------------------------------------*/
+/* Get a unique ID */
+static unsigned int get_mc_kmod_unique_id(
+ void
+)
+{
+ return (unsigned int)atomic_inc_return(
+ &(mc_drv_kmod_ctx.unique_counter));
+}
+
+
+/*----------------------------------------------------------------------------*/
+/* Get kernel pointer to shared L2 table given a per-process reference */
+static struct l2table *get_l2_table_kernel_virt(
+ struct mc_used_l2_table *used_l2table
+)
+{
+ MCDRV_ASSERT(used_l2table != NULL);
+ MCDRV_ASSERT(used_l2table->set != NULL);
+ MCDRV_ASSERT(used_l2table->set->kernel_virt != NULL);
+ return &(used_l2table->set->kernel_virt->table[used_l2table->idx]);
+}
+
+/*----------------------------------------------------------------------------*/
+/* Get physical address of a shared L2 table given a per-process reference */
+static struct l2table *get_l2_table_phys(
+ struct mc_used_l2_table *used_l2table
+)
+{
+ MCDRV_ASSERT(used_l2table != NULL);
+ MCDRV_ASSERT(used_l2table->set != NULL);
+ MCDRV_ASSERT(used_l2table->set->phys != NULL);
+ return &(used_l2table->set->phys->table[used_l2table->idx]);
+}
+
+/*----------------------------------------------------------------------------*/
+static unsigned int is_in_use_used_l2_table(
+ struct mc_used_l2_table *used_l2table
+)
+{
+ return ((used_l2table->flags &
+ (MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_APP
+ | MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC)) != 0);
+}
+
+
+
+/*----------------------------------------------------------------------------*/
+static struct mc_used_l2_table *find_used_l2_table_by_handle(
+ unsigned int handle
+) {
+ struct mc_used_l2_table *used_l2table;
+ struct mc_used_l2_table *used_l2table_with_handle = NULL;
+
+ list_for_each_entry(
+ used_l2table,
+ &(mc_drv_kmod_ctx.mc_used_l2_tables),
+ list
+ ) {
+ if (handle == used_l2table->handle) {
+ used_l2table_with_handle = used_l2table;
+ break;
+ }
+ }
+
+ return used_l2table_with_handle;
+}
+
+/*
+#############################################################################
+##
+## L2 Table Pool
+##
+#############################################################################*/
+
+/*----------------------------------------------------------------------------*/
+static struct mc_used_l2_table *allocate_used_l2_table(
+ struct mc_instance *instance
+) {
+ int ret = 0;
+ struct mc_l2_table_store *l2table_store = NULL;
+ struct mc_l2_tables_set *l2table_set = NULL;
+ struct mc_used_l2_table *used_l2table = NULL;
+ struct page *page;
+ unsigned int i = 0;
+
+ do {
+ /* allocate a WSM L2 descriptor */
+ used_l2table = kmalloc(sizeof(*used_l2table), GFP_KERNEL);
+ if (used_l2table == NULL) {
+ ret = -ENOMEM;
+ MCDRV_DBG_ERROR("out of memory\n");
+ break;
+ }
+ /* clean */
+ memset(used_l2table, 0, sizeof(*used_l2table));
+ used_l2table->handle = get_mc_kmod_unique_id();
+ used_l2table->owner = instance;
+
+ /* add to global list. */
+ init_and_add_to_list(
+ &(used_l2table->list),
+ &(mc_drv_kmod_ctx.mc_used_l2_tables));
+
+ /* walk though list to find free set. */
+ list_for_each_entry(
+ l2table_set,
+ &(mc_drv_kmod_ctx.mc_l2_tables_sets),
+ list
+ ) {
+ for (i = 0; i < MC_DRV_KMOD_L2_TABLE_PER_PAGES; i++) {
+ if ((l2table_set->usage_bitmap & (1U << i))
+ == 0) {
+ /* found a set,
+ l2table_set and i are set. */
+ l2table_store =
+ l2table_set->kernel_virt;
+ break;
+ }
+ }
+ if (l2table_store != NULL)
+ break;
+ } /* end while */
+
+ if (l2table_store == NULL) {
+ l2table_store = (struct mc_l2_table_store *)
+ get_zeroed_page(GFP_KERNEL);
+ if (l2table_store == NULL) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ /* Actually, locking is not necessary, because kernel
+ memory is not supposed to get swapped out. But
+ we play safe.... */
+ page = virt_to_page(l2table_store);
+ SetPageReserved(page);
+
+ /* allocate a descriptor */
+ l2table_set = kmalloc(sizeof(*l2table_set), GFP_KERNEL);
+ if (l2table_set == NULL) {
+ kfree(l2table_store);
+ ret = -ENOMEM;
+ break;
+ }
+ /* initialize */
+ memset(l2table_set, 0, sizeof(*l2table_set));
+
+ l2table_set->kernel_virt = l2table_store;
+ l2table_set->page = page;
+ l2table_set->phys = (void *)virt_to_phys(l2table_store);
+
+ /* init add to list. */
+ init_and_add_to_list(
+ &(l2table_set->list),
+ &(mc_drv_kmod_ctx.mc_l2_tables_sets));
+
+ /* use first table */
+ i = 0;
+ }
+
+ /* set set usage */
+ l2table_set->usage_bitmap |= (1U << i);
+
+ /* set set reference */
+ used_l2table->set = l2table_set;
+ used_l2table->idx = i;
+
+ MCDRV_DBG_VERBOSE(
+ "chunkPhys=%p,idx=%d\n",
+ l2table_set->phys, i);
+
+ } while (FALSE);
+
+ if (ret != 0) {
+ if (used_l2table != NULL) {
+ /* remove from list */
+ list_del(&(l2table_set->list));
+ /* free memory */
+ kfree(used_l2table);
+ used_l2table = NULL;
+ }
+ }
+
+ return used_l2table;
+}
+
+/*----------------------------------------------------------------------------*/
+static void free_used_l2_table(
+ struct mc_used_l2_table *used_l2table
+)
+{
+ struct mc_l2_tables_set *l2table_set;
+ unsigned int idx;
+
+ MCDRV_ASSERT(used_l2table != NULL);
+
+ l2table_set = used_l2table->set;
+ MCDRV_ASSERT(l2table_set != NULL);
+
+ /* clean usage flag */
+ idx = used_l2table->idx;
+ MCDRV_ASSERT(idx < MC_DRV_KMOD_L2_TABLE_PER_PAGES);
+ l2table_set->usage_bitmap &= ~(1U << idx);
+
+ /* if nobody uses this set, we can release it. */
+ if (l2table_set->usage_bitmap == 0) {
+ MCDRV_ASSERT(l2table_set->page != NULL);
+ ClearPageReserved(l2table_set->page);
+
+ MCDRV_ASSERT(l2table_set->kernel_virt != NULL);
+ free_page((unsigned long)l2table_set->kernel_virt);
+
+ /* remove from list */
+ list_del(&(l2table_set->list));
+
+ /* free memory */
+ kfree(l2table_set);
+ }
+
+ return;
+}
+
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Create a L2 table in a WSM container that has been allocates previously.
+ *
+ * @param task pointer to task owning WSM
+ * @param wsm_buffer user space WSM start
+ * @param wsm_len WSM length
+ * @param used_l2table Pointer to L2 table details
+ */
+static int map_buffer_into_used_l2_table(
+ struct task_struct *task,
+ void *wsm_buffer,
+ unsigned int wsm_len,
+ struct mc_used_l2_table *used_l2table
+)
+{
+ int ret = 0;
+ unsigned int i, nr_of_pages;
+ void *virt_addr_page;
+ struct page *page;
+ struct l2table *l2table;
+ struct page **l2table_as_array_of_pointers_to_page;
+
+ /* task can be null when called from kernel space */
+ MCDRV_ASSERT(wsm_buffer != NULL);
+ MCDRV_ASSERT(wsm_len != 0);
+ MCDRV_ASSERT(used_l2table != NULL);
+
+ MCDRV_DBG_VERBOSE("WSM addr=0x%p, len=0x%08x\n", wsm_buffer, wsm_len);
+
+ /* Check if called from kernel space wsm_buffer is actually
+ * vmalloced or not */
+ if (task == NULL && !is_vmalloc_addr(wsm_buffer)) {
+ MCDRV_DBG_ERROR("WSM addr is not a vmalloc address");
+ return -EINVAL;
+ }
+
+ l2table = get_l2_table_kernel_virt(used_l2table);
+ /* We use the memory for the L2 table to hold the pointer
+ and convert them later. This works, as everything comes
+ down to a 32 bit value. */
+ l2table_as_array_of_pointers_to_page = (struct page **)l2table;
+
+ do {
+
+ /* no size > 1Mib supported */
+ if (wsm_len > SZ_1M) {
+ MCDRV_DBG_ERROR("size > 1 MiB\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ /* calculate page usage */
+ virt_addr_page = get_page_start(wsm_buffer);
+ nr_of_pages = get_nr_of_pages_for_buffer(wsm_buffer, wsm_len);
+
+
+ MCDRV_DBG_VERBOSE("virt addr pageStart=0x%p,pages=%d\n",
+ virt_addr_page,
+ nr_of_pages);
+
+ /* L2 table can hold max 1MiB in 256 pages. */
+ if ((nr_of_pages*PAGE_SIZE) > SZ_1M) {
+ MCDRV_DBG_ERROR("WSM paged exceed 1 MiB\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ /* Request comes from user space */
+ if (task != NULL) {
+ /* lock user page in memory, so they do not get swapped
+ * out.
+ * REV axh:
+ * Kernel 2.6.27 added a new get_user_pages_fast()
+ * function, maybe it is called fast_gup() in some
+ * versions.
+ * handle user process doing a fork().
+ * Child should not get things.
+ * http://osdir.com/ml/linux-media/2009-07/msg00813.html
+ * http://lwn.net/Articles/275808/ */
+
+ ret = lock_user_pages(
+ task,
+ virt_addr_page,
+ nr_of_pages,
+ l2table_as_array_of_pointers_to_page);
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("lock_user_pages() failed\n");
+ break;
+ }
+ }
+ /* Request comes from kernel space(vmalloc buffer) */
+ else {
+ void *uaddr = wsm_buffer;
+ for (i = 0; i < nr_of_pages; i++) {
+ page = vmalloc_to_page(uaddr);
+ if (!page) {
+ MCDRV_DBG_ERROR(
+ "vmalloc_to_Page()"
+ " failed to map address\n");
+ ret = -EINVAL;
+ break;
+ }
+ get_page(page);
+ /* Lock the page in memory, it can't be swapped
+ * out */
+ SetPageReserved(page);
+ l2table_as_array_of_pointers_to_page[i] = page;
+ uaddr += PAGE_SIZE;
+ }
+ }
+
+ used_l2table->nr_of_pages = nr_of_pages;
+ used_l2table->flags |= MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_APP;
+
+ /* create L2 Table entries. used_l2table->table contains a list
+ of page pointers here. For a proper cleanup we have to ensure
+ that the following code either works and used_l2table contains
+ a valid L2 table - or fails and used_l2table->table contains the
+ list of page pointers. Any mixed contents will make cleanup
+ difficult.*/
+
+ for (i = 0; i < nr_of_pages; i++) {
+ pte_t pte;
+ page = l2table_as_array_of_pointers_to_page[i];
+
+ /* create L2 table entry, see ARM MMU docu for details
+ about flags stored in the lowest 12 bits. As a side
+ reference, the Article "ARM's multiply-mapped memory
+ mess" found in the collection at at
+ http://lwn.net/Articles/409032/ is also worth reading.*/
+ pte = page_to_l2_pte(page)
+ | L2_FLAG_AP1 | L2_FLAG_AP0
+ | L2_FLAG_C | L2_FLAG_B
+ | L2_FLAG_SMALL | L2_FLAG_SMALL_XN
+ /* Linux uses different mappings for SMP systems(the
+ * sharing flag is set for the pte. In order not to
+ * confuse things too much in Mobicore make sure the
+ * shared buffers have the same flags.
+ * This should also be done in SWD side
+ */
+#ifdef CONFIG_SMP
+ | L2_FLAG_S | L2_FLAG_SMALL_TEX0
+#endif
+ ;
+
+ l2table->table_entries[i] = pte;
+ MCDRV_DBG_VERBOSE("L2 entry %d: 0x%08x\n", i,
+ (unsigned int)(pte));
+ }
+
+ /* ensure rest of table is empty */
+ while (i < 255)
+ l2table->table_entries[i++] = (pte_t)0;
+
+ } while (FALSE);
+
+ return ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Remove a L2 table in a WSM container. Afterwards the container may be
+ * released.
+ *
+ * @param used_l2table Pointer to L2 table details
+ */
+
+static void unmap_buffers_from_used_l2_table(
+ struct mc_used_l2_table *used_l2table
+)
+{
+ unsigned int i;
+ struct l2table *l2table;
+
+ MCDRV_ASSERT(used_l2table != NULL);
+ /* this should not happen, as we have no empty tables. */
+ MCDRV_ASSERT(!is_in_use_used_l2_table(used_l2table));
+
+ /* found the table, now release the resources. */
+ MCDRV_DBG_VERBOSE("clear L2 table, phys_base=%p, nr_of_pages=%d\n",
+ get_l2_table_phys(used_l2table),
+ used_l2table->nr_of_pages);
+
+ l2table = get_l2_table_kernel_virt(used_l2table);
+
+ /* release all locked user space pages */
+ for (i = 0; i < used_l2table->nr_of_pages; i++) {
+ /* convert physical entries from L2 table to page pointers */
+ pte_t pte = get_l2_table_kernel_virt(used_l2table)->
+ table_entries[i];
+ struct page *page = l2_pte_to_page(pte);
+ unlock_page_from_used_l2_table(page);
+ }
+
+ /* remember that all pages have been freed */
+ used_l2table->nr_of_pages = 0;
+
+ return;
+}
+
+
+/*
+#############################################################################
+##
+## Helper functions
+##
+#############################################################################*/
+/*----------------------------------------------------------------------------*/
+#define FREE_FROM_SWD TRUE
+#define FREE_FROM_NWD FALSE
+/** Delete a used l2 table. */
+static void delete_used_l2_table(
+ struct mc_used_l2_table *used_l2table,
+ unsigned int is_swd
+)
+{
+ if (is_swd) {
+ used_l2table->flags &=
+ ~MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC;
+ } else {
+ used_l2table->flags &=
+ ~MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_APP;
+ used_l2table->owner = NULL;
+ }
+
+ /* release if Nwd and Swd/MC do no longer use it. */
+ if (is_in_use_used_l2_table(used_l2table)) {
+ MCDRV_DBG_WARN(
+ "WSM L2 table still in use: physBase=%p, "
+ "nr_of_pages=%d\n",
+ get_l2_table_phys(used_l2table),
+ used_l2table->nr_of_pages);
+ } else {
+ unmap_buffers_from_used_l2_table(used_l2table);
+ free_used_l2_table(used_l2table);
+
+ list_del(&(used_l2table->list));
+
+ kfree(used_l2table);
+ }
+ return;
+}
+
+/*----------------------------------------------------------------------------*/
+/** Allocate L2 table and map buffer into it. That is, create respective table
+ entries. Must hold Semaphore mc_drv_kmod_ctx.wsm_l2_sem */
+static struct mc_used_l2_table *new_used_l2_table(
+ struct mc_instance *instance,
+ struct task_struct *task,
+ void *wsm_buffer,
+ unsigned int wsm_len
+) {
+ int ret = 0;
+ struct mc_used_l2_table *used_l2table;
+
+ do {
+ used_l2table = allocate_used_l2_table(instance);
+ if (used_l2table == NULL) {
+ MCDRV_DBG_ERROR(
+ "allocate_used_l2_table() failed\n");
+ break;
+ }
+
+ /* create the L2 page for the WSM */
+ ret = map_buffer_into_used_l2_table(
+ task,
+ wsm_buffer,
+ wsm_len,
+ used_l2table);
+ if (ret != 0) {
+ MCDRV_DBG_ERROR(
+ "map_buffer_into_used_l2_table() failed\n");
+ delete_used_l2_table(used_l2table, FREE_FROM_NWD);
+ used_l2table = NULL;
+ break;
+ }
+
+ } while (FALSE);
+
+
+ return used_l2table;
+}
+
+/*
+#############################################################################
+##
+## IoCtl handler
+##
+#############################################################################*/
+
+/**
+ * Map a virtual memory buffer structure to Mobicore
+ * @param instance
+ * @param addr address of the buffer(NB it must be kernel virtual!)
+ * @param len buffer length
+ * @param handle pointer to handle
+ * @param phys_wsm_l2_table pointer to physical L2 table(?)
+ *
+ * @return 0 if no error
+ *
+ */
+/*----------------------------------------------------------------------------*/
+int mobicore_map_vmem(
+ struct mc_instance *instance,
+ void *addr,
+ uint32_t len,
+ uint32_t *handle,
+ void **phys_wsm_l2_table
+)
+{
+ int ret = 0;
+ struct mc_used_l2_table *used_l2table = NULL;
+ MCDRV_ASSERT(instance != NULL);
+
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ if (len == 0) {
+ MCDRV_DBG_ERROR("len=0 is not supported!\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ /* try to get the semaphore */
+ ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("down_interruptible() failed with %d\n",
+ ret);
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ do {
+ used_l2table = new_used_l2_table(
+ instance,
+ NULL,
+ addr,
+ len);
+
+ if (used_l2table == NULL) {
+ MCDRV_DBG_ERROR("new_used_l2_table() failed\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ /* set response */
+ *handle = used_l2table->handle;
+ *phys_wsm_l2_table =
+ (void *)get_l2_table_phys(used_l2table);
+ MCDRV_DBG_VERBOSE("handle: %d, phys=%p\n",
+ *handle,
+ (void *)(*phys_wsm_l2_table));
+
+ } while (FALSE);
+
+ /* release semaphore */
+ up(&(mc_drv_kmod_ctx.wsm_l2_sem));
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL(mobicore_map_vmem);
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_app_register_wsm_l2(
+ struct mc_instance *instance,
+ union mc_ioctl_app_reg_wsm_l2_params *user_params
+)
+{
+ int ret = 0;
+ union mc_ioctl_app_reg_wsm_l2_params params;
+ struct mc_used_l2_table *used_l2table = NULL;
+ struct pid *pid_struct = NULL;
+ struct task_struct *task = current;
+
+ MCDRV_ASSERT(instance != NULL);
+
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ /* get use parameters */
+ ret = copy_from_user(
+ &(params.in),
+ &(user_params->in),
+ sizeof(params.in));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_from_user() failed\n");
+ break;
+ }
+
+ /* daemon can do this for another task. */
+ if (params.in.pid != 0) {
+ MCDRV_DBG_ERROR("pid != 0 unsupported\n");
+ ret = -EINVAL;
+ break;
+ }
+ if (params.in.len == 0) {
+ MCDRV_DBG_ERROR("len=0 is not supported!\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ /* try to get the semaphore */
+ ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("down_interruptible() failed with %d\n",
+ ret);
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ do {
+ used_l2table = new_used_l2_table(
+ instance,
+ task,
+ (void *)(params.in.buffer),
+ params.in.len);
+
+ if (used_l2table == NULL) {
+ MCDRV_DBG_ERROR("new_used_l2_table() failed\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ /* if the daemon does this, we set the MC lock */
+ if (is_caller_mc_daemon(instance))
+ used_l2table->flags |=
+ MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC;
+
+ /* set response */
+ memset(¶ms.out, 0, sizeof(params.out));
+ params.out.handle = used_l2table->handle;
+ /* TODO: return the physical address for daemon only,
+ otherwise set NULL */
+ params.out.phys_wsm_l2_table =
+ (uint32_t)get_l2_table_phys(used_l2table);
+
+ MCDRV_DBG_VERBOSE("handle: %d, phys=%p\n",
+ params.out.handle,
+ (void *)(params.out.phys_wsm_l2_table));
+
+
+ /* copy L2Table to user space */
+ ret = copy_to_user(
+ &(user_params->out),
+ &(params.out),
+ sizeof(params.out));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_to_user() failed\n");
+
+ /* free the table again, as app does not know
+ about anything. */
+ if (is_caller_mc_daemon(instance)) {
+ used_l2table->flags &=
+ ~MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC;
+ }
+ delete_used_l2_table(used_l2table,
+ FREE_FROM_NWD);
+ used_l2table = NULL;
+ break;
+ }
+
+ } while (FALSE);
+
+ /* release semaphore */
+ up(&(mc_drv_kmod_ctx.wsm_l2_sem));
+
+ } while (FALSE);
+
+
+
+ /* release PID struct reference */
+ if (pid_struct != NULL)
+ put_pid(pid_struct);
+
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Unmap a virtual memory buffer from mobicore
+ * @param instance
+ * @param handle
+ *
+ * @return 0 if no error
+ *
+ */
+int mobicore_unmap_vmem(
+ struct mc_instance *instance,
+ uint32_t handle
+)
+{
+ int ret = 0;
+ struct mc_used_l2_table *used_l2table = NULL;
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ /* try to get the semaphore */
+ ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("processOpenSession() failed with %d\n",
+ ret);
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ do {
+ used_l2table = find_used_l2_table_by_handle(handle);
+ if (used_l2table == NULL) {
+ ret = -EINVAL;
+ MCDRV_DBG_ERROR("entry not found\n");
+ break;
+ }
+
+ if (instance != used_l2table->owner) {
+ ret = -EINVAL;
+ MCDRV_DBG_ERROR("instance does no own it\n");
+ break;
+ }
+
+ /* free table (if no further locks exist) */
+ delete_used_l2_table(used_l2table, FREE_FROM_NWD);
+ used_l2table = NULL;
+ /* there are no out parameters */
+ } while (FALSE);
+ /* release semaphore */
+ up(&(mc_drv_kmod_ctx.wsm_l2_sem));
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL(mobicore_unmap_vmem);
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_app_unregister_wsm_l2(
+ struct mc_instance *instance,
+ struct mc_ioctl_app_unreg_wsm_l2_params *user_params
+)
+{
+ int ret = 0;
+ struct mc_ioctl_app_unreg_wsm_l2_params params;
+ struct mc_used_l2_table *used_l2table = NULL;
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ ret = copy_from_user(
+ &(params.in),
+ &(user_params->in),
+ sizeof(params.in));
+
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_from_user\n");
+ break;
+ }
+
+ /* try to get the semaphore */
+ ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("down_interruptible() failed with %d\n",
+ ret);
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ do {
+ /* daemon can do this for another task. */
+ if (params.in.pid != 0) {
+ MCDRV_DBG_ERROR("pid != 0 unsupported\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ used_l2table =
+ find_used_l2_table_by_handle(params.in.handle);
+ if (used_l2table == NULL) {
+ ret = -EINVAL;
+ MCDRV_DBG_ERROR("entry not found\n");
+ break;
+ }
+
+ if (is_caller_mc_daemon(instance)) {
+ /* if daemon does this, we have to release the
+ MobiCore lock. */
+ used_l2table->flags &=
+ ~MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC;
+ } else if (instance != used_l2table->owner) {
+ ret = -EINVAL;
+ MCDRV_DBG_ERROR("instance does no own it\n");
+ break;
+ }
+
+ /* free table (if no further locks exist) */
+ delete_used_l2_table(used_l2table, FREE_FROM_NWD);
+ used_l2table = NULL;
+
+ /* there are no out parameters */
+
+ } while (FALSE);
+
+ /* release semaphore */
+ up(&(mc_drv_kmod_ctx.wsm_l2_sem));
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+static int handle_ioctl_daemon_lock_wsm_l2(
+ struct mc_instance *instance,
+ struct mc_ioctl_daemon_lock_wsm_l2_params *user_params
+)
+{
+ int ret = 0;
+ struct mc_ioctl_daemon_lock_wsm_l2_params params;
+ struct mc_used_l2_table *used_l2table = NULL;
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ if (!is_caller_mc_daemon(instance)) {
+ MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = copy_from_user(
+ &(params.in),
+ &(user_params->in),
+ sizeof(params.in));
+
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_from_user\n");
+ break;
+ }
+ /* try to get the semaphore */
+ ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("down_interruptible() failed with %d\n",
+ ret);
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ do {
+ used_l2table =
+ find_used_l2_table_by_handle(params.in.handle);
+ if (used_l2table == NULL) {
+ ret = -EINVAL;
+ MCDRV_DBG_ERROR("entry not found\n");
+ break;
+ }
+ if (instance != used_l2table->owner) {
+ ret = -EINVAL;
+ MCDRV_DBG_ERROR("instance does no own it\n");
+ break;
+ }
+
+ /* lock entry */
+ if ((used_l2table->flags &
+ MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC) != 0) {
+ MCDRV_DBG_WARN("entry already locked\n");
+ }
+ used_l2table->flags |=
+ MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC;
+
+ /* prepare response */
+ memset(&(params.out), 0, sizeof(params.out));
+ params.out.phys_wsm_l2_table =
+ (uint32_t)get_l2_table_phys(used_l2table);
+
+ /* copy to user space */
+ ret = copy_to_user(
+ &(user_params->out),
+ &(params.out),
+ sizeof(params.out));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_to_user() failed\n");
+
+ /* undo, as userspace did not get it. */
+ used_l2table->flags |=
+ MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC;
+ break;
+ }
+
+ } while (FALSE);
+
+ /* release semaphore */
+ up(&(mc_drv_kmod_ctx.wsm_l2_sem));
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+static int handle_ioctl_daemon_unlock_wsm_l2(
+ struct mc_instance *instance,
+ struct mc_ioctl_daemon_unlock_wsm_l2_params *user_params
+)
+{
+ int ret = 0;
+ struct mc_ioctl_daemon_unlock_wsm_l2_params params;
+ struct mc_used_l2_table *used_l2table = NULL;
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ if (!is_caller_mc_daemon(instance)) {
+ MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = copy_from_user(
+ &(params.in),
+ &(user_params->in),
+ sizeof(params.in));
+
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_from_user\n");
+ break;
+ }
+ /* try to get the semaphore */
+ ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("down_interruptible() failed with %d\n",
+ ret);
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ do {
+ used_l2table =
+ find_used_l2_table_by_handle(params.in.handle);
+ if (used_l2table == NULL) {
+ ret = -EINVAL;
+ MCDRV_DBG_ERROR("entry not found\n");
+ break;
+ }
+ if (instance != used_l2table->owner) {
+ ret = -EINVAL;
+ MCDRV_DBG_ERROR("instance does no own it\n");
+ break;
+ }
+
+ /* lock entry */
+ if ((used_l2table->flags &
+ MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC) == 0) {
+ MCDRV_DBG_WARN("entry is not locked locked\n");
+ }
+
+ /* free table (if no further locks exist) */
+ delete_used_l2_table(used_l2table, FREE_FROM_SWD);
+ used_l2table = NULL;
+
+ /* there are no out parameters */
+
+ } while (FALSE);
+
+ } while (FALSE);
+
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/** Clears the reserved bit of each page and frees the pages */
+static inline void free_continguous_pages(
+ void *addr,
+ unsigned int size
+)
+{
+ struct page *page = virt_to_page(addr);
+ int i;
+ for (i = 0; i < size; i++) {
+ MCDRV_DBG_VERBOSE("free page at 0x%p\n", page);
+ ClearPageReserved(page);
+ page++;
+ }
+ /* REV luh: see man kmalloc */
+ free_pages((unsigned long)addr, size_to_order(size));
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Free a WSM buffer allocated with mobicore_allocate_wsm
+ * @param instance
+ * @param handle handle of the buffer
+ *
+ * @return 0 if no error
+ *
+ */
+int mobicore_free(
+ struct mc_instance *instance,
+ uint32_t handle
+)
+{
+ int ret = 0;
+ unsigned int i;
+ struct mc_contg_buffer *contg_buffer;
+
+ do {
+ /* search for the given address in the contg_buffers list */
+ for (i = 0; i < MC_DRV_KMOD_CONTG_BUFFER_MAX; i++) {
+ contg_buffer = &(instance->contg_buffers[i]);
+ if (contg_buffer->handle == handle)
+ break;
+ }
+ if (i == MC_DRV_KMOD_CONTG_BUFFER_MAX) {
+ MCDRV_DBG_ERROR("contigous buffer not found\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ MCDRV_DBG_VERBOSE("phys_addr=0x%p, virt_addr=0x%p\n",
+ contg_buffer->phys_addr,
+ contg_buffer->virt_kernel_addr);
+
+ free_continguous_pages(contg_buffer->virt_kernel_addr,
+ contg_buffer->num_pages);
+
+ memset(contg_buffer, 0, sizeof(*contg_buffer));
+
+ /* there are no out parameters */
+
+ } while (FALSE);
+
+
+ return ret;
+}
+EXPORT_SYMBOL(mobicore_free);
+/*----------------------------------------------------------------------------*/
+
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_free(
+ struct mc_instance *instance,
+ union mc_ioctl_free_params *user_params
+)
+{
+ int ret = 0;
+ union mc_ioctl_free_params params;
+
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ ret = copy_from_user(
+ &(params.in),
+ &(user_params->in),
+ sizeof(params.in));
+
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_from_user\n");
+ break;
+ }
+
+ /* daemon can do this for another task. */
+ if (params.in.pid != 0) {
+ MCDRV_DBG_ERROR("pid != 0 unsupported\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ ret = mobicore_free(instance, params.in.handle);
+
+ /* there are no out parameters */
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_info(
+ struct mc_instance *instance,
+ union mc_ioctl_info_params *user_params
+)
+{
+ int ret = 0;
+ union mc_ioctl_info_params params;
+ union mc_fc_info fc_info;
+
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ /* only the MobiCore Daemon is allowed to call this function */
+ if (!is_caller_mc_daemon(instance)) {
+ MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = copy_from_user(
+ &(params.in),
+ &(user_params->in),
+ sizeof(params.in));
+
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_from_user\n");
+ break;
+ }
+
+
+ memset(&fc_info, 0, sizeof(fc_info));
+ fc_info.as_in.cmd = MC_FC_INFO;
+ fc_info.as_in.ext_info_id = params.in.ext_info_id;
+
+ MCDRV_DBG(
+ "fc_info in cmd=0x%08x, ext_info_id=0x%08x "
+ "rfu=(0x%08x, 0x%08x)\n",
+ fc_info.as_in.cmd,
+ fc_info.as_in.ext_info_id,
+ fc_info.as_in.rfu[0],
+ fc_info.as_in.rfu[1]);
+
+ mc_fastcall(&(fc_info.as_generic));
+
+ MCDRV_DBG(
+ "fc_info out resp=0x%08x, ret=0x%08x "
+ "state=0x%08x, ext_info=0x%08x\n",
+ fc_info.as_out.resp,
+ fc_info.as_out.ret,
+ fc_info.as_out.state,
+ fc_info.as_out.ext_info);
+
+ ret = convert_fc_ret(fc_info.as_out.ret);
+ if (ret != 0)
+ break;
+
+ memset(&(params.out), 0, sizeof(params.out));
+ params.out.state = fc_info.as_out.state;
+ params.out.ext_info = fc_info.as_out.ext_info;
+
+ ret = copy_to_user(
+ &(user_params->out),
+ &(params.out),
+ sizeof(params.out));
+
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_to_user\n");
+ break;
+ }
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_yield(
+ struct mc_instance *instance
+)
+{
+ int ret = 0;
+ union mc_fc_s_yield fc_s_yield;
+
+ MCDRV_ASSERT(instance != NULL);
+
+ /* avoid putting debug output here, as we do this very often */
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ /* only the MobiCore Daemon is allowed to call this function */
+ if (!is_caller_mc_daemon(instance)) {
+ MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ memset(&fc_s_yield, 0, sizeof(fc_s_yield));
+ fc_s_yield.as_in.cmd = MC_SMC_N_YIELD;
+ mc_fastcall(&(fc_s_yield.as_generic));
+ ret = convert_fc_ret(fc_s_yield.as_out.ret);
+ if (ret != 0)
+ break;
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * handle ioctl and call common notify
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_nsiq(
+ struct mc_instance *instance,
+ unsigned long arg
+)
+{
+ int ret = 0;
+
+ MCDRV_ASSERT(instance != NULL);
+
+ /* avoid putting debug output here, as we do this very often */
+ MCDRV_DBG_VERBOSE("enter\n");
+ /* only the MobiCore Daemon is allowed to call this function */
+ if (!is_caller_mc_daemon(instance)) {
+ MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+ return -EFAULT;
+ }
+
+ do {
+ union mc_fc_nsiq fc_nsiq;
+ memset(&fc_nsiq, 0, sizeof(fc_nsiq));
+ fc_nsiq.as_in.cmd = MC_SMC_N_SIQ;
+ mc_fastcall(&(fc_nsiq.as_generic));
+ ret = convert_fc_ret(fc_nsiq.as_out.ret);
+ if (ret != 0)
+ break;
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_dump_status(
+ struct mc_instance *instance,
+ unsigned long arg
+)
+{
+ int ret = 0;
+ int i = 0;
+ union mc_fc_info fc_info;
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ /* anybody with root access can do this. */
+ if (!is_userland_caller_privileged()) {
+ MCDRV_DBG_ERROR("caller must have root privileges\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ /* loop ext_info */
+ while (TRUE) {
+ memset(&fc_info, 0, sizeof(fc_info));
+ fc_info.as_in.cmd = MC_FC_INFO;
+ fc_info.as_in.ext_info_id = i;
+
+ MCDRV_DBG(
+ "fc_info in cmd=0x%08x, ext_info_id=0x%08x "
+ "rfu=(0x%08x, 0x%08x)\n",
+ fc_info.as_in.cmd,
+ fc_info.as_in.ext_info_id,
+ fc_info.as_in.rfu[0],
+ fc_info.as_in.rfu[1]);
+
+ mc_fastcall(&(fc_info.as_generic));
+
+ MCDRV_DBG(
+ "fc_info out resp=0x%08x, ret=0x%08x "
+ "state=0x%08x, ext_info=0x%08x\n",
+ fc_info.as_out.resp,
+ fc_info.as_out.ret,
+ fc_info.as_out.state,
+ fc_info.as_out.ext_info);
+
+ ret = convert_fc_ret(fc_info.as_out.ret);
+ if (ret != 0)
+ break;
+
+ MCDRV_DBG("state=%08X, idx=%02d: ext_info=%08X\n",
+ fc_info.as_out.state,
+ i,
+ fc_info.as_out.ext_info);
+ i++;
+ };
+
+ if (ret != 0)
+ break;
+
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_init(
+ struct mc_instance *instance,
+ union mc_ioctl_init_params *user_params
+)
+{
+ int ret = 0;
+ union mc_ioctl_init_params params;
+ union mc_fc_init fc_init;
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ /* only the MobiCore Daemon is allowed to call this function */
+ if (!is_caller_mc_daemon(instance)) {
+ MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = copy_from_user(
+ &(params.in),
+ &(user_params->in),
+ sizeof(params.in));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_from_user failed\n");
+ break;
+ }
+
+ memset(&fc_init, 0, sizeof(fc_init));
+
+ fc_init.as_in.cmd = MC_FC_INIT;
+ /* base address of mci buffer 4KB aligned */
+ fc_init.as_in.base = (uint32_t)params.in.base;
+ /* notification buffer start/length [16:16] [start, length] */
+ fc_init.as_in.nq_info = (params.in.nq_offset << 16)
+ | (params.in.nq_length & 0xFFFF);
+ /* mcp buffer start/length [16:16] [start, length] */
+ fc_init.as_in.mcp_info = (params.in.mcp_offset << 16)
+ | (params.in.mcp_length & 0xFFFF);
+
+ /* Set KMOD notification queue to start of MCI
+ mciInfo was already set up in mmap */
+ if (!mci_base) {
+ MCDRV_DBG_ERROR("No MCI set yet.\n");
+ return -EFAULT;
+ }
+ MCDRV_DBG("in cmd=0x%08x, base=0x%08x, "
+ "nq_info=0x%08x, mcp_info=0x%08x\n",
+ fc_init.as_in.cmd,
+ fc_init.as_in.base,
+ fc_init.as_in.nq_info,
+ fc_init.as_in.mcp_info);
+
+ mc_fastcall(&(fc_init.as_generic));
+
+ MCDRV_DBG("out cmd=0x%08x, ret=0x%08x rfu=(0x%08x, 0x%08x)\n",
+ fc_init.as_out.resp,
+ fc_init.as_out.ret,
+ fc_init.as_out.rfu[0],
+ fc_init.as_out.rfu[1]);
+
+ ret = convert_fc_ret(fc_init.as_out.ret);
+ if (ret != 0)
+ break;
+
+ /* no ioctl response parameters */
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_fc_execute(
+ struct mc_instance *instance,
+ union mc_ioctl_fc_execute_params *user_params
+)
+{
+ int ret = 0;
+ union mc_ioctl_fc_execute_params params;
+ union fc_generic fc_params;
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ /* only the MobiCore Daemon is allowed to call this function */
+ if (!is_caller_mc_daemon(instance)) {
+ MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = copy_from_user(
+ &(params.in),
+ &(user_params->in),
+ sizeof(params.in));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_from_user failed\n");
+ break;
+ }
+
+ fc_params.as_in.cmd = -4;/*FC_EXECUTE */
+ fc_params.as_in.param[0] = params.in.phys_start_addr;
+ fc_params.as_in.param[1] = params.in.length;
+ fc_params.as_in.param[2] = 0;
+
+ MCDRV_DBG("in cmd=0x%08x, startAddr=0x%08x, length=0x%08x\n",
+ fc_params.as_in.cmd,
+ fc_params.as_in.param[0],
+ fc_params.as_in.param[1]);
+
+ mc_fastcall(&fc_params);
+
+ MCDRV_DBG("out cmd=0x%08x, ret=0x%08x rfu=(0x%08x, 0x%08x)\n",
+ fc_params.as_out.resp,
+ fc_params.as_out.ret,
+ fc_params.as_out.param[0],
+ fc_params.as_out.param[1]);
+
+ ret = convert_fc_ret(fc_params.as_out.ret);
+ if (ret != 0)
+ break;
+
+ /* no ioctl response parameters */
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+#define MC_MAKE_VERSION(major, minor) \
+ (((major & 0x0000ffff) << 16) | (minor & 0x0000ffff))
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_get_version(
+ struct mc_instance *instance,
+ struct mc_ioctl_get_version_params *user_params
+)
+{
+ int ret = 0;
+ struct mc_ioctl_get_version_params params = {
+ {
+ MC_MAKE_VERSION(MCDRVMODULEAPI_VERSION_MAJOR,
+ MCDRVMODULEAPI_VERSION_MINOR)
+ }
+ };
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ MCDRV_DBG("mcDrvModuleApi version is %i.%i\n",
+ MCDRVMODULEAPI_VERSION_MAJOR,
+ MCDRVMODULEAPI_VERSION_MINOR);
+
+ /* no ioctl response parameters */
+ ret = copy_to_user(
+ &(user_params->out),
+ &(params.out),
+ sizeof(params.out));
+ if (ret != 0)
+ MCDRV_DBG_ERROR("copy_to_user() failed\n");
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function will be called from user space as ioctl(...).
+ * @param file pointer to file
+ * @param cmd command
+ * @param arg arguments
+ *
+ * @return int 0 for OK and an errno in case of error
+ */
+static long mc_kernel_module_ioctl(
+ struct file *file,
+ unsigned int cmd,
+ unsigned long arg
+)
+{
+ int ret;
+ struct mc_instance *instance = get_instance(file);
+
+ MCDRV_ASSERT(instance != NULL);
+
+ switch (cmd) {
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_DUMP_STATUS:
+ ret = handle_ioctl_dump_status(
+ instance,
+ arg);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_FC_INIT:
+ ret = handle_ioctl_init(
+ instance,
+ (union mc_ioctl_init_params *)arg);
+ break;
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_FC_INFO:
+ ret = handle_ioctl_info(
+ instance,
+ (union mc_ioctl_info_params *)arg);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_FC_YIELD:
+ ret = handle_ioctl_yield(
+ instance);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_FC_NSIQ:
+ ret = handle_ioctl_nsiq(
+ instance,
+ arg);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_DAEMON_LOCK_WSM_L2:
+ ret = handle_ioctl_daemon_lock_wsm_l2(
+ instance,
+ (struct mc_ioctl_daemon_lock_wsm_l2_params *)arg);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_DAEMON_UNLOCK_WSM_L2:
+ ret = handle_ioctl_daemon_unlock_wsm_l2(
+ instance,
+ (struct mc_ioctl_daemon_unlock_wsm_l2_params *)arg);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_FREE:
+ /* called by ClientLib */
+ ret = handle_ioctl_free(
+ instance,
+ (union mc_ioctl_free_params *)arg);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_APP_REGISTER_WSM_L2:
+ /* called by ClientLib */
+ ret = handle_ioctl_app_register_wsm_l2(
+ instance,
+ (union mc_ioctl_app_reg_wsm_l2_params *)arg);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_APP_UNREGISTER_WSM_L2:
+ /* called by ClientLib */
+ ret = handle_ioctl_app_unregister_wsm_l2(
+ instance,
+ (struct mc_ioctl_app_unreg_wsm_l2_params *)arg);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_FC_EXECUTE:
+ ret = handle_ioctl_fc_execute(
+ instance,
+ (union mc_ioctl_fc_execute_params *)arg);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_GET_VERSION:
+ ret = handle_ioctl_get_version(
+ instance,
+ (struct mc_ioctl_get_version_params *)arg);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ default:
+ MCDRV_DBG_ERROR("unsupported cmd=%d\n", cmd);
+ ret = -EFAULT;
+ break;
+
+ } /* end switch(cmd) */
+
+#ifdef MC_MEM_TRACES
+ mobicore_log_read();
+#endif
+
+ return (int)ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function will be called from user space as read(...).
+ * The read function is blocking until a interrupt occurs. In that case the
+ * event counter is copied into user space and the function is finished.
+ * @param *file
+ * @param *buffer buffer where to copy to(userspace)
+ * @param buffer_len number of requested data
+ * @param *pos not used
+ * @return ssize_t ok case: number of copied data
+ * error case: return errno
+ */
+static ssize_t mc_kernel_module_read(
+ struct file *file,
+ char *buffer,
+ size_t buffer_len,
+ loff_t *pos
+)
+{
+ int ret = 0, ssiq_counter;
+ size_t retLen = 0;
+ struct mc_instance *instance = get_instance(file);
+
+ MCDRV_ASSERT(instance != NULL);
+
+ /* avoid debug output on non-error, because this is call quite often */
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ /* only the MobiCore Daemon is allowed to call this function */
+ if (!is_caller_mc_daemon(instance)) {
+ MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ if (buffer_len < sizeof(unsigned int)) {
+ MCDRV_DBG_ERROR("invalid length\n");
+ ret = (ssize_t)(-EINVAL);
+ break;
+ }
+
+ for (;;) {
+ if (down_interruptible(
+ &mc_drv_kmod_ctx.daemon_ctx.sem)) {
+ MCDRV_DBG_VERBOSE("read interrupted\n");
+ ret = (ssize_t)-ERESTARTSYS;
+ break;
+ }
+
+ ssiq_counter = atomic_read(
+ &(mc_drv_kmod_ctx.ssiq_ctx.counter));
+ MCDRV_DBG_VERBOSE("ssiq_counter=%i, ctx.counter=%i\n",
+ ssiq_counter,
+ mc_drv_kmod_ctx.daemon_ctx.ssiq_counter);
+
+ if (ssiq_counter !=
+ mc_drv_kmod_ctx.daemon_ctx.ssiq_counter) {
+ /* read data and exit loop without
+ error */
+ mc_drv_kmod_ctx.daemon_ctx.ssiq_counter =
+ ssiq_counter;
+ ret = 0;
+ break;
+ }
+
+ /* end loop if non-blocking */
+ if ((file->f_flags & O_NONBLOCK) != 0) {
+ MCDRV_DBG_ERROR("non-blocking read\n");
+ ret = (ssize_t)(-EAGAIN);
+ break;
+ }
+
+ if (signal_pending(current) != 0) {
+ MCDRV_DBG_VERBOSE("received signal.\n");
+ ret = (ssize_t)(-ERESTARTSYS);
+ break;
+ }
+
+ }
+
+ /* we are here if an event occurred or we had an
+ error.*/
+ if (ret != 0)
+ break;
+
+ /* read data and exit loop */
+ ret = copy_to_user(
+ buffer,
+ &(mc_drv_kmod_ctx.daemon_ctx.ssiq_counter),
+ sizeof(unsigned int));
+
+
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_to_user failed\n");
+ ret = (ssize_t)(-EFAULT);
+ break;
+ }
+
+ retLen = sizeof(s32);
+
+ } while (FALSE);
+
+ /* avoid debug on non-error. */
+ if (ret == 0)
+ ret = (size_t)retLen;
+ else
+ MCDRV_DBG("exit with %d/0x%08X\n", ret, ret);
+
+ return (ssize_t)ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Allocate WSM for given instance
+ *
+ * @param instance instance
+ * @param requested_size size of the WSM
+ * @param handle pointer where the handle will be saved
+ * @param virt_kernel_addr pointer for the kernel virtual address
+ * @param phys_addr pointer for the physical address
+ *
+ * @return error code or 0 for success
+ */
+int mobicore_allocate_wsm(
+ struct mc_instance *instance,
+ unsigned long requested_size,
+ uint32_t *handle,
+ void **virt_kernel_addr,
+ void **phys_addr
+)
+{
+ unsigned int i;
+ unsigned int order;
+ unsigned long allocated_size;
+ int ret = 0;
+ struct mc_contg_buffer *contg_buffer = 0;
+ void *virt_kernel_addr_stack;
+ void *phys_addr_stack;
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG("%s (size=%ld)\n", __func__, requested_size);
+
+ order = size_to_order(requested_size);
+ if (order == INVALID_ORDER) {
+ MCDRV_DBG_ERROR(
+ "size to order converting failed for size %ld\n",
+ requested_size);
+ return INVALID_ORDER;
+ }
+
+ allocated_size = (1<<order)*PAGE_SIZE;
+
+ MCDRV_DBG("size %ld -> order %d --> %ld (2^n pages)\n",
+ requested_size, order, allocated_size);
+
+ do {
+ /* Usual Wsm request, allocate contigous buffer. */
+ /* search for a free entry in the wsm buffer list
+ * REV axh: serialize this over multiple instances. */
+ for (i = 0; i < MC_DRV_KMOD_CONTG_BUFFER_MAX; i++) {
+ contg_buffer = &(instance->contg_buffers[i]);
+ if (contg_buffer->handle == 0) {
+ contg_buffer->handle = get_mc_kmod_unique_id();
+ break;
+ }
+ }
+ if (i == MC_DRV_KMOD_CONTG_BUFFER_MAX) {
+ MCDRV_DBG_ERROR("no free contigous buffer\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ /* Common code for all allocation paths */
+ virt_kernel_addr_stack = (void *)__get_free_pages(
+ GFP_USER | __GFP_COMP,
+ order);
+ if (virt_kernel_addr_stack == NULL) {
+ MCDRV_DBG_ERROR("get_free_pages failed\n");
+ ret = -ENOMEM;
+ break;
+ }
+
+ /* Get physical address to instance data */
+ phys_addr_stack = (void *)virt_to_phys(virt_kernel_addr_stack);
+ /* TODO: check for INVALID_ADDRESS? */
+
+ MCDRV_DBG(
+ "allocated phys=0x%p - 0x%p, "
+ "size=%ld, kernel_virt=0x%p, handle=%d\n",
+ phys_addr_stack,
+ (void *)((unsigned int)phys_addr_stack+allocated_size),
+ allocated_size,
+ virt_kernel_addr_stack,
+ contg_buffer->handle);
+
+ /* Usual Wsm request, allocate contg_buffer.
+ * Also, we never free a persistent Tci */
+ contg_buffer->phys_addr = phys_addr_stack;
+ contg_buffer->virt_kernel_addr = virt_kernel_addr_stack;
+ contg_buffer->virt_user_addr = virt_kernel_addr_stack;
+ contg_buffer->num_pages = (1U << order);
+ *handle = contg_buffer->handle;
+ *virt_kernel_addr = virt_kernel_addr_stack;
+ *phys_addr = phys_addr_stack;
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("%s: exit with 0x%08X\n", __func__, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL(mobicore_allocate_wsm);
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function will be called from user space as address = mmap(...).
+ *
+ * @param file
+ * @param vmarea
+ * vmarea.pg_offset != 0 is mapping of MCI is requested
+ *
+ * @return 0 if OK or -ENOMEM in case of error.
+ */
+static int mc_kernel_module_mmap(
+ struct file *file,
+ struct vm_area_struct *vmarea
+)
+{
+ unsigned int i;
+ unsigned int order;
+ void *virt_kernel_addr_stack = 0;
+ void *phys_addr = 0;
+ unsigned long requested_size =
+ vmarea->vm_end - vmarea->vm_start;
+ unsigned long allocated_size;
+ int ret = 0;
+ struct mc_contg_buffer *contg_buffer = 0;
+ unsigned int handle = 0;
+ struct mc_instance *instance = get_instance(file);
+ unsigned int request = vmarea->vm_pgoff * 4096;
+#if defined(DEBUG)
+ bool release = false;
+#else
+ bool release = true;
+#endif
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG("enter (vmaStart=0x%p, size=%ld, request=0x%x, mci=0x%x)\n",
+ (void *)vmarea->vm_start,
+ requested_size,
+ request,
+ mci_base);
+
+ order = size_to_order(requested_size);
+ if (order == INVALID_ORDER) {
+ MCDRV_DBG_ERROR(
+ "size to order converting failed for size %ld\n",
+ requested_size);
+ return -ENOMEM;
+ }
+
+ allocated_size = (1<<order)*PAGE_SIZE;
+
+ MCDRV_DBG("size %ld -> order %d --> %ld (2^n pages)\n",
+ requested_size, order, allocated_size);
+
+ do {
+ /* Daemon tries to get an existing MCI */
+ if ((request == MC_DRV_KMOD_MMAP_MCI) && (mci_base != 0)) {
+ MCDRV_DBG("Request MCI, it is at (%x)\n", mci_base);
+
+ if (!is_caller_mc_daemon(instance)) {
+ ret = -EPERM;
+ break;
+ }
+ virt_kernel_addr_stack = (void *)mci_base;
+ phys_addr =
+ (void *)virt_to_phys(virt_kernel_addr_stack);
+ } else {
+ /* Usual Wsm request, allocate buffer. */
+ if (request == MC_DRV_KMOD_MMAP_WSM) {
+ /* search for a free entry in the buffer list
+ REV axh: serialize this over multiple instances.
+ */
+ for (i = 0; i < MC_DRV_KMOD_CONTG_BUFFER_MAX;
+ i++) {
+ contg_buffer =
+ &(instance->contg_buffers[i]);
+ if (contg_buffer->handle == 0) {
+ contg_buffer->handle =
+ get_mc_kmod_unique_id();
+ break;
+ }
+ }
+ if (i == MC_DRV_KMOD_CONTG_BUFFER_MAX) {
+ MCDRV_DBG_ERROR(
+ "no free contigous buffer\n");
+ ret = -EFAULT;
+ break;
+ }
+ } else {
+ if (request <= MC_DRV_KMOD_MMAP_PERSISTENTWSM
+ || release) {
+ /* Special Wsm request
+ --> only Daemon is allowed */
+ if (!is_caller_mc_daemon(instance)) {
+ ret = -EPERM;
+ break;
+ }
+ }
+ }
+ if (request <= MC_DRV_KMOD_MMAP_PERSISTENTWSM) {
+ /* Common code for all allocation paths
+ * get physical address, */
+ virt_kernel_addr_stack =
+ (void *)__get_free_pages(
+ GFP_USER | __GFP_COMP,
+ order);
+ if (virt_kernel_addr_stack == NULL) {
+ MCDRV_DBG_ERROR(
+ "get_free_pages failed\n");
+ ret = -ENOMEM;
+ break;
+ }
+ if (request == MC_DRV_KMOD_MMAP_WSM)
+ handle = contg_buffer->handle;
+ /* Get physical address to instance data */
+ /* TODO: check for INVALID_ADDRESS? */
+ phys_addr = (void *)virt_to_phys(
+ virt_kernel_addr_stack);
+ } else {
+#if defined(DEBUG)
+ phys_addr = (void *)request;
+ virt_kernel_addr_stack = phys_to_virt(request);
+#endif
+ }
+ }
+ /* Common code for all mmap calls:
+ * map page to user
+ * store data in page */
+
+ MCDRV_DBG("allocated phys=0x%p - 0x%p, "
+ "size=%ld, kernel_virt=0x%p, handle=%d\n",
+ phys_addr,
+ (void *)((unsigned int)phys_addr+allocated_size),
+ allocated_size, virt_kernel_addr_stack, handle);
+
+ vmarea->vm_flags |= VM_RESERVED;
+ /* convert Kernel address to User Address. Kernel address begins
+ at PAGE_OFFSET, user Address range is below PAGE_OFFSET.
+ Remapping the area is always done, so multiple mappings
+ of one region are possible. Now remap kernel address
+ space into user space */
+ ret = (int)remap_pfn_range(
+ vmarea,
+ (vmarea->vm_start),
+ addr_to_pfn(phys_addr),
+ requested_size,
+ vmarea->vm_page_prot);
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("remapPfnRange failed\n");
+
+ /* free allocated pages when mmap fails, however, do not
+ do it, when daemon tried to get an MCI that
+ existed */
+ if (!((request == MC_DRV_KMOD_MMAP_MCI) &&
+ (mci_base != 0)))
+ free_continguous_pages(virt_kernel_addr_stack,
+ (1U << order));
+ break;
+ }
+
+ /* Usual Wsm request, allocate contg_buffer.
+ When requesting Mci, we do not associate the page with
+ the process.
+ Note: we also never free the Mci
+ Also, we never free a persistent Tci */
+ if (request == MC_DRV_KMOD_MMAP_WSM) {
+ contg_buffer->phys_addr = phys_addr;
+ contg_buffer->virt_kernel_addr = virt_kernel_addr_stack;
+ contg_buffer->virt_user_addr =
+ (void *)(vmarea->vm_start);
+ contg_buffer->num_pages = (1U << order);
+ }
+
+ /* set response in allocated buffer */
+ {
+ struct mc_mmap_resp *mmap_resp =
+ (struct mc_mmap_resp *)virt_kernel_addr_stack;
+ /* TODO: do this for daemon only, otherwise set NULL */
+ mmap_resp->phys_addr = (uint32_t)phys_addr;
+ mmap_resp->handle = handle;
+ if ((request == MC_DRV_KMOD_MMAP_MCI) &&
+ (mci_base != 0)) {
+ mmap_resp->is_reused = 1;
+ } else
+ mmap_resp->is_reused = 0;
+ }
+
+ /* store MCI pointer */
+ if ((request == MC_DRV_KMOD_MMAP_MCI) && (mci_base == 0)) {
+ mci_base = (uint32_t)virt_kernel_addr_stack;
+ MCDRV_DBG("MCI base set to 0x%x\n", mci_base);
+ }
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return (int)ret;
+}
+
+#ifdef CONFIG_SMP
+/*----------------------------------------------------------------------------*/
+/**
+ * Force migration of current task to CPU0(where the monitor resides)
+ *
+ * @return Error code or 0 for success
+ */
+static int goto_cpu0(
+ void
+)
+{
+ int ret = 0;
+ struct cpumask mask = CPU_MASK_CPU0;
+
+ MCDRV_DBG_VERBOSE("System has %d CPU's, we are on CPU #%d\n"
+ "\tBinding this process to CPU #0.\n"
+ "\tactive mask is %lx, setting it to mask=%lx\n",
+ nr_cpu_ids,
+ raw_smp_processor_id(),
+ cpu_active_mask->bits[0],
+ mask.bits[0]);
+ ret = set_cpus_allowed_ptr(current, &mask);
+ if (ret != 0)
+ MCDRV_DBG_ERROR("set_cpus_allowed_ptr=%d.\n", ret);
+ MCDRV_DBG_VERBOSE("And now we are on CPU #%d\n",
+ raw_smp_processor_id());
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Restore CPU mask for current to ALL Cpus(reverse of goto_cpu0)
+ *
+ * @return Error code or 0 for success
+ */
+static int goto_all_cpu(
+ void
+)
+{
+ int ret = 0;
+
+ struct cpumask mask = CPU_MASK_ALL;
+
+ MCDRV_DBG_VERBOSE("System has %d CPU's, we are on CPU #%d\n"
+ "\tBinding this process to CPU #0.\n"
+ "\tactive mask is %lx, setting it to mask=%lx\n",
+ nr_cpu_ids,
+ raw_smp_processor_id(),
+ cpu_active_mask->bits[0],
+ mask.bits[0]);
+ ret = set_cpus_allowed_ptr(current, &mask);
+ if (ret != 0)
+ MCDRV_DBG_ERROR("set_cpus_allowed_ptr=%d.\n", ret);
+ MCDRV_DBG_VERBOSE("And now we are on CPU #%d\n",
+ raw_smp_processor_id());
+
+ return ret;
+}
+
+#else
+static int goto_cpu0(void)
+{
+ return 0;
+}
+
+static int goto_all_cpu(void)
+{
+ return 0;
+}
+#endif
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Initialize a new mobicore API instance object
+ *
+ * @return Instance or NULL if no allocation was possible.
+ */
+struct mc_instance *mobicore_open(
+ void
+) {
+ struct mc_instance *instance;
+ pid_t pid_vnr;
+
+ instance = kzalloc(sizeof(*instance), GFP_KERNEL);
+ if (instance == NULL)
+ return NULL;
+
+ /* get a unique ID for this instance (PIDs are not unique) */
+ instance->handle = get_mc_kmod_unique_id();
+
+ /* get the PID of the calling process. We avoid using
+ * current->pid directly, as 2.6.24 introduced PID
+ * namespaces. See also http://lwn.net/Articles/259217 */
+ pid_vnr = task_pid_vnr(current);
+ instance->pid_vnr = pid_vnr;
+
+ return instance;
+}
+EXPORT_SYMBOL(mobicore_open);
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function will be called from user space as fd = open(...).
+ * A set of internal instance data are created and initialized.
+ *
+ * @param inode
+ * @param file
+ * @return 0 if OK or -ENOMEM if no allocation was possible.
+ */
+static int mc_kernel_module_open(
+ struct inode *inode,
+ struct file *file
+)
+{
+ struct mc_instance *instance;
+ int ret = 0;
+
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ instance = mobicore_open();
+ if (instance == NULL)
+ return -ENOMEM;
+
+ /* check if Daemon. We simply assume that the first to open us
+ with root privileges must be the daemon. */
+ if ((is_userland_caller_privileged())
+ && (mc_drv_kmod_ctx.daemon_inst == NULL)) {
+ MCDRV_DBG("accept this as MobiCore Daemon\n");
+
+ /* Set the caller's CPU mask to CPU0*/
+ ret = goto_cpu0();
+ if (ret != 0) {
+ mobicore_release(instance);
+ file->private_data = NULL;
+ MCDRV_DBG("changing core failed!\n");
+ break;
+ }
+
+ mc_drv_kmod_ctx.daemon_inst = instance;
+ sema_init(&mc_drv_kmod_ctx.daemon_ctx.sem,
+ DAEMON_SEM_VAL);
+ /* init ssiq event counter */
+ mc_drv_kmod_ctx.daemon_ctx.ssiq_counter =
+ atomic_read(
+ &(mc_drv_kmod_ctx.ssiq_ctx.counter));
+
+#ifdef MC_MEM_TRACES
+ /* The traces have to be setup on CPU-0 since we must
+ * do a fastcall to MobiCore. */
+ if (!mci_base)
+ /* Do the work only if MCI base is not
+ * initialized properly */
+ work_on_cpu(0, mobicore_log_setup, NULL);
+#endif
+ }
+
+ /* store instance data reference */
+ file->private_data = instance;
+
+ /* TODO axh: link all instances to allow clean up? */
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return (int)ret;
+
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Release a mobicore instance object and all objects related to it
+ * @param instance instance
+ * @return 0 if Ok or -E ERROR
+ */
+int mobicore_release(
+ struct mc_instance *instance
+)
+{
+ int ret = 0;
+ int i;
+ struct mc_used_l2_table *used_l2table, *used_l2table_temp;
+
+ do {
+ /* try to get the semaphore */
+ ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR(
+ "down_interruptible() failed with %d\n", ret);
+ /* TODO: can be block here? */
+ ret = -ERESTARTSYS;
+ } else {
+ /* Check if some WSM is still in use. */
+ list_for_each_entry_safe(
+ used_l2table,
+ used_l2table_temp,
+ &(mc_drv_kmod_ctx.mc_used_l2_tables),
+ list
+ ) {
+ if (used_l2table->owner == instance) {
+ MCDRV_DBG_WARN(
+ "trying to release WSM L2: "
+ "physBase=%p ,nr_of_pages=%d\n",
+ get_l2_table_phys(used_l2table),
+ used_l2table->nr_of_pages);
+
+ /* unlock app usage and free if MobiCore
+ does not use it */
+ delete_used_l2_table(used_l2table,
+ FREE_FROM_NWD);
+ }
+ } /* end while */
+
+ /* release semaphore */
+ up(&(mc_drv_kmod_ctx.wsm_l2_sem));
+ }
+
+
+ /* release all mapped data */
+ for (i = 0; i < MC_DRV_KMOD_CONTG_BUFFER_MAX; i++) {
+ struct mc_contg_buffer *contg_buffer =
+ &(instance->contg_buffers[i]);
+
+ if (contg_buffer->virt_user_addr != 0) {
+ free_continguous_pages(
+ contg_buffer->virt_kernel_addr,
+ contg_buffer->num_pages);
+ }
+ }
+
+ /* release instance context */
+ kfree(instance);
+ } while (FALSE);
+
+ return ret;
+}
+EXPORT_SYMBOL(mobicore_release);
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function will be called from user space as close(...).
+ * The instance data are freed and the associated memory pages are unreserved.
+ *
+ * @param inode
+ * @param file
+ *
+ * @return 0
+ */
+static int mc_kernel_module_release(
+ struct inode *inode,
+ struct file *file
+)
+{
+ int ret = 0;
+ struct mc_instance *instance = get_instance(file);
+
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ /* check if daemon closes us. */
+ if (is_caller_mc_daemon(instance)) {
+ /* TODO: cleanup?
+ * mc_drv_kmod_ctx.mc_used_l2_tables remains */
+ MCDRV_DBG_WARN("WARNING: MobiCore Daemon died\n");
+ mc_drv_kmod_ctx.daemon_inst = NULL;
+ }
+
+ ret = mobicore_release(instance);
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return (int)ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function represents the interrupt function of the mcDrvModule.
+ * It signals by incrementing of an event counter and the start of the read
+ * waiting queue, the read function a interrupt has occurred.
+ *
+ * @param intr
+ * @param *context pointer to registered device data
+ *
+ * @return IRQ_HANDLED
+ */
+static irqreturn_t mc_kernel_module_intr_ssiq(
+ int intr,
+ void *context
+)
+{
+ irqreturn_t ret = IRQ_NONE;
+
+ /* we know the context. */
+ MCDRV_ASSERT(&mc_drv_kmod_ctx == context);
+
+ do {
+ if (intr != MC_INTR_SSIQ) {
+ /* this should not happen, as we did no register for any
+ other interrupt. For debugging, we print a
+ message, but continue */
+ MCDRV_DBG_WARN(
+ "unknown interrupt %d, expecting only %d\n",
+ intr, MC_INTR_SSIQ);
+ }
+ MCDRV_DBG_VERBOSE("received interrupt %d\n",
+ intr);
+
+ /* increment interrupt event counter */
+ atomic_inc(&(mc_drv_kmod_ctx.ssiq_ctx.counter));
+
+ /* signal the daemon */
+ up(&mc_drv_kmod_ctx.daemon_ctx.sem);
+
+
+ ret = IRQ_HANDLED;
+
+ } while (FALSE);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/** function table structure of this device driver. */
+static const struct file_operations mc_kernel_module_file_operations = {
+ .owner = THIS_MODULE, /**< driver owner */
+ .open = mc_kernel_module_open, /**< driver open function */
+ .release = mc_kernel_module_release, /**< driver release function*/
+ .unlocked_ioctl = mc_kernel_module_ioctl, /**< driver ioctl function */
+ .mmap = mc_kernel_module_mmap, /**< driver mmap function */
+ .read = mc_kernel_module_read, /**< driver read function */
+};
+
+/*----------------------------------------------------------------------------*/
+/** registration structure as miscdevice. */
+static struct miscdevice mc_kernel_module_device = {
+ .name = MC_DRV_MOD_DEVNODE, /**< device name */
+ .minor = MISC_DYNAMIC_MINOR, /**< device minor number */
+ /** device interface function structure */
+ .fops = &mc_kernel_module_file_operations,
+};
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function is called the kernel during startup or by a insmod command.
+ * This device is installed and registered as miscdevice, then interrupt and
+ * queue handling is set up
+ *
+ * @return 0 for no error or -EIO if registration fails
+ */
+static int __init mc_kernel_module_init(
+ void
+)
+{
+ int ret = 0;
+
+ MCDRV_DBG("enter (Build " __TIMESTAMP__ ")\n");
+ MCDRV_DBG("mcDrvModuleApi version is %i.%i\n",
+ MCDRVMODULEAPI_VERSION_MAJOR,
+ MCDRVMODULEAPI_VERSION_MINOR);
+#ifdef MOBICORE_COMPONENT_BUILD_TAG
+ MCDRV_DBG("%s\n", MOBICORE_COMPONENT_BUILD_TAG);
+#endif
+ do {
+ /* Hardware does not support ARM TrustZone
+ -> Cannot continue! */
+ if (!has_security_extensions()) {
+ MCDRV_DBG_ERROR(
+ "Hardware does't support ARM TrustZone!\n");
+ ret = -ENODEV;
+ break;
+ }
+
+ /* Running in secure mode -> Cannot load the driver! */
+ if (is_secure_mode()) {
+ MCDRV_DBG_ERROR("Running in secure MODE!\n");
+ ret = -ENODEV;
+ break;
+ }
+
+ sema_init(&mc_drv_kmod_ctx.daemon_ctx.sem, DAEMON_SEM_VAL);
+ /* set up S-SIQ interrupt handler */
+ ret = request_irq(
+ MC_INTR_SSIQ,
+ mc_kernel_module_intr_ssiq,
+ IRQF_TRIGGER_RISING,
+ MC_DRV_MOD_DEVNODE,
+ &mc_drv_kmod_ctx);
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("interrupt request failed\n");
+ break;
+ }
+
+ ret = misc_register(&mc_kernel_module_device);
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("device register failed\n");
+ break;
+ }
+
+ /* initialize event counter for signaling of an IRQ to zero */
+ atomic_set(&(mc_drv_kmod_ctx.ssiq_ctx.counter), 0);
+
+ /* init list for WSM L2 chunks. */
+ INIT_LIST_HEAD(&(mc_drv_kmod_ctx.mc_l2_tables_sets));
+
+ /* L2 table descriptor list. */
+ INIT_LIST_HEAD(&(mc_drv_kmod_ctx.mc_used_l2_tables));
+
+ sema_init(&(mc_drv_kmod_ctx.wsm_l2_sem), 1);
+
+ /* initialize unique number counter which we can use for
+ handles. It is limited to 2^32, but this should be
+ enough to be roll-over safe for us. We start with 1
+ instead of 0. */
+ atomic_set(&(mc_drv_kmod_ctx.unique_counter), 1);
+
+ mci_base = 0;
+ MCDRV_DBG("initialized\n");
+
+ ret = 0;
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return (int)ret;
+}
+
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function removes this device driver from the Linux device manager .
+ */
+static void __exit mc_kernel_module_exit(
+ void
+)
+{
+ struct mc_used_l2_table *used_l2table;
+
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ mobicore_log_free();
+
+ /* Check if some WSM is still in use. */
+ list_for_each_entry(
+ used_l2table,
+ &(mc_drv_kmod_ctx.mc_used_l2_tables),
+ list
+ ) {
+ MCDRV_DBG_WARN(
+ "WSM L2 still in use: physBase=%p ,nr_of_pages=%d\n",
+ get_l2_table_phys(used_l2table),
+ used_l2table->nr_of_pages);
+ } /* end while */
+
+ free_irq(MC_INTR_SSIQ, &mc_drv_kmod_ctx);
+
+ misc_deregister(&mc_kernel_module_device);
+ MCDRV_DBG_VERBOSE("exit");
+}
+
+
+/*----------------------------------------------------------------------------*/
+/* Linux Driver Module Macros */
+module_init(mc_kernel_module_init);
+module_exit(mc_kernel_module_exit);
+MODULE_AUTHOR("Giesecke & Devrient GmbH");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MobiCore driver");
+
+/** @} */
+
diff --git a/drivers/gud/mobicore_driver/mc_drv_module.h b/drivers/gud/mobicore_driver/mc_drv_module.h
new file mode 100644
index 0000000..8b402d6
--- /dev/null
+++ b/drivers/gud/mobicore_driver/mc_drv_module.h
@@ -0,0 +1,238 @@
+/**
+ * Header file of MobiCore Driver Kernel Module.
+ *
+ * @addtogroup MCD_MCDIMPL_KMOD_IMPL
+ * @{
+ * Internal structures of the McDrvModule
+ * @file
+ *
+ * Header file the MobiCore Driver Kernel Module,
+ * its internal structures and defines.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * 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
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _MC_DRV_KMOD_H_
+#define _MC_DRV_KMOD_H_
+
+#include "mc_drv_module_linux_api.h"
+#include "public/mc_drv_module_api.h"
+/** Platform specific settings */
+#include "platform.h"
+
+/** ARM Specific masks and modes */
+#define ARM_CPSR_MASK 0x1F
+#define ARM_MONITOR_MODE 0b10110
+#define ARM_SECURITY_EXTENSION_MASK 0x30
+
+/**
+ * Number of page table entries in one L2 table. This is ARM specific, an
+ * L2 table covers 1 MiB by using 256 entry referring to 4KiB pages each.
+ */
+#define MC_ARM_L2_TABLE_ENTRIES 256
+
+/** Maximum number of contiguous buffer allocations for one driver instance. */
+#define MC_DRV_KMOD_CONTG_BUFFER_MAX 16
+
+/** Number of pages for L2 tables. There are 4 table in each page. */
+#define MC_DRV_KMOD_L2_TABLE_PER_PAGES 4
+
+/** ARM level 2 (L2) table with 256 entries. Size: 1k */
+struct l2table {
+ pte_t table_entries[MC_ARM_L2_TABLE_ENTRIES];
+};
+
+#define INVALID_ADDRESS ((void *)(-1))
+
+/** ARM L2 PTE bits */
+#define L2_FLAG_SMALL_XN (1U << 0)
+#define L2_FLAG_SMALL (1U << 1)
+#define L2_FLAG_B (1U << 2)
+#define L2_FLAG_C (1U << 3)
+#define L2_FLAG_AP0 (1U << 4)
+#define L2_FLAG_AP1 (1U << 5)
+#define L2_FLAG_SMALL_TEX0 (1U << 6)
+#define L2_FLAG_SMALL_TEX1 (1U << 7)
+#define L2_FLAG_SMALL_TEX2 (1U << 8)
+#define L2_FLAG_APX (1U << 9)
+#define L2_FLAG_S (1U << 10)
+#define L2_FLAG_NG (1U << 11)
+
+/**
+ * Contiguous buffer allocated to TLCs.
+ * These buffers are uses as world shared memory (wsm) and shared with
+ * secure world.
+ * The virtual kernel address is added for a simpler search algorithm.
+ */
+struct mc_contg_buffer {
+ unsigned int handle; /* unique handle */
+ void *virt_user_addr; /**< virtual User start address */
+ void *virt_kernel_addr; /**< virtual Kernel start address */
+ void *phys_addr; /**< physical start address */
+ unsigned int num_pages; /**< number of pages */
+};
+
+/** Instance data for MobiCore Daemon and TLCs. */
+struct mc_instance {
+ /** unique handle */
+ unsigned int handle;
+ /** process that opened this instance */
+ pid_t pid_vnr;
+ /** buffer list for mmap generated address space and
+ its virtual client address */
+ struct mc_contg_buffer contg_buffers[MC_DRV_KMOD_CONTG_BUFFER_MAX];
+};
+
+/** Store for four L2 tables in one 4kb page*/
+struct mc_l2_table_store {
+ struct l2table table[MC_DRV_KMOD_L2_TABLE_PER_PAGES];
+};
+
+/** Usage and maintenance information about mc_l2_table_store */
+struct mc_l2_tables_set {
+ struct list_head list;
+ unsigned int usage_bitmap; /**< usage bitmap */
+ struct mc_l2_table_store *kernel_virt; /**< kernel virtual address */
+ struct mc_l2_table_store *phys; /**< physical address */
+ struct page *page; /**< pointer to page struct */
+};
+
+/**
+ * L2 table allocated to the Daemon or a TLC describing a world shared buffer.
+ * When users map a malloc()ed area into SWd, a L2 table is allocated.
+ * In addition, the area of maximum 1MB virtual address space is mapped into
+ * the L2 table and a handle for this table is returned to the user.
+ */
+struct mc_used_l2_table {
+ struct list_head list;
+
+ /** handle as communicated to user mode */
+ unsigned int handle;
+ unsigned int flags;
+
+ /** owner of this L2 table */
+ struct mc_instance *owner;
+
+ /** set describing where our L2 table is stored */
+ struct mc_l2_tables_set *set;
+
+ /** index into L2 table set */
+ unsigned int idx;
+
+ /** size of buffer */
+ unsigned int nr_of_pages;
+};
+
+#define MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_APP (1U << 0)
+#define MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC (1U << 1)
+
+
+/** MobiCore S-SIQ interrupt context data. */
+struct mc_ssiq_ctx {
+ /** S-SIQ interrupt counter */
+ atomic_t counter;
+};
+
+/** MobiCore Daemon context data. */
+struct mc_daemon_ctx {
+ /** event semaphore */
+ struct semaphore sem;
+ struct fasync_struct *async_queue;
+ /** event counter */
+ unsigned int ssiq_counter;
+};
+
+/** MobiCore Driver Kernel Module context data. */
+struct mc_drv_kmod_ctx {
+
+ /** ever incrementing counter */
+ atomic_t unique_counter;
+
+ /** S-SIQ interrupt context */
+ struct mc_ssiq_ctx ssiq_ctx;
+
+ /** MobiCore Daemon context */
+ struct mc_daemon_ctx daemon_ctx;
+
+ /** pointer to instance of daemon */
+ struct mc_instance *daemon_inst;
+
+ /** Backing store for L2 tables */
+ struct list_head mc_l2_tables_sets;
+
+ /** Bookkeeping for used L2 tables */
+ struct list_head mc_used_l2_tables;
+
+ /** semaphore to synchronize access to above lists */
+ struct semaphore wsm_l2_sem;
+};
+
+/** MobiCore internal trace buffer structure. */
+struct mc_trace_buf {
+ uint32_t version; /**< version of trace buffer */
+ uint32_t length; /**< length of allocated buffer(includes header) */
+ uint32_t write_pos; /**< last write position */
+ char buff[1]; /**< start of the log buffer */
+};
+
+/*** MobiCore internal trace log setup. */
+void mobicore_log_read(void);
+long mobicore_log_setup(void *);
+void mobicore_log_free(void);
+
+#define MCDRV_DBG_ERROR(txt, ...) \
+ printk(KERN_ERR "mcDrvKMod [%d] %s() ### ERROR: " txt, \
+ task_pid_vnr(current), \
+ __func__, \
+ ##__VA_ARGS__)
+
+/* dummy function helper macro. */
+#define DUMMY_FUNCTION() do {} while (0)
+
+#if defined(DEBUG)
+
+/* #define DEBUG_VERBOSE */
+#if defined(DEBUG_VERBOSE)
+#define MCDRV_DBG_VERBOSE MCDRV_DBG
+#else
+#define MCDRV_DBG_VERBOSE(...) DUMMY_FUNCTION()
+#endif
+
+#define MCDRV_DBG(txt, ...) \
+ printk(KERN_INFO "mcDrvKMod [%d on CPU%d] %s(): " txt, \
+ task_pid_vnr(current), \
+ raw_smp_processor_id(), \
+ __func__, \
+ ##__VA_ARGS__)
+
+#define MCDRV_DBG_WARN(txt, ...) \
+ printk(KERN_WARNING "mcDrvKMod [%d] %s() WARNING: " txt, \
+ task_pid_vnr(current), \
+ __func__, \
+ ##__VA_ARGS__)
+
+#define MCDRV_ASSERT(cond) \
+ do { \
+ if (unlikely(!(cond))) { \
+ panic("mcDrvKMod Assertion failed: %s:%d\n", \
+ __FILE__, __LINE__); \
+ } \
+ } while (0)
+
+#else
+
+#define MCDRV_DBG_VERBOSE(...) DUMMY_FUNCTION()
+#define MCDRV_DBG(...) DUMMY_FUNCTION()
+#define MCDRV_DBG_WARN(...) DUMMY_FUNCTION()
+
+#define MCDRV_ASSERT(...) DUMMY_FUNCTION()
+
+#endif /* [not] defined(DEBUG) */
+
+
+#endif /* _MC_DRV_KMOD_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/mc_drv_module_android.h b/drivers/gud/mobicore_driver/mc_drv_module_android.h
new file mode 100644
index 0000000..319509f
--- /dev/null
+++ b/drivers/gud/mobicore_driver/mc_drv_module_android.h
@@ -0,0 +1,37 @@
+/**
+ * Header file of MobiCore Driver Kernel Module.
+ *
+ * @addtogroup MobiCore_Driver_Kernel_Module
+ * @{
+ * Android specific defines
+ * @file
+ *
+ * Android specific defines
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * 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
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _MC_DRV_MODULE_ANDROID_H_
+#define _MC_DRV_MODULE_ANDROID_H_
+
+/* Defines needed to identify the Daemon in Android systems
+ * For the full list see:
+ * platform_system_core/include/private/android_filesystem_config.h in the
+ * Android source tree
+ */
+/* traditional unix root user */
+#define AID_ROOT 0
+/* system server */
+#define AID_SYSTEM 1000
+/* access to misc storage */
+#define AID_MISC 9998
+#define AID_NOBODY 9999
+/* first app user */
+#define AID_APP 10000
+
+#endif /* _MC_DRV_MODULE_ANDROID_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/mc_drv_module_fastcalls.h b/drivers/gud/mobicore_driver/mc_drv_module_fastcalls.h
new file mode 100644
index 0000000..d058043
--- /dev/null
+++ b/drivers/gud/mobicore_driver/mc_drv_module_fastcalls.h
@@ -0,0 +1,227 @@
+/**
+ * Header file of MobiCore Driver Kernel Module.
+ *
+ * @addtogroup MobiCore_Driver_Kernel_Module
+ * @{
+ * Internal structures of the McDrvModule
+ * @file
+ *
+ * MobiCore Fast Call interface
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * 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
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _MC_DRV_MODULE_FC_H_
+#define _MC_DRV_MODULE_FC_H_
+
+#include "mc_drv_module.h"
+
+/**
+ * MobiCore SMCs
+ */
+enum mc_smc_codes {
+ MC_SMC_N_YIELD = 0x3, /**< Yield to switch from NWd to SWd. */
+ MC_SMC_N_SIQ = 0x4 /**< SIQ to switch from NWd to SWd. */
+};
+
+/**
+ * MobiCore fast calls. See MCI documentation
+ */
+enum mc_fast_call_codes {
+ MC_FC_INIT = -1,
+ MC_FC_INFO = -2,
+ MC_FC_POWER = -3,
+ MC_FC_DUMP = -4,
+ MC_FC_NWD_TRACE = -31 /**< Mem trace setup fastcall */
+};
+
+/**
+ * return code for fast calls
+ */
+enum mc_fast_calls_result {
+ MC_FC_RET_OK = 0,
+ MC_FC_RET_ERR_INVALID = 1,
+ MC_FC_RET_ERR_ALREADY_INITIALIZED = 5
+};
+
+
+
+/*------------------------------------------------------------------------------
+ structure wrappers for specific fastcalls
+------------------------------------------------------------------------------*/
+
+/** generic fast call parameters */
+union fc_generic {
+ struct {
+ uint32_t cmd;
+ uint32_t param[3];
+ } as_in;
+ struct {
+ uint32_t resp;
+ uint32_t ret;
+ uint32_t param[2];
+ } as_out;
+};
+
+
+/** fast call init */
+union mc_fc_init {
+ union fc_generic as_generic;
+ struct {
+ uint32_t cmd;
+ uint32_t base;
+ uint32_t nq_info;
+ uint32_t mcp_info;
+ } as_in;
+ struct {
+ uint32_t resp;
+ uint32_t ret;
+ uint32_t rfu[2];
+ } as_out;
+};
+
+
+/** fast call info parameters */
+union mc_fc_info {
+ union fc_generic as_generic;
+ struct {
+ uint32_t cmd;
+ uint32_t ext_info_id;
+ uint32_t rfu[2];
+ } as_in;
+ struct {
+ uint32_t resp;
+ uint32_t ret;
+ uint32_t state;
+ uint32_t ext_info;
+ } as_out;
+};
+
+
+/** fast call S-Yield parameters */
+union mc_fc_s_yield {
+ union fc_generic as_generic;
+ struct {
+ uint32_t cmd;
+ uint32_t rfu[3];
+ } as_in;
+ struct {
+ uint32_t resp;
+ uint32_t ret;
+ uint32_t rfu[2];
+ } as_out;
+};
+
+
+/** fast call N-SIQ parameters */
+union mc_fc_nsiq {
+ union fc_generic as_generic;
+ struct {
+ uint32_t cmd;
+ uint32_t rfu[3];
+ } as_in;
+ struct {
+ uint32_t resp;
+ uint32_t ret;
+ uint32_t rfu[2];
+ } as_out;
+};
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * fast call to MobiCore
+ *
+ * @param fc_generic pointer to fast call data
+ */
+static inline void mc_fastcall(
+ union fc_generic *fc_generic
+)
+{
+ MCDRV_ASSERT(fc_generic != NULL);
+ /* We only expect to make smc calls on CPU0 otherwise something wrong
+ * will happen */
+ MCDRV_ASSERT(raw_smp_processor_id() == 0);
+ mb();
+#ifdef MC_SMC_FASTCALL
+ {
+ int ret = 0;
+ MCDRV_DBG("Going into SCM()");
+ ret = smc_fastcall((void *)fc_generic, sizeof(*fc_generic));
+ MCDRV_DBG("Coming from SCM, scm_call=%i, resp=%d/0x%x\n",
+ ret,
+ fc_generic->as_out.resp, fc_generic->as_out.resp);
+ }
+#else
+ {
+ /* SVC expect values in r0-r3 */
+ register u32 reg0 __asm__("r0") = fc_generic->as_in.cmd;
+ register u32 reg1 __asm__("r1") = fc_generic->as_in.param[0];
+ register u32 reg2 __asm__("r2") = fc_generic->as_in.param[1];
+ register u32 reg3 __asm__("r3") = fc_generic->as_in.param[2];
+
+ /* one of the famous preprocessor hacks to stingitize things.*/
+#define __STR2(x) #x
+#define __STR(x) __STR2(x)
+
+ /* compiler does not support certain instructions
+ "SMC": secure monitor call.*/
+#define ASM_ARM_SMC 0xE1600070
+ /* "BPKT": debugging breakpoint. We keep this, as is comes
+ quite handy for debugging. */
+#define ASM_ARM_BPKT 0xE1200070
+#define ASM_THUMB_BPKT 0xBE00
+
+
+ __asm__ volatile (
+ ".word " __STR(ASM_ARM_SMC) "\n"
+ : "+r"(reg0), "+r"(reg1), "+r"(reg2), "+r"(reg3)
+ );
+
+ /* set response */
+ fc_generic->as_out.resp = reg0;
+ fc_generic->as_out.ret = reg1;
+ fc_generic->as_out.param[0] = reg2;
+ fc_generic->as_out.param[1] = reg3;
+ }
+#endif
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * convert fast call return code to linux driver module error code
+ *
+ */
+static inline int convert_fc_ret(
+ uint32_t sret
+)
+{
+ int ret = -EFAULT;
+
+ switch (sret) {
+
+ case MC_FC_RET_OK:
+ ret = 0;
+ break;
+
+ case MC_FC_RET_ERR_INVALID:
+ ret = -EINVAL;
+ break;
+
+ case MC_FC_RET_ERR_ALREADY_INITIALIZED:
+ ret = -EBUSY;
+ break;
+
+ default:
+ break;
+ } /* end switch( sret ) */
+ return ret;
+}
+
+#endif /* _MC_DRV_MODULE_FC_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/mc_drv_module_linux_api.h b/drivers/gud/mobicore_driver/mc_drv_module_linux_api.h
new file mode 100644
index 0000000..b2a99f1
--- /dev/null
+++ b/drivers/gud/mobicore_driver/mc_drv_module_linux_api.h
@@ -0,0 +1,187 @@
+/**
+ * Header file of MobiCore Driver Kernel Module.
+ *
+ * @addtogroup MobiCore_Driver_Kernel_Module
+ * @{
+ * Wrapper for Linux API
+ * @file
+ *
+ * Some convenient wrappers for memory functions
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * 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
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _MC_DRV_MODULE_LINUX_API_H_
+#define _MC_DRV_MODULE_LINUX_API_H_
+
+#include <linux/version.h>
+#include <linux/miscdevice.h>
+#include <linux/interrupt.h>
+#include <linux/highmem.h>
+#include <linux/kthread.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <asm/sizes.h>
+#include <asm/pgtable.h>
+#include <linux/semaphore.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+
+
+/* make some nice types */
+#if !defined(TRUE)
+#define TRUE (1 == 1)
+#endif
+
+#if !defined(FALSE)
+#define FALSE (1 != 1)
+#endif
+
+
+/* Linux GCC modifiers */
+#if !defined(__init)
+#warning "missing definition: __init"
+/* define a dummy */
+#define __init
+#endif
+
+
+#if !defined(__exit)
+#warning "missing definition: __exit"
+/* define a dummy */
+#define __exit
+#endif
+
+
+#if !defined(__must_check)
+#warning "missing definition: __must_check"
+/* define a dummy */
+#define __must_check
+#endif
+
+
+#if !defined(__user)
+#warning "missing definition: __user"
+/* define a dummy */
+#define __user
+#endif
+
+#define INVALID_ORDER ((unsigned int)(-1))
+
+/*----------------------------------------------------------------------------*/
+/* get start address of the 4 KiB page where the given addres is located in. */
+static inline void *get_page_start(
+ void *addr
+)
+{
+ return (void *)(((unsigned long)(addr)) & PAGE_MASK);
+}
+
+/*----------------------------------------------------------------------------*/
+/* get offset into the 4 KiB page where the given addres is located in. */
+static inline unsigned int get_offset_in_page(
+ void *addr
+)
+{
+ return (unsigned int)(((unsigned long)(addr)) & (~PAGE_MASK));
+}
+
+/*----------------------------------------------------------------------------*/
+/* get number of pages for a given buffer. */
+static inline unsigned int get_nr_of_pages_for_buffer(
+ void *addr_start, /* may be null */
+ unsigned int len
+)
+{
+ /* calculate used number of pages. Example:
+ offset+size newSize+PAGE_SIZE-1 nr_of_pages
+ 0 4095 0
+ 1 4096 1
+ 4095 8190 1
+ 4096 8191 1
+ 4097 8192 2 */
+
+ return (get_offset_in_page(addr_start) + len + PAGE_SIZE-1) / PAGE_SIZE;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * convert a given size to page order, which is equivalent to finding log_2(x).
+ * The maximum for order was 5 in Linux 2.0 corresponding to 32 pages.
+ * Later versions allow 9 corresponding to 512 pages, which is 2 MB on
+ * most platforms). Anyway, the bigger order is, the more likely it is
+ * that the allocation will fail.
+ * Size 0 1 4097 8193 12289 24577 28673 40961 61441
+ * Pages - 1 2 3 4 7 8 15 16
+ * Order INVALID_ORDER 0 1 1 2 2 3 3 4
+ *
+ * @param size
+ * @return order
+ */
+static inline unsigned int size_to_order(
+ unsigned int size
+)
+{
+ unsigned int order = INVALID_ORDER;
+
+ if (size != 0) {
+ /* ARMv5 as a CLZ instruction which count the leading zeros of
+ the binary representation of a value. It return a value
+ between 0 and 32.
+ Value 0 1 2 3 4 5 6 7 8 9 10 ...
+ CLZ 32 31 30 30 29 29 29 29 28 28 28 ...
+
+ We have excluded Size==0 before, so this is safe. */
+ order = __builtin_clz(
+ get_nr_of_pages_for_buffer(NULL, size));
+
+ /* there is a size overflow in get_nr_of_pages_for_buffer when
+ * the size is too large */
+ if (unlikely(order > 31))
+ return INVALID_ORDER;
+ order = 31 - order;
+
+ /* above algorithm rounds down: clz(5)=2 instead of 3 */
+ /* quick correction to fix it: */
+ if (((1<<order)*PAGE_SIZE) < size)
+ order++;
+ }
+ return order;
+}
+
+/* magic linux macro */
+#if !defined(list_for_each_entry)
+/* stop compiler */
+#error "missing macro: list_for_each_entry()"
+/* define a dummy */
+#define list_for_each_entry(a, b, c) if (0)
+#endif
+
+/*----------------------------------------------------------------------------*/
+/* return the page frame number of an address */
+static inline unsigned int addr_to_pfn(
+ void *addr
+)
+{
+ /* there is no real API for this */
+ return ((unsigned int)(addr)) >> PAGE_SHIFT;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/* return the address of a page frame number */
+static inline void *pfn_to_addr(
+ unsigned int pfn
+)
+{
+ /* there is no real API for this */
+ return (void *)(pfn << PAGE_SHIFT);
+}
+
+#endif /* _MC_DRV_MODULE_LINUX_API_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/platforms/msm8960_surf_std/platform.h b/drivers/gud/mobicore_driver/platforms/msm8960_surf_std/platform.h
new file mode 100644
index 0000000..7034cb0
--- /dev/null
+++ b/drivers/gud/mobicore_driver/platforms/msm8960_surf_std/platform.h
@@ -0,0 +1,50 @@
+/**
+ * Header file of MobiCore Driver Kernel Module Platform
+ * specific structures
+ *
+ * @addtogroup MobiCore_Driver_Kernel_Module
+ * @{
+ * Internal structures of the McDrvModule
+ * @file
+ *
+ * Header file the MobiCore Driver Kernel Module,
+ * its internal structures and defines.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * 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
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _MC_DRV_PLATFORM_H_
+#define _MC_DRV_PLATFORM_H_
+
+/** MobiCore Interrupt for Qualcomm */
+#define MC_INTR_SSIQ 218
+
+/** Use SMC for fastcalls */
+#define MC_SMC_FASTCALL
+
+
+/*--------------- Implementation -------------- */
+#include <mach/scm.h>
+/* from following file */
+#define SCM_SVC_MOBICORE 250
+#define SCM_CMD_MOBICORE 1
+
+extern int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
+ void *resp_buf, size_t resp_len);
+
+static inline int smc_fastcall(void *fc_generic, size_t size)
+{
+ return scm_call(SCM_SVC_MOBICORE, SCM_CMD_MOBICORE,
+ fc_generic, size,
+ fc_generic, size);
+}
+
+/** Enable mobicore mem traces */
+#define MC_MEM_TRACES
+
+#endif /* _MC_DRV_PLATFORM_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/public/mc_drv_module_api.h b/drivers/gud/mobicore_driver/public/mc_drv_module_api.h
new file mode 100644
index 0000000..59366f3
--- /dev/null
+++ b/drivers/gud/mobicore_driver/public/mc_drv_module_api.h
@@ -0,0 +1,311 @@
+/** @addtogroup MCD_MCDIMPL_KMOD_API Mobicore Driver Module API
+ * @ingroup MCD_MCDIMPL_KMOD
+ * @{
+ * Interface to Mobicore Driver Kernel Module.
+ * @file
+ *
+ * <h2>Introduction</h2>
+ * The MobiCore Driver Kernel Module is a Linux device driver, which represents
+ * the command proxy on the lowest layer to the secure world (Swd). Additional
+ * services like memory allocation via mmap and generation of a L2 tables for
+ * given virtual memory are also supported. IRQ functionallity receives
+ * information from the SWd in the non secure world (NWd).
+ * As customary the driver is handled as linux device driver with "open",
+ * "close" and "ioctl" commands. Access to the driver is possible after the
+ * device "/dev/mobicore" has been opened.
+ * The MobiCore Driver Kernel Module must be installed via
+ * "insmod mcDrvModule.ko".
+ *
+ *
+ * <h2>Version history</h2>
+ * <table class="customtab">
+ * <tr><td width="100px"><b>Date</b></td><td width="80px"><b>Version</b></td>
+ * <td><b>Changes</b></td></tr>
+ * <tr><td>2010-05-25</td><td>0.1</td><td>Initial Release</td></tr>
+ * </table>
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2010-2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MC_DRV_MODULEAPI_H_
+#define _MC_DRV_MODULEAPI_H_
+
+#include "version.h"
+
+#define MC_DRV_MOD_DEVNODE "mobicore"
+#define MC_DRV_MOD_DEVNODE_FULLPATH "/dev/" MC_DRV_MOD_DEVNODE
+
+/**
+ * Data exchange structure of the MC_DRV_MODULE_INIT ioctl command.
+ * INIT request data to SWD
+ */
+union mc_ioctl_init_params {
+ struct {
+ /** base address of mci buffer 4KB align */
+ uint32_t base;
+ /** notification buffer start/length [16:16] [start, length] */
+ uint32_t nq_offset;
+ /** length of notification queue */
+ uint32_t nq_length;
+ /** mcp buffer start/length [16:16] [start, length] */
+ uint32_t mcp_offset;
+ /** length of mcp buffer */
+ uint32_t mcp_length;
+ } in;
+ struct {
+ /* nothing */
+ } out;
+};
+
+
+/**
+ * Data exchange structure of the MC_DRV_MODULE_INFO ioctl command.
+ * INFO request data to the SWD
+ */
+union mc_ioctl_info_params {
+ struct {
+ uint32_t ext_info_id; /**< extended info ID */
+ } in;
+ struct {
+ uint32_t state; /**< state */
+ uint32_t ext_info; /**< extended info */
+ } out;
+};
+
+/**
+ * Mmap allocates and maps contiguous memory into a process.
+ * We use the third parameter, void *offset, to distinguish between some cases
+ * offset = MC_DRV_KMOD_MMAP_WSM usual operation, pages are registered in
+ device structure and freed later.
+ * offset = MC_DRV_KMOD_MMAP_MCI get Instance of MCI, allocates or mmaps
+ the MCI to daemon
+ * offset = MC_DRV_KMOD_MMAP_PERSISTENTWSM special operation, without
+ registration of pages
+ *
+ * In mmap(), the offset specifies which of several device I/O pages is
+ * requested. Linux only transfers the page number, i.e. the upper 20 bits to
+ * kernel module. Therefore we define our special offsets as multiples of page
+ * size.
+ */
+enum mc_mmap_memtype {
+ MC_DRV_KMOD_MMAP_WSM = 0,
+ MC_DRV_KMOD_MMAP_MCI = 4096,
+ MC_DRV_KMOD_MMAP_PERSISTENTWSM = 8192
+};
+
+struct mc_mmap_resp {
+ uint32_t handle; /**< WSN handle */
+ uint32_t phys_addr; /**< physical address of WSM (or NULL) */
+ bool is_reused; /**< if WSM memory was reused, or new allocated */
+};
+
+/**
+ * Data exchange structure of the MC_DRV_KMOD_IOCTL_FREE ioctl command.
+ */
+union mc_ioctl_free_params {
+ struct {
+ uint32_t handle; /**< driver handle */
+ uint32_t pid; /**< process id */
+ } in;
+ struct {
+ /* nothing */
+ } out;
+};
+
+
+/**
+ * Data exchange structure of the MC_DRV_KMOD_IOCTL_APP_REGISTER_WSM_L2 command.
+ *
+ * Allocates a physical L2 table and maps the buffer into this page.
+ * Returns the physical address of the L2 table.
+ * The page alignment will be created and the appropriated pSize and pOffsetL2
+ * will be modified to the used values.
+ */
+union mc_ioctl_app_reg_wsm_l2_params {
+ struct {
+ uint32_t buffer; /**< base address of the virtual address */
+ uint32_t len; /**< size of the virtual address space */
+ uint32_t pid; /**< process id */
+ } in;
+ struct {
+ uint32_t handle; /**< driver handle for locked memory */
+ uint32_t phys_wsm_l2_table; /* physical address of the L2 table */
+ } out;
+};
+
+
+/**
+ * Data exchange structure of the MC_DRV_KMOD_IOCTL_APP_UNREGISTER_WSM_L2
+ * command.
+ */
+struct mc_ioctl_app_unreg_wsm_l2_params {
+ struct {
+ uint32_t handle; /**< driver handle for locked memory */
+ uint32_t pid; /**< process id */
+ } in;
+ struct {
+ /* nothing */
+ } out;
+};
+
+
+/**
+ * Data exchange structure of the MC_DRV_KMOD_IOCTL_DAEMON_LOCK_WSM_L2 command.
+ */
+struct mc_ioctl_daemon_lock_wsm_l2_params {
+ struct {
+ uint32_t handle; /**< driver handle for locked memory */
+ } in;
+ struct {
+ uint32_t phys_wsm_l2_table;
+ } out;
+};
+
+
+/**
+ * Data exchange structure of the MC_DRV_KMOD_IOCTL_DAEMON_UNLOCK_WSM_L2
+ * command.
+ */
+struct mc_ioctl_daemon_unlock_wsm_l2_params {
+ struct {
+ uint32_t handle; /**< driver handle for locked memory */
+ } in;
+ struct {
+ /* nothing */
+ } out;
+};
+
+/**
+ * Data exchange structure of the MC_DRV_MODULE_FC_EXECUTE ioctl command.
+ */
+union mc_ioctl_fc_execute_params {
+ struct {
+ /**< base address of mobicore binary */
+ uint32_t phys_start_addr;
+ /**< length of DDR area */
+ uint32_t length;
+ } in;
+ struct {
+ /* nothing */
+ } out;
+};
+
+/**
+ * Data exchange structure of the MC_DRV_MODULE_GET_VERSION ioctl command.
+ */
+struct mc_ioctl_get_version_params {
+ struct {
+ uint32_t kernel_module_version;
+ } out;
+};
+
+/* @defgroup Mobicore_Driver_Kernel_Module_Interface IOCTL */
+
+
+
+
+/* TODO: use IOCTL macros like _IOWR. See Documentation/ioctl/ioctl-number.txt,
+ Documentation/ioctl/ioctl-decoding.txt */
+/**
+ * defines for the ioctl mobicore driver module function call from user space.
+ */
+enum mc_kmod_ioctl {
+
+ /*
+ * get detailed MobiCore Status
+ */
+ MC_DRV_KMOD_IOCTL_DUMP_STATUS = 200,
+
+ /*
+ * initialize MobiCore
+ */
+ MC_DRV_KMOD_IOCTL_FC_INIT = 201,
+
+ /*
+ * get MobiCore status
+ */
+ MC_DRV_KMOD_IOCTL_FC_INFO = 202,
+
+ /**
+ * ioctl parameter to send the YIELD command to the SWD.
+ * Only possible in Privileged Mode.
+ * ioctl(fd, MC_DRV_MODULE_YIELD)
+ */
+ MC_DRV_KMOD_IOCTL_FC_YIELD = 203,
+ /**
+ * ioctl parameter to send the NSIQ signal to the SWD.
+ * Only possible in Privileged Mode
+ * ioctl(fd, MC_DRV_MODULE_NSIQ)
+ */
+ MC_DRV_KMOD_IOCTL_FC_NSIQ = 204,
+ /**
+ * ioctl parameter to tzbsp to start Mobicore binary from DDR.
+ * Only possible in Privileged Mode
+ * ioctl(fd, MC_DRV_KMOD_IOCTL_FC_EXECUTE)
+ */
+ MC_DRV_KMOD_IOCTL_FC_EXECUTE = 205,
+
+ /**
+ * Free's memory which is formerly allocated by the driver's mmap
+ * command. The parameter must be this mmaped address.
+ * The internal instance data regarding to this address are deleted as
+ * well as each according memory page and its appropriated reserved bit
+ * is cleared (ClearPageReserved).
+ * Usage: ioctl(fd, MC_DRV_MODULE_FREE, &address) with address beeing of
+ * type long address
+ */
+ MC_DRV_KMOD_IOCTL_FREE = 218,
+
+ /**
+ * Creates a L2 Table of the given base address and the size of the
+ * data.
+ * Parameter: mc_ioctl_app_reg_wsm_l2_params
+ */
+ MC_DRV_KMOD_IOCTL_APP_REGISTER_WSM_L2 = 220,
+
+ /**
+ * Frees the L2 table created by a MC_DRV_KMOD_IOCTL_APP_REGISTER_WSM_L2
+ * ioctl.
+ * Parameter: mc_ioctl_app_unreg_wsm_l2_params
+ */
+ MC_DRV_KMOD_IOCTL_APP_UNREGISTER_WSM_L2 = 221,
+
+
+ /* TODO: comment this. */
+ MC_DRV_KMOD_IOCTL_DAEMON_LOCK_WSM_L2 = 222,
+ MC_DRV_KMOD_IOCTL_DAEMON_UNLOCK_WSM_L2 = 223,
+
+ /**
+ * Return kernel driver version.
+ * Parameter: mc_ioctl_get_version_params
+ */
+ MC_DRV_KMOD_IOCTL_GET_VERSION = 224,
+};
+
+
+#endif /* _MC_DRV_MODULEAPI_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/public/mc_kernel_api.h b/drivers/gud/mobicore_driver/public/mc_kernel_api.h
new file mode 100644
index 0000000..fdfc618
--- /dev/null
+++ b/drivers/gud/mobicore_driver/public/mc_kernel_api.h
@@ -0,0 +1,100 @@
+/** @addtogroup MCD_MCDIMPL_KMOD_KAPI Mobicore Driver Module API inside Kernel.
+ * @ingroup MCD_MCDIMPL_KMOD
+ * @{
+ * Interface to Mobicore Driver Kernel Module inside Kernel.
+ * @file
+ *
+ * Interface to be used by module MobiCoreKernelAPI.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2010-2012 -->
+ *
+ * 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
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _MOBICORE_KERNELMODULE_API_H_
+#define _MOBICORE_KERNELMODULE_API_H_
+
+struct mc_instance;
+
+/**
+ * Initialize a new mobicore API instance object
+ *
+ * @return Instance or NULL if no allocation was possible.
+ */
+struct mc_instance *mobicore_open(
+ void
+);
+
+/**
+ * Release a mobicore instance object and all objects related to it
+ * @param instance instance
+ * @return 0 if Ok or -E ERROR
+ */
+int mobicore_release(
+ struct mc_instance *instance
+);
+
+/**
+ * Free a WSM buffer allocated with mobicore_allocate_wsm
+ * @param instance
+ * @param handle handle of the buffer
+ *
+ * @return 0 if no error
+ *
+ */
+int mobicore_allocate_wsm(
+ struct mc_instance *instance,
+ unsigned long requested_size,
+ uint32_t *handle,
+ void **kernel_virt_addr,
+ void **phys_addr
+);
+
+/**
+ * Free a WSM buffer allocated with mobicore_allocate_wsm
+ * @param instance
+ * @param handle handle of the buffer
+ *
+ * @return 0 if no error
+ *
+ */
+int mobicore_free(
+ struct mc_instance *instance,
+ uint32_t handle
+);
+
+/**
+ * Map a virtual memory buffer structure to Mobicore
+ * @param instance
+ * @param addr address of the buffer(NB it must be kernel virtual!)
+ * @param len buffer length
+ * @param handle pointer to handle
+ * @param phys_wsm_l2_table pointer to physical L2 table(?)
+ *
+ * @return 0 if no error
+ *
+ */
+int mobicore_map_vmem(
+ struct mc_instance *instance,
+ void *addr,
+ uint32_t len,
+ uint32_t *handle,
+ void **phys_wsm_l2_table
+);
+
+/**
+ * Unmap a virtual memory buffer from mobicore
+ * @param instance
+ * @param handle
+ *
+ * @return 0 if no error
+ *
+ */
+int mobicore_unmap_vmem(
+ struct mc_instance *instance,
+ uint32_t handle
+);
+#endif /* _MOBICORE_KERNELMODULE_API_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/public/version.h b/drivers/gud/mobicore_driver/public/version.h
new file mode 100644
index 0000000..9b2dbca
--- /dev/null
+++ b/drivers/gud/mobicore_driver/public/version.h
@@ -0,0 +1,36 @@
+/** @addtogroup MCD_MCDIMPL_KMOD
+ * @{
+ * <!-- Copyright Giesecke & Devrient GmbH 2010-2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MC_DRV_VERSION_H_
+#define _MC_DRV_VERSION_H_
+
+#define MCDRVMODULEAPI_VERSION_MAJOR 0
+#define MCDRVMODULEAPI_VERSION_MINOR 1
+
+#endif /* _MC_DRV_VERSION_H_ */
diff --git a/drivers/gud/mobicore_kernelapi/clientlib.c b/drivers/gud/mobicore_kernelapi/clientlib.c
new file mode 100644
index 0000000..13826f2
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/clientlib.c
@@ -0,0 +1,1093 @@
+/**
+ * MobiCore KernelApi module
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * 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
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/netlink.h>
+#include <net/sock.h>
+#include <net/net_namespace.h>
+#include <linux/list.h>
+
+#include "public/mobicore_driver_api.h"
+#include "public/mobicore_driver_cmd.h"
+#include "device.h"
+#include "session.h"
+
+/* device list */
+LIST_HEAD(devices);
+
+/*----------------------------------------------------------------------------*/
+static struct mcore_device_t *resolve_device_id(
+ uint32_t device_id
+) {
+ struct mcore_device_t *tmp;
+ struct list_head *pos;
+
+ /* Get mcore_device_t for device_id */
+ list_for_each(pos, &devices) {
+ tmp = list_entry(pos, struct mcore_device_t, list);
+ if (tmp->device_id == device_id)
+ return tmp;
+ }
+ return NULL;
+}
+
+
+/*----------------------------------------------------------------------------*/
+static void add_device(
+ struct mcore_device_t *device
+) {
+ list_add_tail(&(device->list), &devices);
+}
+
+
+/*----------------------------------------------------------------------------*/
+static bool remove_device(
+ uint32_t device_id
+) {
+ struct mcore_device_t *tmp;
+ struct list_head *pos, *q;
+
+ list_for_each_safe(pos, q, &devices) {
+ tmp = list_entry(pos, struct mcore_device_t, list);
+ if (tmp->device_id == device_id) {
+ list_del(pos);
+ mcore_device_cleanup(tmp);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_open_device(
+ uint32_t device_id
+) {
+ enum mc_result mc_result = MC_DRV_OK;
+ struct connection *dev_con = NULL;
+
+ MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+ /* Enter critical section */
+
+ do {
+ struct mcore_device_t *device = resolve_device_id(device_id);
+ if (device != NULL) {
+ MCDRV_DBG_ERROR("Device %d already opened", device_id);
+ mc_result = MC_DRV_ERR_INVALID_OPERATION;
+ break;
+ }
+
+ /* Open new connection to device */
+ dev_con = connection_new();
+ if (!connection_connect(dev_con, MC_DAEMON_PID)) {
+ MCDRV_DBG_ERROR(
+ "Could not setup netlink connection to PID %u",
+ MC_DAEMON_PID);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ /* Forward device open to the daemon and read result */
+ struct mc_drv_cmd_open_device_t mc_drv_cmd_open_device = {
+ /* C++ does not support C99 designated initializers */
+ /* .header = */ {
+ /* .command_id = */ MC_DRV_CMD_OPEN_DEVICE
+ },
+ /* .payload = */ {
+ /* .device_id = */ device_id
+ }
+ };
+
+ int len = connection_write_data(
+ dev_con,
+ &mc_drv_cmd_open_device,
+ sizeof(struct mc_drv_cmd_open_device_t));
+ if (len < 0) {
+ MCDRV_DBG_ERROR("CMD_OPEN_DEVICE writeCmd failed "
+ "ret=%d", len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ struct mc_drv_response_header_t rsp_header;
+ len = connection_read_datablock(
+ dev_con,
+ &rsp_header,
+ sizeof(rsp_header));
+ if (len != sizeof(rsp_header)) {
+ MCDRV_DBG_ERROR("CMD_OPEN_DEVICE readRsp failed "
+ "ret=%d", len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+ if (rsp_header.response_id != MC_DRV_RSP_OK) {
+ MCDRV_DBG_ERROR("CMD_OPEN_DEVICE failed, respId=%d",
+ rsp_header.response_id);
+ switch (rsp_header.response_id) {
+ case MC_DRV_RSP_PAYLOAD_LENGTH_ERROR:
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ case MC_DRV_INVALID_DEVICE_NAME:
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ case MC_DRV_RSP_DEVICE_ALREADY_OPENED:
+ default:
+ mc_result = MC_DRV_ERR_INVALID_OPERATION;
+ break;
+ }
+ break;
+ }
+
+ /* there is no payload to read */
+
+ device = mcore_device_create(device_id, dev_con);
+ if (!mcore_device_open(device, MC_DRV_MOD_DEVNODE_FULLPATH)) {
+ mcore_device_cleanup(device);
+ MCDRV_DBG_ERROR("could not open device file: %s",
+ MC_DRV_MOD_DEVNODE_FULLPATH);
+ mc_result = MC_DRV_ERR_INVALID_DEVICE_FILE;
+ break;
+ }
+
+ add_device(device);
+
+ } while (false);
+
+ if (mc_result != MC_DRV_OK)
+ connection_cleanup(dev_con);
+
+ /* Exit critical section */
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_open_device);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_close_device(
+ uint32_t device_id
+) {
+ enum mc_result mc_result = MC_DRV_OK;
+
+ MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+ /* Enter critical section */
+ do {
+ struct mcore_device_t *device = resolve_device_id(device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR("Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+ struct connection *dev_con = device->connection;
+
+ /* Return if not all sessions have been closed */
+ if (mcore_device_has_sessions(device)) {
+ MCDRV_DBG_ERROR("cannot close with sessions pending");
+ mc_result = MC_DRV_ERR_SESSION_PENDING;
+ break;
+ }
+
+ struct mc_drv_cmd_close_device_t mc_drv_cmd_close_device = {
+ /* C++ does not support C99 designated initializers */
+ /* .header = */ {
+ /* .command_id = */ MC_DRV_CMD_CLOSE_DEVICE
+ }
+ };
+ int len = connection_write_data(
+ dev_con,
+ &mc_drv_cmd_close_device,
+ sizeof(struct mc_drv_cmd_close_device_t));
+ /* ignore error, but log details */
+ if (len < 0) {
+ MCDRV_DBG_ERROR("CMD_CLOSE_DEVICE writeCmd failed "
+ "ret=%d", len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ }
+
+ struct mc_drv_response_header_t rsp_header;
+ len = connection_read_datablock(
+ dev_con,
+ &rsp_header,
+ sizeof(rsp_header));
+ if (len != sizeof(rsp_header)) {
+ MCDRV_DBG_ERROR("CMD_CLOSE_DEVICE readResp failed "
+ " ret=%d", len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ if (rsp_header.response_id != MC_DRV_RSP_OK) {
+ MCDRV_DBG_ERROR("CMD_CLOSE_DEVICE failed, respId=%d",
+ rsp_header.response_id);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ remove_device(device_id);
+
+ } while (false);
+
+ /* Exit critical section */
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_close_device);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_open_session(
+ struct mc_session_handle *session,
+ const struct mc_uuid_t *uuid,
+ uint8_t *tci,
+ uint32_t len
+) {
+ enum mc_result mc_result = MC_DRV_OK;
+
+ MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+ /* Enter critical section */
+
+ do {
+ if (session == NULL) {
+ MCDRV_DBG_ERROR("Session is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (uuid == NULL) {
+ MCDRV_DBG_ERROR("UUID is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (tci == NULL) {
+ MCDRV_DBG_ERROR("TCI is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (len > MC_MAX_TCI_LEN) {
+ MCDRV_DBG_ERROR("TCI length is longer than %d",
+ MC_MAX_TCI_LEN);
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ /* Get the device associated with the given session */
+ struct mcore_device_t *device =
+ resolve_device_id(session->device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR("Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+ struct connection *dev_con = device->connection;
+
+ /* Get the physical address of the given TCI */
+ struct wsm *wsm =
+ mcore_device_find_contiguous_wsm(device, tci);
+ if (wsm == NULL) {
+ MCDRV_DBG_ERROR("Could not resolve TCI phy address ");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ if (wsm->len < len) {
+ MCDRV_DBG_ERROR("length is more than allocated TCI");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ /* Prepare open session command */
+ struct mc_drv_cmd_open_session_t cmdOpenSession = {
+ /* C++ does not support C99 designated initializers */
+ /* .header = */ {
+ /* .command_id = */ MC_DRV_CMD_OPEN_SESSION
+ },
+ /* .payload = */ {
+ /* .device_id = */ session->device_id,
+ /* .uuid = */ *uuid,
+ /* .tci = */ (uint32_t)wsm->phys_addr,
+ /* .len = */ len
+ }
+ };
+
+ /* Transmit command data */
+
+ int len = connection_write_data(
+ dev_con,
+ &cmdOpenSession,
+ sizeof(cmdOpenSession));
+ if (len != sizeof(cmdOpenSession)) {
+ MCDRV_DBG_ERROR("CMD_OPEN_SESSION writeData failed "
+ "ret=%d", len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ /* Read command response */
+
+ /* read header first */
+ struct mc_drv_response_header_t rsp_header;
+ len = connection_read_datablock(
+ dev_con,
+ &rsp_header,
+ sizeof(rsp_header));
+ if (len != sizeof(rsp_header)) {
+ MCDRV_DBG_ERROR("CMD_OPEN_SESSION readResp failed "
+ " ret=%d", len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ if (rsp_header.response_id != MC_DRV_RSP_OK) {
+ MCDRV_DBG_ERROR("CMD_OPEN_SESSION failed, respId=%d",
+ rsp_header.response_id);
+ switch (rsp_header.response_id) {
+ case MC_DRV_RSP_TRUSTLET_NOT_FOUND:
+ mc_result = MC_DRV_ERR_INVALID_DEVICE_FILE;
+ break;
+ case MC_DRV_RSP_PAYLOAD_LENGTH_ERROR:
+ case MC_DRV_RSP_DEVICE_NOT_OPENED:
+ case MC_DRV_RSP_FAILED:
+ default:
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+ break;
+ }
+
+ /* read payload */
+ struct mc_drv_rsp_open_session_payload_t
+ rsp_open_session_payload;
+ len = connection_read_datablock(
+ dev_con,
+ &rsp_open_session_payload,
+ sizeof(rsp_open_session_payload));
+ if (len != sizeof(rsp_open_session_payload)) {
+ MCDRV_DBG_ERROR("CMD_OPEN_SESSION readPayload failed "
+ "ret=%d", len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ /* Register session with handle */
+ session->session_id = rsp_open_session_payload.session_id;
+
+ /* Set up second channel for notifications */
+ struct connection *session_connection = connection_new();
+ /*TODO: no real need to connect here? */
+ if (!connection_connect(session_connection, MC_DAEMON_PID)) {
+ MCDRV_DBG_ERROR(
+ "Could not setup netlink connection to PID %u",
+ MC_DAEMON_PID);
+ connection_cleanup(session_connection);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ /*TODO CONTINOUE HERE !!!! FIX RW RETURN HANDLING!!!! */
+
+ /* Write command to use channel for notifications */
+ struct mc_drv_cmd_nqconnect_t cmd_nqconnect = {
+ /* C++ does not support C99 designated initializers */
+ /* .header = */ {
+ /* .command_id = */ MC_DRV_CMD_NQ_CONNECT
+ },
+ /* .payload = */ {
+ /* .device_id = */ session->device_id,
+ /* .session_id = */ session->session_id,
+ /* .device_session_id = */
+ rsp_open_session_payload.device_session_id,
+ /* .session_magic = */
+ rsp_open_session_payload.session_magic
+ }
+ };
+ connection_write_data(session_connection,
+ &cmd_nqconnect,
+ sizeof(cmd_nqconnect));
+
+ /* Read command response, header first */
+ len = connection_read_datablock(
+ session_connection,
+ &rsp_header,
+ sizeof(rsp_header));
+ if (len != sizeof(rsp_header)) {
+ MCDRV_DBG_ERROR("CMD_NQ_CONNECT readRsp failed "
+ "ret=%d", len);
+ connection_cleanup(session_connection);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ if (rsp_header.response_id != MC_DRV_RSP_OK) {
+ MCDRV_DBG_ERROR("CMD_NQ_CONNECT failed, respId=%d",
+ rsp_header.response_id);
+ connection_cleanup(session_connection);
+ mc_result = MC_DRV_ERR_NQ_FAILED;
+ break;
+ }
+
+ /* there is no payload. */
+
+ /* Session established, new session object must be created */
+ mcore_device_create_new_session(
+ device,
+ session->session_id,
+ session_connection);
+
+ } while (false);
+
+ /* Exit critical section */
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_open_session);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_close_session(
+ struct mc_session_handle *session
+) {
+ enum mc_result mc_result = MC_DRV_OK;
+
+ MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+ /* Enter critical section */
+
+ do {
+ if (session == NULL) {
+ MCDRV_DBG_ERROR("Session is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ struct mcore_device_t *device =
+ resolve_device_id(session->device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR("Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+ struct connection *dev_con = device->connection;
+
+ struct session *nq_session =
+ mcore_device_resolve_session_id(device, session->session_id);
+ if (nq_session == NULL) {
+ MCDRV_DBG_ERROR("Session not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+ break;
+ }
+
+ /* Write close session command */
+ struct mc_drv_cmd_close_session_t cmd_close_session = {
+ /* C++ does not support C99 designated initializers */
+ /* .header = */ {
+ /* .command_id = */ MC_DRV_CMD_CLOSE_SESSION
+ },
+ /* .payload = */ {
+ /* .session_id = */ session->session_id,
+ }
+ };
+ connection_write_data(
+ dev_con,
+ &cmd_close_session,
+ sizeof(cmd_close_session));
+
+ /* Read command response */
+ struct mc_drv_response_header_t rsp_header;
+ int len = connection_read_datablock(
+ dev_con,
+ &rsp_header,
+ sizeof(rsp_header));
+ if (len != sizeof(rsp_header)) {
+ MCDRV_DBG_ERROR("CMD_CLOSE_SESSION readRsp failed "
+ "ret=%d", len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ if (rsp_header.response_id != MC_DRV_RSP_OK) {
+ MCDRV_DBG_ERROR("CMD_CLOSE_SESSION failed, respId=%d",
+ rsp_header.response_id);
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+
+ mcore_device_remove_session(device, session->session_id);
+ mc_result = MC_DRV_OK;
+
+ } while (false);
+
+ /* Exit critical section */
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_close_session);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_notify(
+ struct mc_session_handle *session
+) {
+ enum mc_result mc_result = MC_DRV_OK;
+
+ MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+ do {
+ if (session == NULL) {
+ MCDRV_DBG_ERROR("Session is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ struct mcore_device_t *device =
+ resolve_device_id(session->device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR("Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+ struct connection *dev_con = device->connection;
+
+ struct session *nqsession =
+ mcore_device_resolve_session_id(device, session->session_id);
+ if (nqsession == NULL) {
+ MCDRV_DBG_ERROR("Session not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+ break;
+ }
+
+ struct mc_drv_cmd_notify_t cmd_notify = {
+ /* C++ does not support C99 designated initializers */
+ /* .header = */ {
+ /* .command_id = */ MC_DRV_CMD_NOTIFY
+ },
+ /* .payload = */ {
+ /* .session_id = */ session->session_id,
+ }
+ };
+
+ connection_write_data(
+ dev_con,
+ &cmd_notify,
+ sizeof(cmd_notify));
+
+ /* Daemon will not return a response */
+
+ } while (false);
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_notify);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_wait_notification(
+ struct mc_session_handle *session,
+ int32_t timeout
+) {
+ enum mc_result mc_result = MC_DRV_OK;
+
+ MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+ do {
+ if (session == NULL) {
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ struct mcore_device_t *device =
+ resolve_device_id(session->device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR("Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+
+ struct session *nq_session =
+ mcore_device_resolve_session_id(device, session->session_id);
+ if (nq_session == NULL) {
+ MCDRV_DBG_ERROR("Session not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+ break;
+ }
+
+ struct connection *nqconnection =
+ nq_session->notification_connection;
+ uint32_t count = 0;
+
+ /* Read notification queue till it's empty */
+ for (;;) {
+ struct notification notification;
+ ssize_t num_read = connection_read_data(
+ nqconnection,
+ ¬ification,
+ sizeof(notification),
+ timeout);
+ /* Exit on timeout in first run. Later runs have
+ * timeout set to 0.
+ * -2 means, there is no more data. */
+ if (count == 0 && num_read == -2) {
+ MCDRV_DBG_ERROR("read timeout");
+ mc_result = MC_DRV_ERR_TIMEOUT;
+ break;
+ }
+ /* After first notification the queue will be
+ * drained, Thus we set no timeout for the
+ * following reads */
+ timeout = 0;
+
+ if (num_read != sizeof(struct notification)) {
+ if (count == 0) {
+ /* failure in first read, notify it */
+ mc_result = MC_DRV_ERR_NOTIFICATION;
+ MCDRV_DBG_ERROR(
+ "read notification failed, "
+ "%i bytes received", (int)num_read);
+ break;
+ } else {
+ /* Read of the n-th notification
+ failed/timeout. We don't tell the
+ caller, as we got valid notifications
+ before. */
+ mc_result = MC_DRV_OK;
+ break;
+ }
+ }
+
+ count++;
+ MCDRV_DBG_VERBOSE("readNq count=%d, SessionID=%d, "
+ "Payload=%d", count,
+ notification.session_id, notification.payload);
+
+ if (notification.payload != 0) {
+ /* Session end point died -> store exit code */
+ session_set_error_info(nq_session,
+ notification.payload);
+
+ mc_result = MC_DRV_INFO_NOTIFICATION;
+ break;
+ }
+ } /* for(;;) */
+
+ } while (false);
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_wait_notification);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_malloc_wsm(
+ uint32_t device_id,
+ uint32_t align,
+ uint32_t len,
+ uint8_t **wsm,
+ uint32_t wsm_flags
+) {
+ enum mc_result mc_result = MC_DRV_ERR_UNKNOWN;
+
+ MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+ /* Enter critical section */
+
+ do {
+ struct mcore_device_t *device = resolve_device_id(device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR("Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+ if (wsm == NULL) {
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ struct wsm *wsm_stack =
+ mcore_device_allocate_contiguous_wsm(device, len);
+ if (wsm_stack == NULL) {
+ MCDRV_DBG_ERROR("Allocation of WSM failed");
+ mc_result = MC_DRV_ERR_NO_FREE_MEMORY;
+ break;
+ }
+
+ *wsm = (uint8_t *)wsm_stack->virt_addr;
+ mc_result = MC_DRV_OK;
+
+ } while (false);
+
+ /* Exit critical section */
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_malloc_wsm);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_free_wsm(
+ uint32_t device_id,
+ uint8_t *wsm
+) {
+ enum mc_result mc_result = MC_DRV_ERR_UNKNOWN;
+ struct mcore_device_t *device;
+
+
+ MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+ /* Enter critical section */
+
+ do {
+
+ /* Get the device associated wit the given session */
+ device = resolve_device_id(device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR("Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+
+ /* find WSM object */
+ struct wsm *wsm_stack =
+ mcore_device_find_contiguous_wsm(device, wsm);
+ if (wsm_stack == NULL) {
+ MCDRV_DBG_ERROR("unknown address");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ /* Free the given virtual address */
+ if (!mcore_device_free_contiguous_wsm(device, wsm_stack)) {
+ MCDRV_DBG_ERROR("Free of virtual address failed");
+ mc_result = MC_DRV_ERR_FREE_MEMORY_FAILED;
+ break;
+ }
+ mc_result = MC_DRV_OK;
+
+ } while (false);
+
+ /* Exit critical section */
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_free_wsm);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_map(
+ struct mc_session_handle *session_handle,
+ void *buf,
+ uint32_t buf_len,
+ struct mc_bulk_map *map_info
+) {
+ enum mc_result mc_result = MC_DRV_ERR_UNKNOWN;
+
+ MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+ /* Enter critical section */
+
+ do {
+ if (session_handle == NULL) {
+ MCDRV_DBG_ERROR("session_handle is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (map_info == NULL) {
+ MCDRV_DBG_ERROR("map_info is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (buf == NULL) {
+ MCDRV_DBG_ERROR("buf is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ /* Determine device the session belongs to */
+ struct mcore_device_t *device = resolve_device_id(
+ session_handle->device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR("Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+ struct connection *dev_con = device->connection;
+
+ /* Get session */
+ struct session *session =
+ mcore_device_resolve_session_id(device,
+ session_handle->session_id);
+ if (session == NULL) {
+ MCDRV_DBG_ERROR("Session not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+ break;
+ }
+
+ /* Register mapped bulk buffer to Kernel Module and keep mapped
+ bulk buffer in mind */
+ struct bulk_buffer_descriptor *bulk_buf = session_add_bulk_buf(
+ session, buf, buf_len);
+ if (bulk_buf == NULL) {
+ MCDRV_DBG_ERROR("Error mapping bulk buffer");
+ mc_result = MC_DRV_ERR_BULK_MAPPING;
+ break;
+ }
+
+ /* Prepare map command */
+ struct mc_drv_cmd_map_bulk_mem_t mc_drv_cmd_map_bulk_mem = {
+ /* C++ does not support C99 designated initializers */
+ /* .header = */ {
+ /* .command_id = */ MC_DRV_CMD_MAP_BULK_BUF
+ },
+ /* .payload = */ {
+ /* .session_id = */ session->session_id,
+ /* .phys_addr_l2; = */
+ (uint32_t)bulk_buf->phys_addr_wsm_l2,
+ /* .offset_payload = */
+ (uint32_t)(bulk_buf->virt_addr) & 0xFFF,
+ /* .len_bulk_mem = */ bulk_buf->len
+ }
+ };
+
+ /* Transmit map command to MobiCore device */
+ connection_write_data(
+ dev_con,
+ &mc_drv_cmd_map_bulk_mem,
+ sizeof(mc_drv_cmd_map_bulk_mem));
+
+ /* Read command response */
+ struct mc_drv_response_header_t rsp_header;
+ int len = connection_read_datablock(
+ dev_con,
+ &rsp_header,
+ sizeof(rsp_header));
+ if (len != sizeof(rsp_header)) {
+ MCDRV_DBG_ERROR("CMD_MAP_BULK_BUF readRsp failed, "
+ "ret=%d", len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ if (rsp_header.response_id != MC_DRV_RSP_OK) {
+ MCDRV_DBG_ERROR("CMD_MAP_BULK_BUF failed, respId=%d",
+ rsp_header.response_id);
+ /* REV We ignore Daemon Error code because client cannot
+ handle it anyhow. */
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+
+ /* Unregister mapped bulk buffer from Kernel Module and
+ remove mapped bulk buffer from session maintenance */
+ if (!session_remove_bulk_buf(session, buf)) {
+ /* Removing of bulk buffer not possible */
+ MCDRV_DBG_ERROR("Unregistering of bulk memory"
+ "from Kernel Module failed");
+ }
+ break;
+ }
+
+ struct mc_drv_rsp_map_bulk_mem_payload_t
+ rsp_map_bulk_mem_payload;
+ connection_read_datablock(
+ dev_con,
+ &rsp_map_bulk_mem_payload,
+ sizeof(rsp_map_bulk_mem_payload));
+
+ /* Set mapping info for Trustlet */
+ map_info->secure_virt_addr =
+ (void *)(rsp_map_bulk_mem_payload.secure_virtual_adr);
+ map_info->secure_virt_len = buf_len;
+ mc_result = MC_DRV_OK;
+
+ } while (false);
+
+ /* Exit critical section */
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_map);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_unmap(
+ struct mc_session_handle *session_handle,
+ void *buf,
+ struct mc_bulk_map *map_info
+) {
+ enum mc_result mc_result = MC_DRV_ERR_UNKNOWN;
+
+ MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+ /* Enter critical section */
+
+ do {
+ if (session_handle == NULL) {
+ MCDRV_DBG_ERROR("session_handle is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (map_info == NULL) {
+ MCDRV_DBG_ERROR("map_info is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (buf == NULL) {
+ MCDRV_DBG_ERROR("buf is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ /* Determine device the session belongs to */
+ struct mcore_device_t *device =
+ resolve_device_id(session_handle->device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR("Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+ struct connection *dev_con = device->connection;
+
+ /* Get session */
+ struct session *session =
+ mcore_device_resolve_session_id(device,
+ session_handle->session_id);
+ if (session == NULL) {
+ MCDRV_DBG_ERROR("Session not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+ break;
+ }
+
+ /* Prepare unmap command */
+ struct mc_drv_cmd_unmap_bulk_mem_t cmd_unmap_bulk_mem = {
+ /* .header = */ {
+ /* .command_id = */
+ MC_DRV_CMD_UNMAP_BULK_BUF
+ },
+ /* .payload = */ {
+ /* .session_id = */ session->session_id,
+ /* .secure_virtual_adr = */
+ (uint32_t)(map_info->secure_virt_addr),
+ /* .len_bulk_mem =
+ map_info->secure_virt_len*/
+ }
+ };
+
+ connection_write_data(
+ dev_con,
+ &cmd_unmap_bulk_mem,
+ sizeof(cmd_unmap_bulk_mem));
+
+ /* Read command response */
+ struct mc_drv_response_header_t rsp_header;
+ int len = connection_read_datablock(
+ dev_con,
+ &rsp_header,
+ sizeof(rsp_header));
+ if (len != sizeof(rsp_header)) {
+ MCDRV_DBG_ERROR("CMD_UNMAP_BULK_BUF readRsp failed, "
+ "ret=%d", len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ if (rsp_header.response_id != MC_DRV_RSP_OK) {
+ MCDRV_DBG_ERROR("CMD_UNMAP_BULK_BUF failed, respId=%d",
+ rsp_header.response_id);
+ /* REV We ignore Daemon Error code because client
+ cannot handle it anyhow. */
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ struct mc_drv_rsp_unmap_bulk_mem_payload_t
+ rsp_unmap_bulk_mem_payload;
+ connection_read_datablock(
+ dev_con,
+ &rsp_unmap_bulk_mem_payload,
+ sizeof(rsp_unmap_bulk_mem_payload));
+
+ /* REV axh: what about check the payload? */
+
+ /* Unregister mapped bulk buffer from Kernel Module and
+ * remove mapped bulk buffer from session maintenance */
+ if (!session_remove_bulk_buf(session, buf)) {
+ /* Removing of bulk buffer not possible */
+ MCDRV_DBG_ERROR("Unregistering of bulk memory from "
+ "Kernel Module failed");
+ mc_result = MC_DRV_ERR_BULK_UNMAPPING;
+ break;
+ }
+
+ mc_result = MC_DRV_OK;
+
+ } while (false);
+
+ /* Exit critical section */
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_unmap);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_get_session_error_code(
+ struct mc_session_handle *session,
+ int32_t *last_error
+) {
+ enum mc_result mc_result = MC_DRV_OK;
+
+ MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+ do {
+ if (session == NULL || last_error == NULL) {
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ /* Get device */
+ struct mcore_device_t *device =
+ resolve_device_id(session->device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR("Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+
+ /* Get session */
+ struct session *nqsession =
+ mcore_device_resolve_session_id(device, session->session_id);
+ if (nqsession == NULL) {
+ MCDRV_DBG_ERROR("Session not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+ break;
+ }
+
+ /* get session error code from session */
+ *last_error = session_get_last_err(nqsession);
+
+ } while (false);
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_get_session_error_code);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_driver_ctrl(
+ enum mc_driver_ctrl param,
+ uint8_t *data,
+ uint32_t len
+) {
+ MCDRV_DBG_WARN("not implemented");
+ return MC_DRV_ERR_NOT_IMPLEMENTED;
+}
+EXPORT_SYMBOL(mc_driver_ctrl);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_manage(
+ uint32_t device_id,
+ uint8_t *data,
+ uint32_t len
+) {
+ MCDRV_DBG_WARN("not implemented");
+ return MC_DRV_ERR_NOT_IMPLEMENTED;
+}
+EXPORT_SYMBOL(mc_manage);
+
diff --git a/drivers/gud/mobicore_kernelapi/common.h b/drivers/gud/mobicore_kernelapi/common.h
new file mode 100644
index 0000000..2a73474
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/common.h
@@ -0,0 +1,97 @@
+/**
+ *
+ * Common data types
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * 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
+ * published by the Free Software Foundation.
+ */
+#ifndef COMMON_H
+#define COMMON_H
+
+#include "connection.h"
+#include "mcinq.h"
+
+void mcapi_insert_connection(
+ struct connection *connection
+);
+
+void mcapi_remove_connection(
+ uint32_t seq
+);
+
+unsigned int mcapi_unique_id(
+ void
+);
+
+
+#define MC_DAEMON_PID 0xFFFFFFFF
+#define MC_DRV_MOD_DEVNODE_FULLPATH "/dev/mobicore"
+
+/* dummy function helper macro. */
+#define DUMMY_FUNCTION() do {} while (0)
+
+#define MCDRV_ERROR(txt, ...) \
+ printk(KERN_ERR "mcKernelApi %s() ### ERROR: " txt, \
+ __func__, \
+ ##__VA_ARGS__)
+
+#if defined(DEBUG)
+
+/* #define DEBUG_VERBOSE */
+#if defined(DEBUG_VERBOSE)
+#define MCDRV_DBG_VERBOSE MCDRV_DBG
+#else
+#define MCDRV_DBG_VERBOSE(...) DUMMY_FUNCTION()
+#endif
+
+#define MCDRV_DBG(txt, ...) \
+ printk(KERN_INFO "mcKernelApi %s(): " txt, \
+ __func__, \
+ ##__VA_ARGS__)
+
+#define MCDRV_DBG_WARN(txt, ...) \
+ printk(KERN_WARNING "mcKernelApi %s() WARNING: " txt, \
+ __func__, \
+ ##__VA_ARGS__)
+
+#define MCDRV_DBG_ERROR(txt, ...) \
+ printk(KERN_ERR "mcKernelApi %s() ### ERROR: " txt, \
+ __func__, \
+ ##__VA_ARGS__)
+
+
+#define MCDRV_ASSERT(cond) \
+ do { \
+ if (unlikely(!(cond))) { \
+ panic("mcKernelApi Assertion failed: %s:%d\n", \
+ __FILE__, __LINE__); \
+ } \
+ } while (0)
+
+#elif defined(NDEBUG)
+
+#define MCDRV_DBG_VERBOSE(...) DUMMY_FUNCTION()
+#define MCDRV_DBG(...) DUMMY_FUNCTION()
+#define MCDRV_DBG_WARN(...) DUMMY_FUNCTION()
+#define MCDRV_DBG_ERROR(...) DUMMY_FUNCTION()
+
+#define MCDRV_ASSERT(...) DUMMY_FUNCTION()
+
+#else
+#error "Define DEBUG or NDEBUG"
+#endif /* [not] defined(DEBUG_MCMODULE) */
+
+
+#define LOG_I MCDRV_DBG_VERBOSE
+#define LOG_W MCDRV_DBG_WARN
+#define LOG_E MCDRV_DBG_ERROR
+
+
+#define assert(expr) MCDRV_ASSERT(expr)
+
+#endif /* COMMON_H */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/connection.c b/drivers/gud/mobicore_kernelapi/connection.c
new file mode 100644
index 0000000..9048ae8
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/connection.c
@@ -0,0 +1,229 @@
+/** @addtogroup MCD_MCDIMPL_DAEMON_SRV
+ * @{
+ * @file
+ *
+ * Connection data.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * 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
+ * published by the Free Software Foundation.
+ */
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/netlink.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/semaphore.h>
+#include <linux/time.h>
+#include <net/sock.h>
+#include <net/net_namespace.h>
+
+#include "connection.h"
+#include "common.h"
+
+/* Define the initial state of the Data Available Semaphore */
+#define SEM_NO_DATA_AVAILABLE 0
+
+/*----------------------------------------------------------------------------*/
+struct connection *connection_new(
+ void
+) {
+ struct connection *conn = kzalloc(sizeof(struct connection),
+ GFP_KERNEL);
+ conn->sequence_magic = mcapi_unique_id();
+ mutex_init(&conn->data_lock);
+ /* No data available */
+ sema_init(&conn->data_available_sem, SEM_NO_DATA_AVAILABLE);
+
+ mcapi_insert_connection(conn);
+ return conn;
+}
+
+/*----------------------------------------------------------------------------*/
+struct connection *connection_create(
+ int socket_descriptor,
+ pid_t dest
+) {
+ struct connection *conn = connection_new();
+
+ conn->peer_pid = dest;
+ return conn;
+}
+
+
+/*----------------------------------------------------------------------------*/
+void connection_cleanup(
+ struct connection *conn
+) {
+ if (!conn)
+ return;
+
+ kfree_skb(conn->skb);
+
+ mcapi_remove_connection(conn->sequence_magic);
+ kfree(conn);
+}
+
+
+/*----------------------------------------------------------------------------*/
+bool connection_connect(
+ struct connection *conn,
+ pid_t dest
+) {
+ /* Nothing to connect */
+ conn->peer_pid = dest;
+ return true;
+}
+
+/*----------------------------------------------------------------------------*/
+size_t connection_readDataMsg(
+ struct connection *conn,
+ void *buffer,
+ uint32_t len
+) {
+ size_t ret = -1;
+ MCDRV_DBG_VERBOSE("reading connection data %u, connection data left %u",
+ len, conn->data_len);
+ /* trying to read more than the left data */
+ if (len > conn->data_len) {
+ ret = conn->data_len;
+ memcpy(buffer, conn->data_start, conn->data_len);
+ conn->data_len = 0;
+ } else {
+ ret = len;
+ memcpy(buffer, conn->data_start, len);
+ conn->data_len -= len;
+ conn->data_start += len;
+ }
+
+ if (conn->data_len == 0) {
+ conn->data_start = NULL;
+ kfree_skb(conn->skb);
+ conn->skb = NULL;
+ }
+ MCDRV_DBG_VERBOSE("read %u", ret);
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+size_t connection_read_datablock(
+ struct connection *conn,
+ void *buffer,
+ uint32_t len
+) {
+ return connection_read_data(conn, buffer, len, -1);
+}
+
+
+/*----------------------------------------------------------------------------*/
+size_t connection_read_data(
+ struct connection *conn,
+ void *buffer,
+ uint32_t len,
+ int32_t timeout
+) {
+ size_t ret = 0;
+
+ MCDRV_ASSERT(buffer != NULL);
+ MCDRV_ASSERT(conn->socket_descriptor != NULL);
+
+ MCDRV_DBG_VERBOSE("read data len = %u for PID = %u",
+ len, conn->sequence_magic);
+ do {
+ /* Wait until data is available or timeout
+ msecs_to_jiffies(-1) -> wait forever for the sem */
+ if (down_timeout(&(conn->data_available_sem),
+ msecs_to_jiffies(timeout))) {
+ MCDRV_DBG_VERBOSE("Timeout reading the data sem");
+ ret = -2;
+ break;
+ }
+
+ if (mutex_lock_interruptible(&(conn->data_lock))) {
+ MCDRV_DBG_ERROR("interrupted reading the data sem");
+ ret = -1;
+ break;
+ }
+ /* Have data, use it */
+ if (conn->data_len > 0)
+ ret = connection_readDataMsg(conn, buffer, len);
+
+ mutex_unlock(&(conn->data_lock));
+
+ /* There is still some data left */
+ if (conn->data_len > 0)
+ up(&conn->data_available_sem);
+ } while (0);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+size_t connection_write_data(
+ struct connection *conn,
+ void *buffer,
+ uint32_t len
+) {
+ struct sk_buff *skb = NULL;
+ struct nlmsghdr *nlh;
+ int ret = 0;
+
+ MCDRV_DBG_VERBOSE("buffer length %u from pid %u\n",
+ len, conn->sequence_magic);
+ do {
+ skb = nlmsg_new(NLMSG_SPACE(len), GFP_KERNEL);
+ if (!skb) {
+ ret = -1;
+ break;
+ }
+
+ nlh = nlmsg_put(skb, 0, conn->sequence_magic, 2,
+ NLMSG_LENGTH(len), NLM_F_REQUEST);
+ if (!nlh) {
+ ret = -1;
+ break;
+ }
+ memcpy(NLMSG_DATA(nlh), buffer, len);
+
+ netlink_unicast(conn->socket_descriptor, skb,
+ conn->peer_pid, MSG_DONTWAIT);
+ ret = len;
+ } while (0);
+
+ if (!ret && skb != NULL)
+ kfree_skb(skb);
+
+ return ret;
+}
+
+int connection_process(
+ struct connection *conn,
+ struct sk_buff *skb
+)
+{
+ int ret = 0;
+ do {
+ if (mutex_lock_interruptible(&(conn->data_lock))) {
+ MCDRV_DBG_ERROR("Interrupted getting data semaphore!");
+ ret = -1;
+ break;
+ }
+
+ kfree_skb(conn->skb);
+
+ /* Get a reference to the incomming skb */
+ conn->skb = skb_get(skb);
+ if (conn->skb) {
+ conn->data_msg = nlmsg_hdr(conn->skb);
+ conn->data_len = NLMSG_PAYLOAD(conn->data_msg, 0);
+ conn->data_start = NLMSG_DATA(conn->data_msg);
+ up(&(conn->data_available_sem));
+ }
+ mutex_unlock(&(conn->data_lock));
+ ret = 0;
+ } while (0);
+ return ret;
+}
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/connection.h b/drivers/gud/mobicore_kernelapi/connection.h
new file mode 100644
index 0000000..0b468e6
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/connection.h
@@ -0,0 +1,122 @@
+/** @addtogroup MCD_MCDIMPL_DAEMON_SRV
+ * @{
+ * @file
+ *
+ * Connection data.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * 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
+ * published by the Free Software Foundation.
+ */
+#ifndef CONNECTION_H_
+#define CONNECTION_H_
+
+#include <linux/semaphore.h>
+
+#include <stddef.h>
+#include <stdbool.h>
+
+#define MAX_PAYLOAD_SIZE 128
+
+struct connection {
+ struct sock *socket_descriptor; /**< Netlink socket */
+ uint32_t sequence_magic; /**< Random? magic to match requests/answers */
+
+ struct nlmsghdr *data_msg;
+ uint32_t data_len; /**< How much connection data is left */
+ void *data_start; /**< Start pointer of remaining data */
+ struct sk_buff *skb;
+
+ struct mutex data_lock; /**< Data protection lock */
+ struct semaphore data_available_sem; /**< Data protection semaphore */
+
+ pid_t self_pid; /**< PID address used for local connection */
+ pid_t peer_pid; /**< Remote PID for connection */
+
+ struct list_head list; /**< The list param for using the kernel lists*/
+};
+
+struct connection *connection_new(
+ void
+);
+
+struct connection *connection_create(
+ int socket_descriptor,
+ pid_t dest
+);
+
+void connection_cleanup(
+ struct connection *conn
+);
+
+/**
+ * Connect to destination.
+ *
+ * @param Destination pointer.
+ * @return true on success.
+ */
+bool connection_connect(
+ struct connection *conn,
+ pid_t dest
+);
+
+
+/**
+ * Read bytes from the connection.
+ *
+ * @param buffer Pointer to destination buffer.
+ * @param len Number of bytes to read.
+ * @return Number of bytes read.
+ */
+size_t connection_read_datablock(
+ struct connection *conn,
+ void *buffer,
+ uint32_t len
+);
+/**
+ * Read bytes from the connection.
+ *
+ * @param buffer Pointer to destination buffer.
+ * @param len Number of bytes to read.
+ * @param timeout Timeout in milliseconds
+ * @return Number of bytes read.
+ * @return -1 if select() failed (returned -1)
+ * @return -2 if no data available, i.e. timeout
+ */
+size_t connection_read_data(
+ struct connection *conn,
+ void *buffer,
+ uint32_t len,
+ int32_t timeout
+);
+
+/**
+ * Write bytes to the connection.
+ *
+ * @param buffer Pointer to source buffer.
+ * @param len Number of bytes to read.
+ * @return Number of bytes written.
+ */
+size_t connection_write_data(
+ struct connection *conn,
+ void *buffer,
+ uint32_t len
+);
+
+/**
+ * Write bytes to the connection.
+ *
+ * @param buffer Pointer to source buffer.
+ * @param len Number of bytes to read.
+ * @return Number of bytes written.
+ */
+int connection_process(
+ struct connection *conn,
+ struct sk_buff *skb
+);
+
+#endif /* CONNECTION_H_ */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/device.c b/drivers/gud/mobicore_kernelapi/device.c
new file mode 100644
index 0000000..dbeee6a
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/device.c
@@ -0,0 +1,257 @@
+/** @addtogroup MCD_IMPL_LIB
+ * @{
+ * @file
+ *
+ * Client library device management.
+ *
+ * Device and Trustlet Session management Funtions.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * 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
+ * published by the Free Software Foundation.
+ */
+#include <linux/list.h>
+#include <linux/slab.h>
+#include "mc_kernel_api.h"
+#include "public/mobicore_driver_api.h"
+
+#include "device.h"
+#include "common.h"
+
+/*----------------------------------------------------------------------------*/
+struct wsm *wsm_create(
+ void *virt_addr,
+ uint32_t len,
+ uint32_t handle,
+ void *phys_addr /*= NULL this may be unknown, so is can be omitted.*/
+ )
+{
+ struct wsm *wsm = kzalloc(sizeof(struct wsm), GFP_KERNEL);
+ wsm->virt_addr = virt_addr;
+ wsm->len = len;
+ wsm->handle = handle;
+ wsm->phys_addr = phys_addr;
+ return wsm;
+}
+
+
+/*----------------------------------------------------------------------------*/
+struct mcore_device_t *mcore_device_create(
+ uint32_t device_id,
+ struct connection *connection
+) {
+ struct mcore_device_t *dev =
+ kzalloc(sizeof(struct mcore_device_t), GFP_KERNEL);
+ dev->device_id = device_id;
+ dev->connection = connection;
+
+ INIT_LIST_HEAD(&dev->session_vector);
+ INIT_LIST_HEAD(&dev->wsm_l2_vector);
+
+ return dev;
+}
+
+
+/*----------------------------------------------------------------------------*/
+void mcore_device_cleanup(
+ struct mcore_device_t *dev
+) {
+ struct session *tmp;
+ struct wsm *wsm;
+ struct list_head *pos, *q;
+
+ /* Delete all session objects. Usually this should not be needed
+ * as closeDevice()requires that all sessions have been closed before.*/
+ list_for_each_safe(pos, q, &dev->session_vector) {
+ tmp = list_entry(pos, struct session, list);
+ list_del(pos);
+ session_cleanup(tmp);
+ }
+
+ /* Free all allocated WSM descriptors */
+ list_for_each_safe(pos, q, &dev->wsm_l2_vector) {
+ wsm = list_entry(pos, struct wsm, list);
+ /* mcKMod_free(dev->instance, wsm->handle); */
+ list_del(pos);
+ kfree(wsm);
+ }
+ connection_cleanup(dev->connection);
+
+ mcore_device_close(dev);
+ kfree(dev);
+}
+
+
+/*----------------------------------------------------------------------------*/
+bool mcore_device_open(
+ struct mcore_device_t *dev,
+ const char *deviceName
+) {
+ dev->instance = mobicore_open();
+ return (dev->instance != NULL);
+}
+
+
+/*----------------------------------------------------------------------------*/
+void mcore_device_close(
+ struct mcore_device_t *dev
+) {
+ mobicore_release(dev->instance);
+}
+
+
+/*----------------------------------------------------------------------------*/
+bool mcore_device_has_sessions(
+ struct mcore_device_t *dev
+) {
+ return !list_empty(&dev->session_vector);
+}
+
+
+/*----------------------------------------------------------------------------*/
+bool mcore_device_create_new_session(
+ struct mcore_device_t *dev,
+ uint32_t session_id,
+ struct connection *connection
+) {
+ /* Check if session_id already exists */
+ if (mcore_device_resolve_session_id(dev, session_id)) {
+ MCDRV_DBG_ERROR(" session %u already exists", session_id);
+ return false;
+ }
+ struct session *session = session_create(session_id, dev->instance,
+ connection);
+ list_add_tail(&(session->list), &(dev->session_vector));
+ return true;
+}
+
+
+/*----------------------------------------------------------------------------*/
+bool mcore_device_remove_session(
+ struct mcore_device_t *dev,
+ uint32_t session_id
+) {
+ bool ret = false;
+ struct session *tmp;
+ struct list_head *pos, *q;
+
+ list_for_each_safe(pos, q, &dev->session_vector) {
+ tmp = list_entry(pos, struct session, list);
+ if (tmp->session_id == session_id) {
+ list_del(pos);
+ session_cleanup(tmp);
+ ret = true;
+ break;
+ }
+ }
+ return ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+struct session *mcore_device_resolve_session_id(
+ struct mcore_device_t *dev,
+ uint32_t session_id
+) {
+ struct session *ret = NULL;
+ struct session *tmp;
+ struct list_head *pos;
+
+
+ /* Get session for session_id */
+ list_for_each(pos, &dev->session_vector) {
+ tmp = list_entry(pos, struct session, list);
+ if (tmp->session_id == session_id) {
+ ret = tmp;
+ break;
+ }
+ }
+ return ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+struct wsm *mcore_device_allocate_contiguous_wsm(
+ struct mcore_device_t *dev,
+ uint32_t len
+) {
+ struct wsm *wsm = NULL;
+ do {
+ if (len == 0)
+ break;
+
+ /* Allocate shared memory */
+ void *virt_addr;
+ uint32_t handle;
+ void *phys_addr;
+ int ret = mobicore_allocate_wsm(dev->instance,
+ len,
+ &handle,
+ &virt_addr,
+ &phys_addr);
+ if (ret != 0)
+ break;
+
+ /* Register (vaddr,paddr) with device */
+ wsm = wsm_create(virt_addr, len, handle, phys_addr);
+
+ list_add_tail(&(wsm->list), &(dev->wsm_l2_vector));
+
+ } while (0);
+
+ /* Return pointer to the allocated memory */
+ return wsm;
+}
+
+
+/*----------------------------------------------------------------------------*/
+bool mcore_device_free_contiguous_wsm(
+ struct mcore_device_t *dev,
+ struct wsm *wsm
+) {
+ bool ret = false;
+ struct wsm *tmp;
+ struct list_head *pos;
+
+ list_for_each(pos, &dev->wsm_l2_vector) {
+ tmp = list_entry(pos, struct wsm, list);
+ if (tmp == wsm) {
+ ret = true;
+ break;
+ }
+ }
+
+ if (ret) {
+ MCDRV_DBG_VERBOSE("freeWsm virt_addr=0x%p, handle=%d",
+ wsm->virt_addr, wsm->handle);
+
+ /* ignore return code */
+ mobicore_free(dev->instance, wsm->handle);
+
+ list_del(pos);
+ kfree(wsm);
+ }
+ return ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+struct wsm *mcore_device_find_contiguous_wsm(
+ struct mcore_device_t *dev,
+ void *virt_addr
+) {
+ struct wsm *wsm;
+ struct list_head *pos;
+
+ list_for_each(pos, &dev->wsm_l2_vector) {
+ wsm = list_entry(pos, struct wsm, list);
+ if (virt_addr == wsm->virt_addr)
+ return wsm;
+ }
+
+ return NULL;
+}
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/device.h b/drivers/gud/mobicore_kernelapi/device.h
new file mode 100644
index 0000000..f40d993
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/device.h
@@ -0,0 +1,139 @@
+/** @addtogroup MCD_IMPL_LIB
+ * @{
+ * @file
+ *
+ * Client library device management.
+ *
+ * Device and Trustlet Session management Functions.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * 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
+ * published by the Free Software Foundation.
+ */
+#ifndef DEVICE_H_
+#define DEVICE_H_
+
+#include <linux/list.h>
+
+#include "connection.h"
+#include "session.h"
+#include "wsm.h"
+
+
+struct mcore_device_t {
+ struct list_head session_vector; /**< MobiCore Trustlet session
+ associated with the device */
+ struct list_head wsm_l2_vector; /**< WSM L2 Table */
+
+ uint32_t device_id; /**< Device identifier */
+ struct connection *connection; /**< The device connection */
+ struct mc_instance *instance; /**< MobiCore Driver instance */
+
+ struct list_head list; /**< The list param for using the kernel lists*/
+};
+
+struct mcore_device_t *mcore_device_create(
+ uint32_t device_id,
+ struct connection *connection
+);
+
+void mcore_device_cleanup(
+ struct mcore_device_t *dev
+);
+
+/**
+ * Open the device.
+ * @param deviceName Name of the kernel modules device file.
+ * @return true if the device has been opened successfully
+ */
+bool mcore_device_open(
+ struct mcore_device_t *dev,
+ const char *deviceName
+);
+
+/**
+ * Closes the device.
+ */
+void mcore_device_close(
+ struct mcore_device_t *dev
+);
+
+/**
+ * Check if the device has open sessions.
+ * @return true if the device has one or more open sessions.
+ */
+bool mcore_device_has_sessions(
+ struct mcore_device_t *dev
+);
+
+/**
+ * Add a session to the device.
+ * @param session_id session ID
+ * @param connection session connection
+ */
+bool mcore_device_create_new_session(
+ struct mcore_device_t *dev,
+ uint32_t session_id,
+ struct connection *connection
+);
+
+/**
+ * Remove the specified session from the device.
+ * The session object will be destroyed and all resources associated with it
+ * will be freed.
+ *
+ * @param session_id Session of the session to remove.
+ * @return true if a session has been found and removed.
+ */
+bool mcore_device_remove_session(
+ struct mcore_device_t *dev,
+ uint32_t session_id
+);
+
+/**
+ * Get as session object for a given session ID.
+ * @param session_id Identified of a previously opened session.
+ * @return Session object if available or NULL if no session has been found.
+ */
+struct session *mcore_device_resolve_session_id(
+ struct mcore_device_t *dev,
+ uint32_t session_id
+);
+
+/**
+ * Allocate a block of contiguous WSM.
+ * @param len The virtual address to be registered.
+ * @return The virtual address of the allocated memory or NULL if no memory
+ * is available.
+ */
+struct wsm *mcore_device_allocate_contiguous_wsm(
+ struct mcore_device_t *dev,
+ uint32_t len
+);
+
+/**
+ * Unregister a vaddr from a device.
+ * @param vaddr The virtual address to be registered.
+ * @param paddr The physical address to be registered.
+ */
+bool mcore_device_free_contiguous_wsm(
+ struct mcore_device_t *dev,
+ struct wsm *wsm
+);
+
+/**
+ * Get a WSM object for a given virtual address.
+ * @param vaddr The virtual address which has been allocate with mc_malloc_wsm()
+ * in advance.
+ * @return the WSM object or NULL if no address has been found.
+ */
+struct wsm *mcore_device_find_contiguous_wsm(
+ struct mcore_device_t *dev,
+ void *virt_addr
+);
+
+#endif /* DEVICE_H_ */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/include/mcinq.h b/drivers/gud/mobicore_kernelapi/include/mcinq.h
new file mode 100644
index 0000000..3cb82be
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/include/mcinq.h
@@ -0,0 +1,125 @@
+/** @addtogroup NQ
+ * @{
+ * Notifications inform the MobiCore runtime environment that information is
+ * pending in a WSM buffer.
+ * The Trustlet Connector (TLC) and the corresponding trustlet also utilize
+ * this buffer to notify each other about new data within the
+ * Trustlet Connector Interface (TCI).
+ *
+ * The buffer is set up as a queue, which means that more than one
+ * notification can be written to the buffer before the switch to the other
+ * world is performed. Each side therefore facilitates an incoming and an
+ * outgoing queue for communication with the other side.
+ *
+ * Notifications hold the session ID, which is used to reference the
+ * communication partner in the other world.
+ * So if, e.g., the TLC in the normal world wants to notify his trustlet
+ * about new data in the TLC buffer
+ *
+ * @file
+ * Notification queue declarations.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef NQ_H_
+#define NQ_H_
+
+/** \name NQ Size Defines
+ * Minimum and maximum count of elements in the notification queue.
+ * @{ */
+#define MIN_NQ_ELEM 1 /**< Minimum notification queue elements. */
+#define MAX_NQ_ELEM 64 /**< Maximum notification queue elements. */
+/** @} */
+
+/** \name NQ Length Defines
+ * Minimum and maximum notification queue length.
+ * @{ */
+/**< Minimum notification length (in bytes). */
+#define MIN_NQ_LEN (MIN_NQ_ELEM * sizeof(notification))
+/**< Maximum notification length (in bytes). */
+#define MAX_NQ_LEN (MAX_NQ_ELEM * sizeof(notification))
+/** @} */
+
+/** \name Session ID Defines
+ * Standard Session IDs.
+ * @{ */
+/**< MCP session ID is used when directly communicating with the MobiCore
+ * (e.g. for starting and stopping of trustlets). */
+#define SID_MCP 0
+/**< Invalid session id is returned in case of an error. */
+#define SID_INVALID 0xffffffff
+/** @} */
+
+/** Notification data structure. */
+struct notification {
+ uint32_t session_id; /**< Session ID. */
+ int32_t payload; /**< Additional notification information. */
+};
+
+/** Notification payload codes.
+ * 0 indicated a plain simple notification,
+ * a positive value is a termination reason from the task,
+ * a negative value is a termination reason from MobiCore.
+ * Possible negative values are given below.
+ */
+enum notification_payload {
+ /**< task terminated, but exit code is invalid */
+ ERR_INVALID_EXIT_CODE = -1,
+ /**< task terminated due to session end, no exit code available */
+ ERR_SESSION_CLOSE = -2,
+ /**< task terminated due to invalid operation */
+ ERR_INVALID_OPERATION = -3,
+ /**< session ID is unknown */
+ ERR_INVALID_SID = -4,
+ /**< session is not active */
+ ERR_SID_NOT_ACTIVE = -5
+};
+
+/** Declaration of the notification queue header.
+ * layout as specified in the data structure specification.
+ */
+struct notification_queue_header {
+ uint32_t write_cnt; /**< Write counter. */
+ uint32_t read_cnt; /**< Read counter. */
+ uint32_t queue_size; /**< Queue size. */
+};
+
+/** Queue struct which defines a queue object.
+ * The queue struct is accessed by the queue<operation> type of
+ * function. elementCnt must be a power of two and the power needs
+ * to be smaller than power of uint32_t (obviously 32).
+ */
+struct notification_queue {
+ /**< Queue header. */
+ struct notification_queue_header hdr;
+ /**< Notification elements. */
+ struct notification notification[MIN_NQ_ELEM];
+} ;
+
+#endif /** NQ_H_ */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/include/mcuuid.h b/drivers/gud/mobicore_kernelapi/include/mcuuid.h
new file mode 100644
index 0000000..b72acb8
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/include/mcuuid.h
@@ -0,0 +1,74 @@
+/**
+ * @addtogroup MC_UUID mcUuid - Universally Unique Identifier.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2011-2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @ingroup MC_DATA_TYPES
+ * @{
+ */
+
+#ifndef MC_UUID_H_
+#define MC_UUID_H_
+
+#define UUID_TYPE
+
+/** Universally Unique Identifier (UUID) according to ISO/IEC 11578. */
+struct mc_uuid_t {
+ uint8_t value[16]; /**< Value of the UUID. */
+};
+
+/** UUID value used as free marker in service provider containers. */
+#define MC_UUID_FREE_DEFINE \
+ { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
+
+static const struct mc_uuid_t MC_UUID_FREE = {
+ MC_UUID_FREE_DEFINE
+};
+
+/** Reserved UUID. */
+#define MC_UUID_RESERVED_DEFINE \
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+
+static const struct mc_uuid_t MC_UUID_RESERVED = {
+ MC_UUID_RESERVED_DEFINE
+};
+
+/** UUID for system applications. */
+#define MC_UUID_SYSTEM_DEFINE \
+ { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE }
+
+static const struct mc_uuid_t MC_UUID_SYSTEM = {
+ MC_UUID_SYSTEM_DEFINE
+};
+
+#endif /* MC_UUID_H_ */
+
+/** @} */
+
diff --git a/drivers/gud/mobicore_kernelapi/main.c b/drivers/gud/mobicore_kernelapi/main.c
new file mode 100644
index 0000000..62997f7
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/main.c
@@ -0,0 +1,181 @@
+/**
+ * MobiCore KernelApi module
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * 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
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/netlink.h>
+#include <linux/kthread.h>
+#include <net/sock.h>
+
+#include <linux/list.h>
+
+#include "connection.h"
+#include "common.h"
+
+#define MC_DAEMON_NETLINK 17
+
+struct mc_kernelapi_ctx {
+ struct sock *sk;
+ struct list_head peers;
+ atomic_t counter;
+};
+
+struct mc_kernelapi_ctx *mod_ctx; /* = NULL; */
+
+/*----------------------------------------------------------------------------*/
+/* get a unique ID */
+unsigned int mcapi_unique_id(
+ void
+)
+{
+ return (unsigned int)atomic_inc_return(
+ &(mod_ctx->counter));
+}
+
+
+/*----------------------------------------------------------------------------*/
+static struct connection *mcapi_find_connection(
+ uint32_t seq
+)
+{
+ struct connection *tmp;
+ struct list_head *pos;
+
+ /* Get session for session_id */
+ list_for_each(pos, &mod_ctx->peers) {
+ tmp = list_entry(pos, struct connection, list);
+ if (tmp->sequence_magic == seq)
+ return tmp;
+ }
+
+ return NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+void mcapi_insert_connection(
+ struct connection *connection
+)
+{
+ list_add_tail(&(connection->list), &(mod_ctx->peers));
+ connection->socket_descriptor = mod_ctx->sk;
+}
+
+void mcapi_remove_connection(
+ uint32_t seq
+)
+{
+ struct connection *tmp;
+ struct list_head *pos, *q;
+
+ /* Delete all session objects. Usually this should not be needed as
+ closeDevice() requires that all sessions have been closed before.*/
+ list_for_each_safe(pos, q, &mod_ctx->peers) {
+ tmp = list_entry(pos, struct connection, list);
+ if (tmp->sequence_magic == seq) {
+ list_del(pos);
+ break;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+static int mcapi_process(
+ struct sk_buff *skb,
+ struct nlmsghdr *nlh
+)
+{
+ struct connection *c;
+ int length;
+ int seq;
+ pid_t pid;
+ int ret;
+
+ pid = nlh->nlmsg_pid;
+ length = nlh->nlmsg_len;
+ seq = nlh->nlmsg_seq;
+ MCDRV_DBG_VERBOSE("nlmsg len %d type %d pid 0x%X seq %d\n",
+ length, nlh->nlmsg_type, pid, seq);
+ do {
+ c = mcapi_find_connection(seq);
+ if (!c) {
+ MCDRV_ERROR("Invalid incomming connection - seq=%u!",
+ seq);
+ ret = -1;
+ break;
+ }
+
+ /* Pass the buffer to the appropriate connection */
+ connection_process(c, skb);
+
+ ret = 0;
+ } while (false);
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+static void mcapi_callback(
+ struct sk_buff *skb
+)
+{
+ struct nlmsghdr *nlh = nlmsg_hdr(skb);
+ int len = skb->len;
+ int err = 0;
+
+ while (NLMSG_OK(nlh, len)) {
+ err = mcapi_process(skb, nlh);
+
+ /* if err or if this message says it wants a response */
+ if (err || (nlh->nlmsg_flags & NLM_F_ACK))
+ netlink_ack(skb, nlh, err);
+
+ nlh = NLMSG_NEXT(nlh, len);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+static int __init mcapi_init(void)
+{
+ printk(KERN_INFO "Mobicore API module initialized!\n");
+
+ mod_ctx = kzalloc(sizeof(struct mc_kernelapi_ctx), GFP_KERNEL);
+
+ /* start kernel thread */
+ mod_ctx->sk = netlink_kernel_create(&init_net, MC_DAEMON_NETLINK, 0,
+ mcapi_callback, NULL, THIS_MODULE);
+
+ if (!mod_ctx->sk) {
+ MCDRV_ERROR("register of recieve handler failed");
+ return -EFAULT;
+ }
+
+ INIT_LIST_HEAD(&mod_ctx->peers);
+ return 0;
+}
+
+static void __exit mcapi_exit(void)
+{
+ printk(KERN_INFO "Unloading Mobicore API module.\n");
+
+ if (mod_ctx->sk != NULL) {
+ netlink_kernel_release(mod_ctx->sk);
+ mod_ctx->sk = NULL;
+ }
+ kfree(mod_ctx);
+ mod_ctx = NULL;
+}
+
+module_init(mcapi_init);
+module_exit(mcapi_exit);
+
+MODULE_AUTHOR("Giesecke & Devrient GmbH");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MobiCore API driver");
diff --git a/drivers/gud/mobicore_kernelapi/public/mobicore_driver_api.h b/drivers/gud/mobicore_kernelapi/public/mobicore_driver_api.h
new file mode 100644
index 0000000..ccfb2e5
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/public/mobicore_driver_api.h
@@ -0,0 +1,483 @@
+/**
+ * @defgroup MCD_API MobiCore Driver API
+ * @addtogroup MCD_API
+ * @{
+ *
+ * @if DOXYGEN_MCDRV_API
+ * @mainpage MobiCore Driver API.
+ * @endif
+ *
+ * MobiCore Driver API.
+ *
+ * The MobiCore (MC) Driver API provides access functions to the MobiCore
+ * runtime environment and the contained Trustlets.
+ *
+ * @image html DoxyOverviewDrvApi500x.png
+ * @image latex DoxyOverviewDrvApi500x.png "MobiCore Overview" width=12cm
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef MCDRIVER_H_
+#define MCDRIVER_H_
+
+#define __MC_CLIENT_LIB_API
+
+#include "mcuuid.h"
+
+/**
+ * Return values of MobiCore driver functions.
+ */
+enum mc_result {
+ /**< Function call succeeded. */
+ MC_DRV_OK = 0,
+ /**< No notification available. */
+ MC_DRV_NO_NOTIFICATION = 1,
+ /**< Error during notification on communication level. */
+ MC_DRV_ERR_NOTIFICATION = 2,
+ /**< Function not implemented. */
+ MC_DRV_ERR_NOT_IMPLEMENTED = 3,
+ /**< No more resources available. */
+ MC_DRV_ERR_OUT_OF_RESOURCES = 4,
+ /**< Driver initialization failed. */
+ MC_DRV_ERR_INIT = 5,
+ /**< Unknown error. */
+ MC_DRV_ERR_UNKNOWN = 6,
+ /**< The specified device is unknown. */
+ MC_DRV_ERR_UNKNOWN_DEVICE = 7,
+ /**< The specified session is unknown.*/
+ MC_DRV_ERR_UNKNOWN_SESSION = 8,
+ /**< The specified operation is not allowed. */
+ MC_DRV_ERR_INVALID_OPERATION = 9,
+ /**< The response header from the MC is invalid. */
+ MC_DRV_ERR_INVALID_RESPONSE = 10,
+ /**< Function call timed out. */
+ MC_DRV_ERR_TIMEOUT = 11,
+ /**< Can not allocate additional memory. */
+ MC_DRV_ERR_NO_FREE_MEMORY = 12,
+ /**< Free memory failed. */
+ MC_DRV_ERR_FREE_MEMORY_FAILED = 13,
+ /**< Still some open sessions pending. */
+ MC_DRV_ERR_SESSION_PENDING = 14,
+ /**< MC daemon not reachable */
+ MC_DRV_ERR_DAEMON_UNREACHABLE = 15,
+ /**< The device file of the kernel module could not be opened. */
+ MC_DRV_ERR_INVALID_DEVICE_FILE = 16,
+ /**< Invalid parameter. */
+ MC_DRV_ERR_INVALID_PARAMETER = 17,
+ /**< Unspecified error from Kernel Module*/
+ MC_DRV_ERR_KERNEL_MODULE = 18,
+ /**< Error during mapping of additional bulk memory to session. */
+ MC_DRV_ERR_BULK_MAPPING = 19,
+ /**< Error during unmapping of additional bulk memory to session. */
+ MC_DRV_ERR_BULK_UNMAPPING = 20,
+ /**< Notification received, exit code available. */
+ MC_DRV_INFO_NOTIFICATION = 21,
+ /**< Set up of NWd connection failed. */
+ MC_DRV_ERR_NQ_FAILED = 22
+};
+
+
+/**
+ * Driver control command.
+ */
+enum mc_driver_ctrl {
+ MC_CTRL_GET_VERSION = 1 /**< Return the driver version */
+};
+
+
+/** Structure of Session Handle, includes the Session ID and the Device ID the
+ * Session belongs to.
+ * The session handle will be used for session-based MobiCore communication.
+ * It will be passed to calls which address a communication end point in the
+ * MobiCore environment.
+ */
+struct mc_session_handle {
+ uint32_t session_id; /**< MobiCore session ID */
+ uint32_t device_id; /**< Device ID the session belongs to */
+};
+
+/** Information structure about additional mapped Bulk buffer between the
+ * Trustlet Connector (Nwd) and the Trustlet (Swd). This structure is
+ * initialized from a Trustlet Connector by calling mc_map().
+ * In order to use the memory within a Trustlet the Trustlet Connector has to
+ * inform the Trustlet with the content of this structure via the TCI.
+ */
+struct mc_bulk_map {
+ /**< The virtual address of the Bulk buffer regarding the address space
+ * of the Trustlet, already includes a possible offset! */
+ void *secure_virt_addr;
+ uint32_t secure_virt_len; /**< Length of the mapped Bulk buffer */
+};
+
+
+/**< The default device ID */
+#define MC_DEVICE_ID_DEFAULT 0
+/**< Wait infinite for a response of the MC. */
+#define MC_INFINITE_TIMEOUT ((int32_t)(-1))
+/**< Do not wait for a response of the MC. */
+#define MC_NO_TIMEOUT 0
+/**< TCI/DCI must not exceed 1MiB */
+#define MC_MAX_TCI_LEN 0x100000
+
+
+
+/** Open a new connection to a MobiCore device.
+ *
+ * mc_open_device() initializes all device specific resources required to
+ * communicate with an MobiCore instance located on the specified device in the
+ * system. If the device does not exist the function will return
+ * MC_DRV_ERR_UNKNOWN_DEVICE.
+ *
+ * @param [in] device_id Identifier for the MobiCore device to be used.
+ * MC_DEVICE_ID_DEFAULT refers to the default device.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_ERR_INVALID_OPERATION if device already opened.
+ * @return MC_DRV_ERR_DAEMON_UNREACHABLE when problems with daemon occur.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device_id is unknown.
+ * @return MC_DRV_ERR_INVALID_DEVICE_FILE if kernel module under
+ * /dev/mobicore cannot be opened
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_open_device(
+ uint32_t device_id
+);
+
+/** Close the connection to a MobiCore device.
+ * When closing a device, active sessions have to be closed beforehand.
+ * Resources associated with the device will be released.
+ * The device may be opened again after it has been closed.
+ *
+ * @param [in] device_id Identifier for the MobiCore device.
+ * MC_DEVICE_ID_DEFAULT refers to the default device.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id is invalid.
+ * @return MC_DRV_ERR_SESSION_PENDING when a session is still open.
+ * @return MC_DRV_ERR_DAEMON_UNREACHABLE when problems with daemon occur.
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_close_device(
+ uint32_t device_id
+);
+
+/** Open a new session to a Trustlet. The trustlet with the given UUID has
+ * to be available in the flash filesystem.
+ *
+ * Write MCP open message to buffer and notify MobiCore about the availability
+ * of a new command.
+ * Waits till the MobiCore responses with the new session ID (stored in the MCP
+ * buffer).
+ *
+ * @param [in,out] session On success, the session data will be returned.
+ * Note that session.device_id has to be the device id of an opened device.
+ * @param [in] uuid UUID of the Trustlet to be opened.
+ * @param [in] tci TCI buffer for communicating with the trustlet.
+ * @param [in] tci_len Length of the TCI buffer. Maximum allowed value
+ * is MC_MAX_TCI_LEN.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if session parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id is invalid.
+ * @return MC_DRV_ERR_DAEMON_UNREACHABLE when problems with daemon socket occur.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when daemon returns an error.
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_open_session(
+ struct mc_session_handle *session,
+ const struct mc_uuid_t *uuid,
+ uint8_t *tci,
+ uint32_t tci_len
+);
+
+/** Close a Trustlet session.
+ *
+ * Closes the specified MobiCore session. The call will block until the
+ * session has been closed.
+ *
+ * @pre Device device_id has to be opened in advance.
+ *
+ * @param [in] session Session to be closed.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if session parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_SESSION when session id is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id of session is invalid.
+ * @return MC_DRV_ERR_DAEMON_UNREACHABLE when problems with daemon occur.
+ * @return MC_DRV_ERR_INVALID_DEVICE_FILE when daemon cannot open trustlet file.
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_close_session(
+ struct mc_session_handle *session
+);
+
+/** Notify a session.
+ * Notifies the session end point about available message data.
+ * If the session parameter is correct, notify will always succeed.
+ * Corresponding errors can only be received by mc_wait_notification().
+ * @pre A session has to be opened in advance.
+ *
+ * @param session The session to be notified.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if session parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_SESSION when session id is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id of session is invalid.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_notify(
+ struct mc_session_handle *session
+);
+
+/** Wait for a notification.
+ *
+ * Wait for a notification issued by the MobiCore for a specific session.
+ * The timeout parameter specifies the number of milliseconds the call will wait
+ * for a notification.
+ * If the caller passes 0 as timeout value the call will immediately return.
+ * If timeout value is below 0 the call will block until a notification for the
+ session has been received.
+ *
+ * @attention if timeout is below 0, call will block:
+ * Caller has to trust the other side to send a notification to wake him up
+ * again.
+ *
+ * @param [in] session The session the notification should correspond to.
+ * @param [in] timeout Time in milliseconds to wait
+ * (MC_NO_TIMEOUT : direct return, > 0 : milliseconds,
+ * MC_INFINITE_TIMEOUT : wait infinitely)
+ *
+ * @return MC_DRV_OK if notification is available.
+ * @return MC_DRV_ERR_TIMEOUT if no notification arrived in time.
+ * @return MC_DRV_INFO_NOTIFICATION if a problem with the session was
+ * encountered. Get more details with mc_get_session_error_code().
+ * @return MC_DRV_ERR_NOTIFICATION if a problem with the socket occurred.
+ * @return MC_DRV_INVALID_PARAMETER if a parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_SESSION when session id is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id of session is invalid.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_wait_notification(
+ struct mc_session_handle *session,
+ int32_t timeout
+);
+
+/**
+ * Allocate a block of world shared memory (WSM).
+ * The MC driver allocates a contiguous block of memory which can be used as
+ * WSM.
+ * This implicates that the allocated memory is aligned according to the
+ * alignment parameter.
+ * Always returns a buffer of size WSM_SIZE aligned to 4K.
+ *
+ * @param [in] device_id The ID of an opened device to retrieve the WSM from.
+ * @param [in] align The alignment (number of pages) of the memory block
+ * (e.g. 0x00000001 for 4kb).
+ * @param [in] len Length of the block in bytes.
+ * @param [out] wsm Virtual address of the world shared memory block.
+ * @param [in] wsm_flags Platform specific flags describing the memory to
+ * be allocated.
+ *
+ * @attention: align and wsm_flags are currently ignored
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if a parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id is invalid.
+ * @return MC_DRV_ERR_NO_FREE_MEMORY if no more contiguous memory is available
+ * in this size or for this process.
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_malloc_wsm(
+ uint32_t device_id,
+ uint32_t align,
+ uint32_t len,
+ uint8_t **wsm,
+ uint32_t wsm_flags
+);
+
+/**
+ * Free a block of world shared memory (WSM).
+ * The MC driver will free a block of world shared memory (WSM) previously
+ * allocated with mc_malloc_wsm(). The caller has to assure that the address
+ * handed over to the driver is a valid WSM address.
+ *
+ * @param [in] device_id The ID to which the given address belongs.
+ * @param [in] wsm Address of WSM block to be freed.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if a parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id is invalid.
+ * @return MC_DRV_ERR_FREE_MEMORY_FAILED on failures.
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_free_wsm(
+ uint32_t device_id,
+ uint8_t *wsm
+);
+
+/**
+ * Map additional bulk buffer between a Trustlet Connector (TLC) and
+ * the Trustlet (TL) for a session.
+ * Memory allocated in user space of the TLC can be mapped as additional
+ * communication channel (besides TCI) to the Trustlet. Limitation of the
+ * Trustlet memory structure apply: only 6 chunks can be mapped with a maximum
+ * chunk size of 1 MiB each.
+ *
+ * @attention It is up to the application layer (TLC) to inform the Trustlet
+ * about the additional mapped bulk memory.
+ *
+ * @param [in] session Session handle with information of the device_id and
+ * the session_id. The
+ * given buffer is mapped to the session specified in the sessionHandle.
+ * @param [in] buf Virtual address of a memory portion (relative to TLC)
+ * to be shared with the Trustlet, already includes a possible offset!
+ * @param [in] len length of buffer block in bytes.
+ * @param [out] map_info Information structure about the mapped Bulk buffer
+ * between the TLC (Nwd) and
+ * the TL (Swd).
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if a parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_SESSION when session id is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id of session is invalid.
+ * @return MC_DRV_ERR_DAEMON_UNREACHABLE when problems with daemon occur.
+ * @return MC_DRV_ERR_BULK_MAPPING when buf is already uses as bulk buffer or
+ * when registering the buffer failed.
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_map(
+ struct mc_session_handle *session,
+ void *buf,
+ uint32_t len,
+ struct mc_bulk_map *map_info
+);
+
+/**
+ * Remove additional mapped bulk buffer between Trustlet Connector (TLC)
+ * and the Trustlet (TL) for a session.
+ *
+ * @attention The bulk buffer will immediately be unmapped from the session
+ * context.
+ * @attention The application layer (TLC) must inform the TL about unmapping
+ * of the additional bulk memory before calling mc_unmap!
+ *
+ * @param [in] session Session handle with information of the device_id and
+ * the session_id. The given buffer is unmapped from the session specified
+ * in the sessionHandle.
+ * @param [in] buf Virtual address of a memory portion (relative to TLC)
+ * shared with the TL, already includes a possible offset!
+ * @param [in] map_info Information structure about the mapped Bulk buffer
+ * between the TLC (Nwd) and
+ * the TL (Swd).
+ * @attention The clientlib currently ignores the len field in map_info.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if a parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_SESSION when session id is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id of session is invalid.
+ * @return MC_DRV_ERR_DAEMON_UNREACHABLE when problems with daemon occur.
+ * @return MC_DRV_ERR_BULK_UNMAPPING when buf was not registered earlier
+ * or when unregistering failed.
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_unmap(
+ struct mc_session_handle *session,
+ void *buf,
+ struct mc_bulk_map *map_info
+);
+
+
+/**
+ * @attention: Not implemented.
+ * Execute driver specific command.
+ * mc_driver_ctrl() can be used to execute driver specific commands.
+ * Besides the control command MC_CTRL_GET_VERSION commands are implementation
+ * specific.
+ * Please refer to the corresponding specification of the driver manufacturer.
+ *
+ * @param [in] param Command ID of the command to be executed.
+ * @param [in, out] data Command data and response depending on command.
+ * @param [in] len Length of the data block.
+ *
+ * @return MC_DRV_ERR_NOT_IMPLEMENTED.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_driver_ctrl(
+ enum mc_driver_ctrl param,
+ uint8_t *data,
+ uint32_t len
+);
+
+/**
+ * @attention: Not implemented.
+ * Execute application management command.
+ * mc_manage() shall be used to exchange application management commands with
+ * the MobiCore.
+ * The MobiCore Application Management Protocol is described in [MCAMP].
+ *
+ * @param [in] device_id Identifier for the MobiCore device to be used.
+ * NULL refers to the default device.
+ * @param [in, out] data Command data/response data depending on command.
+ * @param [in] len Length of the data block.
+ *
+ * @return MC_DRV_ERR_NOT_IMPLEMENTED.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_manage(
+ uint32_t device_id,
+ uint8_t *data,
+ uint32_t len
+);
+
+/**
+ * Get additional error information of the last error that occured on a session.
+ * After the request the stored error code will be deleted.
+ *
+ * @param [in] session Session handle with information of the device_id and
+ * the session_id.
+ * @param [out] last_error >0 Trustlet has terminated itself with this value,
+ * <0 Trustlet is dead because of an error within the MobiCore
+ * (e.g. Kernel exception).
+ * See also MCI definition.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if a parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_SESSION when session id is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id of session is invalid.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_get_session_error_code(
+ struct mc_session_handle *session,
+ int32_t *last_error
+);
+
+#endif /** MCDRIVER_H_ */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/public/mobicore_driver_cmd.h b/drivers/gud/mobicore_kernelapi/public/mobicore_driver_cmd.h
new file mode 100644
index 0000000..9ff7989
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/public/mobicore_driver_cmd.h
@@ -0,0 +1,289 @@
+/** @addtogroup MCD_MCDIMPL_DAEMON
+ * @{
+ * @file
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef MCDAEMON_H_
+#define MCDAEMON_H_
+
+
+
+
+#include "mcuuid.h"
+
+enum mc_drv_cmd_t {
+ MC_DRV_CMD_PING = 0,
+ MC_DRV_CMD_GET_INFO = 1,
+ MC_DRV_CMD_OPEN_DEVICE = 2,
+ MC_DRV_CMD_CLOSE_DEVICE = 3,
+ MC_DRV_CMD_NQ_CONNECT = 4,
+ MC_DRV_CMD_OPEN_SESSION = 5,
+ MC_DRV_CMD_CLOSE_SESSION = 6,
+ MC_DRV_CMD_NOTIFY = 7,
+ MC_DRV_CMD_MAP_BULK_BUF = 8,
+ MC_DRV_CMD_UNMAP_BULK_BUF = 9
+};
+
+
+enum mc_drv_rsp_t {
+ MC_DRV_RSP_OK = 0,
+ MC_DRV_RSP_FAILED = 1,
+ MC_DRV_RSP_DEVICE_NOT_OPENED = 2,
+ MC_DRV_RSP_DEVICE_ALREADY_OPENED = 3,
+ MC_DRV_RSP_COMMAND_NOT_ALLOWED = 4,
+ MC_DRV_INVALID_DEVICE_NAME = 5,
+ MC_DRV_RSP_MAP_BULK_ERRO = 6,
+ MC_DRV_RSP_TRUSTLET_NOT_FOUND = 7,
+ MC_DRV_RSP_PAYLOAD_LENGTH_ERROR = 8,
+};
+
+
+struct mc_drv_command_header_t {
+ uint32_t command_id;
+};
+
+struct mc_drv_response_header_t {
+ uint32_t response_id;
+};
+
+#define MC_DEVICE_ID_DEFAULT 0 /**< The default device ID */
+
+
+/*****************************************************************************/
+struct mc_drv_cmd_open_device_payload_t {
+ uint32_t device_id;
+};
+
+struct mc_drv_cmd_open_device_t {
+ struct mc_drv_command_header_t header;
+ struct mc_drv_cmd_open_device_payload_t payload;
+};
+
+
+struct mc_drv_rsp_open_device_payload_t {
+ /* empty */
+};
+
+struct mc_drv_rsp_open_device_t {
+ struct mc_drv_response_header_t header;
+ struct mc_drv_rsp_open_device_payload_t payload;
+};
+
+
+/*****************************************************************************/
+struct mc_drv_cmd_close_device_t {
+ struct mc_drv_command_header_t header;
+ /* no payload here because close has none.
+ If we use an empty struct, C++ will count it as 4 bytes.
+ This will write too much into the socket at write(cmd,sizeof(cmd)) */
+};
+
+
+struct mc_drv_rsp_close_device_payload_t {
+ /* empty */
+};
+
+struct mc_drv_rsp_close_device_t {
+ struct mc_drv_response_header_t header;
+ struct mc_drv_rsp_close_device_payload_t payload;
+};
+
+
+/*****************************************************************************/
+struct mc_drv_cmd_open_session_payload_t {
+ uint32_t device_id;
+ struct mc_uuid_t uuid;
+ uint32_t tci;
+ uint32_t len;
+};
+
+struct mc_drv_cmd_open_session_t {
+ struct mc_drv_command_header_t header;
+ struct mc_drv_cmd_open_session_payload_t payload;
+};
+
+
+struct mc_drv_rsp_open_session_payload_t {
+ uint32_t device_id;
+ uint32_t session_id;
+ uint32_t device_session_id;
+ uint32_t mc_result;
+ uint32_t session_magic;
+};
+
+struct mc_drv_rsp_open_session_t {
+ struct mc_drv_response_header_t header;
+ struct mc_drv_rsp_open_session_payload_t payload;
+};
+
+
+/*****************************************************************************/
+struct mc_drv_cmd_close_session_payload_t {
+ uint32_t session_id;
+};
+
+struct mc_drv_cmd_close_session_t {
+ struct mc_drv_command_header_t header;
+ struct mc_drv_cmd_close_session_payload_t payload;
+};
+
+
+struct mc_drv_rsp_close_session_payload_t {
+ /* empty */
+};
+
+struct mc_drv_rsp_close_session_t {
+ struct mc_drv_response_header_t header;
+ struct mc_drv_rsp_close_session_payload_t payload;
+};
+
+
+/*****************************************************************************/
+struct mc_drv_cmd_notify_payload_t {
+ uint32_t session_id;
+};
+
+struct mc_drv_cmd_notify_t {
+ struct mc_drv_command_header_t header;
+ struct mc_drv_cmd_notify_payload_t payload;
+};
+
+
+struct mc_drv_rsp_notify_payload_t {
+ /* empty */
+};
+
+struct mc_drv_rsp_notify_t {
+ struct mc_drv_response_header_t header;
+ struct mc_drv_rsp_notify_payload_t payload;
+};
+
+
+/*****************************************************************************/
+struct mc_drv_cmd_map_bulk_mem_payload_t {
+ uint32_t session_id;
+ uint32_t phys_addr_l2;
+ uint32_t offset_payload;
+ uint32_t len_bulk_mem;
+};
+
+struct mc_drv_cmd_map_bulk_mem_t {
+ struct mc_drv_command_header_t header;
+ struct mc_drv_cmd_map_bulk_mem_payload_t payload;
+};
+
+
+struct mc_drv_rsp_map_bulk_mem_payload_t {
+ uint32_t session_id;
+ uint32_t secure_virtual_adr;
+ uint32_t mc_result;
+};
+
+struct mc_drv_rsp_map_bulk_mem_t {
+ struct mc_drv_response_header_t header;
+ struct mc_drv_rsp_map_bulk_mem_payload_t payload;
+};
+
+
+/*****************************************************************************/
+struct mc_drv_cmd_unmap_bulk_mem_payload_t {
+ uint32_t session_id;
+ uint32_t secure_virtual_adr;
+ uint32_t len_bulk_mem;
+};
+
+struct mc_drv_cmd_unmap_bulk_mem_t {
+ struct mc_drv_command_header_t header;
+ struct mc_drv_cmd_unmap_bulk_mem_payload_t payload;
+};
+
+
+struct mc_drv_rsp_unmap_bulk_mem_payload_t {
+ uint32_t response_id;
+ uint32_t session_id;
+ uint32_t mc_result;
+};
+
+struct mc_drv_rsp_unmap_bulk_mem_t {
+ struct mc_drv_response_header_t header;
+ struct mc_drv_rsp_unmap_bulk_mem_payload_t payload;
+};
+
+
+/*****************************************************************************/
+struct mc_drv_cmd_nqconnect_payload_t {
+ uint32_t device_id;
+ uint32_t session_id;
+ uint32_t device_session_id;
+ uint32_t session_magic; /* Random data */
+};
+
+struct mc_drv_cmd_nqconnect_t {
+ struct mc_drv_command_header_t header;
+ struct mc_drv_cmd_nqconnect_payload_t payload;
+};
+
+
+struct mc_drv_rsp_nqconnect_payload_t {
+ /* empty; */
+};
+
+struct mc_drv_rsp_nqconnect_t {
+ struct mc_drv_response_header_t header;
+ struct mc_drv_rsp_nqconnect_payload_t payload;
+};
+
+
+/*****************************************************************************/
+union mc_drv_command_t {
+ struct mc_drv_command_header_t header;
+ struct mc_drv_cmd_open_device_t mc_drv_cmd_open_device;
+ struct mc_drv_cmd_close_device_t mc_drv_cmd_close_device;
+ struct mc_drv_cmd_open_session_t mc_drv_cmd_open_session;
+ struct mc_drv_cmd_close_session_t mc_drv_cmd_close_session;
+ struct mc_drv_cmd_nqconnect_t mc_drv_cmd_nqconnect;
+ struct mc_drv_cmd_notify_t mc_drv_cmd_notify;
+ struct mc_drv_cmd_map_bulk_mem_t mc_drv_cmd_map_bulk_mem;
+ struct mc_drv_cmd_unmap_bulk_mem_t mc_drv_cmd_unmap_bulk_mem;
+};
+
+union mc_drv_response_t {
+ struct mc_drv_response_header_t header;
+ struct mc_drv_rsp_open_device_t mc_drv_rsp_open_device;
+ struct mc_drv_rsp_close_device_t mc_drv_rsp_close_device;
+ struct mc_drv_rsp_open_session_t mc_drv_rsp_open_session;
+ struct mc_drv_rsp_close_session_t mc_drv_rsp_close_session;
+ struct mc_drv_rsp_nqconnect_t mc_drv_rsp_nqconnect;
+ struct mc_drv_rsp_notify_t mc_drv_rsp_notify;
+ struct mc_drv_rsp_map_bulk_mem_t mc_drv_rsp_map_bulk_mem;
+ struct mc_drv_rsp_unmap_bulk_mem_t mc_drv_rsp_unmap_bulk_mem;
+};
+
+#endif /* MCDAEMON_H_ */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/session.c b/drivers/gud/mobicore_kernelapi/session.c
new file mode 100644
index 0000000..e62b4b3
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/session.c
@@ -0,0 +1,202 @@
+/** @addtogroup MCD_IMPL_LIB
+ * @{
+ * @file
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * 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
+ * published by the Free Software Foundation.
+ */
+#include <linux/types.h>
+#include <linux/slab.h>
+#include "mc_kernel_api.h"
+#include "public/mobicore_driver_api.h"
+
+#include "session.h"
+
+/*****************************************************************************/
+struct bulk_buffer_descriptor *bulk_buffer_descriptor_create(
+ void *virt_addr,
+ uint32_t len,
+ uint32_t handle,
+ void *phys_addr_wsm_l2
+) {
+ struct bulk_buffer_descriptor *desc =
+ kzalloc(sizeof(struct bulk_buffer_descriptor), GFP_KERNEL);
+ desc->virt_addr = virt_addr;
+ desc->len = len;
+ desc->handle = handle;
+ desc->phys_addr_wsm_l2 = phys_addr_wsm_l2;
+ return desc;
+}
+
+/*****************************************************************************/
+struct session *session_create(
+ uint32_t session_id,
+ void *instance,
+ struct connection *connection
+) {
+ struct session *session =
+ kzalloc(sizeof(struct session), GFP_KERNEL);
+ session->session_id = session_id;
+ session->instance = instance;
+ session->notification_connection = connection;
+
+ session->session_info.last_error = SESSION_ERR_NO;
+ session->session_info.state = SESSION_STATE_INITIAL;
+
+ INIT_LIST_HEAD(&(session->bulk_buffer_descriptors));
+ return session;
+}
+
+
+/*****************************************************************************/
+void session_cleanup(
+ struct session *session
+) {
+ struct bulk_buffer_descriptor *bulk_buf_descr;
+ struct list_head *pos, *q;
+
+ /* Unmap still mapped buffers */
+ list_for_each_safe(pos, q, &session->bulk_buffer_descriptors) {
+ bulk_buf_descr =
+ list_entry(pos, struct bulk_buffer_descriptor, list);
+
+ MCDRV_DBG_VERBOSE("Physical Address of L2 Table = 0x%X, "
+ "handle= %d",
+ (unsigned int)bulk_buf_descr->phys_addr_wsm_l2,
+ bulk_buf_descr->handle);
+
+ /* ignore any error, as we cannot do anything in this case. */
+ int ret = mobicore_unmap_vmem(session->instance,
+ bulk_buf_descr->handle);
+ if (ret != 0)
+ MCDRV_DBG_ERROR("mobicore_unmap_vmem failed: %d", ret);
+
+ list_del(pos);
+ kfree(bulk_buf_descr);
+ }
+
+ /* Finally delete notification connection */
+ connection_cleanup(session->notification_connection);
+ kfree(session);
+}
+
+
+/*****************************************************************************/
+void session_set_error_info(
+ struct session *session,
+ int32_t err
+) {
+ session->session_info.last_error = err;
+}
+
+
+/*****************************************************************************/
+int32_t session_get_last_err(
+ struct session *session
+) {
+ return session->session_info.last_error;
+}
+
+
+/*****************************************************************************/
+struct bulk_buffer_descriptor *session_add_bulk_buf(
+ struct session *session,
+ void *buf,
+ uint32_t len
+) {
+ struct bulk_buffer_descriptor *bulk_buf_descr = NULL;
+ struct bulk_buffer_descriptor *tmp;
+ struct list_head *pos;
+
+ /* Search bulk buffer descriptors for existing vAddr
+ At the moment a virtual address can only be added one time */
+ list_for_each(pos, &session->bulk_buffer_descriptors) {
+ tmp = list_entry(pos, struct bulk_buffer_descriptor, list);
+ if (tmp->virt_addr == buf)
+ return NULL;
+ }
+
+ do {
+ /* Prepare the interface structure for memory registration in
+ Kernel Module */
+ void *l2_table_phys;
+ uint32_t handle;
+
+ int ret = mobicore_map_vmem(session->instance,
+ buf,
+ len,
+ &handle,
+ &l2_table_phys);
+
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("mobicore_map_vmem failed, ret=%d",
+ ret);
+ break;
+ }
+
+ MCDRV_DBG_VERBOSE("Physical Address of L2 Table = 0x%X, "
+ "handle=%d",
+ (unsigned int)l2_table_phys,
+ handle);
+
+ /* Create new descriptor */
+ bulk_buf_descr = bulk_buffer_descriptor_create(
+ buf,
+ len,
+ handle,
+ l2_table_phys);
+
+ /* Add to vector of descriptors */
+ list_add_tail(&(bulk_buf_descr->list),
+ &(session->bulk_buffer_descriptors));
+ } while (0);
+
+ return bulk_buf_descr;
+}
+
+
+/*****************************************************************************/
+bool session_remove_bulk_buf(
+ struct session *session,
+ void *virt_addr
+) {
+ bool ret = true;
+ struct bulk_buffer_descriptor *bulk_buf_descr = NULL;
+ struct bulk_buffer_descriptor *tmp;
+ struct list_head *pos, *q;
+
+ MCDRV_DBG_VERBOSE("Virtual Address = 0x%X", (unsigned int) virt_addr);
+
+ /* Search and remove bulk buffer descriptor */
+ list_for_each_safe(pos, q, &session->bulk_buffer_descriptors) {
+ tmp = list_entry(pos, struct bulk_buffer_descriptor, list);
+ if (tmp->virt_addr == virt_addr) {
+ bulk_buf_descr = tmp;
+ list_del(pos);
+ break;
+ }
+ }
+
+ if (bulk_buf_descr == NULL) {
+ MCDRV_DBG_ERROR("Virtual Address not found");
+ ret = false;
+ } else {
+ MCDRV_DBG_VERBOSE("WsmL2 phys=0x%X, handle=%d",
+ (unsigned int)bulk_buf_descr->phys_addr_wsm_l2,
+ bulk_buf_descr->handle);
+
+ /* ignore any error, as we cannot do anything */
+ int ret = mobicore_unmap_vmem(session->instance,
+ bulk_buf_descr->handle);
+ if (ret != 0)
+ MCDRV_DBG_ERROR("mobicore_unmap_vmem failed: %d", ret);
+
+ kfree(bulk_buf_descr);
+ }
+
+ return ret;
+}
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/session.h b/drivers/gud/mobicore_kernelapi/session.h
new file mode 100644
index 0000000..9a53740
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/session.h
@@ -0,0 +1,136 @@
+/** @addtogroup MCD_IMPL_LIB
+ * @{
+ * @file
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * 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
+ * published by the Free Software Foundation.
+ */
+#ifndef SESSION_H_
+#define SESSION_H_
+
+#include "common.h"
+
+#include <linux/list.h>
+#include "connection.h"
+
+
+struct bulk_buffer_descriptor {
+ void *virt_addr;/**< The virtual address of the Bulk buffer*/
+ uint32_t len; /**< Length of the Bulk buffer*/
+ uint32_t handle;
+ void *phys_addr_wsm_l2; /**< The physical address of the
+ L2 table of the Bulk buffer*/
+ struct list_head list; /**< The list param for using the kernel lists*/
+};
+
+struct bulk_buffer_descriptor *bulk_buffer_descriptor_create(
+ void *virt_addr,
+ uint32_t len,
+ uint32_t handle,
+ void *phys_addr_wsm_l2
+);
+
+/** Session states.
+ * At the moment not used !!.
+ */
+enum session_state {
+ SESSION_STATE_INITIAL,
+ SESSION_STATE_OPEN,
+ SESSION_STATE_TRUSTLET_DEAD
+};
+
+#define SESSION_ERR_NO 0 /**< No session error */
+
+/** Session information structure.
+ * The information structure is used to hold the state of the session, which
+ * will limit further actions for the session.
+ * Also the last error code will be stored till it's read.
+ */
+struct session_information {
+ enum session_state state; /**< Session state */
+ int32_t last_error; /**< Last error of session */
+};
+
+
+struct session {
+ struct mc_instance *instance;
+ /**< Descriptors of additional bulk buffer of a session */
+ struct list_head bulk_buffer_descriptors;
+ /**< Informations about session */
+ struct session_information session_info;
+
+ uint32_t session_id;
+ struct connection *notification_connection;
+
+ /**< The list param for using the kernel lists*/
+ struct list_head list;
+};
+
+struct session *session_create(
+ uint32_t session_id,
+ void *instance,
+ struct connection *connection
+);
+
+void session_cleanup(
+ struct session *session
+);
+
+/**
+ * Add address information of additional bulk buffer memory to session and
+ * register virtual memory in kernel module.
+ *
+ * @attention The virtual address can only be added one time. If the virtual
+ * address already exist, NULL is returned.
+ *
+ * @param buf The virtual address of bulk buffer.
+ * @param len Length of bulk buffer.
+ *
+ * @return On success the actual Bulk buffer descriptor with all address
+ * information is retured, NULL if an error occurs.
+ */
+struct bulk_buffer_descriptor *session_add_bulk_buf(
+ struct session *session,
+ void *buf,
+ uint32_t len
+);
+
+/**
+ * Remove address information of additional bulk buffer memory from session and
+ * unregister virtual memory in kernel module
+ *
+ * @param buf The virtual address of the bulk buffer.
+ *
+ * @return true on success.
+ */
+bool session_remove_bulk_buf(
+ struct session *session,
+ void *buf
+);
+
+/**
+ * Set additional error information of the last error that occured.
+ *
+ * @param errorCode The actual error.
+ */
+void session_set_error_info(
+ struct session *session,
+ int32_t err
+);
+
+/**
+ * Get additional error information of the last error that occured.
+ *
+ * @attention After request the information is set to SESSION_ERR_NO.
+ *
+ * @return Last stored error code or SESSION_ERR_NO.
+ */
+int32_t session_get_last_err(
+ struct session *session
+);
+
+#endif /* SESSION_H_ */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/wsm.h b/drivers/gud/mobicore_kernelapi/wsm.h
new file mode 100644
index 0000000..6877c53
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/wsm.h
@@ -0,0 +1,35 @@
+/** @addtogroup MCD_MCDIMPL_DAEMON_SRV
+ * @{
+ * @file
+ *
+ * World shared memory definitions.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * 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
+ * published by the Free Software Foundation.
+ */
+#ifndef WSM_H_
+#define WSM_H_
+
+#include "common.h"
+#include <linux/list.h>
+
+struct wsm {
+ void *virt_addr;
+ uint32_t len;
+ uint32_t handle;
+ void *phys_addr;
+ struct list_head list;
+};
+
+struct wsm *wsm_create(
+ void *virt_addr,
+ uint32_t len,
+ uint32_t handle,
+ void *phys_addr /*= NULL this may be unknown, so is can be omitted.*/
+);
+#endif /* WSM_H_ */
+
+/** @} */
diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb/dvb-core/demux.h
index a57ad44..e22bc64 100644
--- a/drivers/media/dvb/dvb-core/demux.h
+++ b/drivers/media/dvb/dvb-core/demux.h
@@ -138,6 +138,9 @@
int (*stop_filtering) (struct dmx_ts_feed* feed);
int (*set_indexing_params) (struct dmx_ts_feed *feed,
struct dmx_indexing_video_params *params);
+ int (*get_decoder_buff_status)(
+ struct dmx_ts_feed *feed,
+ struct dmx_buffer_status *dmx_buffer_status);
};
/*--------------------------------------------------------------------------*/
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index 1d310f2..f2f62a4 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -850,6 +850,42 @@
spin_lock_irq(&dmxdevfilter->dev->lock);
+ if ((dmxdevfilter->type == DMXDEV_TYPE_PES) &&
+ (dmxdevfilter->params.pes.output == DMX_OUT_DECODER)) {
+ struct dmxdev_feed *feed;
+ int ret;
+
+ /*
+ * Ask for status of decoder's buffer from underlying HW.
+ * In case of PCR/STC extraction, the filter's ring-buffer
+ * is used to gather the PCR/STC data and not using
+ * an internal decoder buffer.
+ */
+ if (!(dmxdevfilter->dev->capabilities &
+ DMXDEV_CAP_PCR_EXTRACTION) ||
+ ((dmxdevfilter->params.pes.pes_type != DMX_PES_PCR0) &&
+ (dmxdevfilter->params.pes.pes_type != DMX_PES_PCR1) &&
+ (dmxdevfilter->params.pes.pes_type != DMX_PES_PCR2) &&
+ (dmxdevfilter->params.pes.pes_type != DMX_PES_PCR3))) {
+ list_for_each_entry(feed, &dmxdevfilter->feed.ts,
+ next) {
+ if (feed->ts->get_decoder_buff_status)
+ ret = feed->ts->get_decoder_buff_status(
+ feed->ts,
+ dmx_buffer_status);
+ else
+ ret = -ENODEV;
+
+ /*
+ * There should not be more than one ts feed
+ * in the list as this is DECODER feed.
+ */
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+ return ret;
+ }
+ }
+ }
+
dmx_buffer_status->error = buf->error;
if (buf->error)
dvb_ringbuffer_flush(buf);
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index 0ff2a55..bc72fee 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -1027,6 +1027,32 @@
return ret;
}
+static int dmx_ts_feed_decoder_buff_status(struct dmx_ts_feed *ts_feed,
+ struct dmx_buffer_status *dmx_buffer_status)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+ struct dvb_demux *demux = feed->demux;
+ int ret;
+
+ spin_lock_irq(&demux->lock);
+
+ if (feed->state < DMX_STATE_GO) {
+ spin_unlock_irq(&demux->lock);
+ return -EINVAL;
+ }
+
+ if (!demux->decoder_buffer_status) {
+ spin_unlock_irq(&demux->lock);
+ return -ENODEV;
+ }
+
+ ret = demux->decoder_buffer_status(feed, dmx_buffer_status);
+
+ spin_unlock_irq(&demux->lock);
+
+ return ret;
+}
+
static int dmx_ts_set_indexing_params(
struct dmx_ts_feed *ts_feed,
struct dmx_indexing_video_params *params)
@@ -1078,6 +1104,7 @@
(*ts_feed)->stop_filtering = dmx_ts_feed_stop_filtering;
(*ts_feed)->set = dmx_ts_feed_set;
(*ts_feed)->set_indexing_params = dmx_ts_set_indexing_params;
+ (*ts_feed)->get_decoder_buff_status = dmx_ts_feed_decoder_buff_status;
if (!(feed->filter = dvb_dmx_filter_alloc(demux))) {
feed->state = DMX_STATE_FREE;
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h
index 17f4960..ebe34ad 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.h
+++ b/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -116,6 +116,8 @@
int (*decoder_fullness_wait)(struct dvb_demux_feed *feed,
size_t required_space);
int (*decoder_fullness_abort)(struct dvb_demux_feed *feed);
+ int (*decoder_buffer_status)(struct dvb_demux_feed *feed,
+ struct dmx_buffer_status *dmx_buffer_status);
u32 (*check_crc32)(struct dvb_demux_feed *feed,
const u8 *buf, size_t len);
void (*memcopy)(struct dvb_demux_feed *feed, u8 *dst,
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
index 03b3929..7223377 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
@@ -719,7 +719,7 @@
int i;
int dvr_index;
int dmx_index;
- struct dvb_demux *dvb_demux = (struct dvb_demux *)demux->priv;
+ struct dvb_demux *dvb_demux = demux->priv;
struct mpq_demux *mpq_demux;
if ((mpq_dmx_info.devices == NULL) || (dvb_demux == NULL)) {
@@ -727,7 +727,7 @@
return -EINVAL;
}
- mpq_demux = (struct mpq_demux *)dvb_demux->priv;
+ mpq_demux = dvb_demux->priv;
if (mpq_demux == NULL) {
MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
return -EINVAL;
@@ -781,8 +781,7 @@
void *packet_buffer;
void *payload_buffer;
struct mpq_video_feed_info *feed_data;
- struct mpq_demux *mpq_demux =
- (struct mpq_demux *)feed->demux->priv;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
struct mpq_streambuffer *stream_buffer;
int actual_buffer_size;
@@ -993,11 +992,8 @@
return -EINVAL;
}
- mpq_demux =
- (struct mpq_demux *)feed->demux->priv;
-
- feed_data =
- (struct mpq_video_feed_info *)feed->priv;
+ mpq_demux = feed->demux->priv;
+ feed_data = feed->priv;
spin_lock(&mpq_demux->feed_lock);
feed->priv = NULL;
@@ -1024,10 +1020,7 @@
int mpq_dmx_decoder_fullness_init(struct dvb_demux_feed *feed)
{
- struct mpq_demux *mpq_demux;
-
- mpq_demux =
- (struct mpq_demux *)feed->demux->priv;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
if (mpq_dmx_is_video_feed(feed)) {
struct mpq_video_feed_info *feed_data;
@@ -1042,9 +1035,7 @@
return -EINVAL;
}
- feed_data =
- (struct mpq_video_feed_info *)feed->priv;
-
+ feed_data = feed->priv;
feed_data->fullness_wait_cancel = 0;
spin_unlock(&mpq_demux->feed_lock);
@@ -1066,10 +1057,7 @@
struct dvb_demux_feed *feed,
size_t required_space)
{
- struct mpq_demux *mpq_demux;
-
- mpq_demux =
- (struct mpq_demux *)feed->demux->priv;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
if (mpq_dmx_is_video_feed(feed)) {
int ret;
@@ -1083,11 +1071,8 @@
return -EINVAL;
}
- feed_data =
- (struct mpq_video_feed_info *)feed->priv;
-
- video_buff =
- &feed_data->video_buffer->raw_data;
+ feed_data = feed->priv;
+ video_buff = &feed_data->video_buffer->raw_data;
ret = 0;
if ((feed_data != NULL) &&
@@ -1145,10 +1130,7 @@
int mpq_dmx_decoder_fullness_abort(struct dvb_demux_feed *feed)
{
- struct mpq_demux *mpq_demux;
-
- mpq_demux =
- (struct mpq_demux *)feed->demux->priv;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
if (mpq_dmx_is_video_feed(feed)) {
struct mpq_video_feed_info *feed_data;
@@ -1164,11 +1146,9 @@
return -EINVAL;
}
- feed_data =
- (struct mpq_video_feed_info *)feed->priv;
+ feed_data = feed->priv;
- video_buff =
- &feed_data->video_buffer->raw_data;
+ video_buff = &feed_data->video_buffer->raw_data;
feed_data->fullness_wait_cancel = 1;
spin_unlock(&mpq_demux->feed_lock);
@@ -1402,12 +1382,11 @@
u32 pattern_addr = 0;
int is_video_frame = 0;
- mpq_demux = (struct mpq_demux *)feed->demux->priv;
+ mpq_demux = feed->demux->priv;
spin_lock(&mpq_demux->feed_lock);
- feed_data = (struct mpq_video_feed_info *)feed->priv;
-
+ feed_data = feed->priv;
if (unlikely(feed_data == NULL)) {
spin_unlock(&mpq_demux->feed_lock);
return 0;
@@ -1713,12 +1692,11 @@
struct pes_packet_header *pes_header;
struct mpq_demux *mpq_demux;
- mpq_demux = (struct mpq_demux *)feed->demux->priv;
+ mpq_demux = feed->demux->priv;
spin_lock(&mpq_demux->feed_lock);
- feed_data = (struct mpq_video_feed_info *)feed->priv;
-
+ feed_data = feed->priv;
if (unlikely(feed_data == NULL)) {
spin_unlock(&mpq_demux->feed_lock);
return 0;
@@ -1869,6 +1847,50 @@
return 0;
}
+int mpq_dmx_decoder_buffer_status(struct dvb_demux_feed *feed,
+ struct dmx_buffer_status *dmx_buffer_status)
+{
+ struct mpq_demux *mpq_demux = feed->demux->priv;
+
+ if (mpq_dmx_is_video_feed(feed)) {
+ struct mpq_video_feed_info *feed_data;
+ struct dvb_ringbuffer *video_buff;
+
+ spin_lock(&mpq_demux->feed_lock);
+
+ if (feed->priv == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid feed, feed->priv is NULL\n",
+ __func__);
+ spin_unlock(&mpq_demux->feed_lock);
+ return -EINVAL;
+ }
+
+ feed_data = feed->priv;
+ video_buff = &feed_data->video_buffer->raw_data;
+
+ dmx_buffer_status->error = video_buff->error;
+ dmx_buffer_status->fullness = dvb_ringbuffer_avail(video_buff);
+ dmx_buffer_status->free_bytes = dvb_ringbuffer_free(video_buff);
+ dmx_buffer_status->read_offset = video_buff->pread;
+ dmx_buffer_status->write_offset = video_buff->pwrite;
+ dmx_buffer_status->size = video_buff->size;
+
+ spin_unlock(&mpq_demux->feed_lock);
+
+ return 0;
+ }
+
+ /* else */
+ MPQ_DVB_ERR_PRINT(
+ "%s: Invalid feed type %d\n",
+ __func__,
+ feed->pes_type);
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(mpq_dmx_decoder_buffer_status);
+
int mpq_dmx_process_video_packet(
struct dvb_demux_feed *feed,
const u8 *buf)
@@ -1888,8 +1910,7 @@
u64 pcr;
u64 stc;
u8 output[PCR_STC_LEN];
- struct mpq_demux *mpq_demux =
- (struct mpq_demux *)feed->demux->priv;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
const struct ts_packet_header *ts_header;
const struct ts_adaptation_field *adaptation_field;
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
index 0275b14..68cfcd1 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
@@ -426,6 +426,18 @@
struct dvb_demux_feed *feed);
/**
+ * mpq_dmx_decoder_buffer_status - Returns the
+ * status of the decoder's buffer.
+ *
+ * @feed: The decoder's feed
+ * @dmx_buffer_status: Status of decoder's buffer
+ *
+ * Return error code.
+ */
+int mpq_dmx_decoder_buffer_status(struct dvb_demux_feed *feed,
+ struct dmx_buffer_status *dmx_buffer_status);
+
+/**
* mpq_dmx_process_video_packet - Assemble PES data and output it
* to the stream-buffer connected to the decoder.
*
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
index bfd6b96..e7d6b74 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
@@ -414,8 +414,7 @@
static int mpq_tsif_dmx_start_filtering(struct dvb_demux_feed *feed)
{
int ret = 0;
- struct mpq_demux *mpq_demux =
- (struct mpq_demux *)feed->demux->priv;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
MPQ_DVB_DBG_PRINT(
"%s(%d) executed\n",
@@ -482,8 +481,7 @@
static int mpq_tsif_dmx_stop_filtering(struct dvb_demux_feed *feed)
{
int ret = 0;
- struct mpq_demux *mpq_demux =
- (struct mpq_demux *)feed->demux->priv;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
MPQ_DVB_DBG_PRINT(
"%s(%d) executed\n",
@@ -559,7 +557,7 @@
static int mpq_tsif_dmx_get_caps(struct dmx_demux *demux,
struct dmx_caps *caps)
{
- struct dvb_demux *dvb_demux = (struct dvb_demux *)demux->priv;
+ struct dvb_demux *dvb_demux = demux->priv;
if ((dvb_demux == NULL) || (caps == NULL)) {
MPQ_DVB_ERR_PRINT(
@@ -627,6 +625,9 @@
mpq_demux->demux.decoder_fullness_abort =
mpq_dmx_decoder_fullness_abort;
+ mpq_demux->demux.decoder_buffer_status =
+ mpq_dmx_decoder_buffer_status;
+
/* Initialize dvb_demux object */
result = dvb_dmx_init(&mpq_demux->demux);
if (result < 0) {
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
index a552fdf..f374d91 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
@@ -264,7 +264,7 @@
*/
static int mpq_tspp_dmx_add_channel(struct dvb_demux_feed *feed)
{
- struct mpq_demux *mpq_demux = (struct mpq_demux *)feed->demux->priv;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
enum tspp_source tspp_source;
struct tspp_filter tspp_filter;
int tsif;
@@ -455,7 +455,7 @@
int channel_id;
int *channel_ref_count;
struct tspp_filter tspp_filter;
- struct mpq_demux *mpq_demux = (struct mpq_demux *)feed->demux->priv;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
/* determine the TSIF we are reading from */
if (mpq_demux->source == DMX_SOURCE_FRONT0) {
@@ -559,8 +559,7 @@
static int mpq_tspp_dmx_start_filtering(struct dvb_demux_feed *feed)
{
int ret;
- struct mpq_demux *mpq_demux =
- (struct mpq_demux *)feed->demux->priv;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
MPQ_DVB_DBG_PRINT(
"%s(%d) executed\n",
@@ -620,7 +619,7 @@
static int mpq_tspp_dmx_stop_filtering(struct dvb_demux_feed *feed)
{
int ret = 0;
- struct mpq_demux *mpq_demux = (struct mpq_demux *)feed->demux->priv;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
MPQ_DVB_DBG_PRINT(
"%s(%d) executed\n",
@@ -677,7 +676,7 @@
static int mpq_tspp_dmx_get_caps(struct dmx_demux *demux,
struct dmx_caps *caps)
{
- struct dvb_demux *dvb_demux = (struct dvb_demux *)demux->priv;
+ struct dvb_demux *dvb_demux = demux->priv;
if ((dvb_demux == NULL) || (caps == NULL)) {
MPQ_DVB_ERR_PRINT(
@@ -737,6 +736,9 @@
mpq_demux->demux.decoder_fullness_abort =
mpq_dmx_decoder_fullness_abort;
+ mpq_demux->demux.decoder_buffer_status =
+ mpq_dmx_decoder_buffer_status;
+
/* Initialize dvb_demux object */
result = dvb_dmx_init(&mpq_demux->demux);
if (result < 0) {
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c
index 6c484a0..74b7c22 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c
@@ -62,7 +62,7 @@
static int mpq_tspp_dmx_get_caps(struct dmx_demux *demux,
struct dmx_caps *caps)
{
- struct dvb_demux *dvb_demux = (struct dvb_demux *)demux->priv;
+ struct dvb_demux *dvb_demux = demux->priv;
if ((dvb_demux == NULL) || (caps == NULL)) {
MPQ_DVB_ERR_PRINT(
@@ -124,6 +124,7 @@
mpq_demux->demux.decoder_fullness_init = NULL;
mpq_demux->demux.decoder_fullness_wait = NULL;
mpq_demux->demux.decoder_fullness_abort = NULL;
+ mpq_demux->demux.decoder_buffer_status = NULL;
/* Initialize dvb_demux object */
result = dvb_dmx_init(&mpq_demux->demux);
diff --git a/drivers/media/radio/radio-iris.c b/drivers/media/radio/radio-iris.c
index fd72638..3f0f8de 100644
--- a/drivers/media/radio/radio-iris.c
+++ b/drivers/media/radio/radio-iris.c
@@ -1590,8 +1590,8 @@
if (status)
return;
-
- iris_q_event(radio, IRIS_EVT_RADIO_DISABLED);
+ if (radio->mode != FM_CALIB)
+ iris_q_event(radio, IRIS_EVT_RADIO_DISABLED);
radio_hci_req_complete(hdev, status);
}
@@ -1629,8 +1629,8 @@
if (rsp->status)
return;
-
- iris_q_event(radio, IRIS_EVT_RADIO_READY);
+ if (radio->mode != FM_CALIB)
+ iris_q_event(radio, IRIS_EVT_RADIO_READY);
radio_hci_req_complete(hdev, rsp->status);
}
@@ -2455,22 +2455,26 @@
int retval = 0x00;
cal_mode = PROCS_CALIB_MODE;
+ radio->mode = FM_CALIB;
retval = hci_cmd(HCI_FM_ENABLE_RECV_CMD,
radio->fm_hdev);
if (retval < 0) {
FMDERR("Enable failed before calibration %x", retval);
+ radio->mode = FM_OFF;
return retval;
}
retval = radio_hci_request(radio->fm_hdev, hci_fm_do_cal_req,
(unsigned long)cal_mode, RADIO_HCI_TIMEOUT);
if (retval < 0) {
FMDERR("Do Process calibration failed %x", retval);
+ radio->mode = FM_RECV;
return retval;
}
retval = hci_cmd(HCI_FM_DISABLE_RECV_CMD,
radio->fm_hdev);
if (retval < 0)
FMDERR("Disable Failed after calibration %d", retval);
+ radio->mode = FM_OFF;
return retval;
}
static int iris_vidioc_g_ctrl(struct file *file, void *priv,
@@ -2880,12 +2884,12 @@
case FM_TRANS:
retval = hci_cmd(HCI_FM_ENABLE_TRANS_CMD,
radio->fm_hdev);
- radio->mode = FM_TRANS;
if (retval < 0) {
FMDERR("Error while enabling TRANS FM"
" %d\n", retval);
return retval;
}
+ radio->mode = FM_TRANS;
retval = hci_cmd(HCI_FM_GET_TX_CONFIG, radio->fm_hdev);
if (retval < 0)
FMDERR("get frequency failed %d\n", retval);
@@ -2895,17 +2899,23 @@
case FM_RECV:
retval = hci_cmd(HCI_FM_DISABLE_RECV_CMD,
radio->fm_hdev);
- if (retval < 0)
+ if (retval < 0) {
FMDERR("Err on disable recv FM"
" %d\n", retval);
+ return retval;
+ }
+ radio->mode = FM_OFF;
break;
case FM_TRANS:
retval = hci_cmd(HCI_FM_DISABLE_TRANS_CMD,
radio->fm_hdev);
- if (retval < 0)
+ if (retval < 0) {
FMDERR("Err disabling trans FM"
" %d\n", retval);
+ return retval;
+ }
+ radio->mode = FM_OFF;
break;
default:
retval = -EINVAL;
diff --git a/drivers/media/video/msm/Makefile b/drivers/media/video/msm/Makefile
index b60f99f..e4d4081 100644
--- a/drivers/media/video/msm/Makefile
+++ b/drivers/media/video/msm/Makefile
@@ -11,8 +11,9 @@
EXTRA_CFLAGS += -Idrivers/media/video/msm/eeprom
EXTRA_CFLAGS += -Idrivers/media/video/msm/sensors
EXTRA_CFLAGS += -Idrivers/media/video/msm/actuators
+ EXTRA_CFLAGS += -Idrivers/media/video/msm/server
obj-$(CONFIG_MSM_CAMERA) += msm_isp.o msm.o msm_mem.o msm_mctl.o msm_mctl_buf.o msm_mctl_pp.o
- obj-$(CONFIG_MSM_CAMERA) += io/ eeprom/ sensors/ actuators/ csi/
+ obj-$(CONFIG_MSM_CAMERA) += server/ eeprom/ sensors/ actuators/ csi/
obj-$(CONFIG_MSM_CAMERA) += msm_gesture.o
else
obj-$(CONFIG_MSM_CAMERA) += msm_camera.o
diff --git a/drivers/media/video/msm/csi/msm_csid.c b/drivers/media/video/msm/csi/msm_csid.c
index c04ece2..111d878 100644
--- a/drivers/media/video/msm/csi/msm_csid.c
+++ b/drivers/media/video/msm/csi/msm_csid.c
@@ -107,6 +107,8 @@
void __iomem *csidbase;
csid_dev = v4l2_get_subdevdata(cfg_params->subdev);
csidbase = csid_dev->base;
+ if (csidbase == NULL)
+ return -ENOMEM;
csid_params = cfg_params->parms;
val = csid_params->lane_cnt - 1;
diff --git a/drivers/media/video/msm/csi/msm_csiphy.c b/drivers/media/video/msm/csi/msm_csiphy.c
index 7c59ae8..df01c6b 100644
--- a/drivers/media/video/msm/csi/msm_csiphy.c
+++ b/drivers/media/video/msm/csi/msm_csiphy.c
@@ -68,6 +68,9 @@
void __iomem *csiphybase;
csiphy_dev = v4l2_get_subdevdata(cfg_params->subdev);
csiphybase = csiphy_dev->base;
+ if (csiphybase == NULL)
+ return -ENOMEM;
+
csiphy_params = cfg_params->parms;
lane_mask = csiphy_params->lane_mask;
lane_cnt = csiphy_params->lane_cnt;
diff --git a/drivers/media/video/msm/gemini/msm_gemini_platform.h b/drivers/media/video/msm/gemini/msm_gemini_platform.h
index 4542129..eb6b9f0 100644
--- a/drivers/media/video/msm/gemini/msm_gemini_platform.h
+++ b/drivers/media/video/msm/gemini/msm_gemini_platform.h
@@ -16,6 +16,7 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/ion.h>
+#include <linux/iommu.h>
void msm_gemini_platform_p2v(struct file *file,
struct ion_handle **ionhandle);
uint32_t msm_gemini_platform_v2p(int fd, uint32_t len, struct file **file,
diff --git a/drivers/media/video/msm/gemini/msm_gemini_sync.c b/drivers/media/video/msm/gemini/msm_gemini_sync.c
index fe7c99f..b55ec18 100644
--- a/drivers/media/video/msm/gemini/msm_gemini_sync.c
+++ b/drivers/media/video/msm/gemini/msm_gemini_sync.c
@@ -453,6 +453,7 @@
{
struct msm_gemini_core_buf *buf_p;
struct msm_gemini_buf buf_cmd;
+ int rc = 0;
if (copy_from_user(&buf_cmd, arg, sizeof(struct msm_gemini_buf))) {
GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
@@ -469,17 +470,25 @@
(int) buf_cmd.vaddr, buf_cmd.y_len);
if (pgmn_dev->op_mode == MSM_GEMINI_MODE_REALTIME_ENCODE) {
- buf_p->y_buffer_addr = buf_cmd.y_off;
+ rc = msm_iommu_map_contig_buffer(
+ (unsigned long)buf_cmd.y_off, CAMERA_DOMAIN, GEN_POOL,
+ ((buf_cmd.y_len + buf_cmd.cbcr_len + 4095) & (~4095)),
+ SZ_4K, IOMMU_WRITE | IOMMU_READ,
+ (unsigned long *)&buf_p->y_buffer_addr);
+ if (rc < 0) {
+ pr_err("%s iommu mapping failed with error %d\n",
+ __func__, rc);
+ kfree(buf_p);
+ return rc;
+ }
} else {
buf_p->y_buffer_addr = msm_gemini_platform_v2p(buf_cmd.fd,
buf_cmd.y_len + buf_cmd.cbcr_len, &buf_p->file,
&buf_p->handle) + buf_cmd.offset;
}
buf_p->y_len = buf_cmd.y_len;
-
buf_p->cbcr_buffer_addr = buf_p->y_buffer_addr + buf_cmd.y_len;
buf_p->cbcr_len = buf_cmd.cbcr_len;
-
buf_p->num_of_mcu_rows = buf_cmd.num_of_mcu_rows;
GMN_DBG("%s: y_addr=%x,y_len=%x,cbcr_addr=%x,cbcr_len=%x\n", __func__,
buf_p->y_buffer_addr, buf_p->y_len, buf_p->cbcr_buffer_addr,
diff --git a/drivers/media/video/msm/msm.c b/drivers/media/video/msm/msm.c
index 425768a..0696b96 100644
--- a/drivers/media/video/msm/msm.c
+++ b/drivers/media/video/msm/msm.c
@@ -19,13 +19,9 @@
#include <linux/spinlock.h>
#include <linux/proc_fs.h>
#include "msm.h"
-#include "msm_csid.h"
-#include "msm_csic.h"
-#include "msm_csiphy.h"
-#include "msm_ispif.h"
+#include "msm_cam_server.h"
#include "msm_sensor.h"
#include "msm_actuator.h"
-#include "msm_vfe32.h"
#include "msm_camera_eeprom.h"
#define MSM_MAX_CAMERA_SENSORS 5
@@ -37,111 +33,11 @@
#endif
static unsigned msm_camera_v4l2_nr = -1;
-static struct msm_cam_server_dev g_server_dev;
-static struct class *msm_class;
-static dev_t msm_devno;
static int vnode_count;
module_param(msm_camera_v4l2_nr, uint, 0644);
MODULE_PARM_DESC(msm_camera_v4l2_nr, "videoX start number, -1 is autodetect");
-static long msm_server_send_v4l2_evt(void *evt);
-static void msm_cam_server_subdev_notify(struct v4l2_subdev *sd,
- unsigned int notification, void *arg);
-
-static void msm_queue_init(struct msm_device_queue *queue, const char *name)
-{
- D("%s\n", __func__);
- spin_lock_init(&queue->lock);
- queue->len = 0;
- queue->max = 0;
- queue->name = name;
- INIT_LIST_HEAD(&queue->list);
- init_waitqueue_head(&queue->wait);
-}
-
-static void msm_enqueue(struct msm_device_queue *queue,
- struct list_head *entry)
-{
- unsigned long flags;
- spin_lock_irqsave(&queue->lock, flags);
- queue->len++;
- if (queue->len > queue->max) {
- queue->max = queue->len;
- pr_info("%s: queue %s new max is %d\n", __func__,
- queue->name, queue->max);
- }
- list_add_tail(entry, &queue->list);
- wake_up(&queue->wait);
- D("%s: woke up %s\n", __func__, queue->name);
- spin_unlock_irqrestore(&queue->lock, flags);
-}
-
-static void msm_drain_eventq(struct msm_device_queue *queue)
-{
- unsigned long flags;
- struct msm_queue_cmd *qcmd;
- spin_lock_irqsave(&queue->lock, flags);
- while (!list_empty(&queue->list)) {
- qcmd = list_first_entry(&queue->list,
- struct msm_queue_cmd, list_eventdata);
- list_del_init(&qcmd->list_eventdata);
- kfree(qcmd->command);
- free_qcmd(qcmd);
- }
- spin_unlock_irqrestore(&queue->lock, flags);
-}
-
-static int32_t msm_find_free_queue(void)
-{
- int i;
- for (i = 0; i < MAX_NUM_ACTIVE_CAMERA; i++) {
- struct msm_cam_server_queue *queue;
- queue = &g_server_dev.server_queue[i];
- if (!queue->queue_active)
- return i;
- }
- return -EINVAL;
-}
-
-uint32_t msm_camera_get_mctl_handle(void)
-{
- uint32_t i;
- if ((g_server_dev.mctl_handle_cnt << 8) == 0)
- g_server_dev.mctl_handle_cnt++;
- for (i = 0; i < MAX_NUM_ACTIVE_CAMERA; i++) {
- if (g_server_dev.mctl[i].handle == 0) {
- g_server_dev.mctl[i].handle =
- (++g_server_dev.mctl_handle_cnt) << 8 | i;
- memset(&g_server_dev.mctl[i].mctl,
- 0, sizeof(g_server_dev.mctl[i].mctl));
- return g_server_dev.mctl[i].handle;
- }
- }
- return 0;
-}
-
-struct msm_cam_media_controller *msm_camera_get_mctl(uint32_t handle)
-{
- uint32_t mctl_index;
- mctl_index = handle & 0xff;
- if ((mctl_index < MAX_NUM_ACTIVE_CAMERA) &&
- (g_server_dev.mctl[mctl_index].handle == handle))
- return &g_server_dev.mctl[mctl_index].mctl;
- return NULL;
-}
-
-void msm_camera_free_mctl(uint32_t handle)
-{
- uint32_t mctl_index;
- mctl_index = handle & 0xff;
- if ((mctl_index < MAX_NUM_ACTIVE_CAMERA) &&
- (g_server_dev.mctl[mctl_index].handle == handle))
- g_server_dev.mctl[mctl_index].handle = 0;
- else
- pr_err("%s: invalid free handle\n", __func__);
-}
-
/* callback function from all subdevices of a msm_cam_v4l2_device */
static void msm_cam_v4l2_subdev_notify(struct v4l2_subdev *sd,
unsigned int notification, void *arg)
@@ -157,663 +53,11 @@
if (pcam == NULL)
return;
- pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+ pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
if (pmctl == NULL)
return;
}
-static int msm_ctrl_cmd_done(void *arg)
-{
- void __user *uptr;
- struct msm_queue_cmd *qcmd;
- struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;
- struct msm_ctrl_cmd *command =
- kzalloc(sizeof(struct msm_ctrl_cmd), GFP_KERNEL);
- if (!command) {
- pr_err("%s Insufficient memory. return", __func__);
- return -ENOMEM;
- }
-
- D("%s\n", __func__);
- if (copy_from_user(command, (void __user *)ioctl_ptr->ioctl_ptr,
- sizeof(struct msm_ctrl_cmd))) {
- pr_err("%s: copy_from_user failed, size=%d\n",
- __func__, sizeof(struct msm_ctrl_cmd));
- return -EINVAL;
- }
-
- D("%s qid %d evtid %d %d\n", __func__, command->queue_idx,
- command->evt_id,
- g_server_dev.server_queue[command->queue_idx].evt_id);
- g_server_dev.server_queue[command->queue_idx].ctrl = command;
- if (command->evt_id !=
- g_server_dev.server_queue[command->queue_idx].evt_id) {
- pr_err("%s Invalid event id from userspace cmd id %d %d qid %d\n",
- __func__, command->evt_id,
- g_server_dev.server_queue[command->queue_idx].evt_id,
- command->queue_idx);
- return -EINVAL;
- }
-
- mutex_lock(&g_server_dev.server_queue_lock);
- qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
- atomic_set(&qcmd->on_heap, 1);
- uptr = command->value;
- qcmd->command = command;
-
- if (command->length > 0) {
- command->value =
- g_server_dev.server_queue[command->queue_idx].ctrl_data;
- if (command->length > max_control_command_size) {
- pr_err("%s: user data %d is too big (max %d)\n",
- __func__, command->length,
- max_control_command_size);
- free_qcmd(qcmd);
- return -EINVAL;
- }
- if (copy_from_user(command->value, uptr, command->length)) {
- free_qcmd(qcmd);
- return -EINVAL;
- }
- }
- msm_enqueue(&g_server_dev.server_queue
- [command->queue_idx].ctrl_q, &qcmd->list_control);
- mutex_unlock(&g_server_dev.server_queue_lock);
- return 0;
-}
-
-/* send control command to config and wait for results*/
-static int msm_server_control(struct msm_cam_server_dev *server_dev,
- struct msm_ctrl_cmd *out)
-{
- int rc = 0;
- void *value;
- struct msm_queue_cmd *rcmd;
- struct msm_queue_cmd *event_qcmd;
- struct msm_ctrl_cmd *ctrlcmd;
- struct msm_device_queue *queue =
- &server_dev->server_queue[out->queue_idx].ctrl_q;
-
- struct v4l2_event v4l2_evt;
- struct msm_isp_event_ctrl *isp_event;
- isp_event = kzalloc(sizeof(struct msm_isp_event_ctrl), GFP_KERNEL);
- if (!isp_event) {
- pr_err("%s Insufficient memory. return", __func__);
- return -ENOMEM;
- }
- event_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
- if (!event_qcmd) {
- pr_err("%s Insufficient memory. return", __func__);
- return -ENOMEM;
- }
-
- D("%s\n", __func__);
- mutex_lock(&server_dev->server_queue_lock);
- if (++server_dev->server_evt_id == 0)
- server_dev->server_evt_id++;
- D("%s qid %d evtid %d\n", __func__, out->queue_idx,
- server_dev->server_evt_id);
-
- server_dev->server_queue[out->queue_idx].evt_id =
- server_dev->server_evt_id;
- v4l2_evt.type = V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_V4L2;
- v4l2_evt.u.data[0] = out->queue_idx;
- /* setup event object to transfer the command; */
- isp_event->resptype = MSM_CAM_RESP_V4L2;
- isp_event->isp_data.ctrl = *out;
- isp_event->isp_data.ctrl.evt_id = server_dev->server_evt_id;
-
- atomic_set(&event_qcmd->on_heap, 1);
- event_qcmd->command = isp_event;
-
- msm_enqueue(&server_dev->server_queue[out->queue_idx].eventData_q,
- &event_qcmd->list_eventdata);
-
- /* now send command to config thread in userspace,
- * and wait for results */
- v4l2_event_queue(server_dev->server_command_queue.pvdev,
- &v4l2_evt);
- D("%s v4l2_event_queue: type = 0x%x\n", __func__, v4l2_evt.type);
- mutex_unlock(&server_dev->server_queue_lock);
-
- /* wait for config return status */
- D("Waiting for config status\n");
- rc = wait_event_interruptible_timeout(queue->wait,
- !list_empty_careful(&queue->list),
- msecs_to_jiffies(out->timeout_ms));
- D("Waiting is over for config status\n");
- if (list_empty_careful(&queue->list)) {
- if (!rc)
- rc = -ETIMEDOUT;
- if (rc < 0) {
- kfree(isp_event);
- pr_err("%s: wait_event error %d\n", __func__, rc);
- return rc;
- }
- }
-
- rcmd = msm_dequeue(queue, list_control);
- BUG_ON(!rcmd);
- D("%s Finished servicing ioctl\n", __func__);
-
- ctrlcmd = (struct msm_ctrl_cmd *)(rcmd->command);
- value = out->value;
- if (ctrlcmd->length > 0 && value != NULL &&
- ctrlcmd->length <= out->length)
- memcpy(value, ctrlcmd->value, ctrlcmd->length);
-
- memcpy(out, ctrlcmd, sizeof(struct msm_ctrl_cmd));
- out->value = value;
-
- kfree(ctrlcmd);
- server_dev->server_queue[out->queue_idx].ctrl = NULL;
-
- free_qcmd(rcmd);
- kfree(isp_event);
- D("%s: rc %d\n", __func__, rc);
- /* rc is the time elapsed. */
- if (rc >= 0) {
- /* TODO: Refactor msm_ctrl_cmd::status field */
- if (out->status == 0)
- rc = -1;
- else if (out->status == 1 || out->status == 4)
- rc = 0;
- else
- rc = -EINVAL;
- }
- return rc;
-}
-
-/*send open command to server*/
-static int msm_send_open_server(struct msm_cam_v4l2_device *pcam)
-{
- int rc = 0;
- struct msm_ctrl_cmd ctrlcmd;
- D("%s qid %d\n", __func__, pcam->server_queue_idx);
- ctrlcmd.type = MSM_V4L2_OPEN;
- ctrlcmd.timeout_ms = 10000;
- ctrlcmd.length = strnlen(g_server_dev.config_info.config_dev_name[0],
- MAX_DEV_NAME_LEN)+1;
- ctrlcmd.value = (char *)g_server_dev.config_info.config_dev_name[0];
- ctrlcmd.vnode_id = pcam->vnode_id;
- ctrlcmd.queue_idx = pcam->server_queue_idx;
- ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
-
- /* send command to config thread in usersspace, and get return value */
- rc = msm_server_control(&g_server_dev, &ctrlcmd);
-
- return rc;
-}
-
-static int msm_send_close_server(struct msm_cam_v4l2_device *pcam)
-{
- int rc = 0;
- struct msm_ctrl_cmd ctrlcmd;
- D("%s qid %d\n", __func__, pcam->server_queue_idx);
- ctrlcmd.type = MSM_V4L2_CLOSE;
- ctrlcmd.timeout_ms = 10000;
- ctrlcmd.length = strnlen(g_server_dev.config_info.config_dev_name[0],
- MAX_DEV_NAME_LEN)+1;
- ctrlcmd.value = (char *)g_server_dev.config_info.config_dev_name[0];
- ctrlcmd.vnode_id = pcam->vnode_id;
- ctrlcmd.queue_idx = pcam->server_queue_idx;
- ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
-
- /* send command to config thread in usersspace, and get return value */
- rc = msm_server_control(&g_server_dev, &ctrlcmd);
-
- return rc;
-}
-
-static int msm_server_set_fmt(struct msm_cam_v4l2_device *pcam, int idx,
- struct v4l2_format *pfmt)
-{
- int rc = 0;
- int i = 0;
- struct v4l2_pix_format *pix = &pfmt->fmt.pix;
- struct msm_ctrl_cmd ctrlcmd;
- struct img_plane_info plane_info;
-
- plane_info.width = pix->width;
- plane_info.height = pix->height;
- plane_info.pixelformat = pix->pixelformat;
- plane_info.buffer_type = pfmt->type;
- plane_info.ext_mode = pcam->dev_inst[idx]->image_mode;
- plane_info.num_planes = 1;
- D("%s: %d, %d, 0x%x\n", __func__,
- pfmt->fmt.pix.width, pfmt->fmt.pix.height,
- pfmt->fmt.pix.pixelformat);
-
- if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- D("%s, Attention! Wrong buf-type %d\n", __func__, pfmt->type);
-
- for (i = 0; i < pcam->num_fmts; i++)
- if (pcam->usr_fmts[i].fourcc == pix->pixelformat)
- break;
- if (i == pcam->num_fmts) {
- pr_err("%s: User requested pixelformat %x not supported\n",
- __func__, pix->pixelformat);
- return -EINVAL;
- }
-
- ctrlcmd.type = MSM_V4L2_VID_CAP_TYPE;
- ctrlcmd.length = sizeof(struct img_plane_info);
- ctrlcmd.value = (void *)&plane_info;
- ctrlcmd.timeout_ms = 10000;
- ctrlcmd.vnode_id = pcam->vnode_id;
- ctrlcmd.queue_idx = pcam->server_queue_idx;
- ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
-
- /* send command to config thread in usersspace, and get return value */
- rc = msm_server_control(&g_server_dev, &ctrlcmd);
-
- if (rc >= 0) {
- pcam->dev_inst[idx]->vid_fmt = *pfmt;
- pcam->dev_inst[idx]->sensor_pxlcode
- = pcam->usr_fmts[i].pxlcode;
- D("%s:inst=0x%x,idx=%d,width=%d,heigth=%d\n",
- __func__, (u32)pcam->dev_inst[idx], idx,
- pcam->dev_inst[idx]->vid_fmt.fmt.pix.width,
- pcam->dev_inst[idx]->vid_fmt.fmt.pix.height);
- pcam->dev_inst[idx]->plane_info = plane_info;
- }
-
- return rc;
-}
-
-static int msm_server_set_fmt_mplane(struct msm_cam_v4l2_device *pcam, int idx,
- struct v4l2_format *pfmt)
-{
- int rc = 0;
- int i = 0;
- struct v4l2_pix_format_mplane *pix_mp = &pfmt->fmt.pix_mp;
- struct msm_ctrl_cmd ctrlcmd;
- struct img_plane_info plane_info;
-
- plane_info.width = pix_mp->width;
- plane_info.height = pix_mp->height;
- plane_info.pixelformat = pix_mp->pixelformat;
- plane_info.buffer_type = pfmt->type;
- plane_info.ext_mode = pcam->dev_inst[idx]->image_mode;
- plane_info.num_planes = pix_mp->num_planes;
- if (plane_info.num_planes <= 0 ||
- plane_info.num_planes > VIDEO_MAX_PLANES) {
- pr_err("%s Invalid number of planes set %d", __func__,
- plane_info.num_planes);
- return -EINVAL;
- }
- D("%s: %d, %d, 0x%x\n", __func__,
- pfmt->fmt.pix_mp.width, pfmt->fmt.pix_mp.height,
- pfmt->fmt.pix_mp.pixelformat);
-
- if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- pr_err("%s, Attention! Wrong buf-type %d\n",
- __func__, pfmt->type);
- return -EINVAL;
- }
-
- for (i = 0; i < pcam->num_fmts; i++)
- if (pcam->usr_fmts[i].fourcc == pix_mp->pixelformat)
- break;
- if (i == pcam->num_fmts) {
- pr_err("%s: User requested pixelformat %x not supported\n",
- __func__, pix_mp->pixelformat);
- return -EINVAL;
- }
-
- ctrlcmd.type = MSM_V4L2_VID_CAP_TYPE;
- ctrlcmd.length = sizeof(struct img_plane_info);
- ctrlcmd.value = (void *)&plane_info;
- ctrlcmd.timeout_ms = 10000;
- ctrlcmd.vnode_id = pcam->vnode_id;
- ctrlcmd.queue_idx = pcam->server_queue_idx;
-
- /* send command to config thread in usersspace, and get return value */
- rc = msm_server_control(&g_server_dev, &ctrlcmd);
- if (rc >= 0) {
- pcam->dev_inst[idx]->vid_fmt = *pfmt;
- pcam->dev_inst[idx]->sensor_pxlcode
- = pcam->usr_fmts[i].pxlcode;
- D("%s:inst=0x%x,idx=%d,width=%d,heigth=%d\n",
- __func__, (u32)pcam->dev_inst[idx], idx,
- pcam->dev_inst[idx]->vid_fmt.fmt.pix_mp.width,
- pcam->dev_inst[idx]->vid_fmt.fmt.pix_mp.height);
- pcam->dev_inst[idx]->plane_info = plane_info;
- }
-
- return rc;
-}
-
-static int msm_server_streamon(struct msm_cam_v4l2_device *pcam, int idx)
-{
- int rc = 0;
- struct msm_ctrl_cmd ctrlcmd;
- D("%s\n", __func__);
- ctrlcmd.type = MSM_V4L2_STREAM_ON;
- ctrlcmd.timeout_ms = 10000;
- ctrlcmd.length = 0;
- ctrlcmd.value = NULL;
- ctrlcmd.stream_type = pcam->dev_inst[idx]->image_mode;
- ctrlcmd.vnode_id = pcam->vnode_id;
- ctrlcmd.queue_idx = pcam->server_queue_idx;
- ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
-
-
- /* send command to config thread in usersspace, and get return value */
- rc = msm_server_control(&g_server_dev, &ctrlcmd);
-
- return rc;
-}
-
-static int msm_server_streamoff(struct msm_cam_v4l2_device *pcam, int idx)
-{
- int rc = 0;
- struct msm_ctrl_cmd ctrlcmd;
-
- D("%s, pcam = 0x%x\n", __func__, (u32)pcam);
- ctrlcmd.type = MSM_V4L2_STREAM_OFF;
- ctrlcmd.timeout_ms = 10000;
- ctrlcmd.length = 0;
- ctrlcmd.value = NULL;
- ctrlcmd.stream_type = pcam->dev_inst[idx]->image_mode;
- ctrlcmd.vnode_id = pcam->vnode_id;
- ctrlcmd.queue_idx = pcam->server_queue_idx;
- ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
-
- /* send command to config thread in usersspace, and get return value */
- rc = msm_server_control(&g_server_dev, &ctrlcmd);
-
- return rc;
-}
-
-static int msm_server_proc_ctrl_cmd(struct msm_cam_v4l2_device *pcam,
- struct v4l2_control *ctrl, int is_set_cmd)
-{
- int rc = 0;
- struct msm_ctrl_cmd ctrlcmd, *tmp_cmd;
- uint8_t *ctrl_data = NULL;
- void __user *uptr_cmd;
- void __user *uptr_value;
- uint32_t cmd_len = sizeof(struct msm_ctrl_cmd);
- uint32_t value_len;
-
- tmp_cmd = (struct msm_ctrl_cmd *)ctrl->value;
- uptr_cmd = (void __user *)ctrl->value;
- uptr_value = (void __user *)tmp_cmd->value;
- value_len = tmp_cmd->length;
-
- D("%s: cmd type = %d, up1=0x%x, ulen1=%d, up2=0x%x, ulen2=%d\n",
- __func__, tmp_cmd->type, (uint32_t)uptr_cmd, cmd_len,
- (uint32_t)uptr_value, tmp_cmd->length);
-
- ctrl_data = kzalloc(value_len+cmd_len, GFP_KERNEL);
- if (ctrl_data == 0) {
- pr_err("%s could not allocate memory\n", __func__);
- rc = -ENOMEM;
- goto end;
- }
- tmp_cmd = (struct msm_ctrl_cmd *)ctrl_data;
- if (copy_from_user((void *)ctrl_data, uptr_cmd,
- cmd_len)) {
- pr_err("%s: copy_from_user failed.\n", __func__);
- rc = -EINVAL;
- goto end;
- }
- tmp_cmd->value = (void *)(ctrl_data+cmd_len);
- if (uptr_value && tmp_cmd->length > 0) {
- if (copy_from_user((void *)tmp_cmd->value, uptr_value,
- value_len)) {
- pr_err("%s: copy_from_user failed, size=%d\n",
- __func__, value_len);
- rc = -EINVAL;
- goto end;
- }
- } else
- tmp_cmd->value = NULL;
-
- ctrlcmd.type = MSM_V4L2_SET_CTRL_CMD;
- ctrlcmd.length = cmd_len + value_len;
- ctrlcmd.value = (void *)ctrl_data;
- if (tmp_cmd->timeout_ms > 0)
- ctrlcmd.timeout_ms = tmp_cmd->timeout_ms;
- else
- ctrlcmd.timeout_ms = 1000;
- ctrlcmd.vnode_id = pcam->vnode_id;
- ctrlcmd.queue_idx = pcam->server_queue_idx;
- ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
- /* send command to config thread in usersspace, and get return value */
- rc = msm_server_control(&g_server_dev, &ctrlcmd);
- D("%s: msm_server_control rc=%d\n", __func__, rc);
- if (rc == 0) {
- if (uptr_value && tmp_cmd->length > 0 &&
- copy_to_user((void __user *)uptr_value,
- (void *)(ctrl_data+cmd_len), tmp_cmd->length)) {
- pr_err("%s: copy_to_user failed, size=%d\n",
- __func__, tmp_cmd->length);
- rc = -EINVAL;
- goto end;
- }
- tmp_cmd->value = uptr_value;
- if (copy_to_user((void __user *)uptr_cmd,
- (void *)tmp_cmd, cmd_len)) {
- pr_err("%s: copy_to_user failed in cpy, size=%d\n",
- __func__, cmd_len);
- rc = -EINVAL;
- goto end;
- }
- }
-end:
- D("%s: END, type = %d, vaddr = 0x%x, vlen = %d, status = %d, rc = %d\n",
- __func__, tmp_cmd->type, (uint32_t)tmp_cmd->value,
- tmp_cmd->length, tmp_cmd->status, rc);
- kfree(ctrl_data);
- return rc;
-}
-
-static int msm_server_s_ctrl(struct msm_cam_v4l2_device *pcam,
- struct v4l2_control *ctrl)
-{
- int rc = 0;
- struct msm_ctrl_cmd ctrlcmd;
- uint8_t ctrl_data[max_control_command_size];
-
- WARN_ON(ctrl == NULL);
- if (ctrl == NULL) {
- pr_err("%s Invalid control\n", __func__);
- return -EINVAL;
- }
- if (ctrl->id == MSM_V4L2_PID_CTRL_CMD)
- return msm_server_proc_ctrl_cmd(pcam, ctrl, 1);
-
- memset(ctrl_data, 0, sizeof(ctrl_data));
-
- ctrlcmd.type = MSM_V4L2_SET_CTRL;
- ctrlcmd.length = sizeof(struct v4l2_control);
- ctrlcmd.value = (void *)ctrl_data;
- memcpy(ctrlcmd.value, ctrl, ctrlcmd.length);
- ctrlcmd.timeout_ms = 1000;
- ctrlcmd.vnode_id = pcam->vnode_id;
- ctrlcmd.queue_idx = pcam->server_queue_idx;
- ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
-
- /* send command to config thread in usersspace, and get return value */
- rc = msm_server_control(&g_server_dev, &ctrlcmd);
-
- return rc;
-}
-
-static int msm_server_g_ctrl(struct msm_cam_v4l2_device *pcam,
- struct v4l2_control *ctrl)
-{
- int rc = 0;
- struct msm_ctrl_cmd ctrlcmd;
- uint8_t ctrl_data[max_control_command_size];
-
- WARN_ON(ctrl == NULL);
- if (ctrl == NULL) {
- pr_err("%s Invalid control\n", __func__);
- return -EINVAL;
- }
- if (ctrl->id == MSM_V4L2_PID_CTRL_CMD)
- return msm_server_proc_ctrl_cmd(pcam, ctrl, 0);
-
- memset(ctrl_data, 0, sizeof(ctrl_data));
-
- ctrlcmd.type = MSM_V4L2_GET_CTRL;
- ctrlcmd.length = sizeof(struct v4l2_control);
- ctrlcmd.value = (void *)ctrl_data;
- memcpy(ctrlcmd.value, ctrl, ctrlcmd.length);
- ctrlcmd.timeout_ms = 1000;
- ctrlcmd.vnode_id = pcam->vnode_id;
- ctrlcmd.queue_idx = pcam->server_queue_idx;
- ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
-
- /* send command to config thread in usersspace, and get return value */
- rc = msm_server_control(&g_server_dev, &ctrlcmd);
-
- ctrl->value = ((struct v4l2_control *)ctrlcmd.value)->value;
-
- return rc;
-}
-
-static int msm_server_q_ctrl(struct msm_cam_v4l2_device *pcam,
- struct v4l2_queryctrl *queryctrl)
-{
- int rc = 0;
- struct msm_ctrl_cmd ctrlcmd;
- uint8_t ctrl_data[max_control_command_size];
-
- WARN_ON(queryctrl == NULL);
- memset(ctrl_data, 0, sizeof(ctrl_data));
-
- ctrlcmd.type = MSM_V4L2_QUERY_CTRL;
- ctrlcmd.length = sizeof(struct v4l2_queryctrl);
- ctrlcmd.value = (void *)ctrl_data;
- memcpy(ctrlcmd.value, queryctrl, ctrlcmd.length);
- ctrlcmd.timeout_ms = 1000;
- ctrlcmd.vnode_id = pcam->vnode_id;
- ctrlcmd.queue_idx = pcam->server_queue_idx;
- ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
-
- /* send command to config thread in userspace, and get return value */
- rc = msm_server_control(&g_server_dev, &ctrlcmd);
- D("%s: rc = %d\n", __func__, rc);
-
- if (rc >= 0)
- memcpy(queryctrl, ctrlcmd.value, sizeof(struct v4l2_queryctrl));
-
- return rc;
-}
-
-static int msm_server_get_fmt(struct msm_cam_v4l2_device *pcam,
- int idx, struct v4l2_format *pfmt)
-{
- struct v4l2_pix_format *pix = &pfmt->fmt.pix;
-
- pix->width = pcam->dev_inst[idx]->vid_fmt.fmt.pix.width;
- pix->height = pcam->dev_inst[idx]->vid_fmt.fmt.pix.height;
- pix->field = pcam->dev_inst[idx]->vid_fmt.fmt.pix.field;
- pix->pixelformat = pcam->dev_inst[idx]->vid_fmt.fmt.pix.pixelformat;
- pix->bytesperline = pcam->dev_inst[idx]->vid_fmt.fmt.pix.bytesperline;
- pix->colorspace = pcam->dev_inst[idx]->vid_fmt.fmt.pix.colorspace;
- if (pix->bytesperline < 0)
- return pix->bytesperline;
-
- pix->sizeimage = pix->height * pix->bytesperline;
-
- return 0;
-}
-
-static int msm_server_get_fmt_mplane(struct msm_cam_v4l2_device *pcam,
- int idx, struct v4l2_format *pfmt)
-{
- *pfmt = pcam->dev_inst[idx]->vid_fmt;
- return 0;
-}
-
-static int msm_server_try_fmt(struct msm_cam_v4l2_device *pcam,
- struct v4l2_format *pfmt)
-{
- int rc = 0;
- int i = 0;
- struct v4l2_pix_format *pix = &pfmt->fmt.pix;
-
- D("%s: 0x%x\n", __func__, pix->pixelformat);
- if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- pr_err("%s: pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE!\n",
- __func__);
- return -EINVAL;
- }
-
- /* check if the format is supported by this host-sensor combo */
- for (i = 0; i < pcam->num_fmts; i++) {
- D("%s: usr_fmts.fourcc: 0x%x\n", __func__,
- pcam->usr_fmts[i].fourcc);
- if (pcam->usr_fmts[i].fourcc == pix->pixelformat)
- break;
- }
-
- if (i == pcam->num_fmts) {
- pr_err("%s: Format %x not found\n", __func__, pix->pixelformat);
- return -EINVAL;
- }
- return rc;
-}
-
-static int msm_server_try_fmt_mplane(struct msm_cam_v4l2_device *pcam,
- struct v4l2_format *pfmt)
-{
- int rc = 0;
- int i = 0;
- struct v4l2_pix_format_mplane *pix_mp = &pfmt->fmt.pix_mp;
-
- D("%s: 0x%x\n", __func__, pix_mp->pixelformat);
- if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- pr_err("%s: Incorrect format type %d ",
- __func__, pfmt->type);
- return -EINVAL;
- }
-
- /* check if the format is supported by this host-sensor combo */
- for (i = 0; i < pcam->num_fmts; i++) {
- D("%s: usr_fmts.fourcc: 0x%x\n", __func__,
- pcam->usr_fmts[i].fourcc);
- if (pcam->usr_fmts[i].fourcc == pix_mp->pixelformat)
- break;
- }
-
- if (i == pcam->num_fmts) {
- pr_err("%s: Format %x not found\n",
- __func__, pix_mp->pixelformat);
- return -EINVAL;
- }
- return rc;
-}
-
-static int msm_camera_get_crop(struct msm_cam_v4l2_device *pcam,
- int idx, struct v4l2_crop *crop)
-{
- int rc = 0;
- struct msm_ctrl_cmd ctrlcmd;
-
- BUG_ON(crop == NULL);
-
- ctrlcmd.type = MSM_V4L2_GET_CROP;
- ctrlcmd.length = sizeof(struct v4l2_crop);
- ctrlcmd.value = (void *)crop;
- ctrlcmd.timeout_ms = 1000;
- ctrlcmd.vnode_id = pcam->vnode_id;
- ctrlcmd.queue_idx = pcam->server_queue_idx;
- ctrlcmd.stream_type = pcam->dev_inst[idx]->image_mode;
- ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
-
- /* send command to config thread in userspace, and get return value */
- rc = msm_server_control(&g_server_dev, &ctrlcmd);
- D("%s: rc = %d\n", __func__, rc);
-
- return rc;
-}
-
/*
*
* implementation of v4l2_ioctl_ops
@@ -1023,7 +267,7 @@
static int msm_camera_v4l2_dqbuf(struct file *f, void *pctx,
struct v4l2_buffer *pb)
{
- int rc = 0;
+ int rc = 0, i = 0;
/* get the camera device */
struct msm_cam_v4l2_dev_inst *pcam_inst;
pcam_inst = container_of(f->private_data,
@@ -1035,6 +279,27 @@
rc = vb2_dqbuf(&pcam_inst->vid_bufq, pb, f->f_flags & O_NONBLOCK);
D("%s, videobuf_dqbuf returns %d\n", __func__, rc);
+ if (pb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ /* Reject the buffer if planes array was not allocated */
+ if (pb->m.planes == NULL) {
+ pr_err("%s Planes array is null\n", __func__);
+ return -EINVAL;
+ }
+ for (i = 0; i < pcam_inst->plane_info.num_planes; i++) {
+ pb->m.planes[i].data_offset =
+ pcam_inst->buf_offset[pb->index][i].data_offset;
+ pb->m.planes[i].reserved[0] =
+ pcam_inst->buf_offset[pb->index][i].addr_offset;
+ D("%s stored offsets for plane %d as "
+ "addr offset %d, data offset %d\n",
+ __func__, i, pb->m.planes[i].reserved[0],
+ pb->m.planes[i].data_offset);
+ }
+ } else {
+ D("%s stored reserved info %d\n", __func__, pb->reserved);
+ pb->reserved = pcam_inst->buf_offset[pb->index][0].addr_offset;
+ }
+
return rc;
}
@@ -1094,7 +359,7 @@
not in use when we free the buffers */
mutex_lock(&pcam->vid_lock);
pcam_inst->streamon = 0;
- if (g_server_dev.use_count > 0)
+ if (msm_server_get_usecount() > 0)
rc = msm_server_streamoff(pcam, pcam_inst->my_index);
mutex_unlock(&pcam->vid_lock);
if (rc < 0)
@@ -1238,7 +503,7 @@
(void *)pfmt->fmt.pix.priv);
WARN_ON(pctx != f->private_data);
- pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+ pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
if (pmctl == NULL)
return -EINVAL;
@@ -1273,7 +538,7 @@
D("%s Inst %p\n", __func__, pcam_inst);
WARN_ON(pctx != f->private_data);
- pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+ pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
if (pmctl == NULL)
return -EINVAL;
@@ -1326,7 +591,7 @@
WARN_ON(pctx != f->private_data);
mutex_lock(&pcam->vid_lock);
- rc = msm_camera_get_crop(pcam, pcam_inst->my_index, crop);
+ rc = msm_server_get_crop(pcam, pcam_inst->my_index, crop);
mutex_unlock(&pcam->vid_lock);
return rc;
}
@@ -1419,55 +684,6 @@
return rc;
}
-static int msm_server_v4l2_subscribe_event(struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub)
-{
- int rc = 0;
-
- D("%s: fh = 0x%x, type = 0x%x", __func__, (u32)fh, sub->type);
- if (sub->type == V4L2_EVENT_ALL) {
- /*sub->type = MSM_ISP_EVENT_START;*/
- sub->type = V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_CTRL;
- D("sub->type start = 0x%x\n", sub->type);
- do {
- rc = v4l2_event_subscribe(fh, sub);
- if (rc < 0) {
- D("%s: failed for evtType = 0x%x, rc = %d\n",
- __func__, sub->type, rc);
- /* unsubscribe all events here and return */
- sub->type = V4L2_EVENT_ALL;
- v4l2_event_unsubscribe(fh, sub);
- return rc;
- } else
- D("%s: subscribed evtType = 0x%x, rc = %d\n",
- __func__, sub->type, rc);
- sub->type++;
- D("sub->type while = 0x%x\n", sub->type);
- } while (sub->type !=
- V4L2_EVENT_PRIVATE_START + MSM_SVR_RESP_MAX);
- } else {
- D("sub->type not V4L2_EVENT_ALL = 0x%x\n", sub->type);
- rc = v4l2_event_subscribe(fh, sub);
- if (rc < 0)
- D("%s: failed for evtType = 0x%x, rc = %d\n",
- __func__, sub->type, rc);
- }
-
- D("%s: rc = %d\n", __func__, rc);
- return rc;
-}
-
-static int msm_server_v4l2_unsubscribe_event(struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub)
-{
- int rc = 0;
-
- D("%s: fh = 0x%x\n", __func__, (u32)fh);
- rc = v4l2_event_unsubscribe(fh, sub);
- D("%s: rc = %d\n", __func__, rc);
- return rc;
-}
-
/* v4l2_ioctl_ops */
static const struct v4l2_ioctl_ops g_msm_ioctl_ops = {
.vidioc_querycap = msm_camera_v4l2_querycap,
@@ -1509,213 +725,10 @@
.vidioc_unsubscribe_event = msm_camera_v4l2_unsubscribe_event,
};
-/* open an active camera session to manage the streaming logic */
-static int msm_cam_server_open_session(struct msm_cam_server_dev *ps,
- struct msm_cam_v4l2_device *pcam)
-{
- int rc = 0;
- struct msm_cam_media_controller *pmctl;
-
- D("%s\n", __func__);
-
- if (!ps || !pcam) {
- pr_err("%s NULL pointer passed in!\n", __func__);
- return rc;
- }
-
- /* The number of camera instance should be controlled by the
- resource manager. Currently supporting one active instance
- until multiple instances are supported */
- if (atomic_read(&ps->number_pcam_active) > 0) {
- pr_err("%s Cannot have more than one active camera %d\n",
- __func__, atomic_read(&ps->number_pcam_active));
- return -EINVAL;
- }
- /* book keeping this camera session*/
- ps->pcam_active = pcam;
- atomic_inc(&ps->number_pcam_active);
-
- D("config pcam = 0x%p\n", ps->pcam_active);
-
- /* initialization the media controller module*/
- msm_mctl_init(pcam);
-
- /*for single VFE msms (8660, 8960v1), just populate the session
- with our VFE devices that registered*/
- pmctl = msm_camera_get_mctl(pcam->mctl_handle);
- pmctl->axi_sdev = ps->axi_device[0];
- pmctl->isp_sdev = ps->isp_subdev[0];
- return rc;
-}
-
-/* close an active camera session to server */
-static int msm_cam_server_close_session(struct msm_cam_server_dev *ps,
- struct msm_cam_v4l2_device *pcam)
-{
- int rc = 0;
- D("%s\n", __func__);
-
- if (!ps || !pcam) {
- D("%s NULL pointer passed in!\n", __func__);
- return rc;
- }
-
-
- atomic_dec(&ps->number_pcam_active);
- ps->pcam_active = NULL;
-
- msm_mctl_free(pcam);
- return rc;
-}
-
-int msm_server_open_client(int *p_qidx)
-{
- int rc = 0;
- int server_q_idx = 0;
- struct msm_cam_server_queue *queue = NULL;
-
- mutex_lock(&g_server_dev.server_lock);
- server_q_idx = msm_find_free_queue();
- if (server_q_idx < 0) {
- mutex_unlock(&g_server_dev.server_lock);
- return server_q_idx;
- }
-
- *p_qidx = server_q_idx;
- queue = &g_server_dev.server_queue[server_q_idx];
- queue->ctrl = NULL;
- queue->ctrl_data = kzalloc(sizeof(uint8_t) *
- max_control_command_size, GFP_KERNEL);
- msm_queue_init(&queue->ctrl_q, "control");
- msm_queue_init(&queue->eventData_q, "eventdata");
- queue->queue_active = 1;
- mutex_unlock(&g_server_dev.server_lock);
- return rc;
-}
-
-int msm_server_send_ctrl(struct msm_ctrl_cmd *out,
- int ctrl_id)
-{
- int rc = 0;
- void *value;
- struct msm_queue_cmd *rcmd;
- struct msm_queue_cmd *event_qcmd;
- struct msm_ctrl_cmd *ctrlcmd;
- struct msm_cam_server_dev *server_dev = &g_server_dev;
- struct msm_device_queue *queue =
- &server_dev->server_queue[out->queue_idx].ctrl_q;
-
- struct v4l2_event v4l2_evt;
- struct msm_isp_event_ctrl *isp_event;
- isp_event = kzalloc(sizeof(struct msm_isp_event_ctrl), GFP_KERNEL);
- if (!isp_event) {
- pr_err("%s Insufficient memory. return", __func__);
- return -ENOMEM;
- }
- event_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
- if (!event_qcmd) {
- pr_err("%s Insufficient memory. return", __func__);
- kfree(isp_event);
- return -ENOMEM;
- }
-
- D("%s\n", __func__);
- mutex_lock(&server_dev->server_queue_lock);
- if (++server_dev->server_evt_id == 0)
- server_dev->server_evt_id++;
-
- D("%s qid %d evtid %d\n", __func__, out->queue_idx,
- server_dev->server_evt_id);
- server_dev->server_queue[out->queue_idx].evt_id =
- server_dev->server_evt_id;
- v4l2_evt.type = V4L2_EVENT_PRIVATE_START + ctrl_id;
- v4l2_evt.u.data[0] = out->queue_idx;
- /* setup event object to transfer the command; */
- isp_event->resptype = MSM_CAM_RESP_V4L2;
- isp_event->isp_data.ctrl = *out;
- isp_event->isp_data.ctrl.evt_id = server_dev->server_evt_id;
-
- atomic_set(&event_qcmd->on_heap, 1);
- event_qcmd->command = isp_event;
-
- msm_enqueue(&server_dev->server_queue[out->queue_idx].eventData_q,
- &event_qcmd->list_eventdata);
-
- /* now send command to config thread in userspace,
- * and wait for results */
- v4l2_event_queue(server_dev->server_command_queue.pvdev,
- &v4l2_evt);
- D("%s v4l2_event_queue: type = 0x%x\n", __func__, v4l2_evt.type);
- mutex_unlock(&server_dev->server_queue_lock);
-
- /* wait for config return status */
- D("Waiting for config status\n");
- rc = wait_event_interruptible_timeout(queue->wait,
- !list_empty_careful(&queue->list),
- msecs_to_jiffies(out->timeout_ms));
- D("Waiting is over for config status\n");
- if (list_empty_careful(&queue->list)) {
- if (!rc)
- rc = -ETIMEDOUT;
- if (rc < 0) {
- kfree(isp_event);
- pr_err("%s: wait_event error %d\n", __func__, rc);
- return rc;
- }
- }
-
- rcmd = msm_dequeue(queue, list_control);
- BUG_ON(!rcmd);
- D("%s Finished servicing ioctl\n", __func__);
-
- ctrlcmd = (struct msm_ctrl_cmd *)(rcmd->command);
- value = out->value;
- if (ctrlcmd->length > 0)
- memcpy(value, ctrlcmd->value, ctrlcmd->length);
-
- memcpy(out, ctrlcmd, sizeof(struct msm_ctrl_cmd));
- out->value = value;
-
- kfree(ctrlcmd);
- server_dev->server_queue[out->queue_idx].ctrl = NULL;
-
- free_qcmd(rcmd);
- kfree(isp_event);
- D("%s: rc %d\n", __func__, rc);
- /* rc is the time elapsed. */
- if (rc >= 0) {
- /* TODO: Refactor msm_ctrl_cmd::status field */
- if (out->status == 0)
- rc = -1;
- else if (out->status == 1 || out->status == 4)
- rc = 0;
- else
- rc = -EINVAL;
- }
- return rc;
-}
-
-int msm_server_close_client(int idx)
-{
- int rc = 0;
- struct msm_cam_server_queue *queue = NULL;
- mutex_lock(&g_server_dev.server_lock);
- queue = &g_server_dev.server_queue[idx];
- queue->queue_active = 0;
- kfree(queue->ctrl);
- queue->ctrl = NULL;
- kfree(queue->ctrl_data);
- queue->ctrl_data = NULL;
- msm_queue_drain(&queue->ctrl_q, list_control);
- msm_drain_eventq(&queue->eventData_q);
- mutex_unlock(&g_server_dev.server_lock);
- return rc;
-}
/* v4l2_file_operations */
static int msm_open(struct file *f)
{
- int i;
- int rc = -EINVAL;
+ int i, rc = -EINVAL;
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
int ion_client_created = 0;
#endif
@@ -1725,7 +738,6 @@
struct msm_cam_v4l2_device *pcam = video_drvdata(f);
struct msm_cam_v4l2_dev_inst *pcam_inst;
struct msm_cam_media_controller *pmctl = NULL;
- struct msm_cam_server_queue *queue = NULL;
D("%s\n", __func__);
@@ -1733,7 +745,7 @@
pr_err("%s NULL pointer passed in!\n", __func__);
return rc;
}
- if (!g_server_dev.use_count) {
+ if (!msm_server_get_usecount()) {
pr_err("%s: error, daemon not yet started.", __func__);
return -EINVAL;
}
@@ -1768,29 +780,12 @@
pcam->use_count++;
D("%s use_count %d\n", __func__, pcam->use_count);
if (pcam->use_count == 1) {
- struct msm_cam_server_queue *queue;
- int ges_evt = MSM_V4L2_GES_CAM_OPEN;
- pcam->server_queue_idx = server_q_idx;
- queue = &g_server_dev.server_queue[server_q_idx];
- queue->ctrl = NULL;
- queue->ctrl_data = kzalloc(sizeof(uint8_t) *
- max_control_command_size, GFP_KERNEL);
- msm_queue_init(&queue->ctrl_q, "control");
- msm_queue_init(&queue->eventData_q, "eventdata");
- queue->queue_active = 1;
-
- pr_err("%s send gesture evt\n", __func__);
- msm_cam_server_subdev_notify(g_server_dev.gesture_device,
- NOTIFY_GESTURE_CAM_EVT, &ges_evt);
-
- rc = msm_cam_server_open_session(&g_server_dev, pcam);
+ rc = msm_server_begin_session(pcam, server_q_idx);
if (rc < 0) {
- pr_err("%s: cam_server_open_session failed %d\n",
- __func__, rc);
- goto msm_cam_server_open_session_failed;
+ pr_err("%s error starting server session ", __func__);
+ goto msm_cam_server_begin_session_failed;
}
-
- pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+ pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
pmctl->client = msm_ion_client_create(-1, "camera");
@@ -1832,14 +827,14 @@
if (pcam->use_count == 1) {
rc = msm_send_open_server(pcam);
- if (rc < 0) {
+ if (rc < 0 && rc != -ERESTARTSYS) {
pr_err("%s: msm_send_open_server failed %d\n",
__func__, rc);
goto msm_send_open_server_failed;
}
}
mutex_unlock(&pcam->vid_lock);
- D("%s: end", __func__);
+ D("%s: end\n", __func__);
return rc;
msm_send_open_server_failed:
@@ -1853,25 +848,16 @@
if (pcam->use_count == 1) {
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
if (ion_client_created) {
- pr_err("%s: destroy ion client", __func__);
+ D("%s: destroy ion client", __func__);
kref_put(&pmctl->refcount, msm_release_ion_client);
}
#endif
- if (msm_cam_server_close_session(&g_server_dev, pcam) < 0)
- pr_err("%s: msm_cam_server_close_session failed\n",
+ if (msm_server_end_session(pcam) < 0)
+ pr_err("%s: msm_server_end_session failed\n",
__func__);
}
-msm_cam_server_open_session_failed:
+msm_cam_server_begin_session_failed:
if (pcam->use_count == 1) {
- if (queue != NULL) {
- queue->queue_active = 0;
- msm_drain_eventq(&queue->eventData_q);
- kfree(queue->ctrl_data);
- queue->ctrl_data = NULL;
- msm_queue_drain(&queue->ctrl_q, list_control);
- msm_drain_eventq(&queue->eventData_q);
- queue = NULL;
- }
pcam->dev_inst[i] = NULL;
pcam->use_count = 0;
}
@@ -1881,73 +867,6 @@
return rc;
}
-int msm_cam_server_close_mctl_session(struct msm_cam_v4l2_device *pcam)
-{
- int rc = 0;
- struct msm_cam_media_controller *pmctl = NULL;
-
- pmctl = msm_camera_get_mctl(pcam->mctl_handle);
- if (!pmctl) {
- D("%s: invalid handle\n", __func__);
- return -ENODEV;
- }
-
- if (pmctl->mctl_release) {
- rc = pmctl->mctl_release(pmctl);
- if (rc < 0)
- pr_err("mctl_release fails %d\n", rc);
- }
-
-#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
- kref_put(&pmctl->refcount, msm_release_ion_client);
-#endif
-
- rc = msm_cam_server_close_session(&g_server_dev, pcam);
- if (rc < 0)
- pr_err("msm_cam_server_close_session fails %d\n", rc);
-
- return rc;
-}
-
-int msm_cam_server_open_mctl_session(struct msm_cam_v4l2_device *pcam,
- int *p_active)
-{
- int rc = 0;
- struct msm_cam_media_controller *pmctl = NULL;
- D("%s: %p", __func__, g_server_dev.pcam_active);
- *p_active = 0;
- if (g_server_dev.pcam_active) {
- D("%s: Active camera present return", __func__);
- return 0;
- }
- rc = msm_cam_server_open_session(&g_server_dev, pcam);
- if (rc < 0) {
- pr_err("%s: cam_server_open_session failed %d\n",
- __func__, rc);
- return rc;
- }
-
- pmctl = msm_camera_get_mctl(pcam->mctl_handle);
- /* Should be set to sensor ops if any but right now its OK!! */
- if (!pmctl->mctl_open) {
- D("%s: media contoller is not inited\n",
- __func__);
- rc = -ENODEV;
- return rc;
- }
-
- D("%s: call mctl_open\n", __func__);
- rc = pmctl->mctl_open(pmctl, MSM_APPS_ID_V4L2);
-
- if (rc < 0) {
- pr_err("%s: HW open failed rc = 0x%x\n", __func__, rc);
- return rc;
- }
- pmctl->pcam_ptr = pcam;
- *p_active = 1;
- return rc;
-}
-
static int msm_addr_remap(struct msm_cam_v4l2_dev_inst *pcam_inst,
struct vm_area_struct *vma)
{
@@ -1957,7 +876,7 @@
int rc = 0;
struct msm_cam_media_controller *mctl;
- mctl = msm_camera_get_mctl(pcam_inst->pcam->mctl_handle);
+ mctl = msm_cam_server_get_mctl(pcam_inst->pcam->mctl_handle);
if (!mctl) {
pr_err("%s: invalid mctl pointer", __func__);
return -EFAULT;
@@ -2015,8 +934,8 @@
void msm_release_ion_client(struct kref *ref)
{
struct msm_cam_media_controller *mctl = container_of(ref,
- struct msm_cam_media_controller, refcount);
- pr_err("%s Calling ion_client_destroy ", __func__);
+ struct msm_cam_media_controller, refcount);
+ pr_err("%s Calling ion_client_destroy\n", __func__);
ion_client_destroy(mctl->client);
}
@@ -2025,7 +944,6 @@
int rc = 0;
struct msm_cam_v4l2_device *pcam;
struct msm_cam_v4l2_dev_inst *pcam_inst;
- struct msm_cam_server_queue *queue;
struct msm_cam_media_controller *pmctl;
pcam_inst = container_of(f->private_data,
struct msm_cam_v4l2_dev_inst, eventHandle);
@@ -2035,7 +953,7 @@
return -EINVAL;
}
- pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+ pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
if (!pmctl) {
pr_err("%s NULL mctl pointer\n", __func__);
return -EINVAL;
@@ -2071,37 +989,24 @@
f->private_data = NULL;
if (pcam->use_count == 0) {
- int ges_evt = MSM_V4L2_GES_CAM_CLOSE;
- if (g_server_dev.use_count > 0) {
+ if (msm_server_get_usecount() > 0) {
rc = msm_send_close_server(pcam);
if (rc < 0)
pr_err("msm_send_close_server failed %d\n", rc);
}
+
if (pmctl->mctl_release) {
rc = pmctl->mctl_release(pmctl);
if (rc < 0)
pr_err("mctl_release fails %d\n", rc);
}
+
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
kref_put(&pmctl->refcount, msm_release_ion_client);
#endif
- queue = &g_server_dev.server_queue[pcam->server_queue_idx];
- queue->queue_active = 0;
- kfree(queue->ctrl);
- queue->ctrl = NULL;
- kfree(queue->ctrl_data);
- queue->ctrl_data = NULL;
- msm_queue_drain(&queue->ctrl_q, list_control);
- msm_drain_eventq(&queue->eventData_q);
- rc = msm_cam_server_close_session(&g_server_dev, pcam);
+ rc = msm_server_end_session(pcam);
if (rc < 0)
- pr_err("msm_cam_server_close_session fails %d\n", rc);
-
- if (g_server_dev.use_count == 0)
- mutex_unlock(&g_server_dev.server_lock);
-
- msm_cam_server_subdev_notify(g_server_dev.gesture_device,
- NOTIFY_GESTURE_CAM_EVT, &ges_evt);
+ pr_err("msm_server_end_session fails %d\n", rc);
}
mutex_unlock(&pcam->vid_lock);
return rc;
@@ -2136,280 +1041,8 @@
return rc;
}
-static unsigned int msm_poll_server(struct file *fp,
- struct poll_table_struct *wait)
-{
- int rc = 0;
-
- D("%s\n", __func__);
- poll_wait(fp,
- &g_server_dev.server_command_queue.eventHandle.events->wait,
- wait);
- if (v4l2_event_pending(&g_server_dev.server_command_queue.eventHandle))
- rc |= POLLPRI;
-
- return rc;
-}
-static long msm_ioctl_server(struct file *file, void *fh,
- bool valid_prio, int cmd, void *arg)
-{
- int rc = -EINVAL;
- struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;
- struct msm_camera_info temp_cam_info;
- struct msm_cam_config_dev_info temp_config_info;
- struct msm_mctl_node_info temp_mctl_info;
- int i;
-
- D("%s: cmd %d\n", __func__, _IOC_NR(cmd));
-
- switch (cmd) {
- case MSM_CAM_V4L2_IOCTL_GET_CAMERA_INFO:
- if (copy_from_user(&temp_cam_info,
- (void __user *)ioctl_ptr->ioctl_ptr,
- sizeof(struct msm_camera_info))) {
- rc = -EINVAL;
- return rc;
- }
- for (i = 0; i < g_server_dev.camera_info.num_cameras; i++) {
- if (copy_to_user((void __user *)
- temp_cam_info.video_dev_name[i],
- g_server_dev.camera_info.video_dev_name[i],
- strnlen(
- g_server_dev.camera_info.video_dev_name[i],
- MAX_DEV_NAME_LEN))) {
- rc = -EINVAL;
- return rc;
- }
- temp_cam_info.has_3d_support[i] =
- g_server_dev.camera_info.has_3d_support[i];
- temp_cam_info.is_internal_cam[i] =
- g_server_dev.camera_info.is_internal_cam[i];
- temp_cam_info.s_mount_angle[i] =
- g_server_dev.camera_info.s_mount_angle[i];
- temp_cam_info.sensor_type[i] =
- g_server_dev.camera_info.sensor_type[i];
-
- }
- temp_cam_info.num_cameras =
- g_server_dev.camera_info.num_cameras;
- if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
- &temp_cam_info,
- sizeof(struct msm_camera_info))) {
- rc = -EINVAL;
- return rc;
- }
- rc = 0;
- break;
-
- case MSM_CAM_V4L2_IOCTL_GET_CONFIG_INFO:
- if (copy_from_user(&temp_config_info,
- (void __user *)ioctl_ptr->ioctl_ptr,
- sizeof(struct msm_cam_config_dev_info))) {
-
- rc = -EINVAL;
- return rc;
- }
- for (i = 0;
- i < g_server_dev.config_info.num_config_nodes; i++) {
- if (copy_to_user(
- (void __user *)temp_config_info.config_dev_name[i],
- g_server_dev.config_info.config_dev_name[i],
- strlen(g_server_dev.config_info.config_dev_name[i]))) {
- rc = -EINVAL;
- return rc;
- }
- }
- temp_config_info.num_config_nodes =
- g_server_dev.config_info.num_config_nodes;
- if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
- &temp_config_info,
- sizeof(struct msm_cam_config_dev_info))) {
- rc = -EINVAL;
- return rc;
- }
- rc = 0;
- break;
- case MSM_CAM_V4L2_IOCTL_GET_MCTL_INFO:
- if (copy_from_user(&temp_mctl_info,
- (void __user *)ioctl_ptr->ioctl_ptr,
- sizeof(struct msm_mctl_node_info))) {
- rc = -EINVAL;
- return rc;
- }
- for (i = 0; i < g_server_dev.mctl_node_info.num_mctl_nodes;
- i++) {
- if (copy_to_user((void __user *)
- temp_mctl_info.mctl_node_name[i],
- g_server_dev.mctl_node_info.mctl_node_name[i], strnlen(
- g_server_dev.mctl_node_info.mctl_node_name[i],
- MAX_DEV_NAME_LEN))) {
- rc = -EINVAL;
- return rc;
- }
- }
- temp_mctl_info.num_mctl_nodes =
- g_server_dev.mctl_node_info.num_mctl_nodes;
- if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
- &temp_mctl_info,
- sizeof(struct msm_mctl_node_info))) {
- rc = -EINVAL;
- return rc;
- }
- rc = 0;
- break;
-
- case MSM_CAM_V4L2_IOCTL_CTRL_CMD_DONE:
- D("%s: MSM_CAM_IOCTL_CTRL_CMD_DONE\n", __func__);
- rc = msm_ctrl_cmd_done(arg);
- break;
-
- case MSM_CAM_V4L2_IOCTL_GET_EVENT_PAYLOAD: {
- struct msm_queue_cmd *event_cmd;
- struct msm_isp_event_ctrl u_isp_event;
- struct msm_isp_event_ctrl *k_isp_event;
- struct msm_device_queue *queue;
- void __user *u_ctrl_value = NULL;
- if (copy_from_user(&u_isp_event,
- (void __user *)ioctl_ptr->ioctl_ptr,
- sizeof(struct msm_isp_event_ctrl))) {
- rc = -EINVAL;
- return rc;
- }
- queue = &g_server_dev.server_queue
- [u_isp_event.isp_data.ctrl.queue_idx].eventData_q;
- event_cmd = msm_dequeue(queue, list_eventdata);
- if (!event_cmd) {
- pr_err("%s: No event payload\n", __func__);
- rc = -EINVAL;
- return rc;
- }
- k_isp_event = (struct msm_isp_event_ctrl *)
- event_cmd->command;
- free_qcmd(event_cmd);
-
- /* Save the pointer of the user allocated command buffer*/
- u_ctrl_value = u_isp_event.isp_data.ctrl.value;
-
- /* Copy the event structure into user struct*/
- u_isp_event = *k_isp_event;
-
- /* Restore the saved pointer of the user
- * allocated command buffer. */
- u_isp_event.isp_data.ctrl.value = u_ctrl_value;
-
- /* Copy the ctrl cmd, if present*/
- if (k_isp_event->isp_data.ctrl.length > 0) {
- void *k_ctrl_value =
- k_isp_event->isp_data.ctrl.value;
- if (copy_to_user(u_ctrl_value, k_ctrl_value,
- k_isp_event->isp_data.ctrl.length)) {
- rc = -EINVAL;
- break;
- }
- }
- if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
- &u_isp_event,
- sizeof(struct msm_isp_event_ctrl))) {
- rc = -EINVAL;
- return rc;
- }
- rc = 0;
- break;
- }
-
- case MSM_CAM_IOCTL_SEND_EVENT:
- rc = msm_server_send_v4l2_evt(arg);
- break;
-
- default:
- pr_err("%s: Invalid IOCTL = %d", __func__, cmd);
- break;
- }
- return rc;
-}
-
-static int msm_open_server(struct file *fp)
-{
- int rc = 0;
- D("%s: open %s\n", __func__, fp->f_path.dentry->d_name.name);
- mutex_lock(&g_server_dev.server_lock);
- g_server_dev.use_count++;
- if (g_server_dev.use_count == 1)
- fp->private_data =
- &g_server_dev.server_command_queue.eventHandle;
- mutex_unlock(&g_server_dev.server_lock);
- return rc;
-}
-
-static unsigned int msm_poll_config(struct file *fp,
- struct poll_table_struct *wait)
-{
- int rc = 0;
- struct msm_cam_config_dev *config = fp->private_data;
- if (config == NULL)
- return -EINVAL;
-
- D("%s\n", __func__);
-
- poll_wait(fp,
- &config->config_stat_event_queue.eventHandle.events->wait, wait);
- if (v4l2_event_pending(&config->config_stat_event_queue.eventHandle))
- rc |= POLLPRI;
- return rc;
-}
-
-static int msm_close_server(struct file *fp)
-{
- struct v4l2_event_subscription sub;
- D("%s\n", __func__);
- mutex_lock(&g_server_dev.server_lock);
- if (g_server_dev.use_count > 0)
- g_server_dev.use_count--;
- mutex_unlock(&g_server_dev.server_lock);
- if (g_server_dev.use_count == 0) {
- if (g_server_dev.pcam_active) {
- struct v4l2_event v4l2_ev;
- mutex_lock(&g_server_dev.server_lock);
-
- v4l2_ev.type = V4L2_EVENT_PRIVATE_START
- + MSM_CAM_APP_NOTIFY_ERROR_EVENT;
- ktime_get_ts(&v4l2_ev.timestamp);
- v4l2_event_queue(
- g_server_dev.pcam_active->pvdev, &v4l2_ev);
- }
- sub.type = V4L2_EVENT_ALL;
- msm_server_v4l2_unsubscribe_event(
- &g_server_dev.server_command_queue.eventHandle, &sub);
- }
- return 0;
-}
-
-static long msm_server_send_v4l2_evt(void *evt)
-{
- struct v4l2_event *v4l2_ev = (struct v4l2_event *)evt;
- int rc = 0;
-
- if (NULL == evt) {
- pr_err("%s: evt is NULL\n", __func__);
- return -EINVAL;
- }
-
- D("%s: evt type 0x%x\n", __func__, v4l2_ev->type);
- if ((v4l2_ev->type >= MSM_GES_APP_EVT_MIN) &&
- (v4l2_ev->type < MSM_GES_APP_EVT_MAX)) {
- msm_cam_server_subdev_notify(g_server_dev.gesture_device,
- NOTIFY_GESTURE_EVT, v4l2_ev);
- } else {
- pr_err("%s: Invalid evt %d\n", __func__, v4l2_ev->type);
- rc = -EINVAL;
- }
- D("%s: end\n", __func__);
-
- return rc;
-}
-
-static long msm_v4l2_evt_notify(struct msm_cam_media_controller *mctl,
- unsigned int cmd, unsigned long evt)
+long msm_v4l2_evt_notify(struct msm_cam_media_controller *mctl,
+ unsigned int cmd, unsigned long evt)
{
struct v4l2_event v4l2_ev;
struct msm_cam_v4l2_device *pcam = NULL;
@@ -2431,280 +1064,6 @@
return 0;
}
-static long msm_ioctl_config(struct file *fp, unsigned int cmd,
- unsigned long arg)
-{
-
- int rc = 0;
- struct v4l2_event ev;
- struct msm_cam_config_dev *config_cam = fp->private_data;
- struct v4l2_event_subscription temp_sub;
-
- D("%s: cmd %d\n", __func__, _IOC_NR(cmd));
-
- switch (cmd) {
- /* memory management shall be handeld here*/
- case MSM_CAM_IOCTL_REGISTER_PMEM:
- return msm_register_pmem(
- &config_cam->p_mctl->stats_info.pmem_stats_list,
- (void __user *)arg, config_cam->p_mctl->client);
- break;
-
- case MSM_CAM_IOCTL_UNREGISTER_PMEM:
- return msm_pmem_table_del(
- &config_cam->p_mctl->stats_info.pmem_stats_list,
- (void __user *)arg, config_cam->p_mctl->client);
- break;
-
- case VIDIOC_SUBSCRIBE_EVENT:
- if (copy_from_user(&temp_sub,
- (void __user *)arg,
- sizeof(struct v4l2_event_subscription))) {
- rc = -EINVAL;
- return rc;
- }
- rc = msm_server_v4l2_subscribe_event
- (&config_cam->config_stat_event_queue.eventHandle,
- &temp_sub);
- if (rc < 0) {
- pr_err("%s: cam_v4l2_subscribe_event failed rc=%d\n",
- __func__, rc);
- return rc;
- }
- break;
-
- case VIDIOC_UNSUBSCRIBE_EVENT:
- if (copy_from_user(&temp_sub, (void __user *)arg,
- sizeof(struct v4l2_event_subscription))) {
- rc = -EINVAL;
- return rc;
- }
- rc = msm_server_v4l2_unsubscribe_event
- (&config_cam->config_stat_event_queue.eventHandle,
- &temp_sub);
- if (rc < 0) {
- pr_err("%s: server_unsubscribe_event failed rc=%d\n",
- __func__, rc);
- return rc;
- }
- break;
-
- case VIDIOC_DQEVENT: {
- void __user *u_msg_value = NULL, *user_ptr = NULL;
- struct msm_isp_event_ctrl u_isp_event;
- struct msm_isp_event_ctrl *k_isp_event;
-
- /* First, copy the v4l2 event structure from userspace */
- D("%s: VIDIOC_DQEVENT\n", __func__);
- if (copy_from_user(&ev, (void __user *)arg,
- sizeof(struct v4l2_event)))
- break;
- /* Next, get the pointer to event_ctrl structure
- * embedded inside the v4l2_event.u.data array. */
- user_ptr = (void __user *)(*((uint32_t *)ev.u.data));
-
- /* Next, copy the userspace event ctrl structure */
- if (copy_from_user((void *)&u_isp_event, user_ptr,
- sizeof(struct msm_isp_event_ctrl))) {
- break;
- }
- /* Save the pointer of the user allocated command buffer*/
- u_msg_value = u_isp_event.isp_data.isp_msg.data;
-
- /* Dequeue the event queued into the v4l2 queue*/
- rc = v4l2_event_dequeue(
- &config_cam->config_stat_event_queue.eventHandle,
- &ev, fp->f_flags & O_NONBLOCK);
- if (rc < 0) {
- pr_err("no pending events?");
- break;
- }
- /* Use k_isp_event to point to the event_ctrl structure
- * embedded inside v4l2_event.u.data */
- k_isp_event = (struct msm_isp_event_ctrl *)
- (*((uint32_t *)ev.u.data));
- /* Copy the event structure into user struct. */
- u_isp_event = *k_isp_event;
- if (ev.type != (V4L2_EVENT_PRIVATE_START +
- MSM_CAM_RESP_DIV_FRAME_EVT_MSG) &&
- ev.type != (V4L2_EVENT_PRIVATE_START +
- MSM_CAM_RESP_MCTL_PP_EVENT)) {
-
- /* Restore the saved pointer of the
- * user allocated command buffer. */
- u_isp_event.isp_data.isp_msg.data = u_msg_value;
-
- if (ev.type == (V4L2_EVENT_PRIVATE_START +
- MSM_CAM_RESP_STAT_EVT_MSG)) {
- if (k_isp_event->isp_data.isp_msg.len > 0) {
- void *k_msg_value =
- k_isp_event->isp_data.isp_msg.data;
- if (copy_to_user(u_msg_value,
- k_msg_value,
- k_isp_event->isp_data.isp_msg.len)) {
- rc = -EINVAL;
- break;
- }
- kfree(k_msg_value);
- }
- }
- }
- /* Copy the event ctrl structure back
- * into user's structure. */
- if (copy_to_user(user_ptr,
- (void *)&u_isp_event, sizeof(
- struct msm_isp_event_ctrl))) {
- rc = -EINVAL;
- break;
- }
- kfree(k_isp_event);
-
- /* Copy the v4l2_event structure back to the user*/
- if (copy_to_user((void __user *)arg, &ev,
- sizeof(struct v4l2_event))) {
- rc = -EINVAL;
- break;
- }
- }
-
- break;
-
- case MSM_CAM_IOCTL_V4L2_EVT_NOTIFY:
- rc = msm_v4l2_evt_notify(config_cam->p_mctl, cmd, arg);
- break;
-
- case MSM_CAM_IOCTL_SET_MEM_MAP_INFO:
- if (copy_from_user(&config_cam->mem_map, (void __user *)arg,
- sizeof(struct msm_mem_map_info)))
- rc = -EINVAL;
- break;
-
- default:{
- /* For the rest of config command, forward to media controller*/
- struct msm_cam_media_controller *p_mctl = config_cam->p_mctl;
- if (p_mctl && p_mctl->mctl_cmd) {
- rc = config_cam->p_mctl->mctl_cmd(p_mctl, cmd, arg);
- } else {
- rc = -EINVAL;
- pr_err("%s: media controller is null\n", __func__);
- }
-
- break;
- } /* end of default*/
- } /* end of switch*/
- return rc;
-}
-
-static int msm_mmap_config(struct file *fp, struct vm_area_struct *vma)
-{
- struct msm_cam_config_dev *config_cam = fp->private_data;
- int rc = 0;
- int phyaddr;
- int retval;
- unsigned long size;
-
- D("%s: phy_addr=0x%x", __func__, config_cam->mem_map.cookie);
- phyaddr = (int)config_cam->mem_map.cookie;
- if (!phyaddr) {
- pr_err("%s: no physical memory to map", __func__);
- return -EFAULT;
- }
- memset(&config_cam->mem_map, 0,
- sizeof(struct msm_mem_map_info));
- size = vma->vm_end - vma->vm_start;
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- retval = remap_pfn_range(vma, vma->vm_start,
- phyaddr >> PAGE_SHIFT,
- size, vma->vm_page_prot);
- if (retval) {
- pr_err("%s: remap failed, rc = %d",
- __func__, retval);
- rc = -ENOMEM;
- goto end;
- }
- D("%s: phy_addr=0x%x: %08lx-%08lx, pgoff %08lx\n",
- __func__, (uint32_t)phyaddr,
- vma->vm_start, vma->vm_end, vma->vm_pgoff);
-end:
- return rc;
-}
-
-static int msm_open_config(struct inode *inode, struct file *fp)
-{
- int rc;
- struct msm_cam_config_dev *config_cam = container_of(inode->i_cdev,
- struct msm_cam_config_dev, config_cdev);
-
- D("%s: open %s\n", __func__, fp->f_path.dentry->d_name.name);
-
- rc = nonseekable_open(inode, fp);
- if (rc < 0) {
- pr_err("%s: nonseekable_open error %d\n", __func__, rc);
- return rc;
- }
- config_cam->use_count++;
-
- /*config_cam->isp_subdev = g_server_dev.pcam_active->mctl.isp_sdev;*/
- /* assume there is only one active camera possible*/
- config_cam->p_mctl =
- msm_camera_get_mctl(g_server_dev.pcam_active->mctl_handle);
-
- INIT_HLIST_HEAD(&config_cam->p_mctl->stats_info.pmem_stats_list);
- spin_lock_init(&config_cam->p_mctl->stats_info.pmem_stats_spinlock);
-
- config_cam->p_mctl->config_device = config_cam;
-#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
- kref_get(&config_cam->p_mctl->refcount);
-#endif
- fp->private_data = config_cam;
- return rc;
-}
-
-static int msm_close_config(struct inode *node, struct file *f)
-{
-#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
- struct msm_cam_config_dev *config_cam = f->private_data;
- D("%s Decrementing ref count of config node ", __func__);
- kref_put(&config_cam->p_mctl->refcount, msm_release_ion_client);
-#endif
- return 0;
-}
-
-static struct v4l2_file_operations g_msm_fops = {
- .owner = THIS_MODULE,
- .open = msm_open,
- .poll = msm_poll,
- .mmap = msm_mmap,
- .release = msm_close,
- .ioctl = video_ioctl2,
-};
-
-/* Init a config node for ISP control,
- * which will create a config device (/dev/config0/ and plug in
- * ISP's operation "v4l2_ioctl_ops*"
- */
-static const struct v4l2_file_operations msm_fops_server = {
- .owner = THIS_MODULE,
- .open = msm_open_server,
- .poll = msm_poll_server,
- .unlocked_ioctl = video_ioctl2,
- .release = msm_close_server,
-};
-
-static const struct v4l2_ioctl_ops msm_ioctl_ops_server = {
- .vidioc_subscribe_event = msm_server_v4l2_subscribe_event,
- .vidioc_default = msm_ioctl_server,
-};
-
-static const struct file_operations msm_fops_config = {
- .owner = THIS_MODULE,
- .open = msm_open_config,
- .poll = msm_poll_config,
- .unlocked_ioctl = msm_ioctl_config,
- .mmap = msm_mmap_config,
- .release = msm_close_config,
-};
-
int msm_setup_v4l2_event_queue(struct v4l2_fh *eventHandle,
struct video_device *pvdev)
{
@@ -2732,319 +1091,14 @@
return rc;
}
-static int msm_setup_config_dev(int node, char *device_name)
-{
- int rc = -ENODEV;
- struct device *device_config;
- int dev_num = node;
- dev_t devno;
- struct msm_cam_config_dev *config_cam;
-
- config_cam = kzalloc(sizeof(*config_cam), GFP_KERNEL);
- if (!config_cam) {
- pr_err("%s: could not allocate memory for msm_cam_config_device\n",
- __func__);
- return -ENOMEM;
- }
-
- D("%s\n", __func__);
-
- devno = MKDEV(MAJOR(msm_devno), dev_num+1);
- device_config = device_create(msm_class, NULL, devno, NULL, "%s%d",
- device_name, dev_num);
-
- if (IS_ERR(device_config)) {
- rc = PTR_ERR(device_config);
- pr_err("%s: error creating device: %d\n", __func__, rc);
- goto config_setup_fail;
- }
-
- cdev_init(&config_cam->config_cdev, &msm_fops_config);
- config_cam->config_cdev.owner = THIS_MODULE;
-
- rc = cdev_add(&config_cam->config_cdev, devno, 1);
- if (rc < 0) {
- pr_err("%s: error adding cdev: %d\n", __func__, rc);
- device_destroy(msm_class, devno);
- goto config_setup_fail;
- }
-
- g_server_dev.config_info.config_dev_name[dev_num] =
- dev_name(device_config);
- D("%s Connected config device %s\n", __func__,
- g_server_dev.config_info.config_dev_name[dev_num]);
- g_server_dev.config_info.config_dev_id[dev_num] = dev_num;
-
- config_cam->config_stat_event_queue.pvdev = video_device_alloc();
- if (config_cam->config_stat_event_queue.pvdev == NULL) {
- pr_err("%s: video_device_alloc failed\n", __func__);
- goto config_setup_fail;
- }
-
- rc = msm_setup_v4l2_event_queue(
- &config_cam->config_stat_event_queue.eventHandle,
- config_cam->config_stat_event_queue.pvdev);
- if (rc < 0) {
- pr_err("%s failed to initialize event queue\n", __func__);
- video_device_release(config_cam->config_stat_event_queue.pvdev);
- goto config_setup_fail;
- }
-
- return rc;
-
-config_setup_fail:
- kfree(config_cam);
- return rc;
-}
-
-static void msm_cam_server_subdev_notify(struct v4l2_subdev *sd,
- unsigned int notification, void *arg)
-{
- int rc = -EINVAL;
- struct msm_sensor_ctrl_t *s_ctrl;
- struct msm_camera_sensor_info *sinfo;
- struct msm_camera_device_platform_data *camdev;
- uint8_t csid_core = 0;
-
- if (notification == NOTIFY_CID_CHANGE ||
- notification == NOTIFY_ISPIF_STREAM ||
- notification == NOTIFY_PCLK_CHANGE ||
- notification == NOTIFY_CSIPHY_CFG ||
- notification == NOTIFY_CSID_CFG ||
- notification == NOTIFY_CSIC_CFG) {
- s_ctrl = get_sctrl(sd);
- sinfo = (struct msm_camera_sensor_info *) s_ctrl->sensordata;
- camdev = sinfo->pdata;
- csid_core = camdev->csid_core;
- }
-
- switch (notification) {
- case NOTIFY_CID_CHANGE:
- /* reconfig the ISPIF*/
- if (g_server_dev.ispif_device) {
- struct msm_ispif_params_list ispif_params;
- ispif_params.len = 1;
- ispif_params.params[0].intftype = PIX0;
- ispif_params.params[0].cid_mask = 0x0001;
- ispif_params.params[0].csid = csid_core;
-
- rc = v4l2_subdev_call(
- g_server_dev.ispif_device, core, ioctl,
- VIDIOC_MSM_ISPIF_CFG, &ispif_params);
- if (rc < 0)
- return;
- }
- break;
- case NOTIFY_ISPIF_STREAM:
- /* call ISPIF stream on/off */
- rc = v4l2_subdev_call(g_server_dev.ispif_device, video,
- s_stream, (int)arg);
- if (rc < 0)
- return;
-
- break;
- case NOTIFY_ISP_MSG_EVT:
- case NOTIFY_VFE_MSG_OUT:
- case NOTIFY_VFE_MSG_STATS:
- case NOTIFY_VFE_MSG_COMP_STATS:
- case NOTIFY_VFE_BUF_EVT:
- case NOTIFY_VFE_BUF_FREE_EVT:
- if (g_server_dev.isp_subdev[0] &&
- g_server_dev.isp_subdev[0]->isp_notify) {
- rc = g_server_dev.isp_subdev[0]->isp_notify(
- g_server_dev.vfe_device[0], notification, arg);
- }
- break;
- case NOTIFY_VPE_MSG_EVT: {
- struct msm_cam_media_controller *pmctl =
- (struct msm_cam_media_controller *)
- v4l2_get_subdev_hostdata(sd);
- struct msm_vpe_resp *vdata = (struct msm_vpe_resp *)arg;
- msm_mctl_pp_notify(pmctl,
- (struct msm_mctl_pp_frame_info *)
- vdata->extdata);
- break;
- }
- case NOTIFY_VFE_IRQ:{
- struct msm_vfe_cfg_cmd cfg_cmd;
- struct msm_camvfe_params vfe_params;
- cfg_cmd.cmd_type = CMD_VFE_PROCESS_IRQ;
- vfe_params.vfe_cfg = &cfg_cmd;
- vfe_params.data = arg;
- rc = v4l2_subdev_call(g_server_dev.vfe_device[0],
- core, ioctl, 0, &vfe_params);
- }
- break;
- case NOTIFY_AXI_IRQ:
- rc = v4l2_subdev_call(g_server_dev.axi_device[0],
- core, ioctl, VIDIOC_MSM_AXI_IRQ, arg);
- break;
- case NOTIFY_PCLK_CHANGE:
- if (g_server_dev.axi_device[0])
- rc = v4l2_subdev_call(g_server_dev.axi_device[0], video,
- s_crystal_freq, *(uint32_t *)arg, 0);
- else
- rc = v4l2_subdev_call(g_server_dev.vfe_device[0], video,
- s_crystal_freq, *(uint32_t *)arg, 0);
- break;
- case NOTIFY_CSIPHY_CFG:
- rc = v4l2_subdev_call(g_server_dev.csiphy_device[csid_core],
- core, ioctl, VIDIOC_MSM_CSIPHY_CFG, arg);
- break;
- case NOTIFY_CSID_CFG:
- rc = v4l2_subdev_call(g_server_dev.csid_device[csid_core],
- core, ioctl, VIDIOC_MSM_CSID_CFG, arg);
- break;
- case NOTIFY_CSIC_CFG:
- rc = v4l2_subdev_call(g_server_dev.csic_device[csid_core],
- core, ioctl, VIDIOC_MSM_CSIC_CFG, arg);
- break;
- case NOTIFY_GESTURE_EVT:
- rc = v4l2_subdev_call(g_server_dev.gesture_device,
- core, ioctl, VIDIOC_MSM_GESTURE_EVT, arg);
- break;
- case NOTIFY_GESTURE_CAM_EVT:
- rc = v4l2_subdev_call(g_server_dev.gesture_device,
- core, ioctl, VIDIOC_MSM_GESTURE_CAM_EVT, arg);
- break;
- default:
- break;
- }
-
- return;
-}
-
-int msm_cam_register_subdev_node(struct v4l2_subdev *sd,
- enum msm_cam_subdev_type sdev_type, uint8_t index)
-{
- struct video_device *vdev;
- int err = 0;
-
- if (sdev_type == CSIPHY_DEV) {
- if (index >= MAX_NUM_CSIPHY_DEV)
- return -EINVAL;
- g_server_dev.csiphy_device[index] = sd;
- } else if (sdev_type == CSID_DEV) {
- if (index >= MAX_NUM_CSID_DEV)
- return -EINVAL;
- g_server_dev.csid_device[index] = sd;
- } else if (sdev_type == CSIC_DEV) {
- if (index >= MAX_NUM_CSIC_DEV)
- return -EINVAL;
- g_server_dev.csic_device[index] = sd;
- } else if (sdev_type == ISPIF_DEV) {
- g_server_dev.ispif_device = sd;
- } else if (sdev_type == VFE_DEV) {
- if (index >= MAX_NUM_VFE_DEV)
- return -EINVAL;
- g_server_dev.vfe_device[index] = sd;
- } else if (sdev_type == VPE_DEV) {
- if (index >= MAX_NUM_VPE_DEV)
- return -EINVAL;
- g_server_dev.vpe_device[index] = sd;
- } else if (sdev_type == AXI_DEV) {
- if (index >= MAX_NUM_AXI_DEV)
- return -EINVAL;
- g_server_dev.axi_device[index] = sd;
- } else if (sdev_type == GESTURE_DEV) {
- g_server_dev.gesture_device = sd;
- }
-
- err = v4l2_device_register_subdev(&g_server_dev.v4l2_dev, sd);
- if (err < 0)
- return err;
-
- /* Register a device node for every subdev marked with the
- * V4L2_SUBDEV_FL_HAS_DEVNODE flag.
- */
- if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
- return err;
-
- vdev = &sd->devnode;
- strlcpy(vdev->name, sd->name, sizeof(vdev->name));
- vdev->v4l2_dev = &g_server_dev.v4l2_dev;
- vdev->fops = &v4l2_subdev_fops;
- vdev->release = video_device_release_empty;
- err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
- sd->owner);
- if (err < 0)
- return err;
-#if defined(CONFIG_MEDIA_CONTROLLER)
- sd->entity.v4l.major = VIDEO_MAJOR;
- sd->entity.v4l.minor = vdev->minor;
-#endif
- return 0;
-}
-
-static int msm_setup_server_dev(struct platform_device *pdev)
-{
- int rc = -ENODEV, i;
-
- D("%s\n", __func__);
- g_server_dev.server_pdev = pdev;
- g_server_dev.v4l2_dev.dev = &pdev->dev;
- g_server_dev.v4l2_dev.notify = msm_cam_server_subdev_notify;
- rc = v4l2_device_register(g_server_dev.v4l2_dev.dev,
- &g_server_dev.v4l2_dev);
- if (rc < 0)
- return -EINVAL;
-
- g_server_dev.video_dev = video_device_alloc();
- if (g_server_dev.video_dev == NULL) {
- pr_err("%s: video_device_alloc failed\n", __func__);
- return rc;
- }
-
- strlcpy(g_server_dev.video_dev->name, pdev->name,
- sizeof(g_server_dev.video_dev->name));
-
- g_server_dev.video_dev->v4l2_dev = &g_server_dev.v4l2_dev;
- g_server_dev.video_dev->fops = &msm_fops_server;
- g_server_dev.video_dev->ioctl_ops = &msm_ioctl_ops_server;
- g_server_dev.video_dev->release = video_device_release;
- g_server_dev.video_dev->minor = 100;
- g_server_dev.video_dev->vfl_type = 1;
-
- video_set_drvdata(g_server_dev.video_dev, &g_server_dev);
-
- strlcpy(g_server_dev.media_dev.model, "qcamera",
- sizeof(g_server_dev.media_dev.model));
- g_server_dev.media_dev.dev = &pdev->dev;
- rc = media_device_register(&g_server_dev.media_dev);
- g_server_dev.v4l2_dev.mdev = &g_server_dev.media_dev;
-
- rc = video_register_device(g_server_dev.video_dev,
- VFL_TYPE_GRABBER, 100);
-
- mutex_init(&g_server_dev.server_lock);
- mutex_init(&g_server_dev.server_queue_lock);
- g_server_dev.pcam_active = NULL;
- g_server_dev.camera_info.num_cameras = 0;
- atomic_set(&g_server_dev.number_pcam_active, 0);
- g_server_dev.server_evt_id = 0;
-
- /*initialize fake video device and event queue*/
-
- g_server_dev.server_command_queue.pvdev = g_server_dev.video_dev;
- rc = msm_setup_v4l2_event_queue(
- &g_server_dev.server_command_queue.eventHandle,
- g_server_dev.server_command_queue.pvdev);
-
- if (rc < 0) {
- pr_err("%s failed to initialize event queue\n", __func__);
- video_device_release(g_server_dev.server_command_queue.pvdev);
- return rc;
- }
-
- for (i = 0; i < MAX_NUM_ACTIVE_CAMERA; i++) {
- struct msm_cam_server_queue *queue;
- queue = &g_server_dev.server_queue[i];
- queue->queue_active = 0;
- msm_queue_init(&queue->ctrl_q, "control");
- msm_queue_init(&queue->eventData_q, "eventdata");
- }
- return rc;
-}
+static struct v4l2_file_operations g_msm_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_open,
+ .poll = msm_poll,
+ .mmap = msm_mmap,
+ .release = msm_close,
+ .ioctl = video_ioctl2,
+};
static int msm_cam_dev_init(struct msm_cam_v4l2_device *pcam)
{
@@ -3085,7 +1139,7 @@
strlcpy(pvdev->name, pcam->sensor_sdev->name, sizeof(pvdev->name));
pvdev->release = video_device_release;
- pvdev->fops = &g_msm_fops;
+ pvdev->fops = &g_msm_fops;
pvdev->ioctl_ops = &g_msm_ioctl_ops;
pvdev->minor = -1;
pvdev->vfl_type = 1;
@@ -3111,16 +1165,7 @@
pcam->pvdev = pvdev;
video_set_drvdata(pcam->pvdev, pcam);
- /* If isp HW registeration is successful,
- * then create event queue to
- * receievent event froms HW
- */
- /* yyan: no global - each sensor will
- * create a new vidoe node! */
- /* g_pmsm_camera_v4l2_dev = pmsm_camera_v4l2_dev; */
- /* g_pmsm_camera_v4l2_dev->pvdev = pvdev; */
-
- return rc ;
+ return rc;
reg_fail:
video_device_release(pvdev);
@@ -3261,46 +1306,7 @@
__func__, rc);
goto failure;
}
-
- g_server_dev.camera_info.video_dev_name
- [g_server_dev.camera_info.num_cameras]
- = video_device_node_name(pcam->pvdev);
- D("%s Connected video device %s\n", __func__,
- g_server_dev.camera_info.video_dev_name
- [g_server_dev.camera_info.num_cameras]);
-
- g_server_dev.camera_info.s_mount_angle
- [g_server_dev.camera_info.num_cameras]
- = sdata->sensor_platform_info->mount_angle;
-
- g_server_dev.camera_info.is_internal_cam
- [g_server_dev.camera_info.num_cameras]
- = sdata->camera_type;
-
- g_server_dev.mctl_node_info.mctl_node_name
- [g_server_dev.mctl_node_info.num_mctl_nodes]
- = video_device_node_name(pcam->mctl_node.pvdev);
-
- pr_info("%s mctl_node_name[%d] = %s\n", __func__,
- g_server_dev.mctl_node_info.num_mctl_nodes,
- g_server_dev.mctl_node_info.mctl_node_name
- [g_server_dev.mctl_node_info.num_mctl_nodes]);
-
- /*Temporary solution to store info in media device structure
- until we can expand media device structure to support more
- device info*/
- snprintf(pcam->media_dev.serial,
- sizeof(pcam->media_dev.serial),
- "%s-%d-%d", QCAMERA_NAME,
- sdata->sensor_platform_info->mount_angle,
- sdata->camera_type);
-
- g_server_dev.camera_info.num_cameras++;
- g_server_dev.mctl_node_info.num_mctl_nodes++;
-
- D("%s done, rc = %d\n", __func__, rc);
- D("%s number of sensors connected is %d\n", __func__,
- g_server_dev.camera_info.num_cameras);
+ msm_server_update_sensor_info(pcam, sdata);
/* register the subdevice, must be done for callbacks */
rc = msm_cam_register_subdev_node(sensor_sd, SENSOR_DEV, vnode_count);
@@ -3338,83 +1344,3 @@
}
EXPORT_SYMBOL(msm_sensor_register);
-static int __devinit msm_camera_probe(struct platform_device *pdev)
-{
- int rc = 0, i;
- /*for now just create a config 0 node
- put logic here later to know how many configs to create*/
- g_server_dev.config_info.num_config_nodes = 1;
-
- rc = msm_isp_init_module(g_server_dev.config_info.num_config_nodes);
- if (rc < 0) {
- pr_err("Failed to initialize isp\n");
- return rc;
- }
-
- if (!msm_class) {
- rc = alloc_chrdev_region(&msm_devno, 0,
- g_server_dev.config_info.num_config_nodes+1, "msm_camera");
- if (rc < 0) {
- pr_err("%s: failed to allocate chrdev: %d\n", __func__,
- rc);
- return rc;
- }
-
- msm_class = class_create(THIS_MODULE, "msm_camera");
- if (IS_ERR(msm_class)) {
- rc = PTR_ERR(msm_class);
- pr_err("%s: create device class failed: %d\n",
- __func__, rc);
- return rc;
- }
- }
-
- D("creating server and config nodes\n");
- rc = msm_setup_server_dev(pdev);
- if (rc < 0) {
- pr_err("%s: failed to create server dev: %d\n", __func__,
- rc);
- return rc;
- }
-
- for (i = 0; i < g_server_dev.config_info.num_config_nodes; i++) {
- rc = msm_setup_config_dev(i, "config");
- if (rc < 0) {
- pr_err("%s:failed to create config dev: %d\n",
- __func__, rc);
- return rc;
- }
- }
-
- msm_isp_register(&g_server_dev);
- return rc;
-}
-
-static int __exit msm_camera_exit(struct platform_device *pdev)
-{
- msm_isp_unregister(&g_server_dev);
- return 0;
-}
-
-
-static struct platform_driver msm_cam_server_driver = {
- .probe = msm_camera_probe,
- .remove = msm_camera_exit,
- .driver = {
- .name = "msm_cam_server",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init msm_camera_init(void)
-{
- return platform_driver_register(&msm_cam_server_driver);
-}
-
-static void __exit msm_cam_server_exit(void)
-{
- platform_driver_unregister(&msm_cam_server_driver);
-}
-
-module_init(msm_camera_init);
-module_exit(msm_cam_server_exit);
diff --git a/drivers/media/video/msm/msm.h b/drivers/media/video/msm/msm.h
index 6798cbb..9c3b548 100644
--- a/drivers/media/video/msm/msm.h
+++ b/drivers/media/video/msm/msm.h
@@ -32,6 +32,7 @@
#include <mach/camera.h>
#include <media/msm_isp.h>
#include <linux/ion.h>
+#include <linux/iommu.h>
#include <media/msm_gestures.h>
#define MSM_V4L2_DIMENSION_SIZE 96
@@ -271,6 +272,12 @@
/*sensor info*/
struct msm_camera_sensor_info *sdata;
+
+ /*IOMMU mapped IMEM addresses*/
+ uint32_t ping_imem_y;
+ uint32_t ping_imem_cbcr;
+ uint32_t pong_imem_y;
+ uint32_t pong_imem_cbcr;
};
/* abstract camera device represents a VFE and connected sensor */
@@ -284,7 +291,8 @@
unsigned int cmd, unsigned long arg);
int (*isp_notify)(struct v4l2_subdev *sd,
unsigned int notification, void *arg);
- void (*isp_release)(struct v4l2_subdev *sd);
+ void (*isp_release)(struct msm_cam_media_controller *mctl,
+ struct v4l2_subdev *sd);
int (*isp_pp_cmd)(struct msm_cam_media_controller *pmctl,
struct msm_mctl_pp_cmd, void *data);
@@ -397,7 +405,6 @@
uint32_t queue_active;
struct msm_device_queue ctrl_q;
struct msm_device_queue eventData_q;
- struct msm_ctrl_cmd *ctrl;
uint8_t *ctrl_data;
uint32_t evt_id;
};
@@ -456,7 +463,6 @@
/* camera server related functions */
-
/* ISP related functions */
void msm_isp_vfe_dev_init(struct v4l2_subdev *vd);
/*
@@ -571,15 +577,14 @@
void msm_release_ion_client(struct kref *ref);
int msm_cam_register_subdev_node(struct v4l2_subdev *sd,
enum msm_cam_subdev_type sdev_type, uint8_t index);
-uint32_t msm_camera_get_mctl_handle(void);
-struct msm_cam_media_controller *msm_camera_get_mctl(uint32_t handle);
-void msm_camera_free_mctl(uint32_t handle);
int msm_server_open_client(int *p_qidx);
int msm_server_send_ctrl(struct msm_ctrl_cmd *out, int ctrl_id);
int msm_server_close_client(int idx);
int msm_cam_server_open_mctl_session(struct msm_cam_v4l2_device *pcam,
int *p_active);
int msm_cam_server_close_mctl_session(struct msm_cam_v4l2_device *pcam);
+long msm_v4l2_evt_notify(struct msm_cam_media_controller *mctl,
+ unsigned int cmd, unsigned long evt);
#endif /* __KERNEL__ */
#endif /* _MSM_H */
diff --git a/drivers/media/video/msm/msm_gesture.c b/drivers/media/video/msm/msm_gesture.c
index 654594d..73d60aa 100644
--- a/drivers/media/video/msm/msm_gesture.c
+++ b/drivers/media/video/msm/msm_gesture.c
@@ -199,32 +199,31 @@
D("%s: Received gesture evt 0x%x ", __func__, evt->type);
p_gesture_ctrl->event.evt_len = 0;
p_gesture_ctrl->event.evt_data = NULL;
- if (0 != evt->u.data[0]) {
- p_ges_evt = (struct msm_ges_evt *)evt->u.data;
- D("%s: event data %p len %d", __func__,
- p_ges_evt->evt_data,
- p_ges_evt->evt_len);
- if (p_ges_evt->evt_len > 0) {
- p_gesture_ctrl->event.evt_data =
- kzalloc(p_ges_evt->evt_len, GFP_KERNEL);
+ p_ges_evt = (struct msm_ges_evt *)evt->u.data;
+ D("%s: event data %p len %d", __func__,
+ p_ges_evt->evt_data,
+ p_ges_evt->evt_len);
- if (NULL == p_gesture_ctrl->event.evt_data) {
- pr_err("%s: cannot allocate event", __func__);
- rc = -ENOMEM;
+ if (p_ges_evt->evt_len > 0) {
+ p_gesture_ctrl->event.evt_data =
+ kzalloc(p_ges_evt->evt_len, GFP_KERNEL);
+
+ if (NULL == p_gesture_ctrl->event.evt_data) {
+ pr_err("%s: cannot allocate event", __func__);
+ rc = -ENOMEM;
+ } else {
+ if (copy_from_user(
+ (void *)p_gesture_ctrl->event.evt_data,
+ (void __user *)p_ges_evt->evt_data,
+ p_ges_evt->evt_len)) {
+ pr_err("%s: copy_from_user failed",
+ __func__);
+ rc = -EFAULT;
} else {
- if (copy_from_user(
- (void *)p_gesture_ctrl->event.evt_data,
- (void __user *)p_ges_evt->evt_data,
- p_ges_evt->evt_len)) {
- pr_err("%s: copy_from_user failed",
- __func__);
- rc = -EFAULT;
- } else {
- D("%s: copied the event", __func__);
- p_gesture_ctrl->event.evt_len =
- p_ges_evt->evt_len;
- }
+ D("%s: copied the event", __func__);
+ p_gesture_ctrl->event.evt_len =
+ p_ges_evt->evt_len;
}
}
}
diff --git a/drivers/media/video/msm/msm_isp.c b/drivers/media/video/msm/msm_isp.c
index 315f218..bd174fb 100644
--- a/drivers/media/video/msm/msm_isp.c
+++ b/drivers/media/video/msm/msm_isp.c
@@ -210,9 +210,9 @@
case VFE_MSG_V32_JPEG_CAPTURE:
D("%s:VFE_MSG_V32_JPEG_CAPTURE vdata->type %d\n", __func__,
vdata->type);
- free_buf.num_planes = 1;
- free_buf.ch_paddr[0] = IMEM_Y_PING_OFFSET;
- free_buf.ch_paddr[1] = IMEM_CBCR_PING_OFFSET;
+ free_buf.num_planes = 2;
+ free_buf.ch_paddr[0] = pmctl->ping_imem_y;
+ free_buf.ch_paddr[1] = pmctl->ping_imem_cbcr;
cfgcmd.cmd_type = CMD_CONFIG_PING_ADDR;
cfgcmd.value = &vfe_id;
vfe_params.vfe_cfg = &cfgcmd;
@@ -221,8 +221,8 @@
__func__, free_buf.ch_paddr[0], free_buf.ch_paddr[1]);
rc = v4l2_subdev_call(sd, core, ioctl, 0, &vfe_params);
/* Write the same buffer into PONG */
- free_buf.ch_paddr[0] = IMEM_Y_PONG_OFFSET;
- free_buf.ch_paddr[1] = IMEM_CBCR_PONG_OFFSET;
+ free_buf.ch_paddr[0] = pmctl->pong_imem_y;
+ free_buf.ch_paddr[1] = pmctl->pong_imem_cbcr;
cfgcmd.cmd_type = CMD_CONFIG_PONG_ADDR;
cfgcmd.value = &vfe_id;
vfe_params.vfe_cfg = &cfgcmd;
@@ -464,19 +464,54 @@
return -EINVAL;
}
+ rc = msm_iommu_map_contig_buffer(
+ (unsigned long)IMEM_Y_PING_OFFSET, CAMERA_DOMAIN, GEN_POOL,
+ ((IMEM_Y_SIZE + IMEM_CBCR_SIZE + 4095) & (~4095)),
+ SZ_4K, IOMMU_WRITE | IOMMU_READ,
+ (unsigned long *)&mctl->ping_imem_y);
+ mctl->ping_imem_cbcr = mctl->ping_imem_y + IMEM_Y_SIZE;
+ if (rc < 0) {
+ pr_err("%s: ping iommu mapping returned error %d\n",
+ __func__, rc);
+ mctl->ping_imem_y = 0;
+ mctl->ping_imem_cbcr = 0;
+ }
+ msm_iommu_map_contig_buffer(
+ (unsigned long)IMEM_Y_PONG_OFFSET, CAMERA_DOMAIN, GEN_POOL,
+ ((IMEM_Y_SIZE + IMEM_CBCR_SIZE + 4095) & (~4095)),
+ SZ_4K, IOMMU_WRITE | IOMMU_READ,
+ (unsigned long *)&mctl->pong_imem_y);
+ mctl->pong_imem_cbcr = mctl->pong_imem_y + IMEM_Y_SIZE;
+ if (rc < 0) {
+ pr_err("%s: pong iommu mapping returned error %d\n",
+ __func__, rc);
+ mctl->pong_imem_y = 0;
+ mctl->pong_imem_cbcr = 0;
+ }
+
rc = msm_vfe_subdev_init(sd, mctl);
if (rc < 0) {
pr_err("%s: vfe_init failed at %d\n",
- __func__, rc);
+ __func__, rc);
}
return rc;
}
-static void msm_isp_release(
+static void msm_isp_release(struct msm_cam_media_controller *mctl,
struct v4l2_subdev *sd)
{
D("%s\n", __func__);
msm_vfe_subdev_release(sd);
+ msm_iommu_unmap_contig_buffer(mctl->ping_imem_y,
+ CAMERA_DOMAIN, GEN_POOL,
+ ((IMEM_Y_SIZE + IMEM_CBCR_SIZE + 4095) & (~4095)));
+ msm_iommu_unmap_contig_buffer(mctl->pong_imem_y,
+ CAMERA_DOMAIN, GEN_POOL,
+ ((IMEM_Y_SIZE + IMEM_CBCR_SIZE + 4095) & (~4095)));
+ mctl->ping_imem_y = 0;
+ mctl->ping_imem_cbcr = 0;
+ mctl->pong_imem_y = 0;
+ mctl->pong_imem_cbcr = 0;
}
static int msm_config_vfe(struct v4l2_subdev *sd,
diff --git a/drivers/media/video/msm/msm_mctl.c b/drivers/media/video/msm/msm_mctl.c
index e9eb68f..0c87e3e81 100644
--- a/drivers/media/video/msm/msm_mctl.c
+++ b/drivers/media/video/msm/msm_mctl.c
@@ -28,6 +28,7 @@
#include <linux/android_pmem.h>
#include "msm.h"
+#include "msm_cam_server.h"
#include "msm_csid.h"
#include "msm_csic.h"
#include "msm_csiphy.h"
@@ -699,7 +700,7 @@
pr_err("%s: axi release failed %d\n", __func__, rc);
axi_init_failed:
if (p_mctl->isp_sdev && p_mctl->isp_sdev->isp_release)
- p_mctl->isp_sdev->isp_release(p_mctl->isp_sdev->sd);
+ p_mctl->isp_sdev->isp_release(p_mctl, p_mctl->isp_sdev->sd);
isp_open_failed:
if (camdev->is_csic)
if (v4l2_subdev_call(p_mctl->csic_sdev, core, ioctl,
@@ -762,7 +763,7 @@
}
if (p_mctl->isp_sdev && p_mctl->isp_sdev->isp_release)
- p_mctl->isp_sdev->isp_release(
+ p_mctl->isp_sdev->isp_release(p_mctl,
p_mctl->isp_sdev->sd);
if (camdev->is_csid) {
@@ -858,13 +859,13 @@
pr_err("%s: param is NULL", __func__);
return -EINVAL;
}
- pcam->mctl_handle = msm_camera_get_mctl_handle();
+ pcam->mctl_handle = msm_cam_server_get_mctl_handle();
if (pcam->mctl_handle == 0) {
pr_err("%s: cannot get mctl handle", __func__);
return -EINVAL;
}
- pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+ pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
if (!pmctl) {
pr_err("%s: invalid mctl controller", __func__);
return -EINVAL;
@@ -903,7 +904,7 @@
struct msm_cam_media_controller *pmctl = NULL;
D("%s\n", __func__);
- pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+ pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
if (!pmctl) {
pr_err("%s: invalid mctl controller", __func__);
return -EINVAL;
@@ -911,7 +912,7 @@
mutex_destroy(&pmctl->lock);
wake_lock_destroy(&pmctl->wake_lock);
- msm_camera_free_mctl(pcam->mctl_handle);
+ msm_cam_server_free_mctl(pcam->mctl_handle);
return rc;
}
@@ -967,7 +968,7 @@
return rc;
}
- pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+ pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
if (!pmctl) {
pr_err("%s mctl NULL!\n", __func__);
return rc;
@@ -1043,7 +1044,7 @@
return -EINVAL;
}
- pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+ pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
mutex_lock(&pcam->mctl_node.dev_lock);
D("%s : active %d ", __func__, pcam->mctl_node.active);
if (pcam->mctl_node.active == 1) {
@@ -1478,7 +1479,7 @@
(void *)pfmt->fmt.pix.priv);
WARN_ON(pctx != f->private_data);
- pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+ pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
if (!pcam_inst->vbqueue_initialized) {
pmctl->mctl_vbqueue_init(pcam_inst, &pcam_inst->vid_bufq,
V4L2_BUF_TYPE_VIDEO_CAPTURE);
@@ -1502,7 +1503,7 @@
pcam_inst, pcam_inst->vbqueue_initialized);
WARN_ON(pctx != f->private_data);
- pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+ pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
if (!pcam_inst->vbqueue_initialized) {
pmctl->mctl_vbqueue_init(pcam_inst, &pcam_inst->vid_bufq,
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
diff --git a/drivers/media/video/msm/msm_mctl_buf.c b/drivers/media/video/msm/msm_mctl_buf.c
index 42d13a1..522ea7d 100644
--- a/drivers/media/video/msm/msm_mctl_buf.c
+++ b/drivers/media/video/msm/msm_mctl_buf.c
@@ -26,6 +26,7 @@
#include <linux/android_pmem.h>
#include "msm.h"
+#include "msm_cam_server.h"
#include "msm_ispif.h"
#ifdef CONFIG_MSM_CAMERA_DEBUG
@@ -53,7 +54,7 @@
*num_planes = pcam_inst->plane_info.num_planes;
for (i = 0; i < pcam_inst->vid_fmt.fmt.pix_mp.num_planes; i++) {
- sizes[i] = PAGE_ALIGN(pcam_inst->plane_info.plane[i].size);
+ sizes[i] = pcam_inst->plane_info.plane[i].size;
D("%s Inst %p : Plane %d Offset = %d Size = %ld"
"Aligned Size = %ld", __func__, pcam_inst, i,
pcam_inst->plane_info.plane[i].offset,
@@ -113,7 +114,7 @@
pcam_inst->plane_info.plane[0].offset;
}
buf_idx = vb->v4l2_buf.index;
- pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+ pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
for (i = 0; i < vb->num_planes; i++) {
mem = vb2_plane_cookie(vb, i);
if (buf_type == VIDEOBUF2_MULTIPLE_PLANES)
@@ -251,7 +252,7 @@
}
spin_unlock_irqrestore(&pcam_inst->vq_irqlock, flags);
}
- pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+ pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
for (i = 0; i < vb->num_planes; i++) {
mem = vb2_plane_cookie(vb, i);
videobuf2_pmem_contig_user_put(mem, pmctl->client);
@@ -472,7 +473,7 @@
int msm_mctl_buf_init(struct msm_cam_v4l2_device *pcam)
{
struct msm_cam_media_controller *pmctl;
- pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+ pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
pmctl->mctl_vbqueue_init = msm_vbqueue_init;
return 0;
}
diff --git a/drivers/media/video/msm/msm_vfe31_v4l2.c b/drivers/media/video/msm/msm_vfe31_v4l2.c
index 89615ec..d5f37dc 100644
--- a/drivers/media/video/msm/msm_vfe31_v4l2.c
+++ b/drivers/media/video/msm/msm_vfe31_v4l2.c
@@ -367,7 +367,6 @@
static void vfe31_stop(void)
{
- uint8_t axiBusyFlag = true;
unsigned long flags;
atomic_set(&vfe31_ctrl->vstate, 0);
@@ -397,7 +396,52 @@
* at any time. stop camif immediately. */
msm_camera_io_w_mb(CAMIF_COMMAND_STOP_IMMEDIATELY,
vfe31_ctrl->vfebase + VFE_CAMIF_COMMAND);
+}
+void axi_start(void)
+{
+ switch (vfe31_ctrl->operation_mode) {
+ case VFE_OUTPUTS_PREVIEW:
+ case VFE_OUTPUTS_PREVIEW_AND_VIDEO:
+ if (vfe31_ctrl->outpath.output_mode &
+ VFE31_OUTPUT_MODE_PRIMARY) {
+ msm_camera_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
+ msm_camera_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
+ } else if (vfe31_ctrl->outpath.output_mode &
+ VFE31_OUTPUT_MODE_PRIMARY_ALL_CHNLS) {
+ msm_camera_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
+ msm_camera_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
+ msm_camera_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch2]);
+ }
+ break;
+ default:
+ if (vfe31_ctrl->outpath.output_mode &
+ VFE31_OUTPUT_MODE_SECONDARY) {
+ msm_camera_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch0]);
+ msm_camera_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch1]);
+ } else if (vfe31_ctrl->outpath.output_mode &
+ VFE31_OUTPUT_MODE_SECONDARY_ALL_CHNLS) {
+ msm_camera_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch0]);
+ msm_camera_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch1]);
+ msm_camera_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch2]);
+ }
+ break;
+ }
+}
+
+void axi_stop(void)
+{
+ uint8_t axiBusyFlag = true;
/* axi halt command. */
msm_camera_io_w(AXI_HALT,
vfe31_ctrl->vfebase + VFE_AXI_CMD);
@@ -954,43 +998,6 @@
}
msm_camera_io_w(irq_comp_mask, vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
- switch (vfe31_ctrl->operation_mode) {
- case VFE_OUTPUTS_PREVIEW:
- case VFE_OUTPUTS_PREVIEW_AND_VIDEO:
- if (vfe31_ctrl->outpath.output_mode &
- VFE31_OUTPUT_MODE_PRIMARY) {
- msm_camera_io_w(1, vfe31_ctrl->vfebase +
- vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
- msm_camera_io_w(1, vfe31_ctrl->vfebase +
- vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
- } else if (vfe31_ctrl->outpath.output_mode &
- VFE31_OUTPUT_MODE_PRIMARY_ALL_CHNLS) {
- msm_camera_io_w(1, vfe31_ctrl->vfebase +
- vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
- msm_camera_io_w(1, vfe31_ctrl->vfebase +
- vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
- msm_camera_io_w(1, vfe31_ctrl->vfebase +
- vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch2]);
- }
- break;
- default:
- if (vfe31_ctrl->outpath.output_mode &
- VFE31_OUTPUT_MODE_SECONDARY) {
- msm_camera_io_w(1, vfe31_ctrl->vfebase +
- vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch0]);
- msm_camera_io_w(1, vfe31_ctrl->vfebase +
- vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch1]);
- } else if (vfe31_ctrl->outpath.output_mode &
- VFE31_OUTPUT_MODE_SECONDARY_ALL_CHNLS) {
- msm_camera_io_w(1, vfe31_ctrl->vfebase +
- vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch0]);
- msm_camera_io_w(1, vfe31_ctrl->vfebase +
- vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch1]);
- msm_camera_io_w(1, vfe31_ctrl->vfebase +
- vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch2]);
- }
- break;
- }
msm_camio_bus_scale_cfg(
pmctl->sdata->pdata->cam_bus_scale_table, S_PREVIEW);
vfe31_start_common();
@@ -3325,12 +3332,14 @@
cmd->cmd_type != CMD_STATS_RS_BUF_RELEASE &&
cmd->cmd_type != CMD_STATS_CS_BUF_RELEASE &&
cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) {
- if (copy_from_user(&vfecmd,
- (void __user *)(cmd->value),
- sizeof(vfecmd))) {
- pr_err("%s %d: copy_from_user failed\n", __func__,
- __LINE__);
- return -EFAULT;
+ if (NULL != cmd->value) {
+ if (copy_from_user(&vfecmd,
+ (void __user *)(cmd->value),
+ sizeof(vfecmd))) {
+ pr_err("%s %d: copy_from_user failed\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
}
} else {
/* here eith stats release or frame release. */
@@ -3571,6 +3580,14 @@
}
break;
+ case CMD_AXI_START:
+ axi_start();
+ break;
+
+ case CMD_AXI_STOP:
+ axi_stop();
+ break;
+
default:
pr_err("%s Unsupported AXI configuration %x ", __func__,
cmd->cmd_type);
diff --git a/drivers/media/video/msm/msm_vfe32.c b/drivers/media/video/msm/msm_vfe32.c
index d50b778..220752a 100644
--- a/drivers/media/video/msm/msm_vfe32.c
+++ b/drivers/media/video/msm/msm_vfe32.c
@@ -358,7 +358,6 @@
static void vfe32_stop(void)
{
- uint8_t axiBusyFlag = true;
unsigned long flags;
atomic_set(&vfe32_ctrl->vstate, 0);
@@ -389,31 +388,6 @@
msm_camera_io_w(CAMIF_COMMAND_STOP_IMMEDIATELY,
vfe32_ctrl->vfebase + VFE_CAMIF_COMMAND);
- /* axi halt command. */
- msm_camera_io_w(AXI_HALT,
- vfe32_ctrl->vfebase + VFE_AXI_CMD);
- wmb();
- while (axiBusyFlag) {
- if (msm_camera_io_r(vfe32_ctrl->vfebase + VFE_AXI_STATUS) & 0x1)
- axiBusyFlag = false;
- }
- /* Ensure the write order while writing
- to the command register using the barrier */
- msm_camera_io_w_mb(AXI_HALT_CLEAR,
- vfe32_ctrl->vfebase + VFE_AXI_CMD);
-
- /* after axi halt, then ok to apply global reset. */
- /* enable reset_ack and async timer interrupt only while
- stopping the pipeline.*/
- msm_camera_io_w(0xf0000000,
- vfe32_ctrl->vfebase + VFE_IRQ_MASK_0);
- msm_camera_io_w(VFE_IMASK_WHILE_STOPPING_1,
- vfe32_ctrl->vfebase + VFE_IRQ_MASK_1);
-
- /* Ensure the write order while writing
- to the command register using the barrier */
- msm_camera_io_w_mb(VFE_RESET_UPON_STOP_CMD,
- vfe32_ctrl->vfebase + VFE_GLOBAL_RESET);
}
static void vfe32_subdev_notify(int id, int path)
@@ -910,7 +884,6 @@
static int vfe32_start(struct msm_cam_media_controller *pmctl)
{
uint32_t irq_comp_mask = 0;
-
irq_comp_mask =
msm_camera_io_r(vfe32_ctrl->vfebase + VFE_IRQ_COMP_MASK);
@@ -934,44 +907,6 @@
}
msm_camera_io_w(irq_comp_mask, vfe32_ctrl->vfebase + VFE_IRQ_COMP_MASK);
- switch (vfe32_ctrl->operation_mode) {
- case VFE_OUTPUTS_PREVIEW:
- case VFE_OUTPUTS_PREVIEW_AND_VIDEO:
- if (vfe32_ctrl->outpath.output_mode &
- VFE32_OUTPUT_MODE_PRIMARY) {
- msm_camera_io_w(1, vfe32_ctrl->vfebase +
- vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch0]);
- msm_camera_io_w(1, vfe32_ctrl->vfebase +
- vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch1]);
- } else if (vfe32_ctrl->outpath.output_mode &
- VFE32_OUTPUT_MODE_PRIMARY_ALL_CHNLS) {
- msm_camera_io_w(1, vfe32_ctrl->vfebase +
- vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch0]);
- msm_camera_io_w(1, vfe32_ctrl->vfebase +
- vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch1]);
- msm_camera_io_w(1, vfe32_ctrl->vfebase +
- vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch2]);
- }
- break;
- default:
- if (vfe32_ctrl->outpath.output_mode &
- VFE32_OUTPUT_MODE_SECONDARY) {
- msm_camera_io_w(1, vfe32_ctrl->vfebase +
- vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch0]);
- msm_camera_io_w(1, vfe32_ctrl->vfebase +
- vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch1]);
- } else if (vfe32_ctrl->outpath.output_mode &
- VFE32_OUTPUT_MODE_SECONDARY_ALL_CHNLS) {
- msm_camera_io_w(1, vfe32_ctrl->vfebase +
- vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch0]);
- msm_camera_io_w(1, vfe32_ctrl->vfebase +
- vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch1]);
- msm_camera_io_w(1, vfe32_ctrl->vfebase +
- vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch2]);
- }
- break;
- }
-
msm_camio_bus_scale_cfg(
pmctl->sdata->pdata->cam_bus_scale_table, S_PREVIEW);
vfe32_start_common();
@@ -3998,23 +3933,97 @@
vfe32_ctrl->vfebase = 0;
}
+void axi_start(void)
+{
+ switch (vfe32_ctrl->operation_mode) {
+ case VFE_OUTPUTS_PREVIEW:
+ case VFE_OUTPUTS_PREVIEW_AND_VIDEO:
+ if (vfe32_ctrl->outpath.output_mode &
+ VFE32_OUTPUT_MODE_PRIMARY) {
+ msm_camera_io_w(1, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch0]);
+ msm_camera_io_w(1, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch1]);
+ } else if (vfe32_ctrl->outpath.output_mode &
+ VFE32_OUTPUT_MODE_PRIMARY_ALL_CHNLS) {
+ msm_camera_io_w(1, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch0]);
+ msm_camera_io_w(1, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch1]);
+ msm_camera_io_w(1, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch2]);
+ }
+ break;
+ default:
+ if (vfe32_ctrl->outpath.output_mode &
+ VFE32_OUTPUT_MODE_SECONDARY) {
+ msm_camera_io_w(1, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch0]);
+ msm_camera_io_w(1, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch1]);
+ } else if (vfe32_ctrl->outpath.output_mode &
+ VFE32_OUTPUT_MODE_SECONDARY_ALL_CHNLS) {
+ msm_camera_io_w(1, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch0]);
+ msm_camera_io_w(1, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch1]);
+ msm_camera_io_w(1, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch2]);
+ }
+ break;
+ }
+}
+
+void axi_stop(void)
+{
+ uint8_t axiBusyFlag = true;
+ /* axi halt command. */
+ msm_camera_io_w(AXI_HALT,
+ vfe32_ctrl->vfebase + VFE_AXI_CMD);
+ wmb();
+ while (axiBusyFlag) {
+ if (msm_camera_io_r(vfe32_ctrl->vfebase + VFE_AXI_STATUS) & 0x1)
+ axiBusyFlag = false;
+ }
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_camera_io_w_mb(AXI_HALT_CLEAR,
+ vfe32_ctrl->vfebase + VFE_AXI_CMD);
+
+ /* after axi halt, then ok to apply global reset. */
+ /* enable reset_ack and async timer interrupt only while
+ stopping the pipeline.*/
+ msm_camera_io_w(0xf0000000,
+ vfe32_ctrl->vfebase + VFE_IRQ_MASK_0);
+ msm_camera_io_w(VFE_IMASK_WHILE_STOPPING_1,
+ vfe32_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_camera_io_w_mb(VFE_RESET_UPON_STOP_CMD,
+ vfe32_ctrl->vfebase + VFE_GLOBAL_RESET);
+}
+
static int msm_axi_config(struct v4l2_subdev *sd, void __user *arg)
{
struct msm_vfe_cfg_cmd cfgcmd;
struct msm_isp_cmd vfecmd;
int rc = 0;
- if (copy_from_user(&cfgcmd, arg, sizeof(cfgcmd))) {
- ERR_COPY_FROM_USER();
- return -EFAULT;
+ if (NULL != arg) {
+ if (copy_from_user(&cfgcmd, arg, sizeof(cfgcmd))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
}
-
- if (copy_from_user(&vfecmd,
- (void __user *)(cfgcmd.value),
- sizeof(vfecmd))) {
- pr_err("%s %d: copy_from_user failed\n", __func__,
- __LINE__);
- return -EFAULT;
+ if (NULL != cfgcmd.value) {
+ if (copy_from_user(&vfecmd,
+ (void __user *)(cfgcmd.value),
+ sizeof(vfecmd))) {
+ pr_err("%s %d: copy_from_user failed\n", __func__,
+ __LINE__);
+ return -EFAULT;
+ }
}
switch (cfgcmd.cmd_type) {
@@ -4117,6 +4126,12 @@
pr_err("%s Invalid/Unsupported AXI configuration %x",
__func__, cfgcmd.cmd_type);
break;
+ case CMD_AXI_START:
+ axi_start();
+ break;
+ case CMD_AXI_STOP:
+ axi_stop();
+ break;
default:
pr_err("%s Unsupported AXI configuration %x ", __func__,
cfgcmd.cmd_type);
diff --git a/drivers/media/video/msm/sensors/msm_sensor.h b/drivers/media/video/msm/sensors/msm_sensor.h
index 0e51409..556f036 100644
--- a/drivers/media/video/msm/sensors/msm_sensor.h
+++ b/drivers/media/video/msm/sensors/msm_sensor.h
@@ -234,12 +234,6 @@
int32_t msm_sensor_setting1(struct msm_sensor_ctrl_t *s_ctrl,
int update_type, int res);
-int32_t msm_sensor_setting2(struct msm_sensor_ctrl_t *s_ctrl,
- int update_type, int res);
-
-int32_t msm_sensor_setting3(struct msm_sensor_ctrl_t *s_ctrl,
- int update_type, int res);
-
int msm_sensor_enable_debugfs(struct msm_sensor_ctrl_t *s_ctrl);
long msm_sensor_subdev_ioctl(struct v4l2_subdev *sd,
diff --git a/drivers/media/video/msm/sensors/ov5647_v4l2.c b/drivers/media/video/msm/sensors/ov5647_v4l2.c
index aac2f2b..90186ee 100644
--- a/drivers/media/video/msm/sensors/ov5647_v4l2.c
+++ b/drivers/media/video/msm/sensors/ov5647_v4l2.c
@@ -458,7 +458,7 @@
CDBG(KERN_ERR "snapshot exposure seting 0x%x, 0x%x, %d"
, gain, line, line);
s_ctrl->func_tbl->sensor_group_hold_on(s_ctrl);
- if (line > 1964 && line <= 1968) {
+ if (line > 1964) {
msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
s_ctrl->sensor_output_reg_addr->frame_length_lines,
(uint8_t)((line+4) >> 8),
@@ -570,7 +570,7 @@
s_ctrl->func_tbl->sensor_group_hold_on(s_ctrl);
/* adjust frame rate */
- if (line > 980 && line <= 984) {
+ if (line > 980) {
msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
s_ctrl->sensor_output_reg_addr->frame_length_lines,
(uint8_t)((line+4) >> 8),
diff --git a/drivers/media/video/msm/server/Makefile b/drivers/media/video/msm/server/Makefile
new file mode 100644
index 0000000..55abeed
--- /dev/null
+++ b/drivers/media/video/msm/server/Makefile
@@ -0,0 +1,11 @@
+GCC_VERSION := $(shell $(CONFIG_SHELL) $(PWD)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
+
+ifeq ($(CONFIG_MSM_CAMERA_V4L2),y)
+ EXTRA_CFLAGS += -Idrivers/media/video/msm
+ EXTRA_CFLAGS += -Idrivers/media/video/msm/io
+ EXTRA_CFLAGS += -Idrivers/media/video/msm/csi
+ EXTRA_CFLAGS += -Idrivers/media/video/msm/eeprom
+ EXTRA_CFLAGS += -Idrivers/media/video/msm/sensors
+ EXTRA_CFLAGS += -Idrivers/media/video/msm/actuators
+ obj-$(CONFIG_MSM_CAMERA) += msm_cam_server.o
+endif
diff --git a/drivers/media/video/msm/server/msm_cam_server.c b/drivers/media/video/msm/server/msm_cam_server.c
new file mode 100644
index 0000000..7154797f
--- /dev/null
+++ b/drivers/media/video/msm/server/msm_cam_server.c
@@ -0,0 +1,2318 @@
+/* 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 "msm_cam_server.h"
+#include "msm_csid.h"
+#include "msm_csic.h"
+#include "msm_csiphy.h"
+#include "msm_ispif.h"
+#include "msm_sensor.h"
+#include "msm_actuator.h"
+#include "msm_vfe32.h"
+
+#ifdef CONFIG_MSM_CAMERA_DEBUG
+#define D(fmt, args...) pr_debug("msm: " fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+
+static struct msm_cam_server_dev g_server_dev;
+static struct class *msm_class;
+static dev_t msm_devno;
+
+static long msm_server_send_v4l2_evt(void *evt);
+static void msm_cam_server_subdev_notify(struct v4l2_subdev *sd,
+ unsigned int notification, void *arg);
+
+static void msm_queue_init(struct msm_device_queue *queue, const char *name)
+{
+ D("%s\n", __func__);
+ spin_lock_init(&queue->lock);
+ queue->len = 0;
+ queue->max = 0;
+ queue->name = name;
+ INIT_LIST_HEAD(&queue->list);
+ init_waitqueue_head(&queue->wait);
+}
+
+static void msm_enqueue(struct msm_device_queue *queue,
+ struct list_head *entry)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&queue->lock, flags);
+ queue->len++;
+ if (queue->len > queue->max) {
+ queue->max = queue->len;
+ pr_info("%s: queue %s new max is %d\n", __func__,
+ queue->name, queue->max);
+ }
+ list_add_tail(entry, &queue->list);
+ wake_up(&queue->wait);
+ D("%s: woke up %s\n", __func__, queue->name);
+ spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+static void msm_drain_eventq(struct msm_device_queue *queue)
+{
+ unsigned long flags;
+ struct msm_queue_cmd *qcmd;
+ struct msm_isp_event_ctrl *isp_event;
+ spin_lock_irqsave(&queue->lock, flags);
+ while (!list_empty(&queue->list)) {
+ qcmd = list_first_entry(&queue->list,
+ struct msm_queue_cmd, list_eventdata);
+ list_del_init(&qcmd->list_eventdata);
+ isp_event =
+ (struct msm_isp_event_ctrl *)
+ qcmd->command;
+ if (isp_event->isp_data.ctrl.value != NULL)
+ kfree(isp_event->isp_data.ctrl.value);
+ kfree(qcmd->command);
+ free_qcmd(qcmd);
+ }
+ spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+int32_t msm_find_free_queue(void)
+{
+ int i;
+ for (i = 0; i < MAX_NUM_ACTIVE_CAMERA; i++) {
+ struct msm_cam_server_queue *queue;
+ queue = &g_server_dev.server_queue[i];
+ if (!queue->queue_active)
+ return i;
+ }
+ return -EINVAL;
+}
+
+uint32_t msm_cam_server_get_mctl_handle(void)
+{
+ uint32_t i;
+ if ((g_server_dev.mctl_handle_cnt << 8) == 0)
+ g_server_dev.mctl_handle_cnt++;
+ for (i = 0; i < MAX_NUM_ACTIVE_CAMERA; i++) {
+ if (g_server_dev.mctl[i].handle == 0) {
+ g_server_dev.mctl[i].handle =
+ (++g_server_dev.mctl_handle_cnt) << 8 | i;
+ memset(&g_server_dev.mctl[i].mctl,
+ 0, sizeof(g_server_dev.mctl[i].mctl));
+ return g_server_dev.mctl[i].handle;
+ }
+ }
+ return 0;
+}
+
+void msm_cam_server_free_mctl(uint32_t handle)
+{
+ uint32_t mctl_index;
+ mctl_index = handle & 0xff;
+ if ((mctl_index < MAX_NUM_ACTIVE_CAMERA) &&
+ (g_server_dev.mctl[mctl_index].handle == handle))
+ g_server_dev.mctl[mctl_index].handle = 0;
+ else
+ pr_err("%s: invalid free handle\n", __func__);
+}
+
+struct msm_cam_media_controller *msm_cam_server_get_mctl(uint32_t handle)
+{
+ uint32_t mctl_index;
+ mctl_index = handle & 0xff;
+ if ((mctl_index < MAX_NUM_ACTIVE_CAMERA) &&
+ (g_server_dev.mctl[mctl_index].handle == handle))
+ return &g_server_dev.mctl[mctl_index].mctl;
+ return NULL;
+}
+
+static int msm_ctrl_cmd_done(void *arg)
+{
+ void __user *uptr;
+ struct msm_queue_cmd *qcmd;
+ struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;
+ struct msm_ctrl_cmd *command;
+ D("%s\n", __func__);
+
+ command = kzalloc(sizeof(struct msm_ctrl_cmd), GFP_KERNEL);
+ if (!command) {
+ pr_err("%s Insufficient memory. return", __func__);
+ goto command_alloc_fail;
+ }
+
+ qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
+ if (!qcmd) {
+ pr_err("%s Insufficient memory. return", __func__);
+ goto qcmd_alloc_fail;
+ }
+
+ mutex_lock(&g_server_dev.server_queue_lock);
+
+ if (copy_from_user(command, (void __user *)ioctl_ptr->ioctl_ptr,
+ sizeof(struct msm_ctrl_cmd))) {
+ pr_err("%s: copy_from_user failed, size=%d\n",
+ __func__, sizeof(struct msm_ctrl_cmd));
+ goto ctrl_cmd_done_error;
+ }
+
+ if (!g_server_dev.server_queue[command->queue_idx].queue_active) {
+ pr_err("%s: Invalid queue\n", __func__);
+ goto ctrl_cmd_done_error;
+ }
+
+ D("%s qid %d evtid %d %d\n", __func__, command->queue_idx,
+ command->evt_id,
+ g_server_dev.server_queue[command->queue_idx].evt_id);
+
+ if (command->evt_id !=
+ g_server_dev.server_queue[command->queue_idx].evt_id) {
+ pr_err("Invalid event id from userspace\n");
+ goto ctrl_cmd_done_error;
+ }
+
+ atomic_set(&qcmd->on_heap, 1);
+ uptr = command->value;
+ qcmd->command = command;
+
+ if (command->length > 0) {
+ command->value =
+ g_server_dev.server_queue[command->queue_idx].ctrl_data;
+ if (command->length > max_control_command_size) {
+ pr_err("%s: user data %d is too big (max %d)\n",
+ __func__, command->length,
+ max_control_command_size);
+ goto ctrl_cmd_done_error;
+ }
+ if (copy_from_user(command->value, uptr, command->length)) {
+ pr_err("%s: copy_from_user failed, size=%d\n",
+ __func__, sizeof(struct msm_ctrl_cmd));
+ goto ctrl_cmd_done_error;
+ }
+ }
+ msm_enqueue(&g_server_dev.server_queue
+ [command->queue_idx].ctrl_q, &qcmd->list_control);
+ mutex_unlock(&g_server_dev.server_queue_lock);
+ return 0;
+
+ctrl_cmd_done_error:
+ mutex_unlock(&g_server_dev.server_queue_lock);
+ free_qcmd(qcmd);
+qcmd_alloc_fail:
+ kfree(command);
+command_alloc_fail:
+ return -EINVAL;
+}
+
+/* send control command to config and wait for results*/
+static int msm_server_control(struct msm_cam_server_dev *server_dev,
+ struct msm_ctrl_cmd *out)
+{
+ int rc = 0;
+ void *value;
+ struct msm_queue_cmd *rcmd;
+ struct msm_queue_cmd *event_qcmd;
+ struct msm_ctrl_cmd *ctrlcmd;
+ struct msm_device_queue *queue =
+ &server_dev->server_queue[out->queue_idx].ctrl_q;
+
+ struct v4l2_event v4l2_evt;
+ struct msm_isp_event_ctrl *isp_event;
+ void *ctrlcmd_data;
+
+ event_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
+ if (!event_qcmd) {
+ pr_err("%s Insufficient memory. return", __func__);
+ rc = -ENOMEM;
+ goto event_qcmd_alloc_fail;
+ }
+
+ isp_event = kzalloc(sizeof(struct msm_isp_event_ctrl), GFP_KERNEL);
+ if (!isp_event) {
+ pr_err("%s Insufficient memory. return", __func__);
+ rc = -ENOMEM;
+ goto isp_event_alloc_fail;
+ }
+
+ D("%s\n", __func__);
+ mutex_lock(&server_dev->server_queue_lock);
+ if (++server_dev->server_evt_id == 0)
+ server_dev->server_evt_id++;
+
+ D("%s qid %d evtid %d\n", __func__, out->queue_idx,
+ server_dev->server_evt_id);
+ server_dev->server_queue[out->queue_idx].evt_id =
+ server_dev->server_evt_id;
+ v4l2_evt.type = V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_V4L2;
+ v4l2_evt.u.data[0] = out->queue_idx;
+ /* setup event object to transfer the command; */
+ isp_event->resptype = MSM_CAM_RESP_V4L2;
+ isp_event->isp_data.ctrl = *out;
+ isp_event->isp_data.ctrl.evt_id = server_dev->server_evt_id;
+
+ if (out->value != NULL && out->length != 0) {
+ ctrlcmd_data = kzalloc(out->length, GFP_KERNEL);
+ if (!ctrlcmd_data) {
+ rc = -ENOMEM;
+ goto ctrlcmd_alloc_fail;
+ }
+ memcpy(ctrlcmd_data, out->value, out->length);
+ isp_event->isp_data.ctrl.value = ctrlcmd_data;
+ }
+
+ atomic_set(&event_qcmd->on_heap, 1);
+ event_qcmd->command = isp_event;
+
+ msm_enqueue(&server_dev->server_queue[out->queue_idx].eventData_q,
+ &event_qcmd->list_eventdata);
+
+ /* now send command to config thread in userspace,
+ * and wait for results */
+ v4l2_event_queue(server_dev->server_command_queue.pvdev,
+ &v4l2_evt);
+ D("%s v4l2_event_queue: type = 0x%x\n", __func__, v4l2_evt.type);
+ mutex_unlock(&server_dev->server_queue_lock);
+
+ /* wait for config return status */
+ D("Waiting for config status\n");
+ rc = wait_event_interruptible_timeout(queue->wait,
+ !list_empty_careful(&queue->list),
+ msecs_to_jiffies(out->timeout_ms));
+ D("Waiting is over for config status\n");
+ if (list_empty_careful(&queue->list)) {
+ if (!rc)
+ rc = -ETIMEDOUT;
+ if (rc < 0) {
+ if (++server_dev->server_evt_id == 0)
+ server_dev->server_evt_id++;
+ pr_err("%s: wait_event error %d\n", __func__, rc);
+ return rc;
+ }
+ }
+
+ rcmd = msm_dequeue(queue, list_control);
+ BUG_ON(!rcmd);
+ D("%s Finished servicing ioctl\n", __func__);
+
+ ctrlcmd = (struct msm_ctrl_cmd *)(rcmd->command);
+ value = out->value;
+ if (ctrlcmd->length > 0 && value != NULL &&
+ ctrlcmd->length <= out->length)
+ memcpy(value, ctrlcmd->value, ctrlcmd->length);
+
+ memcpy(out, ctrlcmd, sizeof(struct msm_ctrl_cmd));
+ out->value = value;
+
+ kfree(ctrlcmd);
+ free_qcmd(rcmd);
+ D("%s: rc %d\n", __func__, rc);
+ /* rc is the time elapsed. */
+ if (rc >= 0) {
+ /* TODO: Refactor msm_ctrl_cmd::status field */
+ if (out->status == 0)
+ rc = -1;
+ else if (out->status == 1 || out->status == 4)
+ rc = 0;
+ else
+ rc = -EINVAL;
+ }
+ return rc;
+
+ctrlcmd_alloc_fail:
+ kfree(isp_event);
+isp_event_alloc_fail:
+ kfree(event_qcmd);
+event_qcmd_alloc_fail:
+ return rc;
+}
+
+int msm_server_get_crop(struct msm_cam_v4l2_device *pcam,
+ int idx, struct v4l2_crop *crop)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd ctrlcmd;
+
+ BUG_ON(crop == NULL);
+
+ ctrlcmd.type = MSM_V4L2_GET_CROP;
+ ctrlcmd.length = sizeof(struct v4l2_crop);
+ ctrlcmd.value = (void *)crop;
+ ctrlcmd.timeout_ms = 1000;
+ ctrlcmd.vnode_id = pcam->vnode_id;
+ ctrlcmd.queue_idx = pcam->server_queue_idx;
+ ctrlcmd.stream_type = pcam->dev_inst[idx]->image_mode;
+ ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
+
+ /* send command to config thread in userspace, and get return value */
+ rc = msm_server_control(&g_server_dev, &ctrlcmd);
+ D("%s: rc = %d\n", __func__, rc);
+
+ return rc;
+}
+
+/*send open command to server*/
+int msm_send_open_server(struct msm_cam_v4l2_device *pcam)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd ctrlcmd;
+ D("%s qid %d\n", __func__, pcam->server_queue_idx);
+ ctrlcmd.type = MSM_V4L2_OPEN;
+ ctrlcmd.timeout_ms = 10000;
+ ctrlcmd.length = strnlen(g_server_dev.config_info.config_dev_name[0],
+ MAX_DEV_NAME_LEN)+1;
+ ctrlcmd.value = (char *)g_server_dev.config_info.config_dev_name[0];
+ ctrlcmd.vnode_id = pcam->vnode_id;
+ ctrlcmd.queue_idx = pcam->server_queue_idx;
+ ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
+
+ /* send command to config thread in usersspace, and get return value */
+ rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+ return rc;
+}
+
+int msm_send_close_server(struct msm_cam_v4l2_device *pcam)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd ctrlcmd;
+ D("%s qid %d\n", __func__, pcam->server_queue_idx);
+ ctrlcmd.type = MSM_V4L2_CLOSE;
+ ctrlcmd.timeout_ms = 10000;
+ ctrlcmd.length = strnlen(g_server_dev.config_info.config_dev_name[0],
+ MAX_DEV_NAME_LEN)+1;
+ ctrlcmd.value = (char *)g_server_dev.config_info.config_dev_name[0];
+ ctrlcmd.vnode_id = pcam->vnode_id;
+ ctrlcmd.queue_idx = pcam->server_queue_idx;
+ ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
+
+ /* send command to config thread in usersspace, and get return value */
+ rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+ return rc;
+}
+
+int msm_server_set_fmt(struct msm_cam_v4l2_device *pcam, int idx,
+ struct v4l2_format *pfmt)
+{
+ int rc = 0;
+ int i = 0;
+ struct v4l2_pix_format *pix = &pfmt->fmt.pix;
+ struct msm_ctrl_cmd ctrlcmd;
+ struct img_plane_info plane_info;
+
+ plane_info.width = pix->width;
+ plane_info.height = pix->height;
+ plane_info.pixelformat = pix->pixelformat;
+ plane_info.buffer_type = pfmt->type;
+ plane_info.ext_mode = pcam->dev_inst[idx]->image_mode;
+ plane_info.num_planes = 1;
+ D("%s: %d, %d, 0x%x\n", __func__,
+ pfmt->fmt.pix.width, pfmt->fmt.pix.height,
+ pfmt->fmt.pix.pixelformat);
+
+ if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ D("%s, Attention! Wrong buf-type %d\n", __func__, pfmt->type);
+
+ for (i = 0; i < pcam->num_fmts; i++)
+ if (pcam->usr_fmts[i].fourcc == pix->pixelformat)
+ break;
+ if (i == pcam->num_fmts) {
+ pr_err("%s: User requested pixelformat %x not supported\n",
+ __func__, pix->pixelformat);
+ return -EINVAL;
+ }
+
+ ctrlcmd.type = MSM_V4L2_VID_CAP_TYPE;
+ ctrlcmd.length = sizeof(struct img_plane_info);
+ ctrlcmd.value = (void *)&plane_info;
+ ctrlcmd.timeout_ms = 10000;
+ ctrlcmd.vnode_id = pcam->vnode_id;
+ ctrlcmd.queue_idx = pcam->server_queue_idx;
+ ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
+
+ /* send command to config thread in usersspace, and get return value */
+ rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+ if (rc >= 0) {
+ pcam->dev_inst[idx]->vid_fmt = *pfmt;
+ pcam->dev_inst[idx]->sensor_pxlcode
+ = pcam->usr_fmts[i].pxlcode;
+ D("%s:inst=0x%x,idx=%d,width=%d,heigth=%d\n",
+ __func__, (u32)pcam->dev_inst[idx], idx,
+ pcam->dev_inst[idx]->vid_fmt.fmt.pix.width,
+ pcam->dev_inst[idx]->vid_fmt.fmt.pix.height);
+ pcam->dev_inst[idx]->plane_info = plane_info;
+ }
+
+ return rc;
+}
+
+int msm_server_set_fmt_mplane(struct msm_cam_v4l2_device *pcam, int idx,
+ struct v4l2_format *pfmt)
+{
+ int rc = 0;
+ int i = 0;
+ struct v4l2_pix_format_mplane *pix_mp = &pfmt->fmt.pix_mp;
+ struct msm_ctrl_cmd ctrlcmd;
+ struct img_plane_info plane_info;
+
+ plane_info.width = pix_mp->width;
+ plane_info.height = pix_mp->height;
+ plane_info.pixelformat = pix_mp->pixelformat;
+ plane_info.buffer_type = pfmt->type;
+ plane_info.ext_mode = pcam->dev_inst[idx]->image_mode;
+ plane_info.num_planes = pix_mp->num_planes;
+ if (plane_info.num_planes <= 0 ||
+ plane_info.num_planes > VIDEO_MAX_PLANES) {
+ pr_err("%s Invalid number of planes set %d", __func__,
+ plane_info.num_planes);
+ return -EINVAL;
+ }
+ D("%s: %d, %d, 0x%x\n", __func__,
+ pfmt->fmt.pix_mp.width, pfmt->fmt.pix_mp.height,
+ pfmt->fmt.pix_mp.pixelformat);
+
+ if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ pr_err("%s, Attention! Wrong buf-type %d\n",
+ __func__, pfmt->type);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < pcam->num_fmts; i++)
+ if (pcam->usr_fmts[i].fourcc == pix_mp->pixelformat)
+ break;
+ if (i == pcam->num_fmts) {
+ pr_err("%s: User requested pixelformat %x not supported\n",
+ __func__, pix_mp->pixelformat);
+ return -EINVAL;
+ }
+
+ ctrlcmd.type = MSM_V4L2_VID_CAP_TYPE;
+ ctrlcmd.length = sizeof(struct img_plane_info);
+ ctrlcmd.value = (void *)&plane_info;
+ ctrlcmd.timeout_ms = 10000;
+ ctrlcmd.vnode_id = pcam->vnode_id;
+ ctrlcmd.queue_idx = pcam->server_queue_idx;
+
+ /* send command to config thread in usersspace, and get return value */
+ rc = msm_server_control(&g_server_dev, &ctrlcmd);
+ if (rc >= 0) {
+ pcam->dev_inst[idx]->vid_fmt = *pfmt;
+ pcam->dev_inst[idx]->sensor_pxlcode
+ = pcam->usr_fmts[i].pxlcode;
+ D("%s:inst=0x%x,idx=%d,width=%d,heigth=%d\n",
+ __func__, (u32)pcam->dev_inst[idx], idx,
+ pcam->dev_inst[idx]->vid_fmt.fmt.pix_mp.width,
+ pcam->dev_inst[idx]->vid_fmt.fmt.pix_mp.height);
+ pcam->dev_inst[idx]->plane_info = plane_info;
+ }
+
+ return rc;
+}
+
+int msm_server_streamon(struct msm_cam_v4l2_device *pcam, int idx)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd ctrlcmd;
+ D("%s\n", __func__);
+ ctrlcmd.type = MSM_V4L2_STREAM_ON;
+ ctrlcmd.timeout_ms = 10000;
+ ctrlcmd.length = 0;
+ ctrlcmd.value = NULL;
+ ctrlcmd.stream_type = pcam->dev_inst[idx]->image_mode;
+ ctrlcmd.vnode_id = pcam->vnode_id;
+ ctrlcmd.queue_idx = pcam->server_queue_idx;
+ ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
+
+
+ /* send command to config thread in usersspace, and get return value */
+ rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+ return rc;
+}
+
+int msm_server_streamoff(struct msm_cam_v4l2_device *pcam, int idx)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd ctrlcmd;
+
+ D("%s, pcam = 0x%x\n", __func__, (u32)pcam);
+ ctrlcmd.type = MSM_V4L2_STREAM_OFF;
+ ctrlcmd.timeout_ms = 10000;
+ ctrlcmd.length = 0;
+ ctrlcmd.value = NULL;
+ ctrlcmd.stream_type = pcam->dev_inst[idx]->image_mode;
+ ctrlcmd.vnode_id = pcam->vnode_id;
+ ctrlcmd.queue_idx = pcam->server_queue_idx;
+ ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
+
+ /* send command to config thread in usersspace, and get return value */
+ rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+ return rc;
+}
+
+int msm_server_proc_ctrl_cmd(struct msm_cam_v4l2_device *pcam,
+ struct v4l2_control *ctrl, int is_set_cmd)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd ctrlcmd, *tmp_cmd;
+ uint8_t *ctrl_data = NULL;
+ void __user *uptr_cmd;
+ void __user *uptr_value;
+ uint32_t cmd_len = sizeof(struct msm_ctrl_cmd);
+ uint32_t value_len;
+
+ tmp_cmd = (struct msm_ctrl_cmd *)ctrl->value;
+ uptr_cmd = (void __user *)ctrl->value;
+ uptr_value = (void __user *)tmp_cmd->value;
+ value_len = tmp_cmd->length;
+
+ D("%s: cmd type = %d, up1=0x%x, ulen1=%d, up2=0x%x, ulen2=%d\n",
+ __func__, tmp_cmd->type, (uint32_t)uptr_cmd, cmd_len,
+ (uint32_t)uptr_value, tmp_cmd->length);
+
+ ctrl_data = kzalloc(value_len+cmd_len, GFP_KERNEL);
+ if (ctrl_data == 0) {
+ pr_err("%s could not allocate memory\n", __func__);
+ rc = -ENOMEM;
+ goto end;
+ }
+ tmp_cmd = (struct msm_ctrl_cmd *)ctrl_data;
+ if (copy_from_user((void *)ctrl_data, uptr_cmd,
+ cmd_len)) {
+ pr_err("%s: copy_from_user failed.\n", __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+ tmp_cmd->value = (void *)(ctrl_data+cmd_len);
+ if (uptr_value && tmp_cmd->length > 0) {
+ if (copy_from_user((void *)tmp_cmd->value, uptr_value,
+ value_len)) {
+ pr_err("%s: copy_from_user failed, size=%d\n",
+ __func__, value_len);
+ rc = -EINVAL;
+ goto end;
+ }
+ } else
+ tmp_cmd->value = NULL;
+
+ ctrlcmd.type = MSM_V4L2_SET_CTRL_CMD;
+ ctrlcmd.length = cmd_len + value_len;
+ ctrlcmd.value = (void *)ctrl_data;
+ if (tmp_cmd->timeout_ms > 0)
+ ctrlcmd.timeout_ms = tmp_cmd->timeout_ms;
+ else
+ ctrlcmd.timeout_ms = 1000;
+ ctrlcmd.vnode_id = pcam->vnode_id;
+ ctrlcmd.queue_idx = pcam->server_queue_idx;
+ ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
+ /* send command to config thread in usersspace, and get return value */
+ rc = msm_server_control(&g_server_dev, &ctrlcmd);
+ D("%s: msm_server_control rc=%d\n", __func__, rc);
+ if (rc == 0) {
+ if (uptr_value && tmp_cmd->length > 0 &&
+ copy_to_user((void __user *)uptr_value,
+ (void *)(ctrl_data+cmd_len), tmp_cmd->length)) {
+ pr_err("%s: copy_to_user failed, size=%d\n",
+ __func__, tmp_cmd->length);
+ rc = -EINVAL;
+ goto end;
+ }
+ tmp_cmd->value = uptr_value;
+ if (copy_to_user((void __user *)uptr_cmd,
+ (void *)tmp_cmd, cmd_len)) {
+ pr_err("%s: copy_to_user failed in cpy, size=%d\n",
+ __func__, cmd_len);
+ rc = -EINVAL;
+ goto end;
+ }
+ }
+end:
+ D("%s: END, type = %d, vaddr = 0x%x, vlen = %d, status = %d, rc = %d\n",
+ __func__, tmp_cmd->type, (uint32_t)tmp_cmd->value,
+ tmp_cmd->length, tmp_cmd->status, rc);
+ kfree(ctrl_data);
+ return rc;
+}
+
+int msm_server_s_ctrl(struct msm_cam_v4l2_device *pcam,
+ struct v4l2_control *ctrl)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd ctrlcmd;
+ uint8_t ctrl_data[max_control_command_size];
+
+ WARN_ON(ctrl == NULL);
+ if (ctrl == NULL) {
+ pr_err("%s Invalid control\n", __func__);
+ return -EINVAL;
+ }
+ if (ctrl->id == MSM_V4L2_PID_CTRL_CMD)
+ return msm_server_proc_ctrl_cmd(pcam, ctrl, 1);
+
+ memset(ctrl_data, 0, sizeof(ctrl_data));
+
+ ctrlcmd.type = MSM_V4L2_SET_CTRL;
+ ctrlcmd.length = sizeof(struct v4l2_control);
+ ctrlcmd.value = (void *)ctrl_data;
+ memcpy(ctrlcmd.value, ctrl, ctrlcmd.length);
+ ctrlcmd.timeout_ms = 1000;
+ ctrlcmd.vnode_id = pcam->vnode_id;
+ ctrlcmd.queue_idx = pcam->server_queue_idx;
+ ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
+
+ /* send command to config thread in usersspace, and get return value */
+ rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+ return rc;
+}
+
+int msm_server_g_ctrl(struct msm_cam_v4l2_device *pcam,
+ struct v4l2_control *ctrl)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd ctrlcmd;
+ uint8_t ctrl_data[max_control_command_size];
+
+ WARN_ON(ctrl == NULL);
+ if (ctrl == NULL) {
+ pr_err("%s Invalid control\n", __func__);
+ return -EINVAL;
+ }
+ if (ctrl->id == MSM_V4L2_PID_CTRL_CMD)
+ return msm_server_proc_ctrl_cmd(pcam, ctrl, 0);
+
+ memset(ctrl_data, 0, sizeof(ctrl_data));
+
+ ctrlcmd.type = MSM_V4L2_GET_CTRL;
+ ctrlcmd.length = sizeof(struct v4l2_control);
+ ctrlcmd.value = (void *)ctrl_data;
+ memcpy(ctrlcmd.value, ctrl, ctrlcmd.length);
+ ctrlcmd.timeout_ms = 1000;
+ ctrlcmd.vnode_id = pcam->vnode_id;
+ ctrlcmd.queue_idx = pcam->server_queue_idx;
+ ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
+
+ /* send command to config thread in usersspace, and get return value */
+ rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+ ctrl->value = ((struct v4l2_control *)ctrlcmd.value)->value;
+
+ return rc;
+}
+
+int msm_server_q_ctrl(struct msm_cam_v4l2_device *pcam,
+ struct v4l2_queryctrl *queryctrl)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd ctrlcmd;
+ uint8_t ctrl_data[max_control_command_size];
+
+ WARN_ON(queryctrl == NULL);
+ memset(ctrl_data, 0, sizeof(ctrl_data));
+
+ ctrlcmd.type = MSM_V4L2_QUERY_CTRL;
+ ctrlcmd.length = sizeof(struct v4l2_queryctrl);
+ ctrlcmd.value = (void *)ctrl_data;
+ memcpy(ctrlcmd.value, queryctrl, ctrlcmd.length);
+ ctrlcmd.timeout_ms = 1000;
+ ctrlcmd.vnode_id = pcam->vnode_id;
+ ctrlcmd.queue_idx = pcam->server_queue_idx;
+ ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
+
+ /* send command to config thread in userspace, and get return value */
+ rc = msm_server_control(&g_server_dev, &ctrlcmd);
+ D("%s: rc = %d\n", __func__, rc);
+
+ if (rc >= 0)
+ memcpy(queryctrl, ctrlcmd.value, sizeof(struct v4l2_queryctrl));
+
+ return rc;
+}
+
+int msm_server_get_fmt(struct msm_cam_v4l2_device *pcam,
+ int idx, struct v4l2_format *pfmt)
+{
+ struct v4l2_pix_format *pix = &pfmt->fmt.pix;
+
+ pix->width = pcam->dev_inst[idx]->vid_fmt.fmt.pix.width;
+ pix->height = pcam->dev_inst[idx]->vid_fmt.fmt.pix.height;
+ pix->field = pcam->dev_inst[idx]->vid_fmt.fmt.pix.field;
+ pix->pixelformat = pcam->dev_inst[idx]->vid_fmt.fmt.pix.pixelformat;
+ pix->bytesperline = pcam->dev_inst[idx]->vid_fmt.fmt.pix.bytesperline;
+ pix->colorspace = pcam->dev_inst[idx]->vid_fmt.fmt.pix.colorspace;
+ if (pix->bytesperline < 0)
+ return pix->bytesperline;
+
+ pix->sizeimage = pix->height * pix->bytesperline;
+
+ return 0;
+}
+
+int msm_server_get_fmt_mplane(struct msm_cam_v4l2_device *pcam,
+ int idx, struct v4l2_format *pfmt)
+{
+ *pfmt = pcam->dev_inst[idx]->vid_fmt;
+ return 0;
+}
+
+int msm_server_try_fmt(struct msm_cam_v4l2_device *pcam,
+ struct v4l2_format *pfmt)
+{
+ int rc = 0;
+ int i = 0;
+ struct v4l2_pix_format *pix = &pfmt->fmt.pix;
+
+ D("%s: 0x%x\n", __func__, pix->pixelformat);
+ if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ pr_err("%s: pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE!\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* check if the format is supported by this host-sensor combo */
+ for (i = 0; i < pcam->num_fmts; i++) {
+ D("%s: usr_fmts.fourcc: 0x%x\n", __func__,
+ pcam->usr_fmts[i].fourcc);
+ if (pcam->usr_fmts[i].fourcc == pix->pixelformat)
+ break;
+ }
+
+ if (i == pcam->num_fmts) {
+ pr_err("%s: Format %x not found\n", __func__, pix->pixelformat);
+ return -EINVAL;
+ }
+ return rc;
+}
+
+int msm_server_try_fmt_mplane(struct msm_cam_v4l2_device *pcam,
+ struct v4l2_format *pfmt)
+{
+ int rc = 0;
+ int i = 0;
+ struct v4l2_pix_format_mplane *pix_mp = &pfmt->fmt.pix_mp;
+
+ D("%s: 0x%x\n", __func__, pix_mp->pixelformat);
+ if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ pr_err("%s: Incorrect format type %d ",
+ __func__, pfmt->type);
+ return -EINVAL;
+ }
+
+ /* check if the format is supported by this host-sensor combo */
+ for (i = 0; i < pcam->num_fmts; i++) {
+ D("%s: usr_fmts.fourcc: 0x%x\n", __func__,
+ pcam->usr_fmts[i].fourcc);
+ if (pcam->usr_fmts[i].fourcc == pix_mp->pixelformat)
+ break;
+ }
+
+ if (i == pcam->num_fmts) {
+ pr_err("%s: Format %x not found\n",
+ __func__, pix_mp->pixelformat);
+ return -EINVAL;
+ }
+ return rc;
+}
+
+int msm_server_v4l2_subscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ int rc = 0;
+
+ D("%s: fh = 0x%x, type = 0x%x", __func__, (u32)fh, sub->type);
+ if (sub->type == V4L2_EVENT_ALL) {
+ /*sub->type = MSM_ISP_EVENT_START;*/
+ sub->type = V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_CTRL;
+ D("sub->type start = 0x%x\n", sub->type);
+ do {
+ rc = v4l2_event_subscribe(fh, sub);
+ if (rc < 0) {
+ D("%s: failed for evtType = 0x%x, rc = %d\n",
+ __func__, sub->type, rc);
+ /* unsubscribe all events here and return */
+ sub->type = V4L2_EVENT_ALL;
+ v4l2_event_unsubscribe(fh, sub);
+ return rc;
+ } else
+ D("%s: subscribed evtType = 0x%x, rc = %d\n",
+ __func__, sub->type, rc);
+ sub->type++;
+ D("sub->type while = 0x%x\n", sub->type);
+ } while (sub->type !=
+ V4L2_EVENT_PRIVATE_START + MSM_SVR_RESP_MAX);
+ } else {
+ D("sub->type not V4L2_EVENT_ALL = 0x%x\n", sub->type);
+ rc = v4l2_event_subscribe(fh, sub);
+ if (rc < 0)
+ D("%s: failed for evtType = 0x%x, rc = %d\n",
+ __func__, sub->type, rc);
+ }
+
+ D("%s: rc = %d\n", __func__, rc);
+ return rc;
+}
+
+int msm_server_v4l2_unsubscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ int rc = 0;
+
+ D("%s: fh = 0x%x\n", __func__, (u32)fh);
+ rc = v4l2_event_unsubscribe(fh, sub);
+ D("%s: rc = %d\n", __func__, rc);
+ return rc;
+}
+
+/* open an active camera session to manage the streaming logic */
+static int msm_cam_server_open_session(struct msm_cam_server_dev *ps,
+ struct msm_cam_v4l2_device *pcam)
+{
+ int rc = 0;
+ struct msm_cam_media_controller *pmctl;
+
+ D("%s\n", __func__);
+
+ if (!ps || !pcam) {
+ pr_err("%s NULL pointer passed in!\n", __func__);
+ return rc;
+ }
+
+ /* The number of camera instance should be controlled by the
+ resource manager. Currently supporting one active instance
+ until multiple instances are supported */
+ if (atomic_read(&ps->number_pcam_active) > 0) {
+ pr_err("%s Cannot have more than one active camera %d\n",
+ __func__, atomic_read(&ps->number_pcam_active));
+ return -EINVAL;
+ }
+ /* book keeping this camera session*/
+ ps->pcam_active = pcam;
+ atomic_inc(&ps->number_pcam_active);
+
+ D("config pcam = 0x%p\n", ps->pcam_active);
+
+ /* initialization the media controller module*/
+ msm_mctl_init(pcam);
+
+ /*for single VFE msms (8660, 8960v1), just populate the session
+ with our VFE devices that registered*/
+ pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
+ pmctl->axi_sdev = ps->axi_device[0];
+ pmctl->isp_sdev = ps->isp_subdev[0];
+ return rc;
+}
+
+/* close an active camera session to server */
+static int msm_cam_server_close_session(struct msm_cam_server_dev *ps,
+ struct msm_cam_v4l2_device *pcam)
+{
+ int rc = 0;
+ D("%s\n", __func__);
+
+ if (!ps || !pcam) {
+ D("%s NULL pointer passed in!\n", __func__);
+ return rc;
+ }
+
+
+ atomic_dec(&ps->number_pcam_active);
+ ps->pcam_active = NULL;
+
+ msm_mctl_free(pcam);
+ return rc;
+}
+
+static long msm_ioctl_server(struct file *file, void *fh,
+ bool valid_prio, int cmd, void *arg)
+{
+ int rc = -EINVAL;
+ struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;
+ struct msm_camera_info temp_cam_info;
+ struct msm_cam_config_dev_info temp_config_info;
+ struct msm_mctl_node_info temp_mctl_info;
+ int i;
+
+ D("%s: cmd %d\n", __func__, _IOC_NR(cmd));
+
+ switch (cmd) {
+ case MSM_CAM_V4L2_IOCTL_GET_CAMERA_INFO:
+ if (copy_from_user(&temp_cam_info,
+ (void __user *)ioctl_ptr->ioctl_ptr,
+ sizeof(struct msm_camera_info))) {
+ pr_err("%s Copy from user failed for cmd %d",
+ __func__, cmd);
+ rc = -EINVAL;
+ return rc;
+ }
+ for (i = 0; i < g_server_dev.camera_info.num_cameras; i++) {
+ if (copy_to_user((void __user *)
+ temp_cam_info.video_dev_name[i],
+ g_server_dev.camera_info.video_dev_name[i],
+ strnlen(g_server_dev.camera_info.video_dev_name[i],
+ MAX_DEV_NAME_LEN))) {
+ pr_err("%s Copy to user failed for cmd %d",
+ __func__, cmd);
+ rc = -EINVAL;
+ return rc;
+ }
+ temp_cam_info.has_3d_support[i] =
+ g_server_dev.camera_info.has_3d_support[i];
+ temp_cam_info.is_internal_cam[i] =
+ g_server_dev.camera_info.is_internal_cam[i];
+ temp_cam_info.s_mount_angle[i] =
+ g_server_dev.camera_info.s_mount_angle[i];
+ temp_cam_info.sensor_type[i] =
+ g_server_dev.camera_info.sensor_type[i];
+
+ }
+ temp_cam_info.num_cameras =
+ g_server_dev.camera_info.num_cameras;
+ if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
+ &temp_cam_info, sizeof(struct msm_camera_info))) {
+ pr_err("%s Copy to user failed for cmd %d",
+ __func__, cmd);
+ rc = -EINVAL;
+ return rc;
+ }
+ rc = 0;
+ break;
+
+ case MSM_CAM_V4L2_IOCTL_GET_CONFIG_INFO:
+ if (copy_from_user(&temp_config_info,
+ (void __user *)ioctl_ptr->ioctl_ptr,
+ sizeof(struct msm_cam_config_dev_info))) {
+ pr_err("%s Copy from user failed for cmd %d",
+ __func__, cmd);
+ rc = -EINVAL;
+ return rc;
+ }
+ for (i = 0;
+ i < g_server_dev.config_info.num_config_nodes; i++) {
+ if (copy_to_user(
+ (void __user *)temp_config_info.config_dev_name[i],
+ g_server_dev.config_info.config_dev_name[i],
+ strnlen(g_server_dev.config_info.config_dev_name[i],
+ MAX_DEV_NAME_LEN))) {
+ pr_err("%s Copy to user failed for cmd %d",
+ __func__, cmd);
+ rc = -EINVAL;
+ return rc;
+ }
+ }
+ temp_config_info.num_config_nodes =
+ g_server_dev.config_info.num_config_nodes;
+ if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
+ &temp_config_info,
+ sizeof(struct msm_cam_config_dev_info))) {
+ pr_err("%s Copy to user failed for cmd %d",
+ __func__, cmd);
+ rc = -EINVAL;
+ return rc;
+ }
+ rc = 0;
+ break;
+ case MSM_CAM_V4L2_IOCTL_GET_MCTL_INFO:
+ if (copy_from_user(&temp_mctl_info,
+ (void __user *)ioctl_ptr->ioctl_ptr,
+ sizeof(struct msm_mctl_node_info))) {
+ pr_err("%s Copy from user failed for cmd %d",
+ __func__, cmd);
+ rc = -EINVAL;
+ return rc;
+ }
+ for (i = 0; i < g_server_dev.mctl_node_info.num_mctl_nodes;
+ i++) {
+ if (copy_to_user((void __user *)
+ temp_mctl_info.mctl_node_name[i],
+ g_server_dev.mctl_node_info.mctl_node_name[i], strnlen(
+ g_server_dev.mctl_node_info.mctl_node_name[i],
+ MAX_DEV_NAME_LEN))) {
+ pr_err("%s Copy to user failed for cmd %d",
+ __func__, cmd);
+ rc = -EINVAL;
+ return rc;
+ }
+ }
+ temp_mctl_info.num_mctl_nodes =
+ g_server_dev.mctl_node_info.num_mctl_nodes;
+ if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
+ &temp_mctl_info, sizeof(struct msm_mctl_node_info))) {
+ pr_err("%s Copy to user failed for cmd %d",
+ __func__, cmd);
+ rc = -EINVAL;
+ return rc;
+ }
+ rc = 0;
+ break;
+
+ case MSM_CAM_V4L2_IOCTL_CTRL_CMD_DONE:
+ D("%s: MSM_CAM_IOCTL_CTRL_CMD_DONE\n", __func__);
+ rc = msm_ctrl_cmd_done(arg);
+ break;
+
+ case MSM_CAM_V4L2_IOCTL_GET_EVENT_PAYLOAD: {
+ struct msm_queue_cmd *event_cmd;
+ struct msm_isp_event_ctrl u_isp_event;
+ struct msm_isp_event_ctrl *k_isp_event;
+ struct msm_device_queue *queue;
+ void __user *u_ctrl_value = NULL;
+ if (copy_from_user(&u_isp_event,
+ (void __user *)ioctl_ptr->ioctl_ptr,
+ sizeof(struct msm_isp_event_ctrl))) {
+ pr_err("%s Copy from user failed for cmd %d",
+ __func__, cmd);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ mutex_lock(&g_server_dev.server_queue_lock);
+ if (!g_server_dev.server_queue
+ [u_isp_event.isp_data.ctrl.queue_idx].queue_active) {
+ pr_err("%s: Invalid queue\n", __func__);
+ mutex_unlock(&g_server_dev.server_queue_lock);
+ rc = -EINVAL;
+ return rc;
+ }
+ queue = &g_server_dev.server_queue
+ [u_isp_event.isp_data.ctrl.queue_idx].eventData_q;
+ event_cmd = msm_dequeue(queue, list_eventdata);
+ if (!event_cmd) {
+ pr_err("%s: No event payload\n", __func__);
+ rc = -EINVAL;
+ mutex_unlock(&g_server_dev.server_queue_lock);
+ return rc;
+ }
+ k_isp_event = (struct msm_isp_event_ctrl *)
+ event_cmd->command;
+ free_qcmd(event_cmd);
+
+ /* Save the pointer of the user allocated command buffer*/
+ u_ctrl_value = u_isp_event.isp_data.ctrl.value;
+
+ /* Copy the event structure into user struct*/
+ u_isp_event = *k_isp_event;
+
+ /* Restore the saved pointer of the user
+ * allocated command buffer. */
+ u_isp_event.isp_data.ctrl.value = u_ctrl_value;
+
+ /* Copy the ctrl cmd, if present*/
+ if (k_isp_event->isp_data.ctrl.length > 0 &&
+ k_isp_event->isp_data.ctrl.value != NULL) {
+ void *k_ctrl_value =
+ k_isp_event->isp_data.ctrl.value;
+ if (copy_to_user(u_ctrl_value, k_ctrl_value,
+ k_isp_event->isp_data.ctrl.length)) {
+ pr_err("%s Copy to user failed for cmd %d",
+ __func__, cmd);
+ kfree(k_isp_event->isp_data.ctrl.value);
+ kfree(k_isp_event);
+ rc = -EINVAL;
+ mutex_unlock(&g_server_dev.server_queue_lock);
+ break;
+ }
+ kfree(k_isp_event->isp_data.ctrl.value);
+ }
+ if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
+ &u_isp_event, sizeof(struct msm_isp_event_ctrl))) {
+ pr_err("%s Copy to user failed for cmd %d",
+ __func__, cmd);
+ kfree(k_isp_event);
+ mutex_unlock(&g_server_dev.server_queue_lock);
+ rc = -EINVAL;
+ return rc;
+ }
+ kfree(k_isp_event);
+ mutex_unlock(&g_server_dev.server_queue_lock);
+ rc = 0;
+ break;
+ }
+
+ case MSM_CAM_IOCTL_SEND_EVENT:
+ rc = msm_server_send_v4l2_evt(arg);
+ break;
+
+ default:
+ pr_err("%s: Invalid IOCTL = %d", __func__, cmd);
+ break;
+ }
+ return rc;
+}
+
+static int msm_open_server(struct file *fp)
+{
+ int rc = 0;
+ D("%s: open %s\n", __func__, fp->f_path.dentry->d_name.name);
+ mutex_lock(&g_server_dev.server_lock);
+ g_server_dev.use_count++;
+ if (g_server_dev.use_count == 1)
+ fp->private_data =
+ &g_server_dev.server_command_queue.eventHandle;
+ mutex_unlock(&g_server_dev.server_lock);
+ return rc;
+}
+
+static int msm_close_server(struct file *fp)
+{
+ struct v4l2_event_subscription sub;
+ D("%s\n", __func__);
+ mutex_lock(&g_server_dev.server_lock);
+ if (g_server_dev.use_count > 0)
+ g_server_dev.use_count--;
+ mutex_unlock(&g_server_dev.server_lock);
+
+ if (g_server_dev.use_count == 0) {
+ mutex_lock(&g_server_dev.server_lock);
+ if (g_server_dev.pcam_active) {
+ struct v4l2_event v4l2_ev;
+ struct msm_cam_media_controller *pmctl = NULL;
+ int rc;
+
+ pmctl = msm_cam_server_get_mctl(
+ g_server_dev.pcam_active->mctl_handle);
+ if (pmctl && pmctl->mctl_release) {
+ rc = pmctl->mctl_release(pmctl);
+ if (rc < 0)
+ pr_err("mctl_release fails %d\n", rc);
+ /*so that it isn't closed again*/
+ pmctl->mctl_release = NULL;
+ }
+
+ v4l2_ev.type = V4L2_EVENT_PRIVATE_START
+ + MSM_CAM_APP_NOTIFY_ERROR_EVENT;
+ ktime_get_ts(&v4l2_ev.timestamp);
+ v4l2_event_queue(
+ g_server_dev.pcam_active->pvdev, &v4l2_ev);
+ }
+ sub.type = V4L2_EVENT_ALL;
+ msm_server_v4l2_unsubscribe_event(
+ &g_server_dev.server_command_queue.eventHandle, &sub);
+ mutex_unlock(&g_server_dev.server_lock);
+ }
+ return 0;
+}
+
+static unsigned int msm_poll_server(struct file *fp,
+ struct poll_table_struct *wait)
+{
+ int rc = 0;
+
+ D("%s\n", __func__);
+ poll_wait(fp,
+ &g_server_dev.server_command_queue.eventHandle.events->wait,
+ wait);
+ if (v4l2_event_pending(&g_server_dev.server_command_queue.eventHandle))
+ rc |= POLLPRI;
+
+ return rc;
+}
+
+int msm_server_get_usecount(void)
+{
+ return g_server_dev.use_count;
+}
+
+int msm_server_update_sensor_info(struct msm_cam_v4l2_device *pcam,
+ struct msm_camera_sensor_info *sdata)
+{
+ int rc = 0;
+
+ if (!pcam || !sdata) {
+ pr_err("%s Input data is null ", __func__);
+ return -EINVAL;
+ }
+
+ g_server_dev.camera_info.video_dev_name
+ [g_server_dev.camera_info.num_cameras]
+ = video_device_node_name(pcam->pvdev);
+ D("%s Connected video device %s\n", __func__,
+ g_server_dev.camera_info.video_dev_name
+ [g_server_dev.camera_info.num_cameras]);
+
+ g_server_dev.camera_info.s_mount_angle
+ [g_server_dev.camera_info.num_cameras]
+ = sdata->sensor_platform_info->mount_angle;
+
+ g_server_dev.camera_info.is_internal_cam
+ [g_server_dev.camera_info.num_cameras]
+ = sdata->camera_type;
+
+ g_server_dev.mctl_node_info.mctl_node_name
+ [g_server_dev.mctl_node_info.num_mctl_nodes]
+ = video_device_node_name(pcam->mctl_node.pvdev);
+
+ pr_info("%s mctl_node_name[%d] = %s\n", __func__,
+ g_server_dev.mctl_node_info.num_mctl_nodes,
+ g_server_dev.mctl_node_info.mctl_node_name
+ [g_server_dev.mctl_node_info.num_mctl_nodes]);
+
+ /*Temporary solution to store info in media device structure
+ until we can expand media device structure to support more
+ device info*/
+ snprintf(pcam->media_dev.serial,
+ sizeof(pcam->media_dev.serial),
+ "%s-%d-%d", QCAMERA_NAME,
+ sdata->sensor_platform_info->mount_angle,
+ sdata->camera_type);
+
+ g_server_dev.camera_info.num_cameras++;
+ g_server_dev.mctl_node_info.num_mctl_nodes++;
+
+ D("%s done, rc = %d\n", __func__, rc);
+ D("%s number of sensors connected is %d\n", __func__,
+ g_server_dev.camera_info.num_cameras);
+
+ return rc;
+}
+
+int msm_server_begin_session(struct msm_cam_v4l2_device *pcam,
+ int server_q_idx)
+{
+ int rc = -EINVAL, ges_evt;
+ struct msm_cam_server_queue *queue;
+
+ if (!pcam) {
+ pr_err("%s pcam passed is null ", __func__);
+ return rc;
+ }
+
+ ges_evt = MSM_V4L2_GES_CAM_OPEN;
+ D("%s send gesture evt\n", __func__);
+ msm_cam_server_subdev_notify(g_server_dev.gesture_device,
+ NOTIFY_GESTURE_CAM_EVT, &ges_evt);
+
+ pcam->server_queue_idx = server_q_idx;
+ queue = &g_server_dev.server_queue[server_q_idx];
+ queue->ctrl_data = kzalloc(sizeof(uint8_t) *
+ max_control_command_size, GFP_KERNEL);
+ msm_queue_init(&queue->ctrl_q, "control");
+ msm_queue_init(&queue->eventData_q, "eventdata");
+ queue->queue_active = 1;
+
+ rc = msm_cam_server_open_session(&g_server_dev, pcam);
+ if (rc < 0) {
+ pr_err("%s: cam_server_open_session failed %d\n",
+ __func__, rc);
+ goto error;
+ }
+
+ return rc;
+error:
+ ges_evt = MSM_V4L2_GES_CAM_CLOSE;
+ msm_cam_server_subdev_notify(g_server_dev.gesture_device,
+ NOTIFY_GESTURE_CAM_EVT, &ges_evt);
+
+ queue->queue_active = 0;
+ msm_drain_eventq(&queue->eventData_q);
+ msm_queue_drain(&queue->ctrl_q, list_control);
+ kfree(queue->ctrl_data);
+ queue->ctrl_data = NULL;
+ queue = NULL;
+ return rc;
+}
+
+int msm_server_end_session(struct msm_cam_v4l2_device *pcam)
+{
+ int rc = -EINVAL, ges_evt;
+ struct msm_cam_server_queue *queue;
+
+ mutex_lock(&g_server_dev.server_queue_lock);
+ queue = &g_server_dev.server_queue[pcam->server_queue_idx];
+ queue->queue_active = 0;
+ kfree(queue->ctrl_data);
+ queue->ctrl_data = NULL;
+ msm_queue_drain(&queue->ctrl_q, list_control);
+ msm_drain_eventq(&queue->eventData_q);
+ mutex_unlock(&g_server_dev.server_queue_lock);
+
+ rc = msm_cam_server_close_session(&g_server_dev, pcam);
+ if (rc < 0)
+ pr_err("msm_cam_server_close_session fails %d\n", rc);
+
+ ges_evt = MSM_V4L2_GES_CAM_CLOSE;
+ msm_cam_server_subdev_notify(g_server_dev.gesture_device,
+ NOTIFY_GESTURE_CAM_EVT, &ges_evt);
+
+ return rc;
+}
+
+/* Init a config node for ISP control,
+ * which will create a config device (/dev/config0/ and plug in
+ * ISP's operation "v4l2_ioctl_ops*"
+ */
+static const struct v4l2_file_operations msm_fops_server = {
+ .owner = THIS_MODULE,
+ .open = msm_open_server,
+ .poll = msm_poll_server,
+ .unlocked_ioctl = video_ioctl2,
+ .release = msm_close_server,
+};
+
+static const struct v4l2_ioctl_ops msm_ioctl_ops_server = {
+ .vidioc_subscribe_event = msm_server_v4l2_subscribe_event,
+ .vidioc_default = msm_ioctl_server,
+};
+
+static void msm_cam_server_subdev_notify(struct v4l2_subdev *sd,
+ unsigned int notification, void *arg)
+{
+ int rc = -EINVAL;
+ struct msm_sensor_ctrl_t *s_ctrl;
+ struct msm_camera_sensor_info *sinfo;
+ struct msm_camera_device_platform_data *camdev;
+ uint8_t csid_core = 0;
+
+ if (notification == NOTIFY_CID_CHANGE ||
+ notification == NOTIFY_ISPIF_STREAM ||
+ notification == NOTIFY_PCLK_CHANGE ||
+ notification == NOTIFY_CSIPHY_CFG ||
+ notification == NOTIFY_CSID_CFG ||
+ notification == NOTIFY_CSIC_CFG) {
+ s_ctrl = get_sctrl(sd);
+ sinfo = (struct msm_camera_sensor_info *) s_ctrl->sensordata;
+ camdev = sinfo->pdata;
+ csid_core = camdev->csid_core;
+ }
+
+ switch (notification) {
+ case NOTIFY_CID_CHANGE:
+ /* reconfig the ISPIF*/
+ if (g_server_dev.ispif_device) {
+ struct msm_ispif_params_list ispif_params;
+ ispif_params.len = 1;
+ ispif_params.params[0].intftype = PIX0;
+ ispif_params.params[0].cid_mask = 0x0001;
+ ispif_params.params[0].csid = csid_core;
+
+ rc = v4l2_subdev_call(
+ g_server_dev.ispif_device, core, ioctl,
+ VIDIOC_MSM_ISPIF_CFG, &ispif_params);
+ if (rc < 0)
+ return;
+ }
+ break;
+ case NOTIFY_ISPIF_STREAM:
+ /* call ISPIF stream on/off */
+ rc = v4l2_subdev_call(g_server_dev.ispif_device, video,
+ s_stream, (int)arg);
+ if (rc < 0)
+ return;
+
+ break;
+ case NOTIFY_ISP_MSG_EVT:
+ case NOTIFY_VFE_MSG_OUT:
+ case NOTIFY_VFE_MSG_STATS:
+ case NOTIFY_VFE_MSG_COMP_STATS:
+ case NOTIFY_VFE_BUF_EVT:
+ case NOTIFY_VFE_BUF_FREE_EVT:
+ if (g_server_dev.isp_subdev[0] &&
+ g_server_dev.isp_subdev[0]->isp_notify) {
+ rc = g_server_dev.isp_subdev[0]->isp_notify(
+ g_server_dev.vfe_device[0], notification, arg);
+ }
+ break;
+ case NOTIFY_VPE_MSG_EVT: {
+ struct msm_cam_media_controller *pmctl =
+ (struct msm_cam_media_controller *)
+ v4l2_get_subdev_hostdata(sd);
+ struct msm_vpe_resp *vdata = (struct msm_vpe_resp *)arg;
+ msm_mctl_pp_notify(pmctl,
+ (struct msm_mctl_pp_frame_info *)
+ vdata->extdata);
+ break;
+ }
+ case NOTIFY_VFE_IRQ:{
+ struct msm_vfe_cfg_cmd cfg_cmd;
+ struct msm_camvfe_params vfe_params;
+ cfg_cmd.cmd_type = CMD_VFE_PROCESS_IRQ;
+ vfe_params.vfe_cfg = &cfg_cmd;
+ vfe_params.data = arg;
+ rc = v4l2_subdev_call(g_server_dev.vfe_device[0],
+ core, ioctl, 0, &vfe_params);
+ }
+ break;
+ case NOTIFY_AXI_IRQ:
+ rc = v4l2_subdev_call(g_server_dev.axi_device[0],
+ core, ioctl, VIDIOC_MSM_AXI_IRQ, arg);
+ break;
+ case NOTIFY_PCLK_CHANGE:
+ if (g_server_dev.axi_device[0])
+ rc = v4l2_subdev_call(g_server_dev.axi_device[0], video,
+ s_crystal_freq, *(uint32_t *)arg, 0);
+ else
+ rc = v4l2_subdev_call(g_server_dev.vfe_device[0], video,
+ s_crystal_freq, *(uint32_t *)arg, 0);
+ break;
+ case NOTIFY_CSIPHY_CFG:
+ rc = v4l2_subdev_call(g_server_dev.csiphy_device[csid_core],
+ core, ioctl, VIDIOC_MSM_CSIPHY_CFG, arg);
+ break;
+ case NOTIFY_CSID_CFG:
+ rc = v4l2_subdev_call(g_server_dev.csid_device[csid_core],
+ core, ioctl, VIDIOC_MSM_CSID_CFG, arg);
+ break;
+ case NOTIFY_CSIC_CFG:
+ rc = v4l2_subdev_call(g_server_dev.csic_device[csid_core],
+ core, ioctl, VIDIOC_MSM_CSIC_CFG, arg);
+ break;
+ case NOTIFY_GESTURE_EVT:
+ rc = v4l2_subdev_call(g_server_dev.gesture_device,
+ core, ioctl, VIDIOC_MSM_GESTURE_EVT, arg);
+ break;
+ case NOTIFY_GESTURE_CAM_EVT:
+ rc = v4l2_subdev_call(g_server_dev.gesture_device,
+ core, ioctl, VIDIOC_MSM_GESTURE_CAM_EVT, arg);
+ break;
+ default:
+ break;
+ }
+
+ return;
+}
+
+int msm_cam_register_subdev_node(struct v4l2_subdev *sd,
+ enum msm_cam_subdev_type sdev_type, uint8_t index)
+{
+ struct video_device *vdev;
+ int err = 0;
+
+ switch (sdev_type) {
+ case CSIPHY_DEV:
+ if (index >= MAX_NUM_CSIPHY_DEV) {
+ pr_err("%s Invalid CSIPHY idx %d", __func__, index);
+ err = -EINVAL;
+ break;
+ }
+ g_server_dev.csiphy_device[index] = sd;
+ break;
+
+ case CSID_DEV:
+ if (index >= MAX_NUM_CSID_DEV) {
+ pr_err("%s Invalid CSID idx %d", __func__, index);
+ err = -EINVAL;
+ break;
+ }
+ g_server_dev.csid_device[index] = sd;
+ break;
+
+ case CSIC_DEV:
+ if (index >= MAX_NUM_CSIC_DEV) {
+ pr_err("%s Invalid CSIC idx %d", __func__, index);
+ err = -EINVAL;
+ break;
+ }
+ g_server_dev.csic_device[index] = sd;
+ break;
+
+ case ISPIF_DEV:
+ g_server_dev.ispif_device = sd;
+ break;
+
+ case VFE_DEV:
+ if (index >= MAX_NUM_VFE_DEV) {
+ pr_err("%s Invalid VFE idx %d", __func__, index);
+ err = -EINVAL;
+ break;
+ }
+ g_server_dev.vfe_device[index] = sd;
+ break;
+
+ case VPE_DEV:
+ if (index >= MAX_NUM_VPE_DEV) {
+ pr_err("%s Invalid VPE idx %d", __func__, index);
+ err = -EINVAL;
+ break;
+ }
+ g_server_dev.vpe_device[index] = sd;
+ break;
+
+ case AXI_DEV:
+ if (index >= MAX_NUM_VPE_DEV) {
+ pr_err("%s Invalid AXI idx %d", __func__, index);
+ err = -EINVAL;
+ break;
+ }
+ g_server_dev.axi_device[index] = sd;
+ break;
+
+ case GESTURE_DEV:
+ g_server_dev.gesture_device = sd;
+ break;
+ default:
+ break;
+ }
+
+ if (err < 0)
+ return err;
+
+ err = v4l2_device_register_subdev(&g_server_dev.v4l2_dev, sd);
+ if (err < 0) {
+ pr_err("%s v4l2 subdev register failed for %d ret = %d",
+ __func__, sdev_type, err);
+ return err;
+ }
+
+ /* Register a device node for every subdev marked with the
+ * V4L2_SUBDEV_FL_HAS_DEVNODE flag.
+ */
+ if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
+ return err;
+
+ vdev = &sd->devnode;
+ strlcpy(vdev->name, sd->name, sizeof(vdev->name));
+ vdev->v4l2_dev = &g_server_dev.v4l2_dev;
+ vdev->fops = &v4l2_subdev_fops;
+ vdev->release = video_device_release_empty;
+ err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
+ sd->owner);
+ if (err < 0)
+ return err;
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ sd->entity.v4l.major = VIDEO_MAJOR;
+ sd->entity.v4l.minor = vdev->minor;
+#endif
+ return 0;
+}
+
+static int msm_setup_server_dev(struct platform_device *pdev)
+{
+ int rc = -ENODEV, i;
+
+ D("%s\n", __func__);
+ g_server_dev.server_pdev = pdev;
+ g_server_dev.v4l2_dev.dev = &pdev->dev;
+ g_server_dev.v4l2_dev.notify = msm_cam_server_subdev_notify;
+ rc = v4l2_device_register(g_server_dev.v4l2_dev.dev,
+ &g_server_dev.v4l2_dev);
+ if (rc < 0)
+ return -EINVAL;
+
+ g_server_dev.video_dev = video_device_alloc();
+ if (g_server_dev.video_dev == NULL) {
+ pr_err("%s: video_device_alloc failed\n", __func__);
+ return rc;
+ }
+
+ strlcpy(g_server_dev.video_dev->name, pdev->name,
+ sizeof(g_server_dev.video_dev->name));
+
+ g_server_dev.video_dev->v4l2_dev = &g_server_dev.v4l2_dev;
+ g_server_dev.video_dev->fops = &msm_fops_server;
+ g_server_dev.video_dev->ioctl_ops = &msm_ioctl_ops_server;
+ g_server_dev.video_dev->release = video_device_release;
+ g_server_dev.video_dev->minor = 100;
+ g_server_dev.video_dev->vfl_type = 1;
+
+ video_set_drvdata(g_server_dev.video_dev, &g_server_dev);
+
+ strlcpy(g_server_dev.media_dev.model, "qcamera",
+ sizeof(g_server_dev.media_dev.model));
+ g_server_dev.media_dev.dev = &pdev->dev;
+ rc = media_device_register(&g_server_dev.media_dev);
+ g_server_dev.v4l2_dev.mdev = &g_server_dev.media_dev;
+
+ rc = video_register_device(g_server_dev.video_dev,
+ VFL_TYPE_GRABBER, 100);
+
+ mutex_init(&g_server_dev.server_lock);
+ mutex_init(&g_server_dev.server_queue_lock);
+ g_server_dev.pcam_active = NULL;
+ g_server_dev.camera_info.num_cameras = 0;
+ atomic_set(&g_server_dev.number_pcam_active, 0);
+ g_server_dev.server_evt_id = 0;
+
+ /*initialize fake video device and event queue*/
+
+ g_server_dev.server_command_queue.pvdev = g_server_dev.video_dev;
+ rc = msm_setup_v4l2_event_queue(
+ &g_server_dev.server_command_queue.eventHandle,
+ g_server_dev.server_command_queue.pvdev);
+
+ if (rc < 0) {
+ pr_err("%s failed to initialize event queue\n", __func__);
+ video_device_release(g_server_dev.server_command_queue.pvdev);
+ return rc;
+ }
+
+ for (i = 0; i < MAX_NUM_ACTIVE_CAMERA; i++) {
+ struct msm_cam_server_queue *queue;
+ queue = &g_server_dev.server_queue[i];
+ queue->queue_active = 0;
+ msm_queue_init(&queue->ctrl_q, "control");
+ msm_queue_init(&queue->eventData_q, "eventdata");
+ }
+ return rc;
+}
+
+static long msm_server_send_v4l2_evt(void *evt)
+{
+ struct v4l2_event *v4l2_ev = (struct v4l2_event *)evt;
+ int rc = 0;
+
+ if (NULL == evt) {
+ pr_err("%s: evt is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ D("%s: evt type 0x%x\n", __func__, v4l2_ev->type);
+ if ((v4l2_ev->type >= MSM_GES_APP_EVT_MIN) &&
+ (v4l2_ev->type < MSM_GES_APP_EVT_MAX)) {
+ msm_cam_server_subdev_notify(g_server_dev.gesture_device,
+ NOTIFY_GESTURE_EVT, v4l2_ev);
+ } else {
+ pr_err("%s: Invalid evt %d\n", __func__, v4l2_ev->type);
+ rc = -EINVAL;
+ }
+ D("%s: end\n", __func__);
+
+ return rc;
+}
+
+int msm_cam_server_open_mctl_session(struct msm_cam_v4l2_device *pcam,
+ int *p_active)
+{
+ int rc = 0;
+ struct msm_cam_media_controller *pmctl = NULL;
+ D("%s: %p", __func__, g_server_dev.pcam_active);
+ *p_active = 0;
+ if (g_server_dev.pcam_active) {
+ D("%s: Active camera present return", __func__);
+ return 0;
+ }
+ rc = msm_cam_server_open_session(&g_server_dev, pcam);
+ if (rc < 0) {
+ pr_err("%s: cam_server_open_session failed %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
+ if (!pmctl->mctl_open) {
+ D("%s: media contoller is not inited\n",
+ __func__);
+ rc = -ENODEV;
+ return rc;
+ }
+
+ D("%s: call mctl_open\n", __func__);
+ rc = pmctl->mctl_open(pmctl, MSM_APPS_ID_V4L2);
+
+ if (rc < 0) {
+ pr_err("%s: HW open failed rc = 0x%x\n", __func__, rc);
+ return rc;
+ }
+ pmctl->pcam_ptr = pcam;
+ *p_active = 1;
+ return rc;
+}
+
+int msm_cam_server_close_mctl_session(struct msm_cam_v4l2_device *pcam)
+{
+ int rc = 0;
+ struct msm_cam_media_controller *pmctl = NULL;
+
+ pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
+ if (!pmctl) {
+ D("%s: invalid handle\n", __func__);
+ return -ENODEV;
+ }
+
+ if (pmctl->mctl_release) {
+ rc = pmctl->mctl_release(pmctl);
+ if (rc < 0)
+ pr_err("mctl_release fails %d\n", rc);
+ }
+
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+ kref_put(&pmctl->refcount, msm_release_ion_client);
+#endif
+
+ rc = msm_cam_server_close_session(&g_server_dev, pcam);
+ if (rc < 0)
+ pr_err("msm_cam_server_close_session fails %d\n", rc);
+
+ return rc;
+}
+
+int msm_server_open_client(int *p_qidx)
+{
+ int rc = 0;
+ int server_q_idx = 0;
+ struct msm_cam_server_queue *queue = NULL;
+
+ mutex_lock(&g_server_dev.server_lock);
+ server_q_idx = msm_find_free_queue();
+ if (server_q_idx < 0) {
+ mutex_unlock(&g_server_dev.server_lock);
+ return server_q_idx;
+ }
+
+ *p_qidx = server_q_idx;
+ queue = &g_server_dev.server_queue[server_q_idx];
+ queue->ctrl_data = kzalloc(sizeof(uint8_t) *
+ max_control_command_size, GFP_KERNEL);
+ msm_queue_init(&queue->ctrl_q, "control");
+ msm_queue_init(&queue->eventData_q, "eventdata");
+ queue->queue_active = 1;
+ mutex_unlock(&g_server_dev.server_lock);
+ return rc;
+}
+
+int msm_server_send_ctrl(struct msm_ctrl_cmd *out,
+ int ctrl_id)
+{
+ int rc = 0;
+ void *value;
+ struct msm_queue_cmd *rcmd;
+ struct msm_queue_cmd *event_qcmd;
+ struct msm_ctrl_cmd *ctrlcmd;
+ struct msm_cam_server_dev *server_dev = &g_server_dev;
+ struct msm_device_queue *queue =
+ &server_dev->server_queue[out->queue_idx].ctrl_q;
+
+ struct v4l2_event v4l2_evt;
+ struct msm_isp_event_ctrl *isp_event;
+ isp_event = kzalloc(sizeof(struct msm_isp_event_ctrl), GFP_KERNEL);
+ if (!isp_event) {
+ pr_err("%s Insufficient memory. return", __func__);
+ return -ENOMEM;
+ }
+ event_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
+ if (!event_qcmd) {
+ pr_err("%s Insufficient memory. return", __func__);
+ kfree(isp_event);
+ return -ENOMEM;
+ }
+
+ D("%s\n", __func__);
+ mutex_lock(&server_dev->server_queue_lock);
+ if (++server_dev->server_evt_id == 0)
+ server_dev->server_evt_id++;
+
+ D("%s qid %d evtid %d\n", __func__, out->queue_idx,
+ server_dev->server_evt_id);
+ server_dev->server_queue[out->queue_idx].evt_id =
+ server_dev->server_evt_id;
+ v4l2_evt.type = V4L2_EVENT_PRIVATE_START + ctrl_id;
+ v4l2_evt.u.data[0] = out->queue_idx;
+ /* setup event object to transfer the command; */
+ isp_event->resptype = MSM_CAM_RESP_V4L2;
+ isp_event->isp_data.ctrl = *out;
+ isp_event->isp_data.ctrl.evt_id = server_dev->server_evt_id;
+
+ atomic_set(&event_qcmd->on_heap, 1);
+ event_qcmd->command = isp_event;
+
+ msm_enqueue(&server_dev->server_queue[out->queue_idx].eventData_q,
+ &event_qcmd->list_eventdata);
+
+ /* now send command to config thread in userspace,
+ * and wait for results */
+ v4l2_event_queue(server_dev->server_command_queue.pvdev,
+ &v4l2_evt);
+ D("%s v4l2_event_queue: type = 0x%x\n", __func__, v4l2_evt.type);
+ mutex_unlock(&server_dev->server_queue_lock);
+
+ /* wait for config return status */
+ D("Waiting for config status\n");
+ rc = wait_event_interruptible_timeout(queue->wait,
+ !list_empty_careful(&queue->list),
+ msecs_to_jiffies(out->timeout_ms));
+ D("Waiting is over for config status\n");
+ if (list_empty_careful(&queue->list)) {
+ if (!rc)
+ rc = -ETIMEDOUT;
+ if (rc < 0) {
+ kfree(isp_event);
+ pr_err("%s: wait_event error %d\n", __func__, rc);
+ return rc;
+ }
+ }
+
+ rcmd = msm_dequeue(queue, list_control);
+ BUG_ON(!rcmd);
+ D("%s Finished servicing ioctl\n", __func__);
+
+ ctrlcmd = (struct msm_ctrl_cmd *)(rcmd->command);
+ value = out->value;
+ if (ctrlcmd->length > 0 && value != NULL &&
+ ctrlcmd->length <= out->length)
+ memcpy(value, ctrlcmd->value, ctrlcmd->length);
+
+ memcpy(out, ctrlcmd, sizeof(struct msm_ctrl_cmd));
+ out->value = value;
+
+ kfree(ctrlcmd);
+ free_qcmd(rcmd);
+ kfree(isp_event);
+ D("%s: rc %d\n", __func__, rc);
+ /* rc is the time elapsed. */
+ if (rc >= 0) {
+ /* TODO: Refactor msm_ctrl_cmd::status field */
+ if (out->status == 0)
+ rc = -1;
+ else if (out->status == 1 || out->status == 4)
+ rc = 0;
+ else
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+int msm_server_close_client(int idx)
+{
+ int rc = 0;
+ struct msm_cam_server_queue *queue = NULL;
+ mutex_lock(&g_server_dev.server_lock);
+ queue = &g_server_dev.server_queue[idx];
+ queue->queue_active = 0;
+ kfree(queue->ctrl_data);
+ queue->ctrl_data = NULL;
+ msm_queue_drain(&queue->ctrl_q, list_control);
+ msm_drain_eventq(&queue->eventData_q);
+ mutex_unlock(&g_server_dev.server_lock);
+ return rc;
+}
+
+static unsigned int msm_poll_config(struct file *fp,
+ struct poll_table_struct *wait)
+{
+ int rc = 0;
+ struct msm_cam_config_dev *config = fp->private_data;
+ if (config == NULL)
+ return -EINVAL;
+
+ D("%s\n", __func__);
+
+ poll_wait(fp,
+ &config->config_stat_event_queue.eventHandle.events->wait, wait);
+ if (v4l2_event_pending(&config->config_stat_event_queue.eventHandle))
+ rc |= POLLPRI;
+ return rc;
+}
+
+static int msm_mmap_config(struct file *fp, struct vm_area_struct *vma)
+{
+ struct msm_cam_config_dev *config_cam = fp->private_data;
+ int rc = 0;
+ int phyaddr;
+ int retval;
+ unsigned long size;
+
+ D("%s: phy_addr=0x%x", __func__, config_cam->mem_map.cookie);
+ phyaddr = (int)config_cam->mem_map.cookie;
+ if (!phyaddr) {
+ pr_err("%s: no physical memory to map", __func__);
+ return -EFAULT;
+ }
+ memset(&config_cam->mem_map, 0,
+ sizeof(struct msm_mem_map_info));
+ size = vma->vm_end - vma->vm_start;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ retval = remap_pfn_range(vma, vma->vm_start,
+ phyaddr >> PAGE_SHIFT,
+ size, vma->vm_page_prot);
+ if (retval) {
+ pr_err("%s: remap failed, rc = %d",
+ __func__, retval);
+ rc = -ENOMEM;
+ goto end;
+ }
+ D("%s: phy_addr=0x%x: %08lx-%08lx, pgoff %08lx\n",
+ __func__, (uint32_t)phyaddr,
+ vma->vm_start, vma->vm_end, vma->vm_pgoff);
+end:
+ return rc;
+}
+
+static int msm_open_config(struct inode *inode, struct file *fp)
+{
+ int rc;
+ struct msm_cam_config_dev *config_cam = container_of(inode->i_cdev,
+ struct msm_cam_config_dev, config_cdev);
+
+ D("%s: open %s\n", __func__, fp->f_path.dentry->d_name.name);
+
+ rc = nonseekable_open(inode, fp);
+ if (rc < 0) {
+ pr_err("%s: nonseekable_open error %d\n", __func__, rc);
+ return rc;
+ }
+ config_cam->use_count++;
+
+ /* assume there is only one active camera possible*/
+ config_cam->p_mctl =
+ msm_cam_server_get_mctl(g_server_dev.pcam_active->mctl_handle);
+
+ INIT_HLIST_HEAD(&config_cam->p_mctl->stats_info.pmem_stats_list);
+ spin_lock_init(&config_cam->p_mctl->stats_info.pmem_stats_spinlock);
+
+ config_cam->p_mctl->config_device = config_cam;
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+ kref_get(&config_cam->p_mctl->refcount);
+#endif
+ fp->private_data = config_cam;
+ return rc;
+}
+
+static long msm_ioctl_config(struct file *fp, unsigned int cmd,
+ unsigned long arg)
+{
+
+ int rc = 0;
+ struct v4l2_event ev;
+ struct msm_cam_config_dev *config_cam = fp->private_data;
+ struct v4l2_event_subscription temp_sub;
+
+ D("%s: cmd %d\n", __func__, _IOC_NR(cmd));
+
+ switch (cmd) {
+ /* memory management shall be handeld here*/
+ case MSM_CAM_IOCTL_REGISTER_PMEM:
+ return msm_register_pmem(
+ &config_cam->p_mctl->stats_info.pmem_stats_list,
+ (void __user *)arg, config_cam->p_mctl->client);
+ break;
+
+ case MSM_CAM_IOCTL_UNREGISTER_PMEM:
+ return msm_pmem_table_del(
+ &config_cam->p_mctl->stats_info.pmem_stats_list,
+ (void __user *)arg, config_cam->p_mctl->client);
+ break;
+
+ case VIDIOC_SUBSCRIBE_EVENT:
+ if (copy_from_user(&temp_sub,
+ (void __user *)arg,
+ sizeof(struct v4l2_event_subscription))) {
+ pr_err("%s copy_from_user failed for cmd %d ",
+ __func__, cmd);
+ rc = -EINVAL;
+ return rc;
+ }
+ rc = msm_server_v4l2_subscribe_event
+ (&config_cam->config_stat_event_queue.eventHandle,
+ &temp_sub);
+ if (rc < 0) {
+ pr_err("%s: cam_v4l2_subscribe_event failed rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ break;
+
+ case VIDIOC_UNSUBSCRIBE_EVENT:
+ if (copy_from_user(&temp_sub, (void __user *)arg,
+ sizeof(struct v4l2_event_subscription))) {
+ rc = -EINVAL;
+ return rc;
+ }
+ rc = msm_server_v4l2_unsubscribe_event
+ (&config_cam->config_stat_event_queue.eventHandle,
+ &temp_sub);
+ if (rc < 0) {
+ pr_err("%s: cam_v4l2_unsubscribe_event failed rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ break;
+
+ case VIDIOC_DQEVENT: {
+ void __user *u_msg_value = NULL, *user_ptr = NULL;
+ struct msm_isp_event_ctrl u_isp_event;
+ struct msm_isp_event_ctrl *k_isp_event;
+
+ /* First, copy the v4l2 event structure from userspace */
+ D("%s: VIDIOC_DQEVENT\n", __func__);
+ if (copy_from_user(&ev, (void __user *)arg,
+ sizeof(struct v4l2_event)))
+ break;
+ /* Next, get the pointer to event_ctrl structure
+ * embedded inside the v4l2_event.u.data array. */
+ user_ptr = (void __user *)(*((uint32_t *)ev.u.data));
+
+ /* Next, copy the userspace event ctrl structure */
+ if (copy_from_user((void *)&u_isp_event, user_ptr,
+ sizeof(struct msm_isp_event_ctrl))) {
+ break;
+ }
+ /* Save the pointer of the user allocated command buffer*/
+ u_msg_value = u_isp_event.isp_data.isp_msg.data;
+
+ /* Dequeue the event queued into the v4l2 queue*/
+ rc = v4l2_event_dequeue(
+ &config_cam->config_stat_event_queue.eventHandle,
+ &ev, fp->f_flags & O_NONBLOCK);
+ if (rc < 0) {
+ pr_err("no pending events?");
+ break;
+ }
+ /* Use k_isp_event to point to the event_ctrl structure
+ * embedded inside v4l2_event.u.data */
+ k_isp_event = (struct msm_isp_event_ctrl *)
+ (*((uint32_t *)ev.u.data));
+ /* Copy the event structure into user struct. */
+ u_isp_event = *k_isp_event;
+ if (ev.type != (V4L2_EVENT_PRIVATE_START +
+ MSM_CAM_RESP_DIV_FRAME_EVT_MSG) &&
+ ev.type != (V4L2_EVENT_PRIVATE_START +
+ MSM_CAM_RESP_MCTL_PP_EVENT)) {
+
+ /* Restore the saved pointer of the
+ * user allocated command buffer. */
+ u_isp_event.isp_data.isp_msg.data = u_msg_value;
+
+ if (ev.type == (V4L2_EVENT_PRIVATE_START +
+ MSM_CAM_RESP_STAT_EVT_MSG)) {
+ if (k_isp_event->isp_data.isp_msg.len > 0) {
+ void *k_msg_value =
+ k_isp_event->isp_data.isp_msg.data;
+ if (copy_to_user(u_msg_value,
+ k_msg_value,
+ k_isp_event->isp_data.isp_msg.len)) {
+ rc = -EINVAL;
+ break;
+ }
+ kfree(k_msg_value);
+ }
+ }
+ }
+ /* Copy the event ctrl structure back
+ * into user's structure. */
+ if (copy_to_user(user_ptr,
+ (void *)&u_isp_event, sizeof(
+ struct msm_isp_event_ctrl))) {
+ rc = -EINVAL;
+ break;
+ }
+ kfree(k_isp_event);
+
+ /* Copy the v4l2_event structure back to the user*/
+ if (copy_to_user((void __user *)arg, &ev,
+ sizeof(struct v4l2_event))) {
+ rc = -EINVAL;
+ break;
+ }
+ }
+
+ break;
+
+ case MSM_CAM_IOCTL_V4L2_EVT_NOTIFY:
+ rc = msm_v4l2_evt_notify(config_cam->p_mctl, cmd, arg);
+ break;
+
+ case MSM_CAM_IOCTL_SET_MEM_MAP_INFO:
+ if (copy_from_user(&config_cam->mem_map, (void __user *)arg,
+ sizeof(struct msm_mem_map_info)))
+ rc = -EINVAL;
+ break;
+
+ default:{
+ /* For the rest of config command, forward to media controller*/
+ struct msm_cam_media_controller *p_mctl = config_cam->p_mctl;
+ if (p_mctl && p_mctl->mctl_cmd) {
+ rc = config_cam->p_mctl->mctl_cmd(p_mctl, cmd, arg);
+ } else {
+ rc = -EINVAL;
+ pr_err("%s: media controller is null\n", __func__);
+ }
+
+ break;
+ } /* end of default*/
+ } /* end of switch*/
+ return rc;
+}
+
+static int msm_close_config(struct inode *node, struct file *f)
+{
+ struct v4l2_event ev;
+ struct v4l2_event_subscription sub;
+ struct msm_isp_event_ctrl *isp_event;
+ struct msm_cam_config_dev *config_cam = f->private_data;
+
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+ D("%s Decrementing ref count of config node ", __func__);
+ kref_put(&config_cam->p_mctl->refcount, msm_release_ion_client);
+#endif
+ sub.type = V4L2_EVENT_ALL;
+ msm_server_v4l2_unsubscribe_event(
+ &config_cam->config_stat_event_queue.eventHandle,
+ &sub);
+ while (v4l2_event_pending(
+ &config_cam->config_stat_event_queue.eventHandle)) {
+ v4l2_event_dequeue(
+ &config_cam->config_stat_event_queue.eventHandle,
+ &ev, O_NONBLOCK);
+ isp_event = (struct msm_isp_event_ctrl *)
+ (*((uint32_t *)ev.u.data));
+ if (isp_event) {
+ if (isp_event->isp_data.isp_msg.len != 0 &&
+ isp_event->isp_data.isp_msg.data != NULL)
+ kfree(isp_event->isp_data.isp_msg.data);
+ kfree(isp_event);
+ }
+ }
+ return 0;
+}
+
+static const struct file_operations msm_fops_config = {
+ .owner = THIS_MODULE,
+ .open = msm_open_config,
+ .poll = msm_poll_config,
+ .unlocked_ioctl = msm_ioctl_config,
+ .mmap = msm_mmap_config,
+ .release = msm_close_config,
+};
+
+static int msm_setup_config_dev(int node, char *device_name)
+{
+ int rc = -ENODEV;
+ struct device *device_config;
+ int dev_num = node;
+ dev_t devno;
+ struct msm_cam_config_dev *config_cam;
+
+ config_cam = kzalloc(sizeof(*config_cam), GFP_KERNEL);
+ if (!config_cam) {
+ pr_err("%s: could not allocate memory for config_device\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ D("%s\n", __func__);
+
+ devno = MKDEV(MAJOR(msm_devno), dev_num+1);
+ device_config = device_create(msm_class, NULL, devno, NULL, "%s%d",
+ device_name, dev_num);
+
+ if (IS_ERR(device_config)) {
+ rc = PTR_ERR(device_config);
+ pr_err("%s: error creating device: %d\n", __func__, rc);
+ goto config_setup_fail;
+ }
+
+ cdev_init(&config_cam->config_cdev, &msm_fops_config);
+ config_cam->config_cdev.owner = THIS_MODULE;
+
+ rc = cdev_add(&config_cam->config_cdev, devno, 1);
+ if (rc < 0) {
+ pr_err("%s: error adding cdev: %d\n", __func__, rc);
+ device_destroy(msm_class, devno);
+ goto config_setup_fail;
+ }
+ g_server_dev.config_info.config_dev_name[dev_num] =
+ dev_name(device_config);
+ D("%s Connected config device %s\n", __func__,
+ g_server_dev.config_info.config_dev_name[dev_num]);
+ g_server_dev.config_info.config_dev_id[dev_num] =
+ dev_num;
+
+ config_cam->config_stat_event_queue.pvdev = video_device_alloc();
+ if (config_cam->config_stat_event_queue.pvdev == NULL) {
+ pr_err("%s: video_device_alloc failed\n", __func__);
+ goto config_setup_fail;
+ }
+
+ rc = msm_setup_v4l2_event_queue(
+ &config_cam->config_stat_event_queue.eventHandle,
+ config_cam->config_stat_event_queue.pvdev);
+ if (rc < 0) {
+ pr_err("%s failed to initialize event queue\n", __func__);
+ video_device_release(config_cam->config_stat_event_queue.pvdev);
+ goto config_setup_fail;
+ }
+
+ return rc;
+
+config_setup_fail:
+ kfree(config_cam);
+ return rc;
+}
+
+static int __devinit msm_camera_probe(struct platform_device *pdev)
+{
+ int rc = 0, i;
+ /*for now just create a config 0 node
+ put logic here later to know how many configs to create*/
+ g_server_dev.config_info.num_config_nodes = 1;
+
+ rc = msm_isp_init_module(g_server_dev.config_info.num_config_nodes);
+ if (rc < 0) {
+ pr_err("Failed to initialize isp\n");
+ return rc;
+ }
+
+ if (!msm_class) {
+ rc = alloc_chrdev_region(&msm_devno, 0,
+ g_server_dev.config_info.num_config_nodes+1, "msm_camera");
+ if (rc < 0) {
+ pr_err("%s: failed to allocate chrdev: %d\n", __func__,
+ rc);
+ return rc;
+ }
+
+ msm_class = class_create(THIS_MODULE, "msm_camera");
+ if (IS_ERR(msm_class)) {
+ rc = PTR_ERR(msm_class);
+ pr_err("%s: create device class failed: %d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ D("creating server and config nodes\n");
+ rc = msm_setup_server_dev(pdev);
+ if (rc < 0) {
+ pr_err("%s: failed to create server dev: %d\n", __func__,
+ rc);
+ return rc;
+ }
+
+ for (i = 0; i < g_server_dev.config_info.num_config_nodes; i++) {
+ rc = msm_setup_config_dev(i, "config");
+ if (rc < 0) {
+ pr_err("%s:failed to create config dev: %d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ msm_isp_register(&g_server_dev);
+ return rc;
+}
+
+static int __exit msm_camera_exit(struct platform_device *pdev)
+{
+ msm_isp_unregister(&g_server_dev);
+ return 0;
+}
+
+static struct platform_driver msm_cam_server_driver = {
+ .probe = msm_camera_probe,
+ .remove = msm_camera_exit,
+ .driver = {
+ .name = "msm_cam_server",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_cam_server_init(void)
+{
+ return platform_driver_register(&msm_cam_server_driver);
+}
+
+static void __exit msm_cam_server_exit(void)
+{
+ platform_driver_unregister(&msm_cam_server_driver);
+}
+
+module_init(msm_cam_server_init);
+module_exit(msm_cam_server_exit);
+MODULE_DESCRIPTION("msm camera server");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/server/msm_cam_server.h b/drivers/media/video/msm/server/msm_cam_server.h
new file mode 100644
index 0000000..2fe4c2b
--- /dev/null
+++ b/drivers/media/video/msm/server/msm_cam_server.h
@@ -0,0 +1,65 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MSM_CAM_SERVER_H
+#define _MSM_CAM_SERVER_H
+
+#include <linux/proc_fs.h>
+#include <linux/ioctl.h>
+#include <mach/camera.h>
+#include "msm.h"
+
+uint32_t msm_cam_server_get_mctl_handle(void);
+struct msm_cam_media_controller *msm_cam_server_get_mctl(uint32_t handle);
+void msm_cam_server_free_mctl(uint32_t handle);
+/* Server session control APIs */
+int msm_server_begin_session(struct msm_cam_v4l2_device *pcam,
+ int server_q_idx);
+int msm_server_end_session(struct msm_cam_v4l2_device *pcam);
+int msm_send_open_server(struct msm_cam_v4l2_device *pcam);
+int msm_send_close_server(struct msm_cam_v4l2_device *pcam);
+int msm_server_update_sensor_info(struct msm_cam_v4l2_device *pcam,
+ struct msm_camera_sensor_info *sdata);
+/* Server camera control APIs */
+int msm_server_streamon(struct msm_cam_v4l2_device *pcam, int idx);
+int msm_server_streamoff(struct msm_cam_v4l2_device *pcam, int idx);
+int msm_server_get_usecount(void);
+int32_t msm_find_free_queue(void);
+int msm_server_proc_ctrl_cmd(struct msm_cam_v4l2_device *pcam,
+ struct v4l2_control *ctrl, int is_set_cmd);
+int msm_server_s_ctrl(struct msm_cam_v4l2_device *pcam,
+ struct v4l2_control *ctrl);
+int msm_server_g_ctrl(struct msm_cam_v4l2_device *pcam,
+ struct v4l2_control *ctrl);
+int msm_server_q_ctrl(struct msm_cam_v4l2_device *pcam,
+ struct v4l2_queryctrl *queryctrl);
+int msm_server_set_fmt(struct msm_cam_v4l2_device *pcam, int idx,
+ struct v4l2_format *pfmt);
+int msm_server_set_fmt_mplane(struct msm_cam_v4l2_device *pcam, int idx,
+ struct v4l2_format *pfmt);
+int msm_server_get_fmt(struct msm_cam_v4l2_device *pcam,
+ int idx, struct v4l2_format *pfmt);
+int msm_server_get_fmt_mplane(struct msm_cam_v4l2_device *pcam,
+ int idx, struct v4l2_format *pfmt);
+int msm_server_try_fmt(struct msm_cam_v4l2_device *pcam,
+ struct v4l2_format *pfmt);
+int msm_server_try_fmt_mplane(struct msm_cam_v4l2_device *pcam,
+ struct v4l2_format *pfmt);
+int msm_server_v4l2_subscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub);
+int msm_server_v4l2_unsubscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub);
+int msm_server_get_crop(struct msm_cam_v4l2_device *pcam,
+ int idx, struct v4l2_crop *crop);
+
+#endif /* _MSM_CAM_SERVER_H */
diff --git a/drivers/media/video/msm_vidc/msm_vdec.c b/drivers/media/video/msm_vidc/msm_vdec.c
index 3011a2b..9edab16 100644
--- a/drivers/media/video/msm_vidc/msm_vdec.c
+++ b/drivers/media/video/msm_vidc/msm_vdec.c
@@ -26,6 +26,7 @@
#define MAX_NUM_OUTPUT_BUFFERS 6
static const char *const mpeg_video_vidc_divx_format[] = {
+ "DIVX Format 3",
"DIVX Format 4",
"DIVX Format 5",
"DIVX Format 6",
@@ -153,8 +154,7 @@
static u32 get_frame_size_nv12(int plane,
u32 height, u32 width)
{
- int stride = (width + 31) & (~31);
- return height * stride * 3/2;
+ return (ALIGN(height, 32) * ALIGN(width, 32) * 3) / 2;
}
static u32 get_frame_size_nv21(int plane,
u32 height, u32 width)
@@ -217,6 +217,22 @@
.get_frame_size = get_frame_size_nv21,
.type = CAPTURE_PORT,
},
+ {
+ .name = "DIVX 311",
+ .description = "DIVX 311 compressed format",
+ .fourcc = V4L2_PIX_FMT_DIVX_311,
+ .num_planes = 1,
+ .get_frame_size = get_frame_size_compressed,
+ .type = OUTPUT_PORT,
+ },
+ {
+ .name = "DIVX",
+ .description = "DIVX 4/5/6 compressed format",
+ .fourcc = V4L2_PIX_FMT_DIVX,
+ .num_planes = 1,
+ .get_frame_size = get_frame_size_compressed,
+ .type = OUTPUT_PORT,
+ }
};
int msm_vdec_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type i)
@@ -654,7 +670,7 @@
break;
}
if (property_id) {
- pr_err("Control: HAL property=%d,ctrl_id=%d,ctrl_value=%d\n",
+ pr_err("Control: HAL property=%x,ctrl_id=%x,ctrl_value=%d\n",
property_id,
msm_vdec_ctrls[control_idx].id,
control.value);
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.c b/drivers/media/video/msm_vidc/msm_vidc_common.c
index 31879b7..52f1dca 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -149,7 +149,7 @@
enum command_response cmd)
{
int rc = 0;
- rc = wait_for_completion_timeout(
+ rc = wait_for_completion_interruptible_timeout(
&inst->completions[SESSION_MSG_INDEX(cmd)],
msecs_to_jiffies(HW_RESPONSE_TIMEOUT));
if (!rc) {
@@ -418,6 +418,33 @@
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_EOS)
vb->v4l2_buf.flags |= V4L2_BUF_FLAG_EOS;
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ } else {
+ /*
+ * FIXME:
+ * Special handling for EOS case: if we sent a 0 length input
+ * buf with EOS set, Venus doesn't return a valid output buffer.
+ * So pick up a random buffer that's with us, and send it to
+ * v4l2 client with EOS flag set.
+ *
+ * This would normally be OK unless client decides to send
+ * frames even after EOS.
+ *
+ * This should be fixed in upcoming versions of firmware
+ */
+ if (fill_buf_done->flags1 & HAL_BUFFERFLAG_EOS
+ && fill_buf_done->filled_len1 == 0) {
+ struct vb2_queue *q = &inst->vb2_bufq[CAPTURE_PORT];
+
+ if (!list_empty(&q->queued_list)) {
+ vb = list_first_entry(&q->queued_list,
+ struct vb2_buffer, queued_entry);
+ vb->v4l2_planes[0].bytesused = 0;
+ vb->v4l2_buf.flags |= V4L2_BUF_FLAG_EOS;
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ }
+
+ }
+
}
}
@@ -599,9 +626,13 @@
case V4L2_PIX_FMT_VC1_ANNEX_L:
codec = HAL_VIDEO_CODEC_VC1;
break;
+ case V4L2_PIX_FMT_DIVX_311:
+ codec = HAL_VIDEO_CODEC_DIVX_311;
+ break;
+ case V4L2_PIX_FMT_DIVX:
+ codec = HAL_VIDEO_CODEC_DIVX;
+ break;
/*HAL_VIDEO_CODEC_MVC
- HAL_VIDEO_CODEC_DIVX_311
- HAL_VIDEO_CODEC_DIVX
HAL_VIDEO_CODEC_SPARK
HAL_VIDEO_CODEC_VP6
HAL_VIDEO_CODEC_VP7
diff --git a/drivers/media/video/msm_vidc/vidc_hal.c b/drivers/media/video/msm_vidc/vidc_hal.c
index f4c7878..1f33c2c 100644
--- a/drivers/media/video/msm_vidc/vidc_hal.c
+++ b/drivers/media/video/msm_vidc/vidc_hal.c
@@ -1793,7 +1793,7 @@
static void vidc_hal_core_work_handler(struct work_struct *work)
{
- struct hal_device *device = list_first_entry(
+ struct hal_device *device = list_first_entry(
&hal_ctxt.dev_head, struct hal_device, list);
HAL_MSG_INFO(" GOT INTERRUPT %s() ", __func__);
diff --git a/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
index cb44d3a..02b9699 100644
--- a/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
+++ b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
@@ -510,9 +510,12 @@
if (sizeof(struct
hfi_msg_session_fill_buffer_done_compressed_packet)
!= pkt->size) {
- HAL_MSG_ERROR("hal_process_session_ftb_done:"
- "bad_pkt_size");
+ HAL_MSG_ERROR("%s: bad_pkt_size", __func__);
return;
+ } else if (pkt->error_type != HFI_ERR_NONE) {
+ HAL_MSG_ERROR("%s: got buffer back with error %x",
+ __func__, pkt->error_type);
+ /* Proceed with the FBD */
}
data_done.device_id = device->device_id;
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index a427494..e4935ae 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);
@@ -114,6 +119,7 @@
*/
unsigned int part_curr;
struct device_attribute force_ro;
+ struct device_attribute num_wr_reqs_to_start_packing;
};
static DEFINE_MUTEX(open_lock);
@@ -212,6 +218,38 @@
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 int mmc_blk_open(struct block_device *bdev, fmode_t mode)
{
struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk);
@@ -1236,6 +1274,77 @@
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;
+ 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);
+
static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
{
struct request_queue *q = mq->queue;
@@ -1248,6 +1357,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);
@@ -1255,6 +1365,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;
@@ -1282,20 +1395,26 @@
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 (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;
}
@@ -1303,22 +1422,28 @@
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++;
list_add_tail(&next->queuelist, &mq->mqrq_cur->packed_list);
cur = next;
reqs++;
@@ -1330,6 +1455,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;
@@ -1702,6 +1836,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)
@@ -1943,6 +2079,8 @@
static void mmc_blk_remove_req(struct mmc_blk_data *md)
{
if (md) {
+ 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);
@@ -1981,9 +2119,27 @@
md->force_ro.attr.name = "force_ro";
md->force_ro.attr.mode = S_IRUGO | S_IWUSR;
ret = device_create_file(disk_to_dev(md->disk), &md->force_ro);
- if (ret)
+ if (ret) {
del_gendisk(md->disk);
+ goto out;
+ }
+ 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) {
+ device_remove_file(disk_to_dev(md->disk), &md->force_ro);
+ del_gendisk(md->disk);
+ }
+
+out:
return ret;
}
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 652e13a..c3a718a 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)
@@ -193,6 +200,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 d761bf1..6c29e0e 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -44,6 +44,9 @@
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;
};
extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 84983e0..bca06a5 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -246,6 +246,8 @@
card->dev.release = mmc_release_card;
card->dev.type = type;
+ spin_lock_init(&card->wr_pack_stats.lock);
+
return card;
}
@@ -359,6 +361,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 6c82c74..e0217d0 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -40,6 +40,12 @@
#include "sd_ops.h"
#include "sdio_ops.h"
+/*
+ * The Background operations can take a long time, depends on the house keeping
+ * operations the card has to perform
+ */
+#define MMC_BKOPS_MAX_TIMEOUT (4 * 60 * 1000) /* max time to wait in ms */
+
static struct workqueue_struct *workqueue;
/*
@@ -235,6 +241,7 @@
{
int err;
unsigned long flags;
+ int timeout;
BUG_ON(!card);
if (!card->ext_csd.bkops_en || !(card->host->caps2 & MMC_CAP2_BKOPS))
@@ -258,8 +265,12 @@
return;
mmc_claim_host(card->host);
+
+ timeout = (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) ?
+ MMC_BKOPS_MAX_TIMEOUT : 0;
+
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_BKOPS_START, 1, 0);
+ EXT_CSD_BKOPS_START, 1, timeout);
if (err) {
pr_warning("%s: error %d starting bkops\n",
mmc_hostname(card->host), err);
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index c1b0405..0efcf9d 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -287,6 +287,164 @@
.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);
+ }
+
+ 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;
@@ -319,6 +477,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 6178097..169fe68 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1223,6 +1223,24 @@
} 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 (!oldcard)
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 29c09c4..4854614 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -376,8 +376,8 @@
{
host->curr.data = NULL;
host->curr.got_dataend = 0;
- host->curr.wait_for_auto_prog_done = 0;
- host->curr.got_auto_prog_done = 0;
+ host->curr.wait_for_auto_prog_done = false;
+ host->curr.got_auto_prog_done = false;
writel_relaxed(readl_relaxed(host->base + MMCIDATACTRL) &
(~(MCI_DPSM_ENABLE)), host->base + MMCIDATACTRL);
msmsdcc_sync_reg_wr(host); /* Allow the DPSM to be reset */
@@ -1118,7 +1118,7 @@
host->curr.xfer_remain = host->curr.xfer_size;
host->curr.data_xfered = 0;
host->curr.got_dataend = 0;
- host->curr.got_auto_prog_done = 0;
+ host->curr.got_auto_prog_done = false;
datactrl = MCI_DPSM_ENABLE | (data->blksz << 4);
@@ -1644,7 +1644,7 @@
host->curr.cmd = cmd;
} else {
host->prog_enable = 0;
- host->curr.wait_for_auto_prog_done = 0;
+ host->curr.wait_for_auto_prog_done = false;
if (host->dummy_52_needed)
host->dummy_52_needed = 0;
if (cmd->data && cmd->error)
@@ -1808,7 +1808,7 @@
/* Check for prog done */
if (host->curr.wait_for_auto_prog_done &&
(status & MCI_PROGDONE))
- host->curr.got_auto_prog_done = 1;
+ host->curr.got_auto_prog_done = true;
/* Check for data done */
if (!host->curr.got_dataend && (status & MCI_DATAEND))
@@ -2026,35 +2026,26 @@
msecs_to_jiffies(host->curr.req_tout_ms)));
host->curr.mrq = mrq;
+ if (mrq->sbc) {
+ mrq->sbc->mrq = mrq;
+ mrq->sbc->data = mrq->data;
+ }
+
if (mrq->data && (mrq->data->flags & MMC_DATA_WRITE)) {
- if (mrq->cmd->opcode == SD_IO_RW_EXTENDED ||
- mrq->cmd->opcode == 54) {
- if (!host->sdcc_version)
+ if (host->sdcc_version) {
+ if (!mrq->stop)
+ host->curr.wait_for_auto_prog_done = true;
+ } else {
+ if ((mrq->cmd->opcode == SD_IO_RW_EXTENDED) ||
+ (mrq->cmd->opcode == 54))
host->dummy_52_needed = 1;
- else
- /*
- * SDCCv4 supports AUTO_PROG_DONE bit for SDIO
- * write operations using CMD53 and CMD54.
- * Setting this bit with CMD53 would
- * automatically triggers PROG_DONE interrupt
- * without the need of sending dummy CMD52.
- */
- host->curr.wait_for_auto_prog_done = 1;
- } else if (mrq->cmd->opcode == MMC_WRITE_BLOCK &&
- host->sdcc_version) {
- host->curr.wait_for_auto_prog_done = 1;
}
+
if ((mrq->cmd->opcode == MMC_WRITE_BLOCK) ||
(mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK))
host->curr.use_wr_data_pend = true;
}
- if (mrq->data && mrq->sbc) {
- mrq->sbc->mrq = mrq;
- mrq->sbc->data = mrq->data;
- if (mrq->data->flags & MMC_DATA_WRITE)
- host->curr.wait_for_auto_prog_done = 1;
- }
msmsdcc_request_start(host, mrq);
spin_unlock_irqrestore(&host->lock, flags);
@@ -4595,7 +4586,7 @@
}
} else {
host->prog_enable = 0;
- host->curr.wait_for_auto_prog_done = 0;
+ host->curr.wait_for_auto_prog_done = false;
msmsdcc_reset_and_restore(host);
msmsdcc_request_end(host, mrq);
}
@@ -5019,7 +5010,11 @@
mmc->caps |= (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
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;
+
if (pdev->dev.of_node) {
if (of_get_property((&pdev->dev)->of_node,
"qcom,sdcc-hs200", NULL))
@@ -5030,6 +5025,8 @@
mmc->caps |= MMC_CAP_NONREMOVABLE;
mmc->caps |= MMC_CAP_SDIO_IRQ;
+ mmc->caps2 |= MMC_CAP2_INIT_BKOPS | MMC_CAP2_BKOPS;
+
if (plat->is_sdio_al_client)
mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY;
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index 14677c6..0c53102 100644
--- a/drivers/mmc/host/msm_sdcc.h
+++ b/drivers/mmc/host/msm_sdcc.h
@@ -293,8 +293,8 @@
unsigned int xfer_remain; /* Bytes remaining to send */
unsigned int data_xfered; /* Bytes acked by BLKEND irq */
int got_dataend;
- int wait_for_auto_prog_done;
- int got_auto_prog_done;
+ bool wait_for_auto_prog_done;
+ bool got_auto_prog_done;
bool use_wr_data_pend;
int user_pages;
u32 req_tout_ms;
diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c
index 3675d2c..798e9e2 100644
--- a/drivers/mtd/devices/msm_nand.c
+++ b/drivers/mtd/devices/msm_nand.c
@@ -76,6 +76,8 @@
uint32_t ecc_bch_cfg;
uint32_t ecc_parity_bytes;
unsigned cw_size;
+ unsigned int uncorrectable_bit_mask;
+ unsigned int num_err_mask;
};
#define CFG1_WIDE_FLASH (1U << 1)
@@ -1111,9 +1113,8 @@
}
if (pageerr) {
for (n = start_sector; n < cwperpage; n++) {
- if (enable_bch_ecc ?
- (dma_buffer->data.result[n].buffer_status & 0x10) :
- (dma_buffer->data.result[n].buffer_status & 0x8)) {
+ if (dma_buffer->data.result[n].buffer_status &
+ chip->uncorrectable_bit_mask) {
/* not thread safe */
mtd->ecc_stats.failed++;
pageerr = -EBADMSG;
@@ -1123,9 +1124,9 @@
}
if (!rawerr) { /* check for corretable errors */
for (n = start_sector; n < cwperpage; n++) {
- ecc_errors = enable_bch_ecc ?
- (dma_buffer->data.result[n].buffer_status & 0xF) :
- (dma_buffer->data.result[n].buffer_status & 0x7);
+ ecc_errors =
+ (dma_buffer->data.result[n].buffer_status
+ & chip->num_err_mask);
if (ecc_errors) {
total_ecc_errors += ecc_errors;
/* not thread safe */
@@ -1893,7 +1894,7 @@
if (pageerr) {
for (n = start_sector; n < cwperpage; n++) {
if (dma_buffer->data.result[n].buffer_status
- & MSM_NAND_BUF_STAT_UNCRCTBL_ERR) {
+ & chip->uncorrectable_bit_mask) {
/* not thread safe */
mtd->ecc_stats.failed++;
pageerr = -EBADMSG;
@@ -1905,7 +1906,7 @@
for (n = start_sector; n < cwperpage; n++) {
ecc_errors = dma_buffer->data.
result[n].buffer_status
- & MSM_NAND_BUF_STAT_NUM_ERR_MASK;
+ & chip->num_err_mask;
if (ecc_errors) {
total_ecc_errors += ecc_errors;
/* not thread safe */
@@ -7043,6 +7044,15 @@
pr_info("%s: allocated dma buffer at %p, dma_addr %x\n",
__func__, info->msm_nand.dma_buffer, info->msm_nand.dma_addr);
+ /* Let default be VERSION_1 for backward compatibility */
+ info->msm_nand.uncorrectable_bit_mask = BIT(3);
+ info->msm_nand.num_err_mask = 0x7;
+
+ if (plat_data && (plat_data->version == VERSION_2)) {
+ info->msm_nand.uncorrectable_bit_mask = BIT(8);
+ info->msm_nand.num_err_mask = 0x1F;
+ }
+
info->mtd.name = dev_name(&pdev->dev);
info->mtd.priv = &info->msm_nand;
info->mtd.owner = THIS_MODULE;
diff --git a/drivers/mtd/devices/msm_nand.h b/drivers/mtd/devices/msm_nand.h
index 2729c6b..8156b92 100644
--- a/drivers/mtd/devices/msm_nand.h
+++ b/drivers/mtd/devices/msm_nand.h
@@ -187,9 +187,6 @@
#define EBI2_CFG_REG EBI2_REG(0x0004)
#define EBI2_NAND_ADM_MUX EBI2_REG(0x005C)
-#define MSM_NAND_BUF_STAT_UNCRCTBL_ERR (1 << 8)
-#define MSM_NAND_BUF_STAT_NUM_ERR_MASK (enable_bch_ecc ? 0x1F : 0x0F)
-
extern struct flash_platform_data msm_nand_data;
#endif
diff --git a/drivers/platform/msm/sps/bam.c b/drivers/platform/msm/sps/bam.c
index d7edb82..cf98f68 100644
--- a/drivers/platform/msm/sps/bam.c
+++ b/drivers/platform/msm/sps/bam.c
@@ -649,6 +649,8 @@
u32 cfg_bits = 0xffffffff & ~(1 << 11);
u32 ver = 0;
+ SPS_DBG2("sps:%s:bam=0x%x(va).ee=%d.", __func__, (u32) base, ee);
+
ver = bam_read_reg_field(base, REVISION, BAM_REVISION);
if ((ver < BAM_MIN_VERSION) || (ver > BAM_MAX_VERSION)) {
@@ -743,6 +745,8 @@
u32 mask;
u32 pipe;
+ SPS_DBG2("sps:%s:bam=0x%x(va).", __func__, (u32) base);
+
/*
* Discover the hardware version number and the number of pipes
* supported by this BAM
@@ -782,6 +786,8 @@
{
u32 ver = 0;
+ SPS_DBG2("sps:%s:bam=0x%x(va).", __func__, (u32) base);
+
if (!bam_read_reg_field(base, CTRL, BAM_EN)) {
SPS_ERR("sps:%s:bam 0x%x(va) is not enabled.\n",
__func__, (u32) base);
@@ -813,6 +819,8 @@
*/
void bam_exit(void *base, u32 ee)
{
+ SPS_DBG2("sps:%s:bam=0x%x(va).ee=%d.", __func__, (u32) base, ee);
+
bam_write_reg_field(base, IRQ_SRCS_MSK_EE(ee), BAM_IRQ, 0);
bam_write_reg(base, IRQ_EN, 0);
@@ -899,6 +907,8 @@
int bam_pipe_init(void *base, u32 pipe, struct bam_pipe_parameters *param,
u32 ee)
{
+ SPS_DBG2("sps:%s:bam=0x%x(va).pipe=%d.", __func__, (u32) base, pipe);
+
/* Reset the BAM pipe */
bam_write_reg(base, P_RST(pipe), 1);
/* No delay needed */
@@ -967,6 +977,8 @@
*/
void bam_pipe_exit(void *base, u32 pipe, u32 ee)
{
+ SPS_DBG2("sps:%s:bam=0x%x(va).pipe=%d.", __func__, (u32) base, pipe);
+
bam_write_reg(base, P_IRQ_EN(pipe), 0);
/* Disable the Pipe Interrupt at the BAM level */
@@ -982,6 +994,8 @@
*/
void bam_pipe_enable(void *base, u32 pipe)
{
+ SPS_DBG2("sps:%s:bam=0x%x(va).pipe=%d.", __func__, (u32) base, pipe);
+
bam_write_reg_field(base, P_CTRL(pipe), P_EN, 1);
}
@@ -991,6 +1005,8 @@
*/
void bam_pipe_disable(void *base, u32 pipe)
{
+ SPS_DBG2("sps:%s:bam=0x%x(va).pipe=%d.", __func__, (u32) base, pipe);
+
bam_write_reg_field(base, P_CTRL(pipe), P_EN, 0);
}
@@ -1010,6 +1026,8 @@
void bam_pipe_set_irq(void *base, u32 pipe, enum bam_enable irq_en,
u32 src_mask, u32 ee)
{
+ SPS_DBG2("sps:%s:bam=0x%x(va).pipe=%d.", __func__, (u32) base, pipe);
+
bam_write_reg(base, P_IRQ_EN(pipe), src_mask);
bam_write_reg_field(base, IRQ_SRCS_MSK_EE(ee), (1 << pipe), irq_en);
}
@@ -1287,7 +1305,7 @@
"BAM_P_IRQ_STTS: 0x%x\n"
"BAM_P_IRQ_STTS_P_TRNSFR_END_IRQ: 0x%x\n"
"BAM_P_IRQ_STTS_P_PRCSD_DESC_IRQ: 0x%x\n"
- "BAM_P_IRQ_EN: %d\n"
+ "BAM_P_IRQ_EN: 0x%x\n"
"BAM_P_PRDCR_SDBNDn_BAM_P_BYTES_FREE: 0x%x (%d)\n"
"BAM_P_CNSMR_SDBNDn_BAM_P_BYTES_AVAIL: 0x%x (%d)\n"
"BAM_P_SW_DESC_OFST: 0x%x\n"
@@ -1335,4 +1353,50 @@
bam_read_reg_field(base, P_EVNT_GEN_TRSHLD(pipe),
P_EVNT_GEN_TRSHLD_P_TRSHLD));
}
+
+/* output descriptor FIFO of a pipe */
+void print_bam_pipe_desc_fifo(void *virt_addr, u32 pipe_index)
+{
+ void *base = virt_addr;
+ u32 pipe = pipe_index;
+ u32 desc_fifo_addr;
+ u32 desc_fifo_size;
+ u32 *desc_fifo;
+ int i;
+
+ if (base == NULL)
+ return;
+
+ desc_fifo_addr = bam_read_reg(base, P_DESC_FIFO_ADDR(pipe));
+ desc_fifo_size = bam_read_reg_field(base, P_FIFO_SIZES(pipe),
+ P_DESC_FIFO_SIZE);
+
+ if (desc_fifo_addr == 0) {
+ SPS_ERR("sps:%s:desc FIFO address of Pipe %d is NULL.\n",
+ __func__, pipe);
+ return;
+ } else if (desc_fifo_size == 0) {
+ SPS_ERR("sps:%s:desc FIFO size of Pipe %d is 0.\n",
+ __func__, pipe);
+ return;
+ }
+
+ SPS_INFO("\nsps:----- descriptor FIFO of Pipe %d -----\n", pipe);
+
+ SPS_INFO("BAM_P_DESC_FIFO_ADDR: 0x%x\n"
+ "BAM_P_DESC_FIFO_SIZE: 0x%x (%d)\n\n",
+ desc_fifo_addr, desc_fifo_size, desc_fifo_size);
+
+ desc_fifo = (u32 *) phys_to_virt(desc_fifo_addr);
+
+ SPS_INFO("-------------------- begin of FIFO --------------------\n");
+
+ for (i = 0; i < desc_fifo_size; i += 0x10)
+ SPS_INFO("addr 0x%x: 0x%x, 0x%x, 0x%x, 0x%x.\n",
+ desc_fifo_addr + i,
+ desc_fifo[i / 4], desc_fifo[(i / 4) + 1],
+ desc_fifo[(i / 4) + 2], desc_fifo[(i / 4) + 3]);
+
+ SPS_INFO("-------------------- end of FIFO --------------------\n");
+}
#endif
diff --git a/drivers/platform/msm/sps/sps.c b/drivers/platform/msm/sps/sps.c
index 6d61bb6..fbaea09 100644
--- a/drivers/platform/msm/sps/sps.c
+++ b/drivers/platform/msm/sps/sps.c
@@ -327,6 +327,26 @@
print_bam_pipe_selected_reg(vir_addr, 4);
print_bam_pipe_selected_reg(vir_addr, 5);
break;
+ case 6: /* output desc FIFO of all active pipes */
+ for (i = 0; i < num_pipes; i++)
+ print_bam_pipe_desc_fifo(vir_addr, i);
+ break;
+ case 7: /* output desc FIFO of some pipes */
+ print_bam_pipe_desc_fifo(vir_addr, 4);
+ print_bam_pipe_desc_fifo(vir_addr, 5);
+ break;
+ case 8: /* output selected registers and valid desc FIFO of all pipes */
+ for (i = 0; i < num_pipes; i++) {
+ print_bam_pipe_selected_reg(vir_addr, i);
+ print_bam_pipe_desc_fifo(vir_addr, i);
+ }
+ break;
+ case 9: /* output selected registers and desc FIFO of some pipes */
+ print_bam_pipe_selected_reg(vir_addr, 4);
+ print_bam_pipe_desc_fifo(vir_addr, 4);
+ print_bam_pipe_selected_reg(vir_addr, 5);
+ print_bam_pipe_desc_fifo(vir_addr, 5);
+ break;
default:
pr_info("sps:no dump option is chosen yet.");
}
@@ -455,7 +475,7 @@
struct sps_bam_props bamdma_props = {0};
#endif
- SPS_DBG("sps:sps_device_init");
+ SPS_DBG2("sps:%s.", __func__);
success = false;
@@ -581,6 +601,8 @@
*/
static int sps_client_init(struct sps_pipe *client)
{
+ SPS_DBG("sps:%s.", __func__);
+
if (client == NULL)
return -EINVAL;
@@ -609,6 +631,8 @@
*/
static int sps_client_de_init(struct sps_pipe *client)
{
+ SPS_DBG("sps:%s.", __func__);
+
if (client->client_state != SPS_STATE_DISCONNECT) {
SPS_ERR("sps:De-init client in connected state: 0x%x",
client->client_state);
@@ -637,6 +661,8 @@
{
struct sps_bam *bam;
+ SPS_DBG("sps:%s.", __func__);
+
list_for_each_entry(bam, &sps->bams_q, list) {
if (bam->props.phys_addr == phys_addr)
return bam;
@@ -662,6 +688,8 @@
{
struct sps_bam *bam;
+ SPS_DBG("sps:%s.", __func__);
+
if (handle == NULL) {
SPS_ERR("sps:%s:handle is NULL.\n", __func__);
return SPS_ERROR;
@@ -697,6 +725,8 @@
int sps_setup_bam2bam_fifo(struct sps_mem_buffer *mem_buffer,
u32 addr, u32 size, int use_offset)
{
+ SPS_DBG("sps:%s.", __func__);
+
if ((mem_buffer == NULL) || (size == 0)) {
SPS_ERR("sps:invalid buffer address or size.");
return SPS_ERROR;
@@ -746,6 +776,8 @@
{
struct sps_bam *bam;
+ SPS_DBG("sps:%s.", __func__);
+
if (h == SPS_DEV_HANDLE_MEM || h == SPS_DEV_HANDLE_INVALID)
return NULL;
@@ -821,6 +853,8 @@
struct sps_bam *bam;
int result;
+ SPS_DBG2("sps:%s.", __func__);
+
if (h == NULL) {
SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
@@ -915,6 +949,8 @@
struct sps_bam *bam;
int result;
+ SPS_DBG2("sps:%s.", __func__);
+
if (pipe == NULL) {
SPS_ERR("sps:Invalid pipe.");
return SPS_ERROR;
@@ -1292,6 +1328,8 @@
{
struct sps_pipe *pipe = h;
+ SPS_DBG("sps:%s.", __func__);
+
if (h == NULL) {
SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
@@ -1352,6 +1390,8 @@
struct sps_bam *bam;
int result;
+ SPS_DBG("sps:%s.", __func__);
+
if (h == NULL) {
SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
@@ -1401,6 +1441,8 @@
int sps_alloc_mem(struct sps_pipe *h, enum sps_mem mem,
struct sps_mem_buffer *mem_buffer)
{
+ SPS_DBG("sps:%s.", __func__);
+
if (h == NULL) {
SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
@@ -1437,6 +1479,8 @@
*/
int sps_free_mem(struct sps_pipe *h, struct sps_mem_buffer *mem_buffer)
{
+ SPS_DBG("sps:%s.", __func__);
+
if (h == NULL) {
SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
@@ -1500,6 +1544,8 @@
int ok;
int result;
+ SPS_DBG2("sps:%s.", __func__);
+
if (bam_props == NULL) {
SPS_ERR("sps:%s:bam_props is NULL.\n", __func__);
return SPS_ERROR;
@@ -1647,6 +1693,8 @@
{
struct sps_bam *bam;
+ SPS_DBG2("sps:%s.", __func__);
+
if (dev_handle == 0) {
SPS_ERR("sps:%s:device handle should not be 0.\n", __func__);
return SPS_ERROR;
@@ -1766,6 +1814,8 @@
{
struct sps_pipe *ctx = NULL;
+ SPS_DBG("sps:%s.", __func__);
+
ctx = kzalloc(sizeof(struct sps_pipe), GFP_KERNEL);
if (ctx == NULL) {
SPS_ERR("sps:Fail to allocate pipe context.");
@@ -1786,6 +1836,8 @@
{
int res;
+ SPS_DBG("sps:%s.", __func__);
+
if (ctx == NULL) {
SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
@@ -1808,6 +1860,8 @@
struct resource *resource;
struct msm_sps_platform_data *pdata;
+ SPS_DBG("sps:%s.", __func__);
+
pdata = pdev->dev.platform_data;
if (pdata == NULL) {
@@ -1870,6 +1924,8 @@
#ifdef CONFIG_SPS_SUPPORT_BAMDMA
struct resource *resource;
+ SPS_DBG("sps:%s.", __func__);
+
if (of_property_read_u32((&pdev->dev)->of_node,
"qcom,bam-dma-res-pipes",
&sps->bamdma_restricted_pipes)) {
@@ -1920,7 +1976,7 @@
{
int ret;
- SPS_DBG("sps:msm_sps_probe.");
+ SPS_DBG2("sps:%s.", __func__);
if (pdev->dev.of_node) {
if (get_device_tree_data(pdev)) {
diff --git a/drivers/platform/msm/sps/sps_bam.c b/drivers/platform/msm/sps/sps_bam.c
index e0289ad..245ccd2 100644
--- a/drivers/platform/msm/sps/sps_bam.c
+++ b/drivers/platform/msm/sps/sps_bam.c
@@ -221,7 +221,7 @@
IRQF_TRIGGER_HIGH, "sps", dev);
if (result) {
- SPS_ERR("sps:Failed to register BAM 0x%x IRQ %d",
+ SPS_ERR("sps:Failed to enable BAM 0x%x IRQ %d",
BAM_ID(dev), dev->props.irq);
return SPS_ERROR;
}
diff --git a/drivers/platform/msm/sps/spsi.h b/drivers/platform/msm/sps/spsi.h
index 8e4907b..e8ab832 100644
--- a/drivers/platform/msm/sps/spsi.h
+++ b/drivers/platform/msm/sps/spsi.h
@@ -193,6 +193,9 @@
/* output the content of selected BAM pipe registers */
void print_bam_pipe_selected_reg(void *, u32);
+
+/* output descriptor FIFO of a pipe */
+void print_bam_pipe_desc_fifo(void *, u32);
#endif
/**
diff --git a/drivers/power/msm_battery.c b/drivers/power/msm_battery.c
index 0555399..5abc032 100644
--- a/drivers/power/msm_battery.c
+++ b/drivers/power/msm_battery.c
@@ -209,6 +209,7 @@
struct msm_battery_info {
u32 voltage_max_design;
u32 voltage_min_design;
+ u32 voltage_fail_safe;
u32 chg_api_version;
u32 batt_technology;
u32 batt_api_version;
@@ -760,8 +761,10 @@
if (msm_batt_info.batt_handle != INVALID_BATT_HANDLE) {
rc = msm_batt_modify_client(msm_batt_info.batt_handle,
- BATTERY_LOW, BATTERY_VOLTAGE_BELOW_THIS_LEVEL,
- BATTERY_CB_ID_LOW_VOL, BATTERY_LOW);
+ msm_batt_info.voltage_fail_safe,
+ BATTERY_VOLTAGE_BELOW_THIS_LEVEL,
+ BATTERY_CB_ID_LOW_VOL,
+ msm_batt_info.voltage_fail_safe);
if (rc < 0) {
pr_err("%s: msm_batt_modify_client. rc=%d\n",
@@ -784,8 +787,9 @@
if (msm_batt_info.batt_handle != INVALID_BATT_HANDLE) {
rc = msm_batt_modify_client(msm_batt_info.batt_handle,
- BATTERY_LOW, BATTERY_ALL_ACTIVITY,
- BATTERY_CB_ID_ALL_ACTIV, BATTERY_ALL_ACTIVITY);
+ msm_batt_info.voltage_fail_safe,
+ BATTERY_ALL_ACTIVITY,
+ BATTERY_CB_ID_ALL_ACTIV, BATTERY_ALL_ACTIVITY);
if (rc < 0) {
pr_err("%s: msm_batt_modify_client FAIL rc=%d\n",
__func__, rc);
@@ -1373,6 +1377,8 @@
msm_batt_info.voltage_max_design = pdata->voltage_max_design;
msm_batt_info.voltage_min_design = pdata->voltage_min_design;
+ msm_batt_info.voltage_fail_safe = pdata->voltage_fail_safe;
+
msm_batt_info.batt_technology = pdata->batt_technology;
msm_batt_info.calculate_capacity = pdata->calculate_capacity;
@@ -1380,6 +1386,8 @@
msm_batt_info.voltage_min_design = BATTERY_LOW;
if (!msm_batt_info.voltage_max_design)
msm_batt_info.voltage_max_design = BATTERY_HIGH;
+ if (!msm_batt_info.voltage_fail_safe)
+ msm_batt_info.voltage_fail_safe = BATTERY_LOW;
if (msm_batt_info.batt_technology == POWER_SUPPLY_TECHNOLOGY_UNKNOWN)
msm_batt_info.batt_technology = POWER_SUPPLY_TECHNOLOGY_LION;
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
index b1a16bb..2c81f84 100644
--- a/drivers/power/pm8921-charger.c
+++ b/drivers/power/pm8921-charger.c
@@ -258,7 +258,6 @@
int thermal_levels;
struct delayed_work update_heartbeat_work;
struct delayed_work eoc_work;
- struct work_struct unplug_ovp_fet_open_work;
struct delayed_work unplug_check_work;
struct delayed_work vin_collapse_check_work;
struct wake_lock eoc_wake_lock;
@@ -1998,11 +1997,8 @@
#define WRITE_BANK_4 0xC0
#define USB_OVP_DEBOUNCE_TIME 0x06
-static void unplug_ovp_fet_open_worker(struct work_struct *work)
+static void unplug_ovp_fet_open(struct pm8921_chg_chip *chip)
{
- struct pm8921_chg_chip *chip = container_of(work,
- struct pm8921_chg_chip,
- unplug_ovp_fet_open_work);
int chg_gone, usb_chg_plugged_in;
int count = 0;
@@ -2348,7 +2344,7 @@
/* run the worker directly */
pr_debug(" ver5 step: chg_gone=%d, usb_valid = %d\n",
chg_gone, usb_chg_plugged_in);
- schedule_work(&chip->unplug_ovp_fet_open_work);
+ unplug_ovp_fet_open(chip);
}
if (!(reg_loop & VIN_ACTIVE_BIT)) {
@@ -2453,8 +2449,7 @@
static irqreturn_t chg_gone_irq_handler(int irq, void *data)
{
struct pm8921_chg_chip *chip = data;
- u8 reg;
- int rc, chg_gone, usb_chg_plugged_in;
+ int chg_gone, usb_chg_plugged_in;
usb_chg_plugged_in = is_usb_chg_plugged_in(chip);
chg_gone = pm_chg_get_rt_status(chip, CHG_GONE_IRQ);
@@ -2462,14 +2457,6 @@
pr_debug("chg_gone=%d, usb_valid = %d\n", chg_gone, usb_chg_plugged_in);
pr_debug("Chg gone fsm_state=%d\n", pm_chg_get_fsm_state(data));
- rc = pm8xxx_readb(chip->dev->parent, CHG_CNTRL_3, ®);
- if (rc)
- pr_err("Failed to read CHG_CNTRL_3 rc=%d\n", rc);
-
- if (reg & CHG_USB_SUSPEND_BIT)
- return IRQ_HANDLED;
- schedule_work(&chip->unplug_ovp_fet_open_work);
-
power_supply_changed(&chip->batt_psy);
power_supply_changed(&chip->usb_psy);
return IRQ_HANDLED;
@@ -3885,8 +3872,6 @@
INIT_DELAYED_WORK(&chip->eoc_work, eoc_worker);
INIT_DELAYED_WORK(&chip->vin_collapse_check_work,
vin_collapse_check_worker);
- INIT_WORK(&chip->unplug_ovp_fet_open_work,
- unplug_ovp_fet_open_worker);
INIT_DELAYED_WORK(&chip->unplug_check_work, unplug_check_worker);
rc = request_irqs(chip, pdev);
diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c
index 76673c7..4c735df 100644
--- a/drivers/regulator/of_regulator.c
+++ b/drivers/regulator/of_regulator.c
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/of.h>
+#include <linux/string.h>
#include <linux/regulator/machine.h>
static void of_get_regulation_constraints(struct device_node *np,
@@ -57,6 +58,71 @@
constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS;
}
+static const char *consumer_supply_prop_name = "qcom,consumer-supplies";
+#define MAX_DEV_NAME_LEN 256
+/*
+ * Fill in regulator init_data based on qcom legacy requirements.
+ */
+static int of_get_qcom_regulator_init_data(struct device *dev,
+ struct regulator_init_data **init_data)
+{
+ struct device_node *node = dev->of_node;
+ struct regulator_consumer_supply *consumer_supplies;
+ int i, rc, num_consumer_supplies, array_len;
+
+ array_len = of_property_count_strings(node, consumer_supply_prop_name);
+ if (array_len > 0) {
+ /* Array length must be divisible by 2. */
+ if (array_len & 1) {
+ dev_err(dev, "error: %s device node property value "
+ "contains an odd number of elements: %d\n",
+ consumer_supply_prop_name, array_len);
+ return -EINVAL;
+ }
+ num_consumer_supplies = array_len / 2;
+
+ consumer_supplies = devm_kzalloc(dev,
+ sizeof(struct regulator_consumer_supply)
+ * num_consumer_supplies, GFP_KERNEL);
+ if (consumer_supplies == NULL) {
+ dev_err(dev, "devm_kzalloc failed\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < num_consumer_supplies; i++) {
+ rc = of_property_read_string_index(node,
+ consumer_supply_prop_name, i * 2,
+ &consumer_supplies[i].supply);
+ if (rc) {
+ dev_err(dev, "of_property_read_string_index "
+ "failed, rc=%d\n", rc);
+ devm_kfree(dev, consumer_supplies);
+ return rc;
+ }
+
+ rc = of_property_read_string_index(node,
+ consumer_supply_prop_name, (i * 2) + 1,
+ &consumer_supplies[i].dev_name);
+ if (rc) {
+ dev_err(dev, "of_property_read_string_index "
+ "failed, rc=%d\n", rc);
+ devm_kfree(dev, consumer_supplies);
+ return rc;
+ }
+
+ /* Treat dev_name = "" as a wildcard. */
+ if (strnlen(consumer_supplies[i].dev_name,
+ MAX_DEV_NAME_LEN) == 0)
+ consumer_supplies[i].dev_name = NULL;
+ }
+
+ (*init_data)->consumer_supplies = consumer_supplies;
+ (*init_data)->num_consumer_supplies = num_consumer_supplies;
+ }
+
+ return 0;
+}
+
/**
* of_get_regulator_init_data - extract regulator_init_data structure info
* @dev: device requesting for regulator_init_data
@@ -68,6 +134,7 @@
struct regulator_init_data *of_get_regulator_init_data(struct device *dev)
{
struct regulator_init_data *init_data;
+ int rc;
if (!dev->of_node)
return NULL;
@@ -77,5 +144,11 @@
return NULL; /* Out of memory? */
of_get_regulation_constraints(dev->of_node, &init_data);
+ rc = of_get_qcom_regulator_init_data(dev, &init_data);
+ if (rc) {
+ devm_kfree(dev, init_data);
+ return NULL;
+ }
+
return init_data;
}
diff --git a/drivers/regulator/pm8xxx-regulator.c b/drivers/regulator/pm8xxx-regulator.c
index 833c513..dfdbb44 100644
--- a/drivers/regulator/pm8xxx-regulator.c
+++ b/drivers/regulator/pm8xxx-regulator.c
@@ -595,6 +595,29 @@
return enabled;
}
+/*
+ * Adds delay when increasing in voltage to account for the slew rate of
+ * the regulator.
+ */
+static void pm8xxx_vreg_delay_for_slew(struct pm8xxx_vreg *vreg, int prev_uV,
+ int new_uV)
+{
+ int delay;
+
+ if (vreg->pdata.slew_rate == 0 || new_uV <= prev_uV ||
+ !_pm8xxx_vreg_is_enabled(vreg))
+ return;
+
+ delay = DIV_ROUND_UP(new_uV - prev_uV, vreg->pdata.slew_rate);
+
+ if (delay >= 1000) {
+ mdelay(delay / 1000);
+ udelay(delay % 1000);
+ } else {
+ udelay(delay);
+ }
+}
+
static int pm8xxx_pldo_get_voltage(struct regulator_dev *rdev)
{
struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
@@ -655,7 +678,7 @@
{
struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
int rc = 0, uV = min_uV;
- int vmin;
+ int vmin, prev_uV;
unsigned vprog, fine_step;
u8 range_ext, range_sel, fine_step_reg, prev_reg;
bool reg_changed = false;
@@ -699,6 +722,8 @@
return -EINVAL;
}
+ prev_uV = pm8xxx_pldo_get_voltage(rdev);
+
mutex_lock(&vreg->pc_lock);
/* Write fine step, range select and program voltage update. */
@@ -746,8 +771,10 @@
if (rc)
vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
- else
+ else {
+ pm8xxx_vreg_delay_for_slew(vreg, prev_uV, uV);
pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+ }
return rc;
}
@@ -783,7 +810,7 @@
{
struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
unsigned vprog, fine_step_reg, prev_reg;
- int rc;
+ int rc, prev_uV;
int uV = min_uV;
if (uV < NLDO_UV_MIN && max_uV >= NLDO_UV_MIN)
@@ -808,6 +835,8 @@
return -EINVAL;
}
+ prev_uV = pm8xxx_nldo_get_voltage(rdev);
+
mutex_lock(&vreg->pc_lock);
/* Write fine step. */
@@ -840,8 +869,10 @@
if (rc)
vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
- else
+ else {
+ pm8xxx_vreg_delay_for_slew(vreg, prev_uV, uV);
pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+ }
return rc;
}
@@ -897,7 +928,7 @@
int max_uV)
{
u8 vprog, range;
- int rc;
+ int rc, prev_uV;
int uV = min_uV;
if (uV < NLDO1200_LOW_UV_MIN && max_uV >= NLDO1200_LOW_UV_MIN)
@@ -931,6 +962,8 @@
return -EINVAL;
}
+ prev_uV = _pm8xxx_nldo1200_get_voltage(vreg);
+
/* Set to advanced mode */
rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
NLDO1200_ADVANCED_MODE | REGULATOR_BANK_SEL(2)
@@ -948,6 +981,7 @@
vreg->save_uV = uV;
+ pm8xxx_vreg_delay_for_slew(vreg, prev_uV, uV);
bail:
if (rc)
vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
@@ -1208,6 +1242,9 @@
{
struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
int rc = 0;
+ int prev_uV, new_uV;
+
+ prev_uV = pm8xxx_smps_get_voltage(rdev);
mutex_lock(&vreg->pc_lock);
@@ -1218,8 +1255,12 @@
mutex_unlock(&vreg->pc_lock);
- if (!rc)
+ new_uV = pm8xxx_smps_get_voltage(rdev);
+
+ if (!rc) {
+ pm8xxx_vreg_delay_for_slew(vreg, prev_uV, new_uV);
pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+ }
return rc;
}
@@ -1291,6 +1332,7 @@
int rc = 0;
u8 vprog, band;
int uV = min_uV;
+ int prev_uV;
if (uV < FTSMPS_BAND1_UV_MIN && max_uV >= FTSMPS_BAND1_UV_MIN)
uV = FTSMPS_BAND1_UV_MIN;
@@ -1336,6 +1378,8 @@
return -EINVAL;
}
+ prev_uV = _pm8xxx_ftsmps_get_voltage(vreg);
+
/*
* Do not set voltage if regulator is currently disabled because doing
* so will enable it.
@@ -1358,6 +1402,8 @@
vreg->save_uV = uV;
+ pm8xxx_vreg_delay_for_slew(vreg, prev_uV, uV);
+
bail:
if (rc)
vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
@@ -1402,7 +1448,7 @@
int max_uV, unsigned *selector)
{
struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
- int rc;
+ int rc, prev_uV;
int uV = min_uV;
u8 val;
@@ -1426,13 +1472,17 @@
return -EINVAL;
}
+ prev_uV = pm8xxx_ncp_get_voltage(rdev);
+
/* voltage setting */
rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, val,
NCP_VPROG_MASK, &vreg->ctrl_reg);
if (rc)
vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
- else
+ else {
+ pm8xxx_vreg_delay_for_slew(vreg, prev_uV, uV);
pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+ }
return rc;
}
@@ -1460,7 +1510,7 @@
int max_uV, unsigned *selector)
{
struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
- int rc;
+ int rc, prev_uV;
int uV = min_uV;
u8 val;
@@ -1484,13 +1534,17 @@
return -EINVAL;
}
+ prev_uV = pm8xxx_boost_get_voltage(rdev);
+
/* voltage setting */
rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, val,
BOOST_VPROG_MASK, &vreg->ctrl_reg);
if (rc)
vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
- else
+ else {
+ pm8xxx_vreg_delay_for_slew(vreg, prev_uV, uV);
pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+ }
return rc;
}
@@ -3151,6 +3205,16 @@
sizeof(struct pm8xxx_regulator_platform_data));
vreg->pdata.pin_ctrl = pin_ctrl;
vreg->pdata.pin_fn = pin_fn;
+ /*
+ * If slew_rate isn't specified but enable_time is, then set
+ * slew_rate = max_uV / enable_time.
+ */
+ if (vreg->pdata.enable_time > 0
+ && vreg->pdata.init_data.constraints.max_uV > 0
+ && vreg->pdata.slew_rate <= 0)
+ vreg->pdata.slew_rate =
+ DIV_ROUND_UP(vreg->pdata.init_data.constraints.max_uV,
+ vreg->pdata.enable_time);
vreg->dev = &pdev->dev;
} else {
/* Pin control regulator */
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index 70131fa..ccb8012 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -29,14 +29,17 @@
*
*/
-#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/oom.h>
-#include <linux/sched.h>
-#include <linux/notifier.h>
+#include <linux/kobject.h>
#include <linux/memory.h>
#include <linux/memory_hotplug.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/oom.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
static uint32_t lowmem_debug_level = 2;
static int lowmem_adj[6] = {
@@ -54,9 +57,12 @@
};
static int lowmem_minfree_size = 4;
+static size_t lowmem_minfree_notif_trigger;
+
static unsigned int offlining;
static struct task_struct *lowmem_deathpending;
static unsigned long lowmem_deathpending_timeout;
+static struct kobject *lowmem_kobj;
#define lowmem_print(level, x...) \
do { \
@@ -108,6 +114,29 @@
+static void lowmem_notify_killzone_approach(void);
+
+static inline void get_free_ram(int *other_free, int *other_file)
+{
+ struct zone *zone;
+ *other_free = global_page_state(NR_FREE_PAGES);
+ *other_file = global_page_state(NR_FILE_PAGES) -
+ global_page_state(NR_SHMEM);
+
+ if (offlining) {
+ /* Discount all free space in the section being offlined */
+ for_each_zone(zone) {
+ if (zone_idx(zone) == ZONE_MOVABLE) {
+ *other_free -= zone_page_state(zone,
+ NR_FREE_PAGES);
+ lowmem_print(4, "lowmem_shrink discounted "
+ "%lu pages in movable zone\n",
+ zone_page_state(zone, NR_FREE_PAGES));
+ }
+ }
+ }
+}
+
static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
{
struct task_struct *p;
@@ -119,23 +148,8 @@
int selected_tasksize = 0;
int selected_oom_adj;
int array_size = ARRAY_SIZE(lowmem_adj);
- int other_free = global_page_state(NR_FREE_PAGES);
- int other_file = global_page_state(NR_FILE_PAGES) -
- global_page_state(NR_SHMEM);
- struct zone *zone;
-
- if (offlining) {
- /* Discount all free space in the section being offlined */
- for_each_zone(zone) {
- if (zone_idx(zone) == ZONE_MOVABLE) {
- other_free -= zone_page_state(zone,
- NR_FREE_PAGES);
- lowmem_print(4, "lowmem_shrink discounted "
- "%lu pages in movable zone\n",
- zone_page_state(zone, NR_FREE_PAGES));
- }
- }
- }
+ int other_free;
+ int other_file;
/*
* If we already have a death outstanding, then
* bail out right away; indicating to vmscan
@@ -147,6 +161,13 @@
time_before_eq(jiffies, lowmem_deathpending_timeout))
return 0;
+ get_free_ram(&other_free, &other_file);
+
+ if (other_free < lowmem_minfree_notif_trigger &&
+ other_file < lowmem_minfree_notif_trigger) {
+ lowmem_notify_killzone_approach();
+ }
+
if (lowmem_adj_size < array_size)
array_size = lowmem_adj_size;
if (lowmem_minfree_size < array_size)
@@ -228,18 +249,91 @@
.seeks = DEFAULT_SEEKS * 16
};
+static void lowmem_notify_killzone_approach(void)
+{
+ lowmem_print(3, "notification trigger activated\n");
+ sysfs_notify(lowmem_kobj, NULL, "notify_trigger_active");
+}
+
+static ssize_t lowmem_notify_trigger_active_show(struct kobject *k,
+ struct kobj_attribute *attr, char *buf)
+{
+ int other_free, other_file;
+ get_free_ram(&other_free, &other_file);
+ if (other_free < lowmem_minfree_notif_trigger &&
+ other_file < lowmem_minfree_notif_trigger)
+ return snprintf(buf, 3, "1\n");
+ else
+ return snprintf(buf, 3, "0\n");
+}
+
+static struct kobj_attribute lowmem_notify_trigger_active_attr =
+ __ATTR(notify_trigger_active, S_IRUGO,
+ lowmem_notify_trigger_active_show, NULL);
+
+static struct attribute *lowmem_default_attrs[] = {
+ &lowmem_notify_trigger_active_attr.attr,
+ NULL,
+};
+
+static ssize_t lowmem_show(struct kobject *k, struct attribute *attr, char *buf)
+{
+ struct kobj_attribute *kobj_attr;
+ kobj_attr = container_of(attr, struct kobj_attribute, attr);
+ return kobj_attr->show(k, kobj_attr, buf);
+}
+
+static const struct sysfs_ops lowmem_ops = {
+ .show = lowmem_show,
+};
+
+static void lowmem_kobj_release(struct kobject *kobj)
+{
+ /* Nothing to be done here */
+}
+
+static struct kobj_type lowmem_kobj_type = {
+ .release = lowmem_kobj_release,
+ .sysfs_ops = &lowmem_ops,
+ .default_attrs = lowmem_default_attrs,
+};
+
static int __init lowmem_init(void)
{
+ int rc;
task_free_register(&task_nb);
register_shrinker(&lowmem_shrinker);
#ifdef CONFIG_MEMORY_HOTPLUG
hotplug_memory_notifier(lmk_hotplug_callback, 0);
#endif
+
+ lowmem_kobj = kzalloc(sizeof(*lowmem_kobj), GFP_KERNEL);
+ if (!lowmem_kobj) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ rc = kobject_init_and_add(lowmem_kobj, &lowmem_kobj_type,
+ mm_kobj, "lowmemkiller");
+ if (rc)
+ goto err_kobj;
+
return 0;
+
+err_kobj:
+ kfree(lowmem_kobj);
+
+err:
+ unregister_shrinker(&lowmem_shrinker);
+ task_free_unregister(&task_nb);
+
+ return rc;
}
static void __exit lowmem_exit(void)
{
+ kobject_put(lowmem_kobj);
+ kfree(lowmem_kobj);
unregister_shrinker(&lowmem_shrinker);
task_free_unregister(&task_nb);
}
@@ -250,6 +344,8 @@
module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size,
S_IRUGO | S_IWUSR);
module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR);
+module_param_named(notify_trigger, lowmem_minfree_notif_trigger, uint,
+ S_IRUGO | S_IWUSR);
module_init(lowmem_init);
module_exit(lowmem_exit);
diff --git a/drivers/tty/n_smux.c b/drivers/tty/n_smux.c
index 8d2a16e..5b5de03 100644
--- a/drivers/tty/n_smux.c
+++ b/drivers/tty/n_smux.c
@@ -31,7 +31,6 @@
#define SMUX_NOTIFY_FIFO_SIZE 128
#define SMUX_TX_QUEUE_SIZE 256
-#define SMUX_GET_RX_BUFF_MAX_RETRY_CNT 2
#define SMUX_WM_LOW 2
#define SMUX_WM_HIGH 4
#define SMUX_PKT_LOG_SIZE 80
@@ -49,6 +48,10 @@
/* inactivity timeout for no rx/tx activity */
#define SMUX_INACTIVITY_TIMEOUT_MS 1000
+/* RX get_rx_buffer retry timeout values */
+#define SMUX_RX_RETRY_MIN_MS (1 << 0) /* 1 ms */
+#define SMUX_RX_RETRY_MAX_MS (1 << 10) /* 1024 ms */
+
enum {
MSM_SMUX_DEBUG = 1U << 0,
MSM_SMUX_INFO = 1U << 1,
@@ -175,6 +178,11 @@
int (*get_rx_buffer)(void *priv, void **pkt_priv, void **buffer,
int size);
+ /* RX Info */
+ struct list_head rx_retry_queue;
+ unsigned rx_retry_queue_cnt;
+ struct delayed_work rx_retry_work;
+
/* TX Info */
spinlock_t tx_lock_lhb2;
struct list_head tx_queue;
@@ -198,6 +206,33 @@
};
/**
+ * Get RX Buffer Retry structure.
+ *
+ * This is used for clients that are unable to provide an RX buffer
+ * immediately. This temporary structure will be used to temporarily hold the
+ * data and perform a retry.
+ */
+struct smux_rx_pkt_retry {
+ struct smux_pkt_t *pkt;
+ struct list_head rx_retry_list;
+ unsigned timeout_in_ms;
+};
+
+/**
+ * Receive worker data structure.
+ *
+ * One instance is created for every call to smux_rx_state_machine.
+ */
+struct smux_rx_worker_data {
+ const unsigned char *data;
+ int len;
+ int flag;
+
+ struct work_struct work;
+ struct completion work_complete;
+};
+
+/**
* Line discipline and module structure.
*
* Only one instance since multiple instances of line discipline are not
@@ -211,12 +246,14 @@
int ld_open_count;
struct tty_struct *tty;
- /* RX State Machine */
- spinlock_t rx_lock_lha1;
+ /* RX State Machine (singled-threaded access by smux_rx_wq) */
unsigned char recv_buf[SMUX_MAX_PKT_SIZE];
unsigned int recv_len;
unsigned int pkt_remain;
unsigned rx_state;
+
+ /* RX Activity - accessed by multiple threads */
+ spinlock_t rx_lock_lha1;
unsigned rx_activity_flag;
/* TX / Power */
@@ -259,10 +296,13 @@
static DEFINE_SPINLOCK(notify_lock_lhc1);
static struct workqueue_struct *smux_tx_wq;
+static struct workqueue_struct *smux_rx_wq;
static void smux_tx_worker(struct work_struct *work);
static DECLARE_WORK(smux_tx_work, smux_tx_worker);
static void smux_wakeup_worker(struct work_struct *work);
+static void smux_rx_retry_worker(struct work_struct *work);
+static void smux_rx_worker(struct work_struct *work);
static DECLARE_WORK(smux_wakeup_work, smux_wakeup_worker);
static DECLARE_DELAYED_WORK(smux_wakeup_delayed_work, smux_wakeup_worker);
@@ -321,6 +361,7 @@
smux_notify_wq = create_singlethread_workqueue("smux_notify_wq");
smux_tx_wq = create_singlethread_workqueue("smux_tx_wq");
+ smux_rx_wq = create_singlethread_workqueue("smux_rx_wq");
if (IS_ERR(smux_notify_wq) || IS_ERR(smux_tx_wq)) {
SMUX_DBG("%s: create_singlethread_workqueue ENOMEM\n",
@@ -354,6 +395,10 @@
ch->notify = 0;
ch->get_rx_buffer = 0;
+ INIT_LIST_HEAD(&ch->rx_retry_queue);
+ ch->rx_retry_queue_cnt = 0;
+ INIT_DELAYED_WORK(&ch->rx_retry_work, smux_rx_retry_worker);
+
spin_lock_init(&ch->tx_lock_lhb2);
INIT_LIST_HEAD(&ch->tx_queue);
INIT_LIST_HEAD(&ch->tx_ready_list);
@@ -888,8 +933,6 @@
* @lcid Logical channel ID for packet
*
* @returns 0 for success
- *
- * Called with rx_lock_lha1 locked.
*/
static int smux_receive_byte(char ch, int lcid)
{
@@ -931,8 +974,6 @@
* @pkt Received packet
*
* @returns 0 for success
- *
- * Called with rx_lock_lha1 already locked.
*/
static int smux_handle_rx_open_ack(struct smux_pkt_t *pkt)
{
@@ -1021,8 +1062,6 @@
* @pkt Received packet
*
* @returns 0 for success
- *
- * Called with rx_lock_lha1 already locked.
*/
static int smux_handle_rx_open_cmd(struct smux_pkt_t *pkt)
{
@@ -1030,6 +1069,7 @@
int ret;
struct smux_lch_t *ch;
struct smux_pkt_t *ack_pkt;
+ unsigned long flags;
int tx_ready = 0;
int enable_powerdown = 0;
@@ -1039,7 +1079,7 @@
lcid = pkt->hdr.lcid;
ch = &smux_lch[lcid];
- spin_lock(&ch->state_lock_lhb1);
+ spin_lock_irqsave(&ch->state_lock_lhb1, flags);
if (ch->remote_state == SMUX_LCH_REMOTE_CLOSED) {
SMUX_DBG("lcid %d remote state 0x%x -> 0x%x\n", lcid,
@@ -1100,13 +1140,16 @@
}
out:
- spin_unlock(&ch->state_lock_lhb1);
+ spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
if (enable_powerdown) {
- spin_lock(&smux.tx_lock_lha2);
- smux.powerdown_enabled = 1;
- SMUX_DBG("%s: enabling power-collapse support\n", __func__);
- spin_unlock(&smux.tx_lock_lha2);
+ spin_lock_irqsave(&smux.tx_lock_lha2, flags);
+ if (!smux.powerdown_enabled) {
+ smux.powerdown_enabled = 1;
+ SMUX_DBG("%s: enabling power-collapse support\n",
+ __func__);
+ }
+ spin_unlock_irqrestore(&smux.tx_lock_lha2, flags);
}
if (tx_ready)
@@ -1121,8 +1164,6 @@
* @pkt Received packet
*
* @returns 0 for success
- *
- * Called with rx_lock_lha1 already locked.
*/
static int smux_handle_rx_close_cmd(struct smux_pkt_t *pkt)
{
@@ -1131,6 +1172,7 @@
struct smux_lch_t *ch;
struct smux_pkt_t *ack_pkt;
union notifier_metadata meta_disconnected;
+ unsigned long flags;
int tx_ready = 0;
if (pkt->hdr.flags & SMUX_CMD_CLOSE_ACK)
@@ -1140,7 +1182,7 @@
ch = &smux_lch[lcid];
meta_disconnected.disconnected.is_ssr = 0;
- spin_lock(&ch->state_lock_lhb1);
+ spin_lock_irqsave(&ch->state_lock_lhb1, flags);
if (ch->remote_state == SMUX_LCH_REMOTE_OPENED) {
SMUX_DBG("lcid %d remote state 0x%x -> 0x%x\n", lcid,
SMUX_LCH_REMOTE_OPENED,
@@ -1191,7 +1233,7 @@
ret = -EINVAL;
}
out:
- spin_unlock(&ch->state_lock_lhb1);
+ spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
if (tx_ready)
list_channel(ch);
@@ -1204,25 +1246,30 @@
* @pkt Received packet
*
* @returns 0 for success
- *
- * Called with rx_lock_lha1 already locked.
*/
static int smux_handle_rx_data_cmd(struct smux_pkt_t *pkt)
{
uint8_t lcid;
- int ret;
- int i;
+ int ret = 0;
+ int do_retry = 0;
int tmp;
int rx_len;
struct smux_lch_t *ch;
union notifier_metadata metadata;
int remote_loopback;
- int tx_ready = 0;
struct smux_pkt_t *ack_pkt;
unsigned long flags;
- if (!pkt || smux_assert_lch_id(pkt->hdr.lcid))
- return -ENXIO;
+ if (!pkt || smux_assert_lch_id(pkt->hdr.lcid)) {
+ ret = -ENXIO;
+ goto out;
+ }
+
+ rx_len = pkt->hdr.payload_len;
+ if (rx_len == 0) {
+ ret = -EINVAL;
+ goto out;
+ }
lcid = pkt->hdr.lcid;
ch = &smux_lch[lcid];
@@ -1234,6 +1281,7 @@
pr_err("smux: ch %d error data on local state 0x%x",
lcid, ch->local_state);
ret = -EIO;
+ spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
goto out;
}
@@ -1241,69 +1289,110 @@
pr_err("smux: ch %d error data on remote state 0x%x",
lcid, ch->remote_state);
ret = -EIO;
+ spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
goto out;
}
- rx_len = pkt->hdr.payload_len;
- if (rx_len == 0) {
- ret = -EINVAL;
- goto out;
+ if (!list_empty(&ch->rx_retry_queue)) {
+ do_retry = 1;
+ if ((ch->rx_retry_queue_cnt + 1) > SMUX_RX_RETRY_MAX_PKTS) {
+ /* retry queue full */
+ schedule_notify(lcid, SMUX_READ_FAIL, NULL);
+ ret = -ENOMEM;
+ spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
+ goto out;
+ }
}
+ spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
- for (i = 0; i < SMUX_GET_RX_BUFF_MAX_RETRY_CNT; ++i) {
+ if (remote_loopback) {
+ /* Echo the data back to the remote client. */
+ ack_pkt = smux_alloc_pkt();
+ if (ack_pkt) {
+ ack_pkt->hdr.lcid = lcid;
+ ack_pkt->hdr.cmd = SMUX_CMD_DATA;
+ ack_pkt->hdr.flags = 0;
+ ack_pkt->hdr.payload_len = pkt->hdr.payload_len;
+ if (ack_pkt->hdr.payload_len) {
+ smux_alloc_pkt_payload(ack_pkt);
+ memcpy(ack_pkt->payload, pkt->payload,
+ ack_pkt->hdr.payload_len);
+ }
+ ack_pkt->hdr.pad_len = pkt->hdr.pad_len;
+ smux_tx_queue(ack_pkt, ch, 0);
+ list_channel(ch);
+ } else {
+ pr_err("%s: Remote loopack allocation failure\n",
+ __func__);
+ }
+ } else if (!do_retry) {
+ /* request buffer from client */
metadata.read.pkt_priv = 0;
metadata.read.buffer = 0;
+ tmp = ch->get_rx_buffer(ch->priv,
+ (void **)&metadata.read.pkt_priv,
+ (void **)&metadata.read.buffer,
+ rx_len);
- if (!remote_loopback) {
- tmp = ch->get_rx_buffer(ch->priv,
- (void **)&metadata.read.pkt_priv,
- (void **)&metadata.read.buffer,
+ if (tmp == 0 && metadata.read.buffer) {
+ /* place data into RX buffer */
+ memcpy(metadata.read.buffer, pkt->payload,
rx_len);
- if (tmp == 0 && metadata.read.buffer) {
- /* place data into RX buffer */
- memcpy(metadata.read.buffer, pkt->payload,
- rx_len);
- metadata.read.len = rx_len;
- schedule_notify(lcid, SMUX_READ_DONE,
- &metadata);
- ret = 0;
- break;
- } else if (tmp == -EAGAIN) {
- ret = -ENOMEM;
- } else if (tmp < 0) {
- schedule_notify(lcid, SMUX_READ_FAIL, NULL);
- ret = -ENOMEM;
- break;
- } else if (!metadata.read.buffer) {
- pr_err("%s: get_rx_buffer() buffer is NULL\n",
- __func__);
- ret = -ENOMEM;
- }
- } else {
- /* Echo the data back to the remote client. */
- ack_pkt = smux_alloc_pkt();
- if (ack_pkt) {
- ack_pkt->hdr.lcid = lcid;
- ack_pkt->hdr.cmd = SMUX_CMD_DATA;
- ack_pkt->hdr.flags = 0;
- ack_pkt->hdr.payload_len = pkt->hdr.payload_len;
- ack_pkt->payload = pkt->payload;
- ack_pkt->hdr.pad_len = pkt->hdr.pad_len;
- smux_tx_queue(ack_pkt, ch, 0);
- tx_ready = 1;
- } else {
- pr_err("%s: Remote loopack allocation failure\n",
- __func__);
- }
+ metadata.read.len = rx_len;
+ schedule_notify(lcid, SMUX_READ_DONE,
+ &metadata);
+ } else if (tmp == -EAGAIN ||
+ (tmp == 0 && !metadata.read.buffer)) {
+ /* buffer allocation failed - add to retry queue */
+ do_retry = 1;
+ } else if (tmp < 0) {
+ schedule_notify(lcid, SMUX_READ_FAIL, NULL);
+ ret = -ENOMEM;
}
}
+ if (do_retry) {
+ struct smux_rx_pkt_retry *retry;
+
+ retry = kmalloc(sizeof(struct smux_rx_pkt_retry), GFP_KERNEL);
+ if (!retry) {
+ pr_err("%s: retry alloc failure\n", __func__);
+ ret = -ENOMEM;
+ schedule_notify(lcid, SMUX_READ_FAIL, NULL);
+ goto out;
+ }
+ INIT_LIST_HEAD(&retry->rx_retry_list);
+ retry->timeout_in_ms = SMUX_RX_RETRY_MIN_MS;
+
+ /* copy packet */
+ retry->pkt = smux_alloc_pkt();
+ if (!retry->pkt) {
+ kfree(retry);
+ pr_err("%s: pkt alloc failure\n", __func__);
+ ret = -ENOMEM;
+ schedule_notify(lcid, SMUX_READ_FAIL, NULL);
+ goto out;
+ }
+ retry->pkt->hdr.lcid = lcid;
+ retry->pkt->hdr.payload_len = pkt->hdr.payload_len;
+ retry->pkt->hdr.pad_len = pkt->hdr.pad_len;
+ if (retry->pkt->hdr.payload_len) {
+ smux_alloc_pkt_payload(retry->pkt);
+ memcpy(retry->pkt->payload, pkt->payload,
+ retry->pkt->hdr.payload_len);
+ }
+
+ /* add to retry queue */
+ spin_lock_irqsave(&ch->state_lock_lhb1, flags);
+ list_add_tail(&retry->rx_retry_list, &ch->rx_retry_queue);
+ ++ch->rx_retry_queue_cnt;
+ if (ch->rx_retry_queue_cnt == 1)
+ queue_delayed_work(smux_rx_wq, &ch->rx_retry_work,
+ msecs_to_jiffies(retry->timeout_in_ms));
+ spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
+ }
+
out:
- spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
-
- if (tx_ready)
- list_channel(ch);
-
return ret;
}
@@ -1359,8 +1448,6 @@
* @pkt Received packet
*
* @returns 0 for success
- *
- * Called with rx_lock_lha1 already locked.
*/
static int smux_handle_rx_status_cmd(struct smux_pkt_t *pkt)
{
@@ -1416,15 +1503,14 @@
* @pkt Received packet
*
* @returns 0 for success
- *
- * Called with rx_lock_lha1 already locked.
*/
static int smux_handle_rx_power_cmd(struct smux_pkt_t *pkt)
{
int tx_ready = 0;
struct smux_pkt_t *ack_pkt;
+ unsigned long flags;
- spin_lock(&smux.tx_lock_lha2);
+ spin_lock_irqsave(&smux.tx_lock_lha2, flags);
if (pkt->hdr.flags & SMUX_CMD_PWR_CTL_ACK) {
/* local sleep request ack */
if (smux.power_state == SMUX_PWR_TURNING_OFF) {
@@ -1465,7 +1551,7 @@
__func__, smux.power_state);
}
}
- spin_unlock(&smux.tx_lock_lha2);
+ spin_unlock_irqrestore(&smux.tx_lock_lha2, flags);
if (tx_ready)
list_channel(&smux_lch[ack_pkt->hdr.lcid]);
@@ -1479,8 +1565,6 @@
* @pkt Packet to process
*
* @returns 0 for success
- *
- * Called with rx_lock_lha1 already locked.
*/
static int smux_dispatch_rx_pkt(struct smux_pkt_t *pkt)
{
@@ -1527,8 +1611,6 @@
* @len Length of the data
*
* @returns 0 for success
- *
- * Called with rx_lock_lha1 already locked.
*/
static int smux_deserialize(unsigned char *data, int len)
{
@@ -1562,12 +1644,12 @@
/**
* Handle wakeup request byte.
- *
- * Called with rx_lock_lha1 already locked.
*/
static void smux_handle_wakeup_req(void)
{
- spin_lock(&smux.tx_lock_lha2);
+ unsigned long flags;
+
+ spin_lock_irqsave(&smux.tx_lock_lha2, flags);
if (smux.power_state == SMUX_PWR_OFF
|| smux.power_state == SMUX_PWR_TURNING_ON) {
/* wakeup system */
@@ -1582,17 +1664,17 @@
} else {
smux_send_byte(SMUX_WAKEUP_ACK);
}
- spin_unlock(&smux.tx_lock_lha2);
+ spin_unlock_irqrestore(&smux.tx_lock_lha2, flags);
}
/**
* Handle wakeup request ack.
- *
- * Called with rx_lock_lha1 already locked.
*/
static void smux_handle_wakeup_ack(void)
{
- spin_lock(&smux.tx_lock_lha2);
+ unsigned long flags;
+
+ spin_lock_irqsave(&smux.tx_lock_lha2, flags);
if (smux.power_state == SMUX_PWR_TURNING_ON) {
/* received response to wakeup request */
SMUX_DBG("%s: Power %d->%d\n", __func__,
@@ -1607,7 +1689,7 @@
pr_err("%s: wakeup request ack invalid in state %d\n",
__func__, smux.power_state);
}
- spin_unlock(&smux.tx_lock_lha2);
+ spin_unlock_irqrestore(&smux.tx_lock_lha2, flags);
}
/**
@@ -1617,8 +1699,6 @@
* @len Length of the data
* @used Return value of length processed
* @flag Error flag - TTY_NORMAL 0 for no failure
- *
- * Called with rx_lock_lha1 locked.
*/
static void smux_rx_handle_idle(const unsigned char *data,
int len, int *used, int flag)
@@ -1666,8 +1746,6 @@
* @len Length of the data
* @used Return value of length processed
* @flag Error flag - TTY_NORMAL 0 for no failure
- *
- * Called with rx_lock_lha1 locked.
*/
static void smux_rx_handle_magic(const unsigned char *data,
int len, int *used, int flag)
@@ -1707,8 +1785,6 @@
* @len Length of the data
* @used Return value of length processed
* @flag Error flag - TTY_NORMAL 0 for no failure
- *
- * Called with rx_lock_lha1 locked.
*/
static void smux_rx_handle_hdr(const unsigned char *data,
int len, int *used, int flag)
@@ -1744,8 +1820,6 @@
* @len Length of the data
* @used Return value of length processed
* @flag Error flag - TTY_NORMAL 0 for no failure
- *
- * Called with rx_lock_lha1 locked.
*/
static void smux_rx_handle_pkt_payload(const unsigned char *data,
int len, int *used, int flag)
@@ -1784,47 +1858,20 @@
* @data Pointer to data block
* @len Length of data
* @flag TTY_NORMAL (0) for no error, otherwise TTY Error Flag
- *
- * Called with rx_lock_lha1 locked.
*/
void smux_rx_state_machine(const unsigned char *data,
int len, int flag)
{
- unsigned long flags;
- int used;
- int initial_rx_state;
+ struct smux_rx_worker_data work;
+ work.data = data;
+ work.len = len;
+ work.flag = flag;
+ INIT_WORK_ONSTACK(&work.work, smux_rx_worker);
+ work.work_complete = COMPLETION_INITIALIZER_ONSTACK(work.work_complete);
- SMUX_DBG("%s: %p, len=%d, flag=%d\n", __func__, data, len, flag);
- spin_lock_irqsave(&smux.rx_lock_lha1, flags);
- used = 0;
- smux.rx_activity_flag = 1;
- do {
- SMUX_DBG("%s: state %d; %d of %d\n",
- __func__, smux.rx_state, used, len);
- initial_rx_state = smux.rx_state;
-
- switch (smux.rx_state) {
- case SMUX_RX_IDLE:
- smux_rx_handle_idle(data, len, &used, flag);
- break;
- case SMUX_RX_MAGIC:
- smux_rx_handle_magic(data, len, &used, flag);
- break;
- case SMUX_RX_HDR:
- smux_rx_handle_hdr(data, len, &used, flag);
- break;
- case SMUX_RX_PAYLOAD:
- smux_rx_handle_pkt_payload(data, len, &used, flag);
- break;
- default:
- SMUX_DBG("%s: invalid state %d\n",
- __func__, smux.rx_state);
- smux.rx_state = SMUX_RX_IDLE;
- break;
- }
- } while (used < len || smux.rx_state != initial_rx_state);
- spin_unlock_irqrestore(&smux.rx_lock_lha1, flags);
+ queue_work(smux_rx_wq, &work.work);
+ wait_for_completion(&work.work_complete);
}
/**
@@ -2051,6 +2098,167 @@
}
/**
+ * Remove RX retry packet from channel and free it.
+ *
+ * Must be called with state_lock_lhb1 locked.
+ *
+ * @ch Channel for retry packet
+ * @retry Retry packet to remove
+ */
+void smux_remove_rx_retry(struct smux_lch_t *ch,
+ struct smux_rx_pkt_retry *retry)
+{
+ list_del(&retry->rx_retry_list);
+ --ch->rx_retry_queue_cnt;
+ smux_free_pkt(retry->pkt);
+ kfree(retry);
+}
+
+/**
+ * RX worker handles all receive operations.
+ *
+ * @work Work structure contained in TBD structure
+ */
+static void smux_rx_worker(struct work_struct *work)
+{
+ unsigned long flags;
+ int used;
+ int initial_rx_state;
+ struct smux_rx_worker_data *w;
+ const unsigned char *data;
+ int len;
+ int flag;
+
+ w = container_of(work, struct smux_rx_worker_data, work);
+ data = w->data;
+ len = w->len;
+ flag = w->flag;
+
+ spin_lock_irqsave(&smux.rx_lock_lha1, flags);
+ smux.rx_activity_flag = 1;
+ spin_unlock_irqrestore(&smux.rx_lock_lha1, flags);
+
+ SMUX_DBG("%s: %p, len=%d, flag=%d\n", __func__, data, len, flag);
+ used = 0;
+ do {
+ SMUX_DBG("%s: state %d; %d of %d\n",
+ __func__, smux.rx_state, used, len);
+ initial_rx_state = smux.rx_state;
+
+ switch (smux.rx_state) {
+ case SMUX_RX_IDLE:
+ smux_rx_handle_idle(data, len, &used, flag);
+ break;
+ case SMUX_RX_MAGIC:
+ smux_rx_handle_magic(data, len, &used, flag);
+ break;
+ case SMUX_RX_HDR:
+ smux_rx_handle_hdr(data, len, &used, flag);
+ break;
+ case SMUX_RX_PAYLOAD:
+ smux_rx_handle_pkt_payload(data, len, &used, flag);
+ break;
+ default:
+ SMUX_DBG("%s: invalid state %d\n",
+ __func__, smux.rx_state);
+ smux.rx_state = SMUX_RX_IDLE;
+ break;
+ }
+ } while (used < len || smux.rx_state != initial_rx_state);
+
+ complete(&w->work_complete);
+}
+
+/**
+ * RX Retry worker handles retrying get_rx_buffer calls that previously failed
+ * because the client was not ready (-EAGAIN).
+ *
+ * @work Work structure contained in smux_lch_t structure
+ */
+static void smux_rx_retry_worker(struct work_struct *work)
+{
+ struct smux_lch_t *ch;
+ struct smux_rx_pkt_retry *retry;
+ union notifier_metadata metadata;
+ int tmp;
+ unsigned long flags;
+
+ ch = container_of(work, struct smux_lch_t, rx_retry_work.work);
+
+ /* get next retry packet */
+ spin_lock_irqsave(&ch->state_lock_lhb1, flags);
+ if (ch->local_state != SMUX_LCH_LOCAL_OPENED) {
+ /* port has been closed - remove all retries */
+ while (!list_empty(&ch->rx_retry_queue)) {
+ retry = list_first_entry(&ch->rx_retry_queue,
+ struct smux_rx_pkt_retry,
+ rx_retry_list);
+ smux_remove_rx_retry(ch, retry);
+ }
+ }
+
+ if (list_empty(&ch->rx_retry_queue)) {
+ SMUX_DBG("%s: retry list empty for channel %d\n",
+ __func__, ch->lcid);
+ spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
+ return;
+ }
+ retry = list_first_entry(&ch->rx_retry_queue,
+ struct smux_rx_pkt_retry,
+ rx_retry_list);
+ spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
+
+ SMUX_DBG("%s: retrying rx pkt %p\n", __func__, retry);
+ metadata.read.pkt_priv = 0;
+ metadata.read.buffer = 0;
+ tmp = ch->get_rx_buffer(ch->priv,
+ (void **)&metadata.read.pkt_priv,
+ (void **)&metadata.read.buffer,
+ retry->pkt->hdr.payload_len);
+ if (tmp == 0 && metadata.read.buffer) {
+ /* have valid RX buffer */
+ memcpy(metadata.read.buffer, retry->pkt->payload,
+ retry->pkt->hdr.payload_len);
+ metadata.read.len = retry->pkt->hdr.payload_len;
+
+ spin_lock_irqsave(&ch->state_lock_lhb1, flags);
+ smux_remove_rx_retry(ch, retry);
+ spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
+
+ schedule_notify(ch->lcid, SMUX_READ_DONE, &metadata);
+ } else if (tmp == -EAGAIN ||
+ (tmp == 0 && !metadata.read.buffer)) {
+ /* retry again */
+ retry->timeout_in_ms <<= 1;
+ if (retry->timeout_in_ms > SMUX_RX_RETRY_MAX_MS) {
+ /* timed out */
+ spin_lock_irqsave(&ch->state_lock_lhb1, flags);
+ smux_remove_rx_retry(ch, retry);
+ schedule_notify(ch->lcid, SMUX_READ_FAIL, NULL);
+ spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
+ }
+ } else {
+ /* client error - drop packet */
+ spin_lock_irqsave(&ch->state_lock_lhb1, flags);
+ smux_remove_rx_retry(ch, retry);
+ spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
+
+ schedule_notify(ch->lcid, SMUX_READ_FAIL, NULL);
+ }
+
+ /* schedule next retry */
+ spin_lock_irqsave(&ch->state_lock_lhb1, flags);
+ if (!list_empty(&ch->rx_retry_queue)) {
+ retry = list_first_entry(&ch->rx_retry_queue,
+ struct smux_rx_pkt_retry,
+ rx_retry_list);
+ queue_delayed_work(smux_rx_wq, &ch->rx_retry_work,
+ msecs_to_jiffies(retry->timeout_in_ms));
+ }
+ spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
+}
+
+/**
* Transmit worker handles serializing and transmitting packets onto the
* underlying transport.
*
@@ -2402,6 +2610,10 @@
pr_err("%s: pkt allocation failed\n", __func__);
ret = -ENOMEM;
}
+
+ /* Purge RX retry queue */
+ if (ch->rx_retry_queue_cnt)
+ queue_delayed_work(smux_rx_wq, &ch->rx_retry_work, 0);
}
spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
diff --git a/drivers/tty/smux_loopback.c b/drivers/tty/smux_loopback.c
index 52ce17f..bf6d50b 100644
--- a/drivers/tty/smux_loopback.c
+++ b/drivers/tty/smux_loopback.c
@@ -262,8 +262,7 @@
smux_init_pkt(&reply_pkt);
reply_pkt.hdr.lcid = lcid;
reply_pkt.hdr.cmd = SMUX_CMD_PWR_CTL;
- reply_pkt.hdr.flags = SMUX_CMD_PWR_CTL_SLEEP_REQ
- | SMUX_CMD_PWR_CTL_ACK;
+ reply_pkt.hdr.flags = SMUX_CMD_PWR_CTL_ACK;
reply_pkt.hdr.payload_len = 0;
reply_pkt.payload = NULL;
reply_pkt.hdr.pad_len = pkt->hdr.pad_len;
diff --git a/drivers/tty/smux_private.h b/drivers/tty/smux_private.h
index 5ce8fb8..2c8819c 100644
--- a/drivers/tty/smux_private.h
+++ b/drivers/tty/smux_private.h
@@ -29,6 +29,9 @@
#define SMUX_UT_ECHO_ACK_OK 0xF1
#define SMUX_UT_ECHO_ACK_FAIL 0xF2
+/* Maximum number of packets in retry queue */
+#define SMUX_RX_RETRY_MAX_PKTS 32
+
struct tty_struct;
/* Packet header. */
@@ -78,7 +81,6 @@
/* Power command flags */
enum {
SMUX_CMD_PWR_CTL_ACK = 1 << 0,
- SMUX_CMD_PWR_CTL_SLEEP_REQ = 1 << 1,
};
/* Local logical channel states */
diff --git a/drivers/tty/smux_test.c b/drivers/tty/smux_test.c
index 242c66e..62e9465 100644
--- a/drivers/tty/smux_test.c
+++ b/drivers/tty/smux_test.c
@@ -75,9 +75,44 @@
} \
do {} while (0)
+/**
+ * In-range unit test assertion for test cases.
+ *
+ * @a lval
+ * @minv Minimum value
+ * @maxv Maximum value
+ *
+ * Assertion fails if @a is not on the exclusive range minv, maxv
+ * ((@a < @minv) or (@a > @maxv)). In the failure case, the macro
+ * logs the function and line number where the error occurred along
+ * with the values of @a and @minv, @maxv.
+ *
+ * Assumes that the following local variables exist:
+ * @buf - buffer to write failure message to
+ * @i - number of bytes written to buffer
+ * @max - maximum size of the buffer
+ * @failed - set to true if test fails
+ */
+#define UT_ASSERT_INT_IN_RANGE(a, minv, maxv) \
+ if (((a) < (minv)) || ((a) > (maxv))) { \
+ i += scnprintf(buf + i, max - i, \
+ "%s:%d Fail: " #a "(%d) < " #minv "(%d) or " \
+ #a "(%d) > " #maxv "(%d)\n", \
+ __func__, __LINE__, \
+ a, minv, a, maxv); \
+ failed = 1; \
+ break; \
+ } \
+ do {} while (0)
+
+
static unsigned char test_array[] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55,
89, 144, 233};
+/* when 1, forces failure of get_rx_buffer_mock function */
+static int get_rx_buffer_mock_fail;
+
+
/* Used for mapping local to remote TIOCM signals */
struct tiocm_test_vector {
uint32_t input;
@@ -93,7 +128,7 @@
{
void *rx_buf;
- rx_buf = kmalloc(size, GFP_ATOMIC);
+ rx_buf = kmalloc(size, GFP_KERNEL);
*pkt_priv = (void *)0x1234;
*buffer = rx_buf;
@@ -118,6 +153,13 @@
struct smux_meta_write meta;
};
+/* Mock object metadata for get_rx_buffer failure event */
+struct mock_get_rx_buff_event {
+ struct list_head list;
+ int size;
+ unsigned long jiffies;
+};
+
/* Mock object for all SMUX callback events */
struct smux_mock_callback {
int cb_count;
@@ -140,6 +182,10 @@
int event_read_failed;
struct list_head read_events;
+ /* read retry data */
+ int get_rx_buff_retry_count;
+ struct list_head get_rx_buff_retry_events;
+
/* write event data */
int event_write_done;
int event_write_failed;
@@ -156,6 +202,7 @@
init_completion(&cb->cb_completion);
spin_lock_init(&cb->lock);
INIT_LIST_HEAD(&cb->read_events);
+ INIT_LIST_HEAD(&cb->get_rx_buff_retry_events);
INIT_LIST_HEAD(&cb->write_events);
}
@@ -191,6 +238,16 @@
kfree(meta);
}
+ cb->get_rx_buff_retry_count = 0;
+ while (!list_empty(&cb->get_rx_buff_retry_events)) {
+ struct mock_get_rx_buff_event *meta;
+ meta = list_first_entry(&cb->get_rx_buff_retry_events,
+ struct mock_get_rx_buff_event,
+ list);
+ list_del(&meta->list);
+ kfree(meta);
+ }
+
cb->event_write_done = 0;
cb->event_write_failed = 0;
while (!list_empty(&cb->write_events)) {
@@ -229,6 +286,8 @@
"\tevent_read_done=%d\n"
"\tevent_read_failed=%d\n"
"\tread_events=%d\n"
+ "\tget_rx_retry=%d\n"
+ "\tget_rx_retry_events=%d\n"
"\tevent_write_done=%d\n"
"\tevent_write_failed=%d\n"
"\twrite_events=%d\n",
@@ -243,6 +302,8 @@
cb->event_read_done,
cb->event_read_failed,
!list_empty(&cb->read_events),
+ cb->get_rx_buff_retry_count,
+ !list_empty(&cb->get_rx_buff_retry_events),
cb->event_write_done,
cb->event_write_failed,
list_empty(&cb->write_events)
@@ -268,83 +329,105 @@
return;
}
- spin_lock_irqsave(&cb_data_ptr->lock, flags);
switch (event) {
case SMUX_CONNECTED:
+ spin_lock_irqsave(&cb_data_ptr->lock, flags);
++cb_data_ptr->event_connected;
+ spin_unlock_irqrestore(&cb_data_ptr->lock, flags);
break;
case SMUX_DISCONNECTED:
+ spin_lock_irqsave(&cb_data_ptr->lock, flags);
++cb_data_ptr->event_disconnected;
cb_data_ptr->event_disconnected_ssr =
((struct smux_meta_disconnected *)metadata)->is_ssr;
+ spin_unlock_irqrestore(&cb_data_ptr->lock, flags);
break;
case SMUX_READ_DONE:
- ++cb_data_ptr->event_read_done;
read_event_meta = kmalloc(sizeof(struct mock_read_event),
- GFP_ATOMIC);
+ GFP_KERNEL);
+ spin_lock_irqsave(&cb_data_ptr->lock, flags);
+ ++cb_data_ptr->event_read_done;
if (read_event_meta) {
read_event_meta->meta =
*(struct smux_meta_read *)metadata;
list_add_tail(&read_event_meta->list,
&cb_data_ptr->read_events);
}
+ spin_unlock_irqrestore(&cb_data_ptr->lock, flags);
break;
case SMUX_READ_FAIL:
- ++cb_data_ptr->event_read_failed;
read_event_meta = kmalloc(sizeof(struct mock_read_event),
- GFP_ATOMIC);
+ GFP_KERNEL);
+ spin_lock_irqsave(&cb_data_ptr->lock, flags);
+ ++cb_data_ptr->event_read_failed;
if (read_event_meta) {
- read_event_meta->meta =
+ if (metadata)
+ read_event_meta->meta =
*(struct smux_meta_read *)metadata;
+ else
+ memset(&read_event_meta->meta, 0x0,
+ sizeof(struct smux_meta_read));
list_add_tail(&read_event_meta->list,
&cb_data_ptr->read_events);
}
+ spin_unlock_irqrestore(&cb_data_ptr->lock, flags);
break;
case SMUX_WRITE_DONE:
- ++cb_data_ptr->event_write_done;
write_event_meta = kmalloc(sizeof(struct mock_write_event),
- GFP_ATOMIC);
+ GFP_KERNEL);
+ spin_lock_irqsave(&cb_data_ptr->lock, flags);
+ ++cb_data_ptr->event_write_done;
if (write_event_meta) {
write_event_meta->meta =
*(struct smux_meta_write *)metadata;
list_add_tail(&write_event_meta->list,
&cb_data_ptr->write_events);
}
+ spin_unlock_irqrestore(&cb_data_ptr->lock, flags);
break;
case SMUX_WRITE_FAIL:
- ++cb_data_ptr->event_write_failed;
write_event_meta = kmalloc(sizeof(struct mock_write_event),
- GFP_ATOMIC);
+ GFP_KERNEL);
+ spin_lock_irqsave(&cb_data_ptr->lock, flags);
+ ++cb_data_ptr->event_write_failed;
if (write_event_meta) {
write_event_meta->meta =
*(struct smux_meta_write *)metadata;
list_add_tail(&write_event_meta->list,
&cb_data_ptr->write_events);
}
+ spin_unlock_irqrestore(&cb_data_ptr->lock, flags);
break;
case SMUX_LOW_WM_HIT:
+ spin_lock_irqsave(&cb_data_ptr->lock, flags);
++cb_data_ptr->event_low_wm;
+ spin_unlock_irqrestore(&cb_data_ptr->lock, flags);
break;
case SMUX_HIGH_WM_HIT:
+ spin_lock_irqsave(&cb_data_ptr->lock, flags);
++cb_data_ptr->event_high_wm;
+ spin_unlock_irqrestore(&cb_data_ptr->lock, flags);
break;
case SMUX_TIOCM_UPDATE:
+ spin_lock_irqsave(&cb_data_ptr->lock, flags);
++cb_data_ptr->event_tiocm;
cb_data_ptr->tiocm_meta = *(struct smux_meta_tiocm *)metadata;
+ spin_unlock_irqrestore(&cb_data_ptr->lock, flags);
break;
default:
pr_err("%s: unknown event %d\n", __func__, event);
};
+ spin_lock_irqsave(&cb_data_ptr->lock, flags);
++cb_data_ptr->cb_count;
complete(&cb_data_ptr->cb_completion);
spin_unlock_irqrestore(&cb_data_ptr->lock, flags);
@@ -1153,6 +1236,338 @@
return i;
}
+/**
+ * Allocates a new buffer or returns a failure based upon the
+ * global @get_rx_buffer_mock_fail.
+ */
+static int get_rx_buffer_mock(void *priv, void **pkt_priv,
+ void **buffer, int size)
+{
+ void *rx_buf;
+ unsigned long flags;
+ struct smux_mock_callback *cb_ptr;
+
+ cb_ptr = (struct smux_mock_callback *)priv;
+ if (!cb_ptr) {
+ pr_err("%s: no callback data\n", __func__);
+ return -ENXIO;
+ }
+
+ if (get_rx_buffer_mock_fail) {
+ /* force failure and log failure event */
+ struct mock_get_rx_buff_event *meta;
+ meta = kmalloc(sizeof(struct mock_get_rx_buff_event),
+ GFP_KERNEL);
+ if (!meta) {
+ pr_err("%s: unable to allocate metadata\n", __func__);
+ return -ENOMEM;
+ }
+ INIT_LIST_HEAD(&meta->list);
+ meta->size = size;
+ meta->jiffies = jiffies;
+
+ spin_lock_irqsave(&cb_ptr->lock, flags);
+ ++cb_ptr->get_rx_buff_retry_count;
+ list_add_tail(&meta->list, &cb_ptr->get_rx_buff_retry_events);
+ ++cb_ptr->cb_count;
+ complete(&cb_ptr->cb_completion);
+ spin_unlock_irqrestore(&cb_ptr->lock, flags);
+ return -EAGAIN;
+ } else {
+ rx_buf = kmalloc(size, GFP_KERNEL);
+ *pkt_priv = (void *)0x1234;
+ *buffer = rx_buf;
+ return 0;
+ }
+ return 0;
+}
+
+/**
+ * Verify get_rx_buffer callback retry.
+ *
+ * @buf Buffer for status message
+ * @max Size of buffer
+ *
+ * @returns Number of bytes written to @buf
+ */
+static int smux_ut_local_get_rx_buff_retry(char *buf, int max)
+{
+ static struct smux_mock_callback cb_data;
+ static int cb_initialized;
+ int i = 0;
+ int failed = 0;
+ char try_two[] = "try 2";
+ int ret;
+ unsigned long start_j;
+ struct mock_get_rx_buff_event *event;
+ struct mock_read_event *read_event;
+ int try;
+
+ i += scnprintf(buf + i, max - i, "Running %s\n", __func__);
+ pr_err("%s", buf);
+
+ if (!cb_initialized)
+ mock_cb_data_init(&cb_data);
+
+ mock_cb_data_reset(&cb_data);
+ smux_byte_loopback = SMUX_TEST_LCID;
+ while (!failed) {
+ /* open port for loopback */
+ ret = msm_smux_set_ch_option(SMUX_TEST_LCID,
+ SMUX_CH_OPTION_LOCAL_LOOPBACK,
+ 0);
+ UT_ASSERT_INT(ret, ==, 0);
+
+ ret = msm_smux_open(SMUX_TEST_LCID, &cb_data,
+ smux_mock_cb, get_rx_buffer_mock);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_data.cb_completion, HZ), >, 0);
+ UT_ASSERT_INT(cb_data.cb_count, ==, 1);
+ UT_ASSERT_INT(cb_data.event_connected, ==, 1);
+ mock_cb_data_reset(&cb_data);
+
+ /*
+ * Force get_rx_buffer failure for a single RX packet
+ *
+ * The get_rx_buffer calls should follow an exponential
+ * back-off with a maximum timeout of 1024 ms after which we
+ * will get a failure notification.
+ *
+ * Try Post Delay (ms)
+ * 0 -
+ * 1 1
+ * 2 2
+ * 3 4
+ * 4 8
+ * 5 16
+ * 6 32
+ * 7 64
+ * 8 128
+ * 9 256
+ * 10 512
+ * 11 1024
+ * 12 Fail
+ *
+ * All times are limited by the precision of the timer
+ * framework, so ranges are used in the test
+ * verification.
+ */
+ get_rx_buffer_mock_fail = 1;
+ start_j = jiffies;
+ ret = msm_smux_write(SMUX_TEST_LCID, (void *)1,
+ test_array, sizeof(test_array));
+ UT_ASSERT_INT(ret, ==, 0);
+ ret = msm_smux_write(SMUX_TEST_LCID, (void *)2,
+ try_two, sizeof(try_two));
+ UT_ASSERT_INT(ret, ==, 0);
+
+ /* wait for RX failure event */
+ while (cb_data.event_read_failed == 0) {
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_data.cb_completion, 2*HZ),
+ >, 0);
+ INIT_COMPLETION(cb_data.cb_completion);
+ }
+ if (failed)
+ break;
+
+ /* verify retry attempts */
+ UT_ASSERT_INT(cb_data.get_rx_buff_retry_count, ==, 12);
+ event = list_first_entry(&cb_data.get_rx_buff_retry_events,
+ struct mock_get_rx_buff_event, list);
+ pr_err("%s: event->jiffies = %d (ms)\n", __func__,
+ jiffies_to_msecs(event->jiffies - start_j));
+ UT_ASSERT_INT_IN_RANGE(
+ jiffies_to_msecs(event->jiffies - start_j),
+ 0, 0 + 20);
+ start_j = event->jiffies;
+
+ event = list_first_entry(&event->list,
+ struct mock_get_rx_buff_event, list);
+ pr_err("%s: event->jiffies = %d (ms)\n", __func__,
+ jiffies_to_msecs(event->jiffies - start_j));
+ UT_ASSERT_INT_IN_RANGE(
+ jiffies_to_msecs(event->jiffies - start_j),
+ 1, 1 + 20);
+ start_j = event->jiffies;
+
+ event = list_first_entry(&event->list,
+ struct mock_get_rx_buff_event, list);
+ pr_err("%s: event->jiffies = %d (ms)\n", __func__,
+ jiffies_to_msecs(event->jiffies - start_j));
+ UT_ASSERT_INT_IN_RANGE(
+ jiffies_to_msecs(event->jiffies - start_j),
+ 2, 2 + 20);
+ start_j = event->jiffies;
+
+ event = list_first_entry(&event->list,
+ struct mock_get_rx_buff_event, list);
+ pr_err("%s: event->jiffies = %d (ms)\n", __func__,
+ jiffies_to_msecs(event->jiffies - start_j));
+ UT_ASSERT_INT_IN_RANGE(
+ jiffies_to_msecs(event->jiffies - start_j),
+ 4, 4 + 20);
+ start_j = event->jiffies;
+
+ event = list_first_entry(&event->list,
+ struct mock_get_rx_buff_event, list);
+ pr_err("%s: event->jiffies = %d (ms)\n", __func__,
+ jiffies_to_msecs(event->jiffies - start_j));
+ UT_ASSERT_INT_IN_RANGE(
+ jiffies_to_msecs(event->jiffies - start_j),
+ 8, 8 + 20);
+ start_j = event->jiffies;
+
+ event = list_first_entry(&event->list,
+ struct mock_get_rx_buff_event, list);
+ pr_err("%s: event->jiffies = %d (ms)\n", __func__,
+ jiffies_to_msecs(event->jiffies - start_j));
+ UT_ASSERT_INT_IN_RANGE(
+ jiffies_to_msecs(event->jiffies - start_j),
+ 16, 16 + 20);
+ start_j = event->jiffies;
+
+ event = list_first_entry(&event->list,
+ struct mock_get_rx_buff_event, list);
+ pr_err("%s: event->jiffies = %d (ms)\n", __func__,
+ jiffies_to_msecs(event->jiffies - start_j));
+ UT_ASSERT_INT_IN_RANGE(
+ jiffies_to_msecs(event->jiffies - start_j),
+ 32 - 20, 32 + 20);
+ start_j = event->jiffies;
+
+ event = list_first_entry(&event->list,
+ struct mock_get_rx_buff_event, list);
+ pr_err("%s: event->jiffies = %d (ms)\n", __func__,
+ jiffies_to_msecs(event->jiffies - start_j));
+ UT_ASSERT_INT_IN_RANGE(
+ jiffies_to_msecs(event->jiffies - start_j),
+ 64 - 20, 64 + 20);
+ start_j = event->jiffies;
+
+ event = list_first_entry(&event->list,
+ struct mock_get_rx_buff_event, list);
+ pr_err("%s: event->jiffies = %d (ms)\n", __func__,
+ jiffies_to_msecs(event->jiffies - start_j));
+ UT_ASSERT_INT_IN_RANGE(
+ jiffies_to_msecs(event->jiffies - start_j),
+ 128 - 20, 128 + 20);
+ start_j = event->jiffies;
+
+ event = list_first_entry(&event->list,
+ struct mock_get_rx_buff_event, list);
+ pr_err("%s: event->jiffies = %d (ms)\n", __func__,
+ jiffies_to_msecs(event->jiffies - start_j));
+ UT_ASSERT_INT_IN_RANGE(
+ jiffies_to_msecs(event->jiffies - start_j),
+ 256 - 20, 256 + 20);
+ start_j = event->jiffies;
+
+ event = list_first_entry(&event->list,
+ struct mock_get_rx_buff_event, list);
+ pr_err("%s: event->jiffies = %d (ms)\n", __func__,
+ jiffies_to_msecs(event->jiffies - start_j));
+ UT_ASSERT_INT_IN_RANGE(
+ jiffies_to_msecs(event->jiffies - start_j),
+ 512 - 20, 512 + 20);
+ start_j = event->jiffies;
+
+ event = list_first_entry(&event->list,
+ struct mock_get_rx_buff_event, list);
+ pr_err("%s: event->jiffies = %d (ms)\n", __func__,
+ jiffies_to_msecs(event->jiffies - start_j));
+ UT_ASSERT_INT_IN_RANGE(
+ jiffies_to_msecs(event->jiffies - start_j),
+ 1024 - 20, 1024 + 20);
+ mock_cb_data_reset(&cb_data);
+
+ /* verify 2nd pending RX packet goes through */
+ get_rx_buffer_mock_fail = 0;
+ INIT_COMPLETION(cb_data.cb_completion);
+ if (cb_data.event_read_done == 0)
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_data.cb_completion, HZ),
+ >, 0);
+ UT_ASSERT_INT(cb_data.event_read_done, ==, 1);
+ UT_ASSERT_INT(list_empty(&cb_data.read_events), ==, 0);
+ read_event = list_first_entry(&cb_data.read_events,
+ struct mock_read_event, list);
+ UT_ASSERT_PTR(read_event->meta.pkt_priv, ==, (void *)0x1234);
+ UT_ASSERT_PTR(read_event->meta.buffer, !=, NULL);
+ UT_ASSERT_INT(0, ==, memcmp(read_event->meta.buffer, try_two,
+ sizeof(try_two)));
+ mock_cb_data_reset(&cb_data);
+
+ /* Test maximum retry queue size */
+ get_rx_buffer_mock_fail = 1;
+ for (try = 0; try < (SMUX_RX_RETRY_MAX_PKTS + 1); ++try) {
+ mock_cb_data_reset(&cb_data);
+ ret = msm_smux_write(SMUX_TEST_LCID, (void *)1,
+ test_array, sizeof(test_array));
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_data.cb_completion, HZ),
+ >, 0);
+ }
+
+ /* should have 32 successful rx packets and 1 failed */
+ while (cb_data.event_read_failed == 0) {
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_data.cb_completion, 2*HZ),
+ >, 0);
+ INIT_COMPLETION(cb_data.cb_completion);
+ }
+ if (failed)
+ break;
+
+ get_rx_buffer_mock_fail = 0;
+ while (cb_data.event_read_done < SMUX_RX_RETRY_MAX_PKTS) {
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_data.cb_completion, 2*HZ),
+ >, 0);
+ INIT_COMPLETION(cb_data.cb_completion);
+ }
+ if (failed)
+ break;
+
+ UT_ASSERT_INT(1, ==, cb_data.event_read_failed);
+ UT_ASSERT_INT(SMUX_RX_RETRY_MAX_PKTS, ==,
+ cb_data.event_read_done);
+ mock_cb_data_reset(&cb_data);
+
+ /* close port */
+ ret = msm_smux_close(SMUX_TEST_LCID);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_data.cb_completion, HZ),
+ >, 0);
+ UT_ASSERT_INT(cb_data.cb_count, ==, 1);
+ UT_ASSERT_INT(cb_data.event_disconnected, ==, 1);
+ UT_ASSERT_INT(cb_data.event_disconnected_ssr, ==, 0);
+ break;
+ }
+
+ if (!failed) {
+ i += scnprintf(buf + i, max - i, "\tOK\n");
+ } else {
+ pr_err("%s: Failed\n", __func__);
+ i += scnprintf(buf + i, max - i, "\tFailed\n");
+ i += mock_cb_data_print(&cb_data, buf + i, max - i);
+ msm_smux_close(SMUX_TEST_LCID);
+ }
+ smux_byte_loopback = 0;
+ mock_cb_data_reset(&cb_data);
+ return i;
+}
+
static char debug_buffer[DEBUG_BUFMAX];
static ssize_t debug_read(struct file *file, char __user *buf,
@@ -1214,6 +1629,8 @@
debug_create("ut_local_wm", 0444, dent, smux_ut_local_wm);
debug_create("ut_local_smuxld_receive_buf", 0444, dent,
smux_ut_local_smuxld_receive_buf);
+ debug_create("ut_local_get_rx_buff_retry", 0444, dent,
+ smux_ut_local_get_rx_buff_retry);
return 0;
}
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 1d26d45..37f0799 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1588,6 +1588,9 @@
usbmon_urb_complete(&hcd->self, urb, status);
usb_unanchor_urb(urb);
+ if (hcd->driver->log_urb_complete)
+ hcd->driver->log_urb_complete(urb, "C", status);
+
/* pass ownership to the completion handler */
urb->status = status;
urb->complete (urb);
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index 53a6398..414a7b9 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -164,6 +164,8 @@
NULL,
};
+static void frmnet_ctrl_response_available(struct f_rmnet *dev);
+
/* ------- misc functions --------------------*/
static inline struct f_rmnet *func_to_rmnet(struct usb_function *f)
@@ -554,6 +556,7 @@
struct f_rmnet *dev = func_to_rmnet(f);
struct usb_composite_dev *cdev = dev->cdev;
int ret;
+ struct list_head *cpkt;
pr_debug("%s:dev:%p port#%d\n", __func__, dev, dev->port_num);
@@ -590,6 +593,11 @@
atomic_set(&dev->online, 1);
+ /* In case notifications were aborted, but there are pending control
+ packets in the response queue, re-add the notifications */
+ list_for_each(cpkt, &dev->cpkt_resp_q)
+ frmnet_ctrl_response_available(dev);
+
return ret;
}
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 902d07cb..6aa3594 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -632,6 +632,9 @@
hw->hw_alt_next = QTD_NEXT(ehci, ehci->async->dummy->qtd_dma);
/* clear interrupt enables, set irq latency */
+ if (ehci->max_log2_irq_thresh)
+ log2_irq_thresh = ehci->max_log2_irq_thresh;
+
if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
log2_irq_thresh = 0;
temp = 1 << (16 + log2_irq_thresh);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index af638f4..4e7c35f 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -261,6 +261,11 @@
if (t1 & PORT_OWNER)
set_bit(port, &ehci->owned_ports);
else if ((t1 & PORT_PE) && !(t1 & PORT_SUSPEND)) {
+ /*clear RS bit before setting SUSP bit
+ * and wait for HCH to get set*/
+ if (ehci->susp_sof_bug)
+ ehci_halt(ehci);
+
t2 |= PORT_SUSPEND;
set_bit(port, &ehci->bus_suspended);
}
@@ -311,8 +316,10 @@
if (ehci->bus_suspended)
udelay(150);
- /* turn off now-idle HC */
- ehci_halt (ehci);
+ /*if this bit is set, controller is already haled*/
+ if (!ehci->susp_sof_bug)
+ ehci_halt(ehci); /* turn off now-idle HC */
+
hcd->state = HC_STATE_SUSPENDED;
if (ehci->reclaim)
@@ -1199,8 +1206,10 @@
if ((temp & PORT_PE) == 0
|| (temp & PORT_RESET) != 0)
goto error;
-
- ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
+ /*port gets suspended as part of bus suspend routine*/
+ if (!ehci->susp_sof_bug)
+ ehci_writel(ehci, temp | PORT_SUSPEND,
+ status_reg);
#ifdef CONFIG_USB_OTG
if (hcd->self.otg_port == (wIndex + 1) &&
hcd->self.b_hnp_enable) {
@@ -1215,7 +1224,11 @@
*/
temp &= ~PORT_WKCONN_E;
temp |= PORT_WKDISC_E | PORT_WKOC_E;
- ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
+ if (ehci->susp_sof_bug)
+ ehci_writel(ehci, temp, status_reg);
+ else
+ ehci_writel(ehci, temp | PORT_SUSPEND,
+ status_reg);
if (hostpc_reg) {
spin_unlock_irqrestore(&ehci->lock, flags);
msleep(5);/* 5ms for HCD enter low pwr mode */
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
index 82373e2..6bd0577 100644
--- a/drivers/usb/host/ehci-msm-hsic.c
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -39,6 +39,7 @@
#include <mach/msm_iomap.h>
#include <mach/msm_xo.h>
#include <linux/spinlock.h>
+#include <linux/cpu.h>
#define MSM_USB_BASE (hcd->regs)
@@ -56,11 +57,191 @@
struct wake_lock wlock;
int peripheral_status_irq;
int wakeup_irq;
+ int wakeup_gpio;
bool wakeup_irq_enabled;
+ atomic_t pm_usage_cnt;
uint32_t bus_perf_client;
+ uint32_t wakeup_int_cnt;
};
static bool debug_bus_voting_enabled = true;
+
+static unsigned int enable_dbg_log = 1;
+module_param(enable_dbg_log, uint, S_IRUGO | S_IWUSR);
+/*by default log ep0 and efs sync ep*/
+static unsigned int ep_addr_rxdbg_mask = 9;
+module_param(ep_addr_rxdbg_mask, uint, S_IRUGO | S_IWUSR);
+static unsigned int ep_addr_txdbg_mask = 9;
+module_param(ep_addr_txdbg_mask, uint, S_IRUGO | S_IWUSR);
+
+/* Maximum debug message length */
+#define DBG_MSG_LEN 100UL
+
+/* Maximum number of messages */
+#define DBG_MAX_MSG 256UL
+
+#define TIME_BUF_LEN 20
+
+enum event_type {
+ EVENT_UNDEF = -1,
+ URB_SUBMIT,
+ URB_COMPLETE,
+ EVENT_NONE,
+};
+
+#define EVENT_STR_LEN 5
+
+static char *event_to_str(enum event_type e)
+{
+ switch (e) {
+ case URB_SUBMIT:
+ return "S";
+ case URB_COMPLETE:
+ return "C";
+ case EVENT_NONE:
+ return "NONE";
+ default:
+ return "UNDEF";
+ }
+}
+
+static enum event_type str_to_event(const char *name)
+{
+ if (!strncasecmp("S", name, EVENT_STR_LEN))
+ return URB_SUBMIT;
+ if (!strncasecmp("C", name, EVENT_STR_LEN))
+ return URB_COMPLETE;
+ if (!strncasecmp("", name, EVENT_STR_LEN))
+ return EVENT_NONE;
+
+ return EVENT_UNDEF;
+}
+
+/*log ep0 activity*/
+static struct {
+ char (buf[DBG_MAX_MSG])[DBG_MSG_LEN]; /* buffer */
+ unsigned idx; /* index */
+ rwlock_t lck; /* lock */
+} dbg_hsic_ctrl = {
+ .idx = 0,
+ .lck = __RW_LOCK_UNLOCKED(lck)
+};
+
+static struct {
+ char (buf[DBG_MAX_MSG])[DBG_MSG_LEN]; /* buffer */
+ unsigned idx; /* index */
+ rwlock_t lck; /* lock */
+} dbg_hsic_data = {
+ .idx = 0,
+ .lck = __RW_LOCK_UNLOCKED(lck)
+};
+
+/**
+ * dbg_inc: increments debug event index
+ * @idx: buffer index
+ */
+static void dbg_inc(unsigned *idx)
+{
+ *idx = (*idx + 1) & (DBG_MAX_MSG-1);
+}
+
+/*get_timestamp - returns time of day in us */
+static char *get_timestamp(char *tbuf)
+{
+ unsigned long long t;
+ unsigned long nanosec_rem;
+
+ t = cpu_clock(smp_processor_id());
+ nanosec_rem = do_div(t, 1000000000)/1000;
+ scnprintf(tbuf, TIME_BUF_LEN, "[%5lu.%06lu] ", (unsigned long)t,
+ nanosec_rem);
+ return tbuf;
+}
+
+static int allow_dbg_log(int ep_addr)
+{
+ int dir, num;
+
+ dir = ep_addr & USB_DIR_IN ? USB_DIR_IN : USB_DIR_OUT;
+ num = ep_addr & ~USB_DIR_IN;
+ num = 1 << num;
+
+ if ((dir == USB_DIR_IN) && (num & ep_addr_rxdbg_mask))
+ return 1;
+ if ((dir == USB_DIR_OUT) && (num & ep_addr_txdbg_mask))
+ return 1;
+
+ return 0;
+}
+
+static void dbg_log_event(struct urb *urb, char * event, unsigned extra)
+{
+ unsigned long flags;
+ int ep_addr;
+ char tbuf[TIME_BUF_LEN];
+
+ if (!enable_dbg_log)
+ return;
+
+ if (!urb) {
+ write_lock_irqsave(&dbg_hsic_ctrl.lck, flags);
+ scnprintf(dbg_hsic_ctrl.buf[dbg_hsic_ctrl.idx], DBG_MSG_LEN,
+ "%s: %s : %u\n", get_timestamp(tbuf), event, extra);
+ dbg_inc(&dbg_hsic_ctrl.idx);
+ write_unlock_irqrestore(&dbg_hsic_ctrl.lck, flags);
+ return;
+ }
+
+ ep_addr = urb->ep->desc.bEndpointAddress;
+ if (!allow_dbg_log(ep_addr))
+ return;
+
+ if ((ep_addr & 0x0f) == 0x0) {
+ /*submit event*/
+ if (!str_to_event(event)) {
+ write_lock_irqsave(&dbg_hsic_ctrl.lck, flags);
+ scnprintf(dbg_hsic_ctrl.buf[dbg_hsic_ctrl.idx],
+ DBG_MSG_LEN, "%s: [%s : %p]:[%s] "
+ "%02x %02x %04x %04x %04x %u %d\n",
+ get_timestamp(tbuf), event, urb,
+ (ep_addr & USB_DIR_IN) ? "in" : "out",
+ urb->setup_packet[0], urb->setup_packet[1],
+ (urb->setup_packet[3] << 8) |
+ urb->setup_packet[2],
+ (urb->setup_packet[5] << 8) |
+ urb->setup_packet[4],
+ (urb->setup_packet[7] << 8) |
+ urb->setup_packet[6],
+ urb->transfer_buffer_length, urb->status);
+
+ dbg_inc(&dbg_hsic_ctrl.idx);
+ write_unlock_irqrestore(&dbg_hsic_ctrl.lck, flags);
+ } else {
+ write_lock_irqsave(&dbg_hsic_ctrl.lck, flags);
+ scnprintf(dbg_hsic_ctrl.buf[dbg_hsic_ctrl.idx],
+ DBG_MSG_LEN, "%s: [%s : %p]:[%s] %u %d\n",
+ get_timestamp(tbuf), event, urb,
+ (ep_addr & USB_DIR_IN) ? "in" : "out",
+ urb->actual_length, extra);
+
+ dbg_inc(&dbg_hsic_ctrl.idx);
+ write_unlock_irqrestore(&dbg_hsic_ctrl.lck, flags);
+ }
+ } else {
+ write_lock_irqsave(&dbg_hsic_data.lck, flags);
+ scnprintf(dbg_hsic_data.buf[dbg_hsic_data.idx], DBG_MSG_LEN,
+ "%s: [%s : %p]:ep%d[%s] %u %d\n",
+ get_timestamp(tbuf), event, urb, ep_addr & 0x0f,
+ (ep_addr & USB_DIR_IN) ? "in" : "out",
+ str_to_event(event) ? urb->actual_length :
+ urb->transfer_buffer_length,
+ str_to_event(event) ? extra : urb->status);
+
+ dbg_inc(&dbg_hsic_data.idx);
+ write_unlock_irqrestore(&dbg_hsic_data.lck, flags);
+ }
+}
+
static inline struct msm_hsic_hcd *hcd_to_hsic(struct usb_hcd *hcd)
{
return (struct msm_hsic_hcd *) (hcd->hcd_priv);
@@ -186,9 +367,20 @@
goto free_strobe;
}
+ if (mehci->wakeup_gpio) {
+ rc = gpio_request(mehci->wakeup_gpio, "HSIC_WAKEUP_GPIO");
+ if (rc < 0) {
+ dev_err(mehci->dev, "gpio request failed for HSIC WAKEUP\n");
+ goto free_data;
+ }
+ }
+
return 0;
free_gpio:
+ if (mehci->wakeup_gpio)
+ gpio_free(mehci->wakeup_gpio);
+free_data:
gpio_free(pdata->data);
free_strobe:
gpio_free(pdata->strobe);
@@ -321,6 +513,9 @@
ulpi_write(mehci, 0xA9, 0x30);
}
+ /*disable auto resume*/
+ ulpi_write(mehci, ULPI_IFC_CTRL_AUTORESUME, ULPI_CLR(ULPI_IFC_CTRL));
+
return 0;
}
@@ -339,6 +534,12 @@
return 0;
}
+ if (!(readl_relaxed(USB_PORTSC) & PORT_PE)) {
+ dev_dbg(mehci->dev, "%s:port is not enabled skip suspend\n",
+ __func__);
+ return -EAGAIN;
+ }
+
disable_irq(hcd->irq);
/* make sure we don't race against a remote wakeup */
@@ -408,6 +609,11 @@
atomic_set(&mehci->in_lpm, 1);
enable_irq(hcd->irq);
+
+ mehci->wakeup_irq_enabled = 1;
+ enable_irq_wake(mehci->wakeup_irq);
+ enable_irq(mehci->wakeup_irq);
+
wake_unlock(&mehci->wlock);
dev_info(mehci->dev, "HSIC-USB in low power mode\n");
@@ -426,6 +632,12 @@
return 0;
}
+ if (mehci->wakeup_irq_enabled) {
+ disable_irq_wake(mehci->wakeup_irq);
+ disable_irq_nosync(mehci->wakeup_irq);
+ mehci->wakeup_irq_enabled = 0;
+ }
+
wake_lock(&mehci->wlock);
if (mehci->bus_perf_client && debug_bus_voting_enabled) {
@@ -478,6 +690,8 @@
skip_phy_resume:
+ usb_hcd_resume_root_hub(hcd);
+
atomic_set(&mehci->in_lpm, 0);
if (mehci->async_int) {
@@ -486,6 +700,11 @@
enable_irq(hcd->irq);
}
+ if (atomic_read(&mehci->pm_usage_cnt)) {
+ atomic_set(&mehci->pm_usage_cnt, 0);
+ pm_runtime_put_noidle(mehci->dev);
+ }
+
dev_info(mehci->dev, "HSIC-USB exited from low power mode\n");
return 0;
@@ -548,6 +767,25 @@
return 0;
}
+static int ehci_hsic_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+ gfp_t mem_flags)
+{
+ dbg_log_event(urb, event_to_str(URB_SUBMIT), 0);
+ return ehci_urb_enqueue(hcd, urb, mem_flags);
+}
+
+static int ehci_hsic_bus_suspend(struct usb_hcd *hcd)
+{
+ dbg_log_event(NULL, "Suspend RH", 0);
+ return ehci_bus_suspend(hcd);
+}
+
+static int ehci_hsic_bus_resume(struct usb_hcd *hcd)
+{
+ dbg_log_event(NULL, "Resume RH", 0);
+ return ehci_bus_resume(hcd);
+}
+
static struct hc_driver msm_hsic_driver = {
.description = hcd_name,
.product_desc = "Qualcomm EHCI Host Controller using HSIC",
@@ -568,7 +806,7 @@
/*
* managing i/o requests and associated device resources
*/
- .urb_enqueue = ehci_urb_enqueue,
+ .urb_enqueue = ehci_hsic_urb_enqueue,
.urb_dequeue = ehci_urb_dequeue,
.endpoint_disable = ehci_endpoint_disable,
.endpoint_reset = ehci_endpoint_reset,
@@ -590,8 +828,10 @@
/*
* PM support
*/
- .bus_suspend = ehci_bus_suspend,
- .bus_resume = ehci_bus_resume,
+ .bus_suspend = ehci_hsic_bus_suspend,
+ .bus_resume = ehci_hsic_bus_resume,
+
+ .log_urb_complete = dbg_log_event,
};
static int msm_hsic_init_clocks(struct msm_hsic_hcd *mehci, u32 init)
@@ -687,7 +927,12 @@
{
struct msm_hsic_hcd *mehci = data;
- dev_dbg(mehci->dev, "%s: hsic remote wakeup interrupt\n", __func__);
+ mehci->wakeup_int_cnt++;
+ dbg_log_event(NULL, "Remote Wakeup IRQ", mehci->wakeup_int_cnt);
+ dev_dbg(mehci->dev, "%s: hsic remote wakeup interrupt cnt: %u\n",
+ __func__, mehci->wakeup_int_cnt);
+
+ wake_lock(&mehci->wlock);
if (mehci->wakeup_irq_enabled) {
mehci->wakeup_irq_enabled = 0;
@@ -695,6 +940,11 @@
disable_irq_nosync(irq);
}
+ if (!atomic_read(&mehci->pm_usage_cnt)) {
+ atomic_set(&mehci->pm_usage_cnt, 1);
+ pm_runtime_get(mehci->dev);
+ }
+
return IRQ_HANDLED;
}
@@ -751,6 +1001,89 @@
.release = single_release,
};
+static int ehci_hsic_msm_wakeup_cnt_show(struct seq_file *s, void *unused)
+{
+ struct msm_hsic_hcd *mehci = s->private;
+
+ seq_printf(s, "%u\n", mehci->wakeup_int_cnt);
+
+ return 0;
+}
+
+static int ehci_hsic_msm_wakeup_cnt_open(struct inode *inode, struct file *f)
+{
+ return single_open(f, ehci_hsic_msm_wakeup_cnt_show, inode->i_private);
+}
+
+const struct file_operations ehci_hsic_msm_wakeup_cnt_fops = {
+ .open = ehci_hsic_msm_wakeup_cnt_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int ehci_hsic_msm_data_events_show(struct seq_file *s, void *unused)
+{
+ unsigned long flags;
+ unsigned i;
+
+ read_lock_irqsave(&dbg_hsic_data.lck, flags);
+
+ i = dbg_hsic_data.idx;
+ for (dbg_inc(&i); i != dbg_hsic_data.idx; dbg_inc(&i)) {
+ if (!strnlen(dbg_hsic_data.buf[i], DBG_MSG_LEN))
+ continue;
+ seq_printf(s, "%s\n", dbg_hsic_data.buf[i]);
+ }
+
+ read_unlock_irqrestore(&dbg_hsic_data.lck, flags);
+
+ return 0;
+}
+
+static int ehci_hsic_msm_data_events_open(struct inode *inode, struct file *f)
+{
+ return single_open(f, ehci_hsic_msm_data_events_show, inode->i_private);
+}
+
+const struct file_operations ehci_hsic_msm_dbg_data_fops = {
+ .open = ehci_hsic_msm_data_events_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int ehci_hsic_msm_ctrl_events_show(struct seq_file *s, void *unused)
+{
+ unsigned long flags;
+ unsigned i;
+
+ read_lock_irqsave(&dbg_hsic_ctrl.lck, flags);
+
+ i = dbg_hsic_ctrl.idx;
+ for (dbg_inc(&i); i != dbg_hsic_ctrl.idx; dbg_inc(&i)) {
+ if (!strnlen(dbg_hsic_ctrl.buf[i], DBG_MSG_LEN))
+ continue;
+ seq_printf(s, "%s\n", dbg_hsic_ctrl.buf[i]);
+ }
+
+ read_unlock_irqrestore(&dbg_hsic_ctrl.lck, flags);
+
+ return 0;
+}
+
+static int ehci_hsic_msm_ctrl_events_open(struct inode *inode, struct file *f)
+{
+ return single_open(f, ehci_hsic_msm_ctrl_events_show, inode->i_private);
+}
+
+const struct file_operations ehci_hsic_msm_dbg_ctrl_fops = {
+ .open = ehci_hsic_msm_ctrl_events_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static struct dentry *ehci_hsic_msm_dbg_root;
static int ehci_hsic_msm_debugfs_init(struct msm_hsic_hcd *mehci)
{
@@ -771,6 +1104,36 @@
return -ENODEV;
}
+ ehci_hsic_msm_dentry = debugfs_create_file("wakeup_cnt",
+ S_IRUGO,
+ ehci_hsic_msm_dbg_root, mehci,
+ &ehci_hsic_msm_wakeup_cnt_fops);
+
+ if (!ehci_hsic_msm_dentry) {
+ debugfs_remove_recursive(ehci_hsic_msm_dbg_root);
+ return -ENODEV;
+ }
+
+ ehci_hsic_msm_dentry = debugfs_create_file("show_ctrl_events",
+ S_IRUGO,
+ ehci_hsic_msm_dbg_root, mehci,
+ &ehci_hsic_msm_dbg_ctrl_fops);
+
+ if (!ehci_hsic_msm_dentry) {
+ debugfs_remove_recursive(ehci_hsic_msm_dbg_root);
+ return -ENODEV;
+ }
+
+ ehci_hsic_msm_dentry = debugfs_create_file("show_data_events",
+ S_IRUGO,
+ ehci_hsic_msm_dbg_root, mehci,
+ &ehci_hsic_msm_dbg_data_fops);
+
+ if (!ehci_hsic_msm_dentry) {
+ debugfs_remove_recursive(ehci_hsic_msm_dbg_root);
+ return -ENODEV;
+ }
+
return 0;
}
@@ -830,12 +1193,23 @@
mehci = hcd_to_hsic(hcd);
mehci->dev = &pdev->dev;
+ mehci->ehci.susp_sof_bug = 1;
+
+ mehci->ehci.max_log2_irq_thresh = 6;
+
res = platform_get_resource_byname(pdev,
IORESOURCE_IRQ,
"peripheral_status_irq");
if (res)
mehci->peripheral_status_irq = res->start;
+ res = platform_get_resource_byname(pdev, IORESOURCE_IO, "wakeup");
+ if (res) {
+ mehci->wakeup_gpio = res->start;
+ mehci->wakeup_irq = MSM_GPIO_TO_INT(res->start);
+ dev_dbg(mehci->dev, "wakeup_irq: %d\n", mehci->wakeup_irq);
+ }
+
ret = msm_hsic_init_clocks(mehci, 1);
if (ret) {
dev_err(&pdev->dev, "unable to initialize clocks\n");
@@ -878,12 +1252,9 @@
}
/* configure wakeup irq */
- ret = platform_get_irq(pdev, 2);
- if (ret > 0) {
- mehci->wakeup_irq = ret;
- dev_dbg(&pdev->dev, "wakeup_irq: %d\n", mehci->wakeup_irq);
+ if (mehci->wakeup_irq) {
ret = request_irq(mehci->wakeup_irq, msm_hsic_wakeup_irq,
- IRQF_TRIGGER_LOW,
+ IRQF_TRIGGER_HIGH,
"msm_hsic_wakeup", mehci);
if (!ret) {
disable_irq_nosync(mehci->wakeup_irq);
@@ -988,6 +1359,8 @@
dev_dbg(dev, "ehci-msm-hsic PM suspend\n");
+ dbg_log_event(NULL, "PM Suspend", 0);
+
if (device_may_wakeup(dev))
enable_irq_wake(hcd->irq);
@@ -999,39 +1372,17 @@
return ret;
}
-static int msm_hsic_pm_suspend_noirq(struct device *dev)
-{
- struct usb_hcd *hcd = dev_get_drvdata(dev);
- struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
-
- dev_dbg(dev, "ehci-msm-hsic PM suspend_noirq\n");
-
- if (device_may_wakeup(dev) && !mehci->wakeup_irq_enabled) {
- enable_irq(mehci->wakeup_irq);
- enable_irq_wake(mehci->wakeup_irq);
- mehci->wakeup_irq_enabled = 1;
- }
-
- return 0;
-}
-
static int msm_hsic_pm_resume(struct device *dev)
{
int ret;
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
- dev_dbg(dev, "ehci-msm-hsic PM resume\n");
+ dbg_log_event(NULL, "PM Resume", 0);
if (device_may_wakeup(dev))
disable_irq_wake(hcd->irq);
- if (mehci->wakeup_irq_enabled) {
- mehci->wakeup_irq_enabled = 0;
- disable_irq_wake(mehci->wakeup_irq);
- disable_irq_nosync(mehci->wakeup_irq);
- }
-
ret = msm_hsic_resume(mehci);
if (ret)
return ret;
@@ -1058,6 +1409,9 @@
struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
dev_dbg(dev, "EHCI runtime suspend\n");
+
+ dbg_log_event(NULL, "Run Time PM Suspend", 0);
+
return msm_hsic_suspend(mehci);
}
@@ -1067,6 +1421,9 @@
struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
dev_dbg(dev, "EHCI runtime resume\n");
+
+ dbg_log_event(NULL, "Run Time PM Resume", 0);
+
return msm_hsic_resume(mehci);
}
#endif
@@ -1074,7 +1431,6 @@
#ifdef CONFIG_PM
static const struct dev_pm_ops msm_hsic_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(msm_hsic_pm_suspend, msm_hsic_pm_resume)
- .suspend_noirq = msm_hsic_pm_suspend_noirq,
SET_RUNTIME_PM_OPS(msm_hsic_runtime_suspend, msm_hsic_runtime_resume,
msm_hsic_runtime_idle)
};
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 3a5a5cc..a49ab99 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -124,6 +124,8 @@
ktime_t last_periodic_enable;
u32 command;
+ unsigned max_log2_irq_thresh;
+
/* SILICON QUIRKS */
unsigned no_selective_suspend:1;
unsigned has_fsl_port_bug:1; /* FreeScale */
@@ -138,6 +140,7 @@
unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/
unsigned has_synopsys_hc_bug:1; /* Synopsys HC */
unsigned frame_index_bug:1; /* MosChip (AKA NetMos) */
+ unsigned susp_sof_bug:1; /*Chip Idea HC*/
/* required for usb32 quirk */
#define OHCI_CTRL_HCFS (3 << 6)
diff --git a/drivers/usb/misc/mdm_ctrl_bridge.c b/drivers/usb/misc/mdm_ctrl_bridge.c
index c23c7b1..49591cd 100644
--- a/drivers/usb/misc/mdm_ctrl_bridge.c
+++ b/drivers/usb/misc/mdm_ctrl_bridge.c
@@ -255,7 +255,7 @@
}
}
-int ctrl_bridge_start_read(struct ctrl_bridge *dev)
+static int ctrl_bridge_start_read(struct ctrl_bridge *dev)
{
int retval = 0;
@@ -281,7 +281,6 @@
int ctrl_bridge_open(struct bridge *brdg)
{
struct ctrl_bridge *dev;
- int ret;
if (!brdg) {
err("bridge is null\n");
@@ -304,16 +303,10 @@
dev->set_ctrl_line_sts = 0;
dev->notify_ser_state = 0;
- ret = usb_autopm_get_interface(dev->intf);
- if (ret < 0) {
- dev_err(&dev->udev->dev, "%s autopm_get fail: %d\n",
- __func__, ret);
- return ret;
- }
+ if (brdg->ops.send_cbits)
+ brdg->ops.send_cbits(brdg->ctx, dev->cbits_tohost);
- ret = ctrl_bridge_start_read(dev);
- usb_autopm_put_interface(dev->intf);
- return ret;
+ return 0;
}
EXPORT_SYMBOL(ctrl_bridge_open);
@@ -504,11 +497,7 @@
}
}
- /* if the bridge is open, resume reading */
- if (dev->brdg)
- return ctrl_bridge_start_read(dev);
-
- return 0;
+ return ctrl_bridge_start_read(dev);
}
#if defined(CONFIG_DEBUG_FS)
@@ -711,7 +700,7 @@
ch_id++;
- return retval;
+ return ctrl_bridge_start_read(dev);
free_rbuf:
kfree(dev->readbuf);
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 5b05c5b..d3e92f2 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -75,6 +75,7 @@
static struct regulator *hsusb_vddcx;
static struct regulator *vbus_otg;
static struct regulator *mhl_analog_switch;
+static struct power_supply *psy;
static bool aca_id_turned_on;
static inline bool aca_enabled(void)
@@ -837,10 +838,14 @@
clk_disable_unprepare(motg->core_clk);
/* usb phy no more require TCXO clock, hence vote for TCXO disable */
- ret = msm_xo_mode_vote(motg->xo_handle, MSM_XO_MODE_OFF);
- if (ret)
- dev_err(otg->dev, "%s failed to devote for "
- "TCXO D0 buffer%d\n", __func__, ret);
+ if (!host_bus_suspend) {
+ ret = msm_xo_mode_vote(motg->xo_handle, MSM_XO_MODE_OFF);
+ if (ret)
+ dev_err(otg->dev, "%s failed to devote for "
+ "TCXO D0 buffer%d\n", __func__, ret);
+ else
+ motg->lpm_flags |= XO_SHUTDOWN;
+ }
if (motg->caps & ALLOW_PHY_POWER_COLLAPSE &&
!host_bus_suspend && !dcp) {
@@ -885,10 +890,13 @@
wake_lock(&motg->wlock);
/* Vote for TCXO when waking up the phy */
- ret = msm_xo_mode_vote(motg->xo_handle, MSM_XO_MODE_ON);
- if (ret)
- dev_err(otg->dev, "%s failed to vote for "
- "TCXO D0 buffer%d\n", __func__, ret);
+ if (motg->lpm_flags & XO_SHUTDOWN) {
+ ret = msm_xo_mode_vote(motg->xo_handle, MSM_XO_MODE_ON);
+ if (ret)
+ dev_err(otg->dev, "%s failed to vote for "
+ "TCXO D0 buffer%d\n", __func__, ret);
+ motg->lpm_flags &= ~XO_SHUTDOWN;
+ }
clk_prepare_enable(motg->core_clk);
@@ -994,9 +1002,7 @@
static int msm_otg_notify_power_supply(struct msm_otg *motg, unsigned mA)
{
- struct power_supply *psy;
- psy = power_supply_get_by_name("usb");
if (!psy)
goto psy_not_supported;
@@ -2001,6 +2007,9 @@
case OTG_STATE_UNDEFINED:
msm_otg_reset(otg);
msm_otg_init_sm(motg);
+ psy = power_supply_get_by_name("usb");
+ if (!psy)
+ pr_err("couldn't get usb power supply\n");
otg->state = OTG_STATE_B_IDLE;
if (!test_bit(B_SESS_VLD, &motg->inputs) &&
test_bit(ID, &motg->inputs)) {
diff --git a/drivers/video/msm/lvds.c b/drivers/video/msm/lvds.c
index 18225fb..88c24b9 100644
--- a/drivers/video/msm/lvds.c
+++ b/drivers/video/msm/lvds.c
@@ -65,28 +65,14 @@
usleep(1000);
/* LVDS PHY PLL configuration */
- MDP_OUTP(MDP_BASE + 0xc3000, 0x08);
- MDP_OUTP(MDP_BASE + 0xc3004, 0x87);
+ MDP_OUTP(MDP_BASE + 0xc3004, 0x62);
MDP_OUTP(MDP_BASE + 0xc3008, 0x30);
- MDP_OUTP(MDP_BASE + 0xc300c, 0x06);
- MDP_OUTP(MDP_BASE + 0xc3014, 0x20);
- MDP_OUTP(MDP_BASE + 0xc3018, 0x0F);
- MDP_OUTP(MDP_BASE + 0xc301c, 0x01);
+ MDP_OUTP(MDP_BASE + 0xc300c, 0xc4);
+ MDP_OUTP(MDP_BASE + 0xc3014, 0x10);
+ MDP_OUTP(MDP_BASE + 0xc3018, 0x05);
+ MDP_OUTP(MDP_BASE + 0xc301c, 0x62);
MDP_OUTP(MDP_BASE + 0xc3020, 0x41);
MDP_OUTP(MDP_BASE + 0xc3024, 0x0d);
- MDP_OUTP(MDP_BASE + 0xc3028, 0x07);
- MDP_OUTP(MDP_BASE + 0xc302c, 0x00);
- MDP_OUTP(MDP_BASE + 0xc3030, 0x1c);
- MDP_OUTP(MDP_BASE + 0xc3034, 0x01);
- MDP_OUTP(MDP_BASE + 0xc3038, 0x00);
- MDP_OUTP(MDP_BASE + 0xc3040, 0xC0);
- MDP_OUTP(MDP_BASE + 0xc3044, 0x00);
- MDP_OUTP(MDP_BASE + 0xc3048, 0x30);
- MDP_OUTP(MDP_BASE + 0xc304c, 0x00);
-
- MDP_OUTP(MDP_BASE + 0xc3000, 0x11);
- MDP_OUTP(MDP_BASE + 0xc3064, 0x05);
- MDP_OUTP(MDP_BASE + 0xc3050, 0x20);
MDP_OUTP(MDP_BASE + 0xc3000, 0x01);
/* Wait until LVDS PLL is locked and ready */
@@ -210,6 +196,10 @@
if (lvds_clk)
clk_disable_unprepare(lvds_clk);
+ MDP_OUTP(MDP_BASE + 0xc3100, 0x0);
+ MDP_OUTP(MDP_BASE + 0xc3000, 0x0);
+ usleep(10);
+
if (lvds_pdata && lvds_pdata->lcdc_power_save)
lvds_pdata->lcdc_power_save(0);
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index 2a6a900..7fd2d91 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -1573,7 +1573,8 @@
__mdp_histogram_kickoff(mgmt);
if (isr & INTR_HIST_DONE) {
- if (waitqueue_active(&mgmt->mdp_hist_comp.wait)) {
+ if ((waitqueue_active(&mgmt->mdp_hist_comp.wait))
+ && (mgmt->hist != NULL)) {
if (!queue_work(mdp_hist_wq,
&mgmt->mdp_histogram_worker)) {
pr_err("%s %d- can't queue hist_read\n",
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index e6dc795..905fc21 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -48,12 +48,6 @@
OVERLAY_PERF_LEVEL4
};
-enum mdp4_overlay_status {
- MDP4_OVERLAY_TYPE_UNSET,
- MDP4_OVERLAY_TYPE_SET,
- MDP4_OVERLAY_TYPE_MAX
-};
-
typedef int (*cmd_fxn_t)(struct platform_device *pdev);
enum { /* display */
@@ -539,7 +533,7 @@
void mdp4_overlay0_done_lcdc(struct mdp_dma_data *dma);
void mdp4_overlay0_done_mddi(struct mdp_dma_data *dma);
void mdp4_dma_s_done_mddi(void);
-void mdp4_dma_p_done_mddi(void);
+void mdp4_dma_p_done_mddi(struct mdp_dma_data *dma);
void mdp4_dma_p_done_dsi(struct mdp_dma_data *dma);
void mdp4_dma_p_done_dsi_video(struct mdp_dma_data *dma);
void mdp4_dma_p_done_lcdc(void);
@@ -563,10 +557,32 @@
{
/* empty */
}
+static inline void mdp4_mddi_blt_dmap_busy_wait(struct msm_fb_data_type *mfd)
+{
+ /* empty */
+}
static inline void mdp4_mddi_overlay_restore(void)
{
/* empty */
}
+static inline void mdp4_mddi_overlay_blt_start(struct msm_fb_data_type *mfd)
+{
+ /*empty*/
+}
+static inline void mdp4_mddi_overlay_blt_stop(struct msm_fb_data_type *mfd)
+{
+ /*empty*/
+}
+static inline void mdp4_mddi_overlay_blt_offset(struct msm_fb_data_type *mfd,
+ struct msmfb_overlay_blt *req)
+{
+ /* empty */
+}
+static inline void mdp4_mddi_overlay_blt(struct msm_fb_data_type *mfd,
+ struct msmfb_overlay_blt *req)
+{
+ /* empty*/
+}
#endif
void mdp4_mddi_overlay_kickoff(struct msm_fb_data_type *mfd,
@@ -604,6 +620,13 @@
}
#endif
#else
+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,
+ struct msmfb_overlay_blt *req);
+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);
static inline int mdp4_dsi_overlay_blt_start(struct msm_fb_data_type *mfd)
{
return -ENODEV;
@@ -646,9 +669,6 @@
void mdp4_lcdc_overlay_blt_stop(struct msm_fb_data_type *mfd);
void mdp4_dtv_overlay_blt_start(struct msm_fb_data_type *mfd);
void mdp4_dtv_overlay_blt_stop(struct msm_fb_data_type *mfd);
-
-int mdp4_mddi_overlay_blt_offset(int *off);
-void mdp4_mddi_overlay_blt(ulong addr);
void mdp4_overlay_panel_mode(int mixer_num, uint32 mode);
void mdp4_overlay_panel_mode_unset(int mixer_num, uint32 mode);
int mdp4_overlay_mixer_play(int mixer_num);
@@ -727,8 +747,6 @@
void mdp4_overlay_dsi_video_wait4vsync(struct msm_fb_data_type *mfd);
void mdp4_primary_vsync_dsi_video(void);
uint32_t mdp4_ss_table_value(int8_t param, int8_t index);
-void mdp4_overlay_status_write(enum mdp4_overlay_status type, bool val);
-bool mdp4_overlay_status_read(enum mdp4_overlay_status type);
void mdp4_overlay_ctrl_db_reset(void);
int mdp4_overlay_writeback_on(struct platform_device *pdev);
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index 5b9441b..ce0dd3d 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -23,6 +23,7 @@
#include <mach/hardware.h>
#include <mach/iommu_domains.h>
#include <mach/iommu.h>
+#include <linux/iommu.h>
#include <linux/io.h>
#include <linux/debugfs.h>
#include <linux/fb.h>
@@ -197,19 +198,6 @@
}
}
-/* static array with index 0 for unset status and 1 for set status */
-static bool overlay_status[MDP4_OVERLAY_TYPE_MAX];
-
-void mdp4_overlay_status_write(enum mdp4_overlay_status type, bool val)
-{
- overlay_status[type] = val;
-}
-
-bool mdp4_overlay_status_read(enum mdp4_overlay_status type)
-{
- return overlay_status[type];
-}
-
void mdp4_overlay_ctrl_db_reset(void)
{
int i;
@@ -2229,6 +2217,8 @@
mdp4_dsi_video_overlay_blt(mfd, req);
else if (ctrl->panel_mode & MDP4_PANEL_LCDC)
mdp4_lcdc_overlay_blt(mfd, req);
+ else if (ctrl->panel_mode & MDP4_PANEL_MDDI)
+ mdp4_mddi_overlay_blt(mfd, req);
mutex_unlock(&mfd->dma->ov_mutex);
@@ -2250,6 +2240,8 @@
ret = mdp4_dsi_video_overlay_blt_offset(mfd, req);
else if (ctrl->panel_mode & MDP4_PANEL_LCDC)
ret = mdp4_lcdc_overlay_blt_offset(mfd, req);
+ else if (ctrl->panel_mode & MDP4_PANEL_MDDI)
+ mdp4_mddi_overlay_blt_offset(mfd, req);
mutex_unlock(&mfd->dma->ov_mutex);
@@ -2306,7 +2298,7 @@
if (mdp4_extn_disp)
return OVERLAY_PERF_LEVEL1;
- if (req->flags & MDP_DEINTERLACE)
+ if (req->flags & (MDP_DEINTERLACE | MDP_BACKEND_COMPOSITION))
return OVERLAY_PERF_LEVEL1;
for (i = 0, cnt = 0; i < OVERLAY_PIPE_MAX; i++) {
@@ -2377,6 +2369,8 @@
mdp4_dsi_video_blt_start(mfd);
else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD)
mdp4_dsi_overlay_blt_start(mfd);
+ else if (ctrl->panel_mode & MDP4_PANEL_MDDI)
+ mdp4_mddi_overlay_blt_start(mfd);
} else {
if (mfd->panel_info.type == LCDC_PANEL ||
mfd->panel_info.type == LVDS_PANEL)
@@ -2385,6 +2379,8 @@
mdp4_dsi_video_blt_stop(mfd);
else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD)
mdp4_dsi_overlay_blt_stop(mfd);
+ else if (ctrl->panel_mode & MDP4_PANEL_MDDI)
+ mdp4_mddi_overlay_blt_stop(mfd);
}
mfd->ov0_blt_state = mfd->use_ov0_blt;
}
@@ -2425,6 +2421,12 @@
use_blt = 1;
}
+ if (mfd->panel_info.type == MDDI_PANEL) {
+ if ((req->src_rect.h/2) >= req->dst_rect.h ||
+ (req->src_rect.w/2) >= req->dst_rect.w)
+ use_blt = 1;
+ }
+
if (mfd->mdp_rev == MDP_REV_41) {
/*
* writeback (blt) mode to provide work around for
@@ -2516,12 +2518,6 @@
mdp4_stat.overlay_set[pipe->mixer_num]++;
- if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
- if (mdp_hw_revision == MDP4_REVISION_V2_1 &&
- pipe->mixer_num == MDP4_MIXER0)
- mdp4_overlay_status_write(MDP4_OVERLAY_TYPE_SET, true);
- }
-
if (ctrl->panel_mode & MDP4_PANEL_DTV &&
pipe->mixer_num == MDP4_MIXER1) {
u32 use_blt = mdp4_overlay_blt_enable(req, mfd, perf_level);
@@ -2557,6 +2553,7 @@
mdp4_set_perf_level();
} else if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
mdp4_mddi_dma_busy_wait(mfd);
+ mdp4_mddi_blt_dmap_busy_wait(mfd);
mdp4_set_perf_level();
}
} else {
@@ -2607,7 +2604,7 @@
#else
if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
if (mfd->panel_power_on)
- mdp4_mddi_dma_busy_wait(mfd);
+ mdp4_mddi_blt_dmap_busy_wait(mfd);
}
#endif
}
@@ -2639,9 +2636,6 @@
}
#else
if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
- if (mdp_hw_revision == MDP4_REVISION_V2_1)
- mdp4_overlay_status_write(
- MDP4_OVERLAY_TYPE_UNSET, true);
if (mfd->panel_power_on)
mdp4_mddi_overlay_restore();
}
@@ -2748,7 +2742,7 @@
if (mfd->use_ov1_blt)
mdp4_overlay1_update_blt_mode(mfd);
- mdp4_overlay_dtv_wait_for_ov(mfd, pipe);
+ mdp4_overlay_dtv_wait4vsync();
mdp4_iommu_unmap(pipe);
mutex_unlock(&mfd->dma->ov_mutex);
@@ -2936,17 +2930,21 @@
/* primary interface */
ctrl->mixer0_played++;
if (ctrl->panel_mode & MDP4_PANEL_LCDC) {
- if (!mfd->use_ov0_blt)
- mdp4_overlay_update_blt_mode(mfd);
+ mdp4_overlay_reg_flush(pipe, 0);
mdp4_overlay_lcdc_start();
mdp4_overlay_lcdc_vsync_push(mfd, pipe);
+ if (!mfd->use_ov0_blt &&
+ !(pipe->flags & MDP_OV_PLAY_NOWAIT))
+ mdp4_overlay_update_blt_mode(mfd);
}
#ifdef CONFIG_FB_MSM_MIPI_DSI
else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) {
- if (!mfd->use_ov0_blt)
- mdp4_overlay_update_blt_mode(mfd);
+ mdp4_overlay_reg_flush(pipe, 0);
mdp4_overlay_dsi_video_start();
mdp4_overlay_dsi_video_vsync_push(mfd, pipe);
+ if (!mfd->use_ov0_blt &&
+ !(pipe->flags & MDP_OV_PLAY_NOWAIT))
+ mdp4_overlay_update_blt_mode(mfd);
}
#endif
else {
diff --git a/drivers/video/msm/mdp4_overlay_dsi_video.c b/drivers/video/msm/mdp4_overlay_dsi_video.c
index 0db4841..574a657 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_video.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_video.c
@@ -35,6 +35,8 @@
#include "mdp4.h"
#include "mipi_dsi.h"
+#include <mach/iommu_domains.h>
+
#define DSI_VIDEO_BASE 0xE0000
static int first_pixel_start_x;
@@ -183,14 +185,10 @@
pipe->srcp0_ystride = fbi->fix.line_length;
pipe->bpp = bpp;
- if (mfd->map_buffer) {
- pipe->srcp0_addr = (unsigned int)mfd->map_buffer->iova[0] + \
- buf_offset;
- pr_debug("start 0x%lx srcp0_addr 0x%x\n", mfd->
- map_buffer->iova[0], pipe->srcp0_addr);
- } else {
+ if (mfd->display_iova)
+ pipe->srcp0_addr = mfd->display_iova + buf_offset;
+ else
pipe->srcp0_addr = (uint32)(buf + buf_offset);
- }
pipe->dst_h = fbi->var.yres;
pipe->dst_w = fbi->var.xres;
@@ -375,14 +373,10 @@
pipe->dst_y = 0;
pipe->dst_x = 0;
- if (mfd->map_buffer) {
- pipe->srcp0_addr = (unsigned int)mfd->map_buffer->iova[0] + \
- buf_offset;
- pr_debug("start 0x%lx srcp0_addr 0x%x\n", mfd->
- map_buffer->iova[0], pipe->srcp0_addr);
- } else {
+ if (mfd->display_iova)
+ pipe->srcp0_addr = mfd->display_iova + buf_offset;
+ else
pipe->srcp0_addr = (uint32)(buf + buf_offset);
- }
mdp4_overlay_rgb_setup(pipe);
@@ -808,14 +802,10 @@
pipe = dsi_pipe;
- if (mfd->map_buffer) {
- pipe->srcp0_addr = (unsigned int)mfd->map_buffer->iova[0] + \
- buf_offset;
- pr_debug("start 0x%lx srcp0_addr 0x%x\n", mfd->
- map_buffer->iova[0], pipe->srcp0_addr);
- } else {
+ if (mfd->display_iova)
+ pipe->srcp0_addr = mfd->display_iova + buf_offset;
+ else
pipe->srcp0_addr = (uint32)(buf + buf_offset);
- }
mdp4_overlay_rgb_setup(pipe);
mdp4_overlay_reg_flush(pipe, 0);
diff --git a/drivers/video/msm/mdp4_overlay_lcdc.c b/drivers/video/msm/mdp4_overlay_lcdc.c
index 1d3f992..a1fecb6 100644
--- a/drivers/video/msm/mdp4_overlay_lcdc.c
+++ b/drivers/video/msm/mdp4_overlay_lcdc.c
@@ -142,14 +142,11 @@
pipe->src_w = fbi->var.xres;
pipe->src_y = 0;
pipe->src_x = 0;
- if (mfd->map_buffer) {
- pipe->srcp0_addr = (unsigned int)mfd->map_buffer->iova[0] + \
- buf_offset;
- pr_debug("start 0x%lx srcp0_addr 0x%x\n", mfd->
- map_buffer->iova[0], pipe->srcp0_addr);
- } else {
+
+ if (mfd->display_iova)
+ pipe->srcp0_addr = mfd->display_iova + buf_offset;
+ else
pipe->srcp0_addr = (uint32)(buf + buf_offset);
- }
pipe->srcp0_ystride = fbi->fix.line_length;
pipe->bpp = bpp;
@@ -588,14 +585,12 @@
mutex_lock(&mfd->dma->ov_mutex);
pipe = lcdc_pipe;
- if (mfd->map_buffer) {
- pipe->srcp0_addr = (unsigned int)mfd->map_buffer->iova[0] + \
- buf_offset;
- pr_debug("start 0x%lx srcp0_addr 0x%x\n", mfd->
- map_buffer->iova[0], pipe->srcp0_addr);
- } else {
+
+ if (mfd->display_iova)
+ pipe->srcp0_addr = mfd->display_iova + buf_offset;
+ else
pipe->srcp0_addr = (uint32)(buf + buf_offset);
- }
+
mdp4_overlay_rgb_setup(pipe);
mdp4_overlay_reg_flush(pipe, 0);
mdp4_mixer_stage_up(pipe);
diff --git a/drivers/video/msm/mdp4_overlay_mddi.c b/drivers/video/msm/mdp4_overlay_mddi.c
index 83959df..82864918 100644
--- a/drivers/video/msm/mdp4_overlay_mddi.c
+++ b/drivers/video/msm/mdp4_overlay_mddi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-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
@@ -124,7 +124,6 @@
printk(KERN_INFO "%s: format2type failed\n", __func__);
mddi_pipe = pipe; /* keep it */
- mddi_pipe->blt_end = 1; /* mark as end */
mddi_ld_param = 0;
mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt;
@@ -163,6 +162,8 @@
(MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg);
MDP_OUTP(MDP_BASE + 0x00098, 0x01);
+ mdp4_init_writeback_buf(mfd, MDP4_MIXER0);
+ pipe->blt_addr = 0;
} else {
pipe = mddi_pipe;
}
@@ -246,59 +247,82 @@
/* MDP cmd block disable */
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
-
}
-int mdp4_mddi_overlay_blt_offset(int *off)
-{
- if (mdp_hw_revision < MDP4_REVISION_V2_1) { /* need dmas dmap switch */
- if (mddi_pipe->blt_end ||
- (mdp4_overlay_mixer_play(mddi_pipe->mixer_num) == 0)) {
- *off = -1;
- return -EINVAL;
- }
- } else {
- /* no dmas dmap switch */
- if (mddi_pipe->blt_end) {
- *off = -1;
- return -EINVAL;
- }
- }
-
- if (mddi_pipe->blt_cnt & 0x01)
- *off = mddi_pipe->src_height * mddi_pipe->src_width * 3;
- else
- *off = 0;
-
- return 0;
-}
-
-void mdp4_mddi_overlay_blt(ulong addr)
+int mdp4_mddi_overlay_blt_start(struct msm_fb_data_type *mfd)
{
unsigned long flag;
- spin_lock_irqsave(&mdp_spin_lock, flag);
- if (addr) {
- mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
- mdp_intr_mask |= INTR_DMA_P_DONE;
- outp32(MDP_INTR_ENABLE, mdp_intr_mask);
- mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
- mddi_pipe->blt_cnt = 0;
- mddi_pipe->blt_end = 0;
- mddi_pipe->blt_addr = addr;
- } else {
- mddi_pipe->blt_end = 1; /* mark as end */
+ pr_debug("%s: blt_end=%d blt_addr=%x pid=%d\n",
+ __func__, mddi_pipe->blt_end, (int)mddi_pipe->blt_addr, current->pid);
+
+ mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0);
+
+ if (mfd->ov0_wb_buf->phys_addr == 0) {
+ pr_info("%s: no blt_base assigned\n", __func__);
+ return -EBUSY;
}
- spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+ if (mddi_pipe->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->blt_addr = mfd->ov0_wb_buf->phys_addr;
+ mdp4_stat.blt_mddi++;
+ spin_unlock_irqrestore(&mdp_spin_lock, flag);
+ return 0;
+}
+
+ return -EBUSY;
+}
+
+int mdp4_mddi_overlay_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->blt_addr);
+
+ if ((mddi_pipe->blt_end == 0) && mddi_pipe->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);
+}
+
+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);
+
}
void mdp4_blt_xy_update(struct mdp4_overlay_pipe *pipe)
{
- uint32 off, addr;
+ uint32 off, addr, addr2;
int bpp;
char *overlay_base;
-
if (pipe->blt_addr == 0)
return;
@@ -317,29 +341,62 @@
/* dmap */
MDP_OUTP(MDP_BASE + 0x90008, addr);
+ off = 0;
+ if (pipe->ov_cnt & 0x01)
+ off = pipe->src_height * pipe->src_width * bpp;
+ addr2 = pipe->blt_addr + off;
/* overlay 0 */
overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
- outpdw(overlay_base + 0x000c, addr);
- outpdw(overlay_base + 0x001c, addr);
+ outpdw(overlay_base + 0x000c, addr2);
+ outpdw(overlay_base + 0x001c, addr2);
}
/*
* mdp4_dmap_done_mddi: called from isr
*/
-void mdp4_dma_p_done_mddi(void)
+void mdp4_dma_p_done_mddi(struct mdp_dma_data *dma)
{
- if (mddi_pipe->blt_end) {
- mddi_pipe->blt_addr = 0;
- mdp_intr_mask &= ~INTR_DMA_P_DONE;
- outp32(MDP_INTR_ENABLE, mdp_intr_mask);
- mdp4_overlayproc_cfg(mddi_pipe);
- mdp4_overlay_dmap_xy(mddi_pipe);
+ int diff;
+
+ 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);
+
+ 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->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;
}
- /*
- * single buffer, no need to increase
- * mdd_pipe->dmap_cnt here
- */
+ spin_lock(&mdp_spin_lock);
+ dma->busy = FALSE;
+ 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);
+ /* kick off dmap */
+ outpdw(MDP_BASE + 0x000c, 0x0);
+ mdp4_stat.kickoff_dmap++;
+ mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE);
}
/*
@@ -347,37 +404,60 @@
*/
void mdp4_overlay0_done_mddi(struct mdp_dma_data *dma)
{
- mdp_disable_irq_nosync(MDP_OVERLAY0_TERM);
+ int diff;
+ if (mddi_pipe->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;
+ }
+
+ /* blt enabled */
+ if (mddi_pipe->blt_end == 0)
+ mddi_pipe->ov_cnt++;
+
+ pr_debug("%s: ov_cnt=%d dmap_cnt=%d\n",
+ __func__, mddi_pipe->ov_cnt, mddi_pipe->dmap_cnt);
+
+ 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);
+ }
+
+ 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);
- mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK,
- MDP_BLOCK_POWER_OFF, TRUE);
if (busy_wait_cnt)
busy_wait_cnt--;
- pr_debug("%s: ISR-done\n", __func__);
+ pr_debug("%s: kickoff dmap\n", __func__);
- if (mddi_pipe->blt_addr) {
- if (mddi_pipe->blt_cnt == 0) {
- mdp4_overlayproc_cfg(mddi_pipe);
- mdp4_overlay_dmap_xy(mddi_pipe);
- mddi_pipe->ov_cnt = 0;
- mddi_pipe->dmap_cnt = 0;
- /* BLT start from next frame */
- } else {
- mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_ON,
- FALSE);
- mdp4_blt_xy_update(mddi_pipe);
- outpdw(MDP_BASE + 0x000c, 0x0); /* start DMAP */
- }
- mddi_pipe->blt_cnt++;
- mddi_pipe->ov_cnt++;
- }
-
-
-
+ 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)
@@ -392,6 +472,9 @@
if (mddi_mfd && mddi_pipe) {
mdp4_mddi_dma_busy_wait(mddi_mfd);
mdp4_overlay_update_lcd(mddi_mfd);
+
+ if (mddi_pipe->blt_addr)
+ mdp4_mddi_blt_dmap_busy_wait(mddi_mfd);
mdp4_mddi_overlay_kickoff(mddi_mfd, mddi_pipe);
mddi_mfd->dma_update_flag = 1;
}
@@ -399,9 +482,27 @@
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
- * dsi link is a shared resource and it can only be used
+ * 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.
*/
@@ -432,7 +533,24 @@
void mdp4_mddi_kickoff_video(struct msm_fb_data_type *mfd,
struct mdp4_overlay_pipe *pipe)
{
- pr_debug("%s: pid=%d\n", __func__, current->pid);
+ /*
+ * 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.
+ */
+ if (mddi_pipe->blt_addr && mddi_pipe->blt_cnt == 0)
+ mdp4_overlay_update_lcd(mfd); /* first time */
+ else if (mddi_pipe->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->blt_addr, mddi_pipe->blt_cnt);
+
+ if (mddi_pipe->blt_addr)
+ mdp4_mddi_blt_dmap_busy_wait(mddi_mfd);
mdp4_mddi_overlay_kickoff(mfd, pipe);
}
@@ -447,32 +565,16 @@
void mdp4_mddi_overlay_kickoff(struct msm_fb_data_type *mfd,
struct mdp4_overlay_pipe *pipe)
{
+ unsigned long flag;
/* change mdp clk while mdp is idle` */
mdp4_set_perf_level();
- if (mdp_hw_revision == MDP4_REVISION_V2_1) {
- if (mdp4_overlay_status_read(MDP4_OVERLAY_TYPE_UNSET)) {
- 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);
- mdp4_overlay_status_write(MDP4_OVERLAY_TYPE_UNSET,
- false);
- }
- if (mdp4_overlay_status_read(MDP4_OVERLAY_TYPE_SET)) {
- uint32 data;
- data = inpdw(MDP_BASE + 0x0028);
- data &= ~0x0300; /* bit 8, 9, MASTER4 */
- MDP_OUTP(MDP_BASE + 0x00028, data);
- mdp4_overlay_status_write(MDP4_OVERLAY_TYPE_SET, false);
- }
- }
mdp_enable_irq(MDP_OVERLAY0_TERM);
+ spin_lock_irqsave(&mdp_spin_lock, flag);
mfd->dma->busy = TRUE;
+ if (mddi_pipe->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++;
@@ -554,7 +656,10 @@
mdp4_set_perf_level();
mdp_enable_irq(MDP_DMA_S_TERM);
- mfd->dma->busy = TRUE;
+
+ if (mddi_pipe->blt_addr == 0)
+ mfd->dma->busy = TRUE;
+
mfd->ibuf_flushed = TRUE;
/* start dma_s pipe */
mdp_pipe_kickoff(MDP_DMA_S_TERM, mfd);
@@ -582,6 +687,10 @@
if (mfd && mfd->panel_power_on) {
mdp4_mddi_dma_busy_wait(mfd);
+
+ if (mddi_pipe && mddi_pipe->blt_addr)
+ mdp4_mddi_blt_dmap_busy_wait(mfd);
+
mdp4_overlay_update_lcd(mfd);
if (mdp_hw_revision < MDP4_REVISION_V2_1) {
diff --git a/drivers/video/msm/mdp4_overlay_writeback.c b/drivers/video/msm/mdp4_overlay_writeback.c
index 342f565..f1a2ada 100644
--- a/drivers/video/msm/mdp4_overlay_writeback.c
+++ b/drivers/video/msm/mdp4_overlay_writeback.c
@@ -98,11 +98,14 @@
pipe = writeback_pipe;
}
ret = panel_next_on(pdev);
+
/* MDP_LAYERMIXER_WB_MUX_SEL to use mixer1 axi for mixer2 writeback */
- data = inpdw(MDP_BASE + 0x100F4);
- data &= ~0x02; /* clear the mixer1 mux bit */
- data |= 0x02;
+ if (hdmi_prim_display)
+ data = 0x01;
+ else
+ data = 0x02;
outpdw(MDP_BASE + 0x100F4, data);
+
MDP_OUTP(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x5004,
((0x0 & 0xFFF) << 16) | /* 12-bit B */
(0x0 & 0xFFF)); /* 12-bit G */
@@ -117,7 +120,6 @@
int mdp4_overlay_writeback_off(struct platform_device *pdev)
{
int ret;
- uint32 data;
struct msm_fb_data_type *mfd =
(struct msm_fb_data_type *)platform_get_drvdata(pdev);
if (mfd && writeback_pipe) {
@@ -129,11 +131,8 @@
}
ret = panel_next_off(pdev);
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
- /* MDP_LAYERMIXER_WB_MUX_SEL to restore
- * mixer1 axi for mixer1 writeback */
- data = inpdw(MDP_BASE + 0x100F4);
- data &= ~0x02; /* clear the mixer1 mux bit */
- outpdw(MDP_BASE + 0x100F4, data);
+ /* 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);
return ret;
}
@@ -175,14 +174,10 @@
pipe->dst_y = 0;
pipe->dst_x = 0;
- if (mfd->map_buffer) {
- pipe->srcp0_addr = (unsigned int)mfd->map_buffer->iova[0] + \
- buf_offset;
- pr_debug("start 0x%lx srcp0_addr 0x%x\n", mfd->
- map_buffer->iova[0], pipe->srcp0_addr);
- } else {
+ if (mfd->display_iova)
+ pipe->srcp0_addr = mfd->display_iova + buf_offset;
+ else
pipe->srcp0_addr = (uint32)(buf + buf_offset);
- }
mdp4_mixer_stage_up(pipe);
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
index b2657cf..82fbb65 100644
--- a/drivers/video/msm/mdp4_util.c
+++ b/drivers/video/msm/mdp4_util.c
@@ -522,7 +522,7 @@
}
#else
else { /* MDDI */
- mdp4_dma_p_done_mddi();
+ mdp4_dma_p_done_mddi(dma);
mdp_pipe_ctrl(MDP_DMA2_BLOCK,
MDP_BLOCK_POWER_OFF, TRUE);
complete(&dma->comp);
diff --git a/drivers/video/msm/mdp_dma.c b/drivers/video/msm/mdp_dma.c
index 32856ef..2ba2c85 100644
--- a/drivers/video/msm/mdp_dma.c
+++ b/drivers/video/msm/mdp_dma.c
@@ -529,8 +529,9 @@
down(&mfd->sem);
iBuf = &mfd->ibuf;
- if (mfd->map_buffer)
- iBuf->buf = (uint8 *)mfd->map_buffer->iova[0];
+
+ if (mfd->display_iova)
+ iBuf->buf = (uint8 *)mfd->display_iova;
else
iBuf->buf = (uint8 *) info->fix.smem_start;
diff --git a/drivers/video/msm/mdp_ppp_v20.c b/drivers/video/msm/mdp_ppp_v20.c
index d271b85..418528e 100644
--- a/drivers/video/msm/mdp_ppp_v20.c
+++ b/drivers/video/msm/mdp_ppp_v20.c
@@ -2403,7 +2403,10 @@
uint32 width,
uint32 height, int bpp, MDPIBUF *iBuf, int layer)
{
- *src0 += (x + y * width) * bpp;
+ if (iBuf->mdpImg.imgType == MDP_Y_CBCR_H2V2_ADRENO && layer == 0)
+ *src0 += (x + y * ALIGN(width, 32)) * bpp;
+ else
+ *src0 += (x + y * width) * bpp;
/* if it's dest/bg buffer, we need to adjust it for rotation */
if (layer != 0)
@@ -2414,9 +2417,14 @@
* MDP_Y_CBCR_H2V2/MDP_Y_CRCB_H2V2 cosite for now
* we need to shift x direction same as y dir for offsite
*/
- *src1 +=
- ((x / h_slice) * h_slice +
- ((y == 0) ? 0 : ((y + 1) / v_slice - 1) * width)) * bpp;
+ if (iBuf->mdpImg.imgType == MDP_Y_CBCR_H2V2_ADRENO
+ && layer == 0)
+ *src1 += ((x / h_slice) * h_slice + ((y == 0) ? 0 :
+ (((y + 1) / v_slice - 1) * (ALIGN(width/2, 32) * 2))))
+ * bpp;
+ else
+ *src1 += ((x / h_slice) * h_slice +
+ ((y == 0) ? 0 : ((y + 1) / v_slice - 1) * width)) * bpp;
/* if it's dest/bg buffer, we need to adjust it for rotation */
if (layer != 0)
diff --git a/drivers/video/msm/mipi_chimei_wuxga.c b/drivers/video/msm/mipi_chimei_wuxga.c
index 6ddf74d..e9e291e 100644
--- a/drivers/video/msm/mipi_chimei_wuxga.c
+++ b/drivers/video/msm/mipi_chimei_wuxga.c
@@ -143,6 +143,7 @@
pinfo->mipi.data_lane1 = true;
pinfo->mipi.data_lane2 = true;
pinfo->mipi.data_lane3 = true;
+ pinfo->mipi.esc_byte_ratio = 6;
pinfo->mipi.mode = DSI_VIDEO_MODE;
/*
diff --git a/drivers/video/msm/mipi_chimei_wxga_pt.c b/drivers/video/msm/mipi_chimei_wxga_pt.c
index 1ab50b7..88a5193 100644
--- a/drivers/video/msm/mipi_chimei_wxga_pt.c
+++ b/drivers/video/msm/mipi_chimei_wxga_pt.c
@@ -130,6 +130,7 @@
pinfo->mipi.tx_eot_append = true;
pinfo->mipi.t_clk_post = 34; /* Calculated */
pinfo->mipi.t_clk_pre = 64; /* Calculated */
+ pinfo->mipi.esc_byte_ratio = 4;
pinfo->mipi.dsi_phy_db = &dsi_video_mode_phy_db;
diff --git a/drivers/video/msm/mipi_dsi.c b/drivers/video/msm/mipi_dsi.c
index 7564016..1ba1444 100644
--- a/drivers/video/msm/mipi_dsi.c
+++ b/drivers/video/msm/mipi_dsi.c
@@ -36,6 +36,7 @@
#include "mdp4.h"
u32 dsi_irq;
+u32 esc_byte_ratio;
static boolean tlmm_settings = FALSE;
@@ -164,6 +165,7 @@
fbi = mfd->fbi;
var = &fbi->var;
pinfo = &mfd->panel_info;
+ esc_byte_ratio = pinfo->mipi.esc_byte_ratio;
if (mipi_dsi_pdata && mipi_dsi_pdata->dsi_power_save)
mipi_dsi_pdata->dsi_power_save(1);
diff --git a/drivers/video/msm/mipi_dsi.h b/drivers/video/msm/mipi_dsi.h
index ff2910f..2bc49c0 100644
--- a/drivers/video/msm/mipi_dsi.h
+++ b/drivers/video/msm/mipi_dsi.h
@@ -126,6 +126,7 @@
extern struct device dsi_dev;
extern int mipi_dsi_clk_on;
extern u32 dsi_irq;
+extern u32 esc_byte_ratio;
extern void __iomem *periph_base;
extern char *mmss_cc_base; /* mutimedia sub system clock control */
@@ -259,6 +260,8 @@
char *mipi_dsi_buf_reserve_hdr(struct dsi_buf *dp, int hlen);
char *mipi_dsi_buf_init(struct dsi_buf *dp);
void mipi_dsi_init(void);
+void mipi_dsi_lane_cfg(void);
+void mipi_dsi_bist_ctrl(void);
int mipi_dsi_buf_alloc(struct dsi_buf *, int size);
int mipi_dsi_cmd_dma_add(struct dsi_buf *dp, struct dsi_cmd_desc *cm);
int mipi_dsi_cmds_tx(struct msm_fb_data_type *mfd,
diff --git a/drivers/video/msm/mipi_novatek_cmd_qhd_pt.c b/drivers/video/msm/mipi_novatek_cmd_qhd_pt.c
index fbd2495..f139f4c 100644
--- a/drivers/video/msm/mipi_novatek_cmd_qhd_pt.c
+++ b/drivers/video/msm/mipi_novatek_cmd_qhd_pt.c
@@ -73,6 +73,7 @@
pinfo.mipi.dst_format = DSI_CMD_DST_FORMAT_RGB888;
pinfo.mipi.vc = 0;
pinfo.mipi.data_lane0 = TRUE;
+ pinfo.mipi.esc_byte_ratio = 4;
#if defined(NOVATEK_TWO_LANE)
pinfo.mipi.data_lane1 = TRUE;
#endif
diff --git a/drivers/video/msm/mipi_novatek_video_qhd_pt.c b/drivers/video/msm/mipi_novatek_video_qhd_pt.c
index 42ddfbe..7a9d556 100644
--- a/drivers/video/msm/mipi_novatek_video_qhd_pt.c
+++ b/drivers/video/msm/mipi_novatek_video_qhd_pt.c
@@ -73,6 +73,7 @@
pinfo.mipi.vc = 0;
pinfo.mipi.rgb_swap = DSI_RGB_SWAP_BGR;
pinfo.mipi.data_lane0 = TRUE;
+ pinfo.mipi.esc_byte_ratio = 4;
#if defined(NOVATEK_TWO_LANE)
pinfo.mipi.data_lane1 = TRUE;
#endif
diff --git a/drivers/video/msm/mipi_orise_video_720p_pt.c b/drivers/video/msm/mipi_orise_video_720p_pt.c
index 629ff10..da8b5e5 100644
--- a/drivers/video/msm/mipi_orise_video_720p_pt.c
+++ b/drivers/video/msm/mipi_orise_video_720p_pt.c
@@ -85,6 +85,7 @@
pinfo.mipi.frame_rate = 55;
pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db;
pinfo.mipi.tx_eot_append = TRUE;
+ pinfo.mipi.esc_byte_ratio = 4;
ret = mipi_orise_device_register(&pinfo, MIPI_DSI_PRIM,
MIPI_DSI_PANEL_720P_PT);
diff --git a/drivers/video/msm/mipi_toshiba_video_wsvga_pt.c b/drivers/video/msm/mipi_toshiba_video_wsvga_pt.c
index 48bdb1d..6edd776 100644
--- a/drivers/video/msm/mipi_toshiba_video_wsvga_pt.c
+++ b/drivers/video/msm/mipi_toshiba_video_wsvga_pt.c
@@ -20,7 +20,7 @@
static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = {
/* 600*1024, RGB888, 3 Lane 55 fps video mode */
/* regulator */
- {0x03, 0x0a, 0x04, 0x00, 0x20},
+ {0x09, 0x08, 0x05, 0x00, 0x20},
/* timing */
{0xab, 0x8a, 0x18, 0x00, 0x92, 0x97, 0x1b, 0x8c,
0x0c, 0x03, 0x04, 0xa0},
@@ -86,6 +86,7 @@
pinfo.mipi.data_lane2 = TRUE;
pinfo.mipi.t_clk_post = 0x20;
pinfo.mipi.t_clk_pre = 0x2d;
+ pinfo.mipi.esc_byte_ratio = 4;
pinfo.mipi.stream = 0; /* dma_p */
pinfo.mipi.mdp_trigger = 0;
pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
diff --git a/drivers/video/msm/mipi_toshiba_video_wuxga.c b/drivers/video/msm/mipi_toshiba_video_wuxga.c
index 297248f..8eddce4 100644
--- a/drivers/video/msm/mipi_toshiba_video_wuxga.c
+++ b/drivers/video/msm/mipi_toshiba_video_wuxga.c
@@ -85,6 +85,7 @@
pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
pinfo.mipi.frame_rate = 60;
pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db;
+ pinfo.mipi.esc_byte_ratio = 9;
ret = mipi_toshiba_device_register(&pinfo, MIPI_DSI_PRIM,
MIPI_DSI_PANEL_WUXGA);
diff --git a/drivers/video/msm/msm_dss_io_7x27a.c b/drivers/video/msm/msm_dss_io_7x27a.c
index f4f8375..17ee976 100644
--- a/drivers/video/msm/msm_dss_io_7x27a.c
+++ b/drivers/video/msm/msm_dss_io_7x27a.c
@@ -257,10 +257,10 @@
MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0001);/* start phy sw reset */
wmb();
- usleep(10);
+ usleep(1000);
MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0000);/* end phy w reset */
wmb();
- usleep(10);
+ usleep(1000);
MIPI_OUTP(MIPI_DSI_BASE + 0x2cc, 0x0003);/* regulator_ctrl_0 */
MIPI_OUTP(MIPI_DSI_BASE + 0x2d0, 0x0001);/* regulator_ctrl_1 */
MIPI_OUTP(MIPI_DSI_BASE + 0x2d4, 0x0001);/* regulator_ctrl_2 */
diff --git a/drivers/video/msm/msm_dss_io_8960.c b/drivers/video/msm/msm_dss_io_8960.c
index d1ddafb..70c8afc 100644
--- a/drivers/video/msm/msm_dss_io_8960.c
+++ b/drivers/video/msm/msm_dss_io_8960.c
@@ -231,6 +231,45 @@
__func__, (int) ahb, MIPI_INP_SECURE(ahb));
}
+void mipi_dsi_lane_cfg(void)
+{
+ int i, ln_offset;
+
+ ln_offset = 0x300;
+ for (i = 0; i < 4; i++) {
+ /* DSI1_DSIPHY_LN_CFG0 */
+ MIPI_OUTP(MIPI_DSI_BASE + ln_offset, 0x80);
+ /* DSI1_DSIPHY_LN_CFG1 */
+ MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x04, 0x45);
+ /* DSI1_DSIPHY_LN_CFG2 */
+ MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x08, 0x0);
+ /* DSI1_DSIPHY_LN_TEST_DATAPATH */
+ MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x0c, 0x0);
+ /* DSI1_DSIPHY_LN_TEST_STR0 */
+ MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x14, 0x1);
+ /* DSI1_DSIPHY_LN_TEST_STR1 */
+ MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x18, 0x66);
+ ln_offset += 0x40;
+ }
+
+ MIPI_OUTP(MIPI_DSI_BASE + 0x0400, 0x40); /* DSI1_DSIPHY_LNCK_CFG0 */
+ MIPI_OUTP(MIPI_DSI_BASE + 0x0404, 0x67); /* DSI1_DSIPHY_LNCK_CFG1 */
+ MIPI_OUTP(MIPI_DSI_BASE + 0x0408, 0x0); /* DSI1_DSIPHY_LNCK_CFG2 */
+ /* DSI1_DSIPHY_LNCK_TEST_DATAPATH */
+ MIPI_OUTP(MIPI_DSI_BASE + 0x040c, 0x0);
+ MIPI_OUTP(MIPI_DSI_BASE + 0x0414, 0x1); /* DSI1_DSIPHY_LNCK_TEST_STR0 */
+ /* DSI1_DSIPHY_LNCK_TEST_STR1 */
+ MIPI_OUTP(MIPI_DSI_BASE + 0x0418, 0x88);
+}
+
+void mipi_dsi_bist_ctrl(void)
+{
+ MIPI_OUTP(MIPI_DSI_BASE + 0x049c, 0x0f); /* DSI1_DSIPHY_BIST_CTRL4 */
+ MIPI_OUTP(MIPI_DSI_BASE + 0x0490, 0x03); /* DSI1_DSIPHY_BIST_CTRL1 */
+ MIPI_OUTP(MIPI_DSI_BASE + 0x048c, 0x03); /* DSI1_DSIPHY_BIST_CTRL0 */
+ MIPI_OUTP(MIPI_DSI_BASE + 0x049c, 0x0); /* DSI1_DSIPHY_BIST_CTRL4 */
+}
+
static void mipi_dsi_calibration(void)
{
int i = 0;
@@ -238,7 +277,7 @@
int cal_busy = MIPI_INP(MIPI_DSI_BASE + 0x550);
/* DSI1_DSIPHY_REGULATOR_CAL_PWR_CFG */
- MIPI_OUTP(MIPI_DSI_BASE + 0x0518, 0x01);
+ MIPI_OUTP(MIPI_DSI_BASE + 0x0518, 0x03);
/* DSI1_DSIPHY_CAL_SW_CFG2 */
MIPI_OUTP(MIPI_DSI_BASE + 0x0534, 0x0);
@@ -498,6 +537,8 @@
MIPI_OUTP(MIPI_DSI_BASE + 0x50c, 0x0000);/* regulator_ctrl_3 */
MIPI_OUTP(MIPI_DSI_BASE + 0x510, 0x0100);/* regulator_ctrl_4 */
+ MIPI_OUTP(MIPI_DSI_BASE + 0x4b0, 0x04);/* DSIPHY_LDO_CNTRL */
+
pd = (panel_info->mipi).dsi_phy_db;
off = 0x0480; /* strength 0 - 2 */
@@ -521,6 +562,8 @@
off += 4;
}
mipi_dsi_calibration();
+ mipi_dsi_lane_cfg(); /* lane cfgs */
+ mipi_dsi_bist_ctrl(); /* bist ctrl */
off = 0x0204; /* pll ctrl 1 - 19, skip 0 */
for (i = 1; i < 20; i++) {
@@ -618,8 +661,8 @@
if (clk_set_rate(dsi_byte_div_clk, 1) < 0) /* divided by 1 */
pr_err("%s: dsi_byte_div_clk - "
"clk_set_rate failed\n", __func__);
- if (clk_set_rate(dsi_esc_clk, 2) < 0) /* divided by 2 */
- pr_err("%s: dsi_esc_clk - "
+ if (clk_set_rate(dsi_esc_clk, esc_byte_ratio) < 0) /* divided by esc */
+ pr_err("%s: dsi_esc_clk - " /* clk ratio */
"clk_set_rate failed\n", __func__);
mipi_dsi_pclk_ctrl(&dsi_pclk, 1);
mipi_dsi_clk_ctrl(&dsicore_clk, 1);
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index d3126df..450fa85 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -29,6 +29,7 @@
#include <linux/dma-mapping.h>
#include <mach/board.h>
#include <linux/uaccess.h>
+#include <mach/iommu_domains.h>
#include <linux/workqueue.h>
#include <linux/string.h>
@@ -288,6 +289,9 @@
case MIPI_CMD_PANEL:
ret = snprintf(buf, PAGE_SIZE, "mipi dsi cmd panel\n");
break;
+ case WRITEBACK_PANEL:
+ ret = snprintf(buf, PAGE_SIZE, "writeback panel\n");
+ break;
default:
ret = snprintf(buf, PAGE_SIZE, "unknown panel\n");
break;
@@ -1033,9 +1037,6 @@
int *id;
int fbram_offset;
int remainder, remainder_mode2;
- static int subsys_id[2] = {MSM_SUBSYSTEM_DISPLAY,
- MSM_SUBSYSTEM_ROTATOR};
- unsigned int flags = MSM_SUBSYSTEM_MAP_IOVA;
/*
* fb info initialization
@@ -1317,15 +1318,22 @@
fbi->screen_base = fbram;
fbi->fix.smem_start = (unsigned long)fbram_phys;
- mfd->map_buffer = msm_subsystem_map_buffer(
- fbi->fix.smem_start, fbi->fix.smem_len,
- flags, subsys_id, 2);
- if (mfd->map_buffer) {
- pr_debug("%s(): buf 0x%lx, mfd->map_buffer->iova[0] 0x%lx\n"
- "mfd->map_buffer->iova[1] 0x%lx", __func__,
- fbi->fix.smem_start, mfd->map_buffer->iova[0],
- mfd->map_buffer->iova[1]);
- }
+ msm_iommu_map_contig_buffer(fbi->fix.smem_start,
+ DISPLAY_DOMAIN,
+ GEN_POOL,
+ fbi->fix.smem_len,
+ SZ_4K,
+ 1,
+ &(mfd->display_iova));
+
+ msm_iommu_map_contig_buffer(fbi->fix.smem_start,
+ ROTATOR_DOMAIN,
+ GEN_POOL,
+ fbi->fix.smem_len,
+ SZ_4K,
+ 1,
+ &(mfd->rotator_iova));
+
if (!bf_supported || mfd->index == 0)
memset(fbi->screen_base, 0x0, fix->smem_len);
@@ -3670,10 +3678,19 @@
}
mfd = (struct msm_fb_data_type *)info->par;
- if (mfd->map_buffer)
- *start = mfd->map_buffer->iova[subsys_id];
- else
- *start = info->fix.smem_start;
+
+ if (subsys_id == DISPLAY_SUBSYSTEM_ID) {
+ if (mfd->display_iova)
+ *start = mfd->display_iova;
+ else
+ *start = info->fix.smem_start;
+ } else {
+ if (mfd->rotator_iova)
+ *start = mfd->rotator_iova;
+ else
+ *start = info->fix.smem_start;
+ }
+
*len = info->fix.smem_len;
return 0;
diff --git a/drivers/video/msm/msm_fb.h b/drivers/video/msm/msm_fb.h
index 7b3bca0..8d45f07 100644
--- a/drivers/video/msm/msm_fb.h
+++ b/drivers/video/msm/msm_fb.h
@@ -23,7 +23,6 @@
#include "linux/proc_fs.h"
#include <mach/hardware.h>
-#include <mach/msm_subsystem_map.h>
#include <linux/io.h>
#include <mach/board.h>
@@ -179,7 +178,8 @@
struct list_head writeback_register_queue;
wait_queue_head_t wait_q;
struct ion_client *iclient;
- struct msm_mapped_buffer *map_buffer;
+ unsigned long display_iova;
+ unsigned long rotator_iova;
struct mdp_buf_type *ov0_wb_buf;
struct mdp_buf_type *ov1_wb_buf;
u32 ov_start;
diff --git a/drivers/video/msm/msm_fb_panel.h b/drivers/video/msm/msm_fb_panel.h
index 9d16b7b..8ccc21c 100644
--- a/drivers/video/msm/msm_fb_panel.h
+++ b/drivers/video/msm/msm_fb_panel.h
@@ -129,6 +129,8 @@
char mdp_trigger;
char dma_trigger;
uint32 dsi_pclk_rate;
+ /* byte to esc clk ratio */
+ uint32 esc_byte_ratio;
/* The packet-size should not bet changed */
char no_max_pkt_size;
/* Clock required during LP commands */
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_core.h b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_core.h
index 50c3696..8a33512 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_core.h
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_core.h
@@ -30,6 +30,9 @@
#define DDL_MAX_FRAME_WIDTH 1920
#define DDL_MAX_FRAME_HEIGHT 1088
+#define DDL_MAX_VC1_FRAME_WIDTH (DDL_MAX_FRAME_WIDTH)
+#define DDL_MAX_VC1_FRAME_HEIGHT (1280)
+
#define MAX_DPB_SIZE_L4PT0_MBS DDL_KILO_BYTE(32)
#define MAX_FRAME_SIZE_L4PT0_MBS DDL_KILO_BYTE(8)
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c
index b480b42..64d2976 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c
@@ -1064,20 +1064,10 @@
void ddl_set_vidc_timeout(struct ddl_client_context *ddl)
{
- unsigned long core_clk_rate;
u32 vidc_time_out = 0;
- if (ddl->codec_data.decoder.idr_only_decoding) {
+ if (ddl->codec_data.decoder.idr_only_decoding)
vidc_time_out = 2 * DDL_VIDC_1080P_200MHZ_TIMEOUT_VALUE;
- } else {
- res_trk_get_clk_rate(&core_clk_rate);
- if (core_clk_rate == DDL_VIDC_1080P_48MHZ)
- vidc_time_out = DDL_VIDC_1080P_48MHZ_TIMEOUT_VALUE;
- else if (core_clk_rate == DDL_VIDC_1080P_133MHZ)
- vidc_time_out = DDL_VIDC_1080P_133MHZ_TIMEOUT_VALUE;
- else
- vidc_time_out = DDL_VIDC_1080P_200MHZ_TIMEOUT_VALUE;
- }
- DDL_MSG_HIGH("%s Video core time out value = 0x%x"
+ DDL_MSG_HIGH("%s Video core time out value = 0x%x",
__func__, vidc_time_out);
vidc_sm_set_video_core_timeout_value(
&ddl->shared_mem[ddl->command_channel], vidc_time_out);
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_interrupt_handler.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_interrupt_handler.c
index e2c0a2a..6b8f5b2 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_interrupt_handler.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_interrupt_handler.c
@@ -312,6 +312,20 @@
return process_further;
}
break;
+ case VCD_CODEC_VC1:
+ case VCD_CODEC_VC1_RCV:
+ if ((seq_hdr_info.img_size_x >
+ DDL_MAX_VC1_FRAME_WIDTH) ||
+ (seq_hdr_info.img_size_y >
+ DDL_MAX_VC1_FRAME_HEIGHT)) {
+ DDL_MSG_ERROR("Unsupported VC1 clip: "
+ "Resolution X=%d and Y=%d",
+ seq_hdr_info.img_size_x,
+ seq_hdr_info.img_size_y);
+ ddl_client_fatal_cb(ddl);
+ return process_further;
+ }
+ break;
default:
break;
}
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_shared_mem.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_shared_mem.c
index 878db62..839a9c1 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_shared_mem.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_shared_mem.c
@@ -207,7 +207,7 @@
#define VIDC_SM_MPEG4_ASPECT_RATIO_INFO_SHFT 0x0
#define VIDC_SM_EXTENDED_PAR_ADDR 0x00cc
#define VIDC_SM_EXTENDED_PAR_WIDTH_BMSK 0xffff0000
-#define VIDC_SM_EXTENDED_PAR_WIDTH_SHFT 0xf
+#define VIDC_SM_EXTENDED_PAR_WIDTH_SHFT 16
#define VIDC_SM_EXTENDED_PAR_HEIGHT_BMSK 0x0000ffff
#define VIDC_SM_EXTENDED_PAR_HEIGHT_SHFT 0x0
diff --git a/fs/fat/misc.c b/fs/fat/misc.c
index 6d93360..a1d192d 100644
--- a/fs/fat/misc.c
+++ b/fs/fat/misc.c
@@ -56,7 +56,11 @@
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
- printk("%sFAT-fs (%s): %pV\n", level, sb->s_id, &vaf);
+ if (!strncmp(level, KERN_ERR, sizeof(KERN_ERR)))
+ printk_ratelimited("%sFAT-fs (%s): %pV\n", level,
+ sb->s_id, &vaf);
+ else
+ printk("%sFAT-fs (%s): %pV\n", level, sb->s_id, &vaf);
va_end(args);
}
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index 957c5b4..2e0e32e 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -56,6 +56,10 @@
#define CPUFREQ_POLICY_POWERSAVE (1)
#define CPUFREQ_POLICY_PERFORMANCE (2)
+/* Minimum frequency cutoff to notify the userspace about cpu utilization
+ * changes */
+#define MIN_CPU_UTIL_NOTIFY 40
+
/* Frequency values here are CPU kHz so that hardware which doesn't run
* with some frequencies can complain without having to guess what per
* cent / per mille means.
@@ -96,6 +100,7 @@
unsigned int max; /* in kHz */
unsigned int cur; /* in kHz, only needed if cpufreq
* governors are used */
+ unsigned int util; /* CPU utilization at max frequency */
unsigned int policy; /* see above */
struct cpufreq_governor *governor; /* see below */
@@ -255,7 +260,8 @@
void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state);
-
+void cpufreq_notify_utilization(struct cpufreq_policy *policy,
+ unsigned int load);
static inline void cpufreq_verify_within_limits(struct cpufreq_policy *policy, unsigned int min, unsigned int max)
{
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index c05134c..beb5470 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -176,6 +176,25 @@
#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,
+ 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;
+};
+
/*
* MMC device
*/
@@ -246,6 +265,8 @@
unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */
struct dentry *debugfs_root;
+
+ struct mmc_wr_pack_stats wr_pack_stats; /* packed commands stats*/
};
/*
@@ -448,4 +469,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
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 4416e95..ae06ccf 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -255,6 +255,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_BKOPS (1 << 14) /* BKOPS supported */
#define MMC_CAP2_INIT_BKOPS (1 << 15) /* Need to set BKOPS_EN */
diff --git a/include/linux/regulator/pm8xxx-regulator.h b/include/linux/regulator/pm8xxx-regulator.h
index ddf1901..6b42263 100644
--- a/include/linux/regulator/pm8xxx-regulator.h
+++ b/include/linux/regulator/pm8xxx-regulator.h
@@ -66,6 +66,9 @@
* @enable_time: time in us taken to enable a regulator to the maximum
* allowed voltage for the system. This is dependent upon
* the load and capacitance for a regulator on the board.
+ * @slew_rate: worst case rate of change of regulator output voltage
+ * in units of uV/us (V/s). This is dependent upon the
+ * load and capacitance for a regulator on the board.
* @ocp_enable: enable over current protection logic (available for
* LVS and MVS type switches)
* @ocp_enable_time: time in us to delay between enabling the switch and then
@@ -80,6 +83,7 @@
enum pm8xxx_vreg_pin_function pin_fn;
int system_uA;
int enable_time;
+ int slew_rate;
unsigned ocp_enable;
int ocp_enable_time;
};
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 5762463..a255388 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -343,6 +343,10 @@
* address is set
*/
int (*update_device)(struct usb_hcd *, struct usb_device *);
+
+ /* to log completion events*/
+ void (*log_urb_complete)(struct urb *urb, char * event,
+ unsigned extra);
};
extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index c68457e..d36e45f 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -334,6 +334,7 @@
unsigned long lpm_flags;
#define PHY_PWR_COLLAPSED BIT(0)
#define PHY_RETENTIONED BIT(1)
+#define XO_SHUTDOWN BIT(2)
int reset_counter;
unsigned long b_last_se0_sess;
unsigned long tmouts;
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index eda60c0..45855b1 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -386,6 +386,9 @@
#define V4L2_PIX_FMT_XVID v4l2_fourcc('X', 'V', 'I', 'D') /* Xvid */
#define V4L2_PIX_FMT_VC1_ANNEX_G v4l2_fourcc('V', 'C', '1', 'G') /* SMPTE 421M Annex G compliant stream */
#define V4L2_PIX_FMT_VC1_ANNEX_L v4l2_fourcc('V', 'C', '1', 'L') /* SMPTE 421M Annex L compliant stream */
+#define V4L2_PIX_FMT_DIVX_311 v4l2_fourcc('D', 'I', 'V', '3') /* DIVX311 */
+#define V4L2_PIX_FMT_DIVX v4l2_fourcc('D', 'I', 'V', 'X') /* DIVX */
+#define V4L2_PIX_FMT_VP8 v4l2_fourcc('V', 'P', '8', '0') /* ON2 VP8 stream */
/* Vendor-specific formats */
#define V4L2_PIX_FMT_CPIA1 v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */
diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h
index d4cf1d2..8222b67 100644
--- a/include/media/msm_camera.h
+++ b/include/media/msm_camera.h
@@ -448,6 +448,9 @@
#define CMD_AXI_CFG_SEC 0xF4
#define CMD_AXI_CFG_SEC_ALL_CHNLS 0xF8
+#define CMD_AXI_START 0xE1
+#define CMD_AXI_STOP 0xE2
+
/* vfe config command: config command(from config thread)*/
struct msm_vfe_cfg_cmd {
int cmd_type;
diff --git a/include/media/radio-iris.h b/include/media/radio-iris.h
index dfad18d..b5e8f2e 100644
--- a/include/media/radio-iris.h
+++ b/include/media/radio-iris.h
@@ -516,6 +516,7 @@
FM_RECV,
FM_TRANS,
FM_RESET,
+ FM_CALIB
};
enum v4l2_cid_private_iris_t {
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
index 134d044..96795a3 100644
--- a/include/sound/apr_audio.h
+++ b/include/sound/apr_audio.h
@@ -611,6 +611,19 @@
#define VPM_TX_DM_FLUENCE_COPP_TOPOLOGY 0x00010F72
#define VPM_TX_QMIC_FLUENCE_COPP_TOPOLOGY 0x00010F75
+/* SRS TRUMEDIA GUIDS */
+/* topology */
+#define SRS_TRUMEDIA_TOPOLOGY_ID 0x00010D90
+/* module */
+#define SRS_TRUMEDIA_MODULE_ID 0x10005010
+/* parameters */
+#define SRS_TRUMEDIA_PARAMS 0x10005011
+#define SRS_TRUMEDIA_PARAMS_WOWHD 0x10005012
+#define SRS_TRUMEDIA_PARAMS_CSHP 0x10005013
+#define SRS_TRUMEDIA_PARAMS_HPF 0x10005014
+#define SRS_TRUMEDIA_PARAMS_PEQ 0x10005015
+#define SRS_TRUMEDIA_PARAMS_HL 0x10005016
+
#define ASM_MAX_EQ_BANDS 12
struct asm_eq_band {
@@ -1427,4 +1440,96 @@
#define ADSP_ENOTIMPL 0x00000011 /* Operation is not implemented. */
#define ADSP_ENEEDMORE 0x00000012 /* Operation needs more data or resources*/
+/* SRS TRUMEDIA start */
+#define SRS_ID_GLOBAL 0x00000001
+#define SRS_ID_WOWHD 0x00000002
+#define SRS_ID_CSHP 0x00000003
+#define SRS_ID_HPF 0x00000004
+#define SRS_ID_PEQ 0x00000005
+#define SRS_ID_HL 0x00000006
+
+#define SRS_CMD_UPLOAD 0x7FFF0000
+#define SRS_PARAM_INDEX_MASK 0x80000000
+#define SRS_PARAM_OFFSET_MASK 0x3FFF0000
+#define SRS_PARAM_VALUE_MASK 0x0000FFFF
+
+struct srs_trumedia_params_GLOBAL {
+ uint8_t v1;
+ uint8_t v2;
+ uint8_t v3;
+ uint8_t v4;
+ uint8_t v5;
+ uint8_t v6;
+ uint8_t v7;
+ uint8_t v8;
+} __packed;
+
+struct srs_trumedia_params_WOWHD {
+ uint32_t v1;
+ uint16_t v2;
+ uint16_t v3;
+ uint16_t v4;
+ uint16_t v5;
+ uint16_t v6;
+ uint16_t v7;
+ uint16_t v8;
+ uint16_t v____A1;
+ uint32_t v9;
+ uint16_t v10;
+ uint16_t v11;
+ uint32_t v12[16];
+} __packed;
+
+struct srs_trumedia_params_CSHP {
+ uint32_t v1;
+ uint16_t v2;
+ uint16_t v3;
+ uint16_t v4;
+ uint16_t v5;
+ uint16_t v6;
+ uint16_t v____A1;
+ uint32_t v7;
+ uint16_t v8;
+ uint16_t v9;
+ uint32_t v10[16];
+} __packed;
+
+struct srs_trumedia_params_HPF {
+ uint32_t v1;
+ uint32_t v2[26];
+} __packed;
+
+struct srs_trumedia_params_PEQ {
+ uint32_t v1;
+ uint16_t v2;
+ uint16_t v3;
+ uint16_t v4;
+ uint16_t v____A1;
+ uint32_t v5[26];
+ uint32_t v6[26];
+} __packed;
+
+struct srs_trumedia_params_HL {
+ uint16_t v1;
+ uint16_t v2;
+ uint16_t v3;
+ uint16_t v____A1;
+ int32_t v4;
+ uint32_t v5;
+ uint16_t v6;
+ uint16_t v____A2;
+ uint32_t v7;
+} __packed;
+
+struct srs_trumedia_params {
+ struct srs_trumedia_params_GLOBAL global;
+ struct srs_trumedia_params_WOWHD wowhd;
+ struct srs_trumedia_params_CSHP cshp;
+ struct srs_trumedia_params_HPF hpf;
+ struct srs_trumedia_params_PEQ peq;
+ struct srs_trumedia_params_HL hl;
+} __packed;
+int srs_trumedia_open(int port_id, int srs_tech_id, void *srs_params);
+/* SRS TruMedia end */
+
#endif /*_APR_AUDIO_H_*/
diff --git a/include/sound/msm-dai-q6-v2.h b/include/sound/msm-dai-q6-v2.h
new file mode 100644
index 0000000..3d5ffdd
--- /dev/null
+++ b/include/sound/msm-dai-q6-v2.h
@@ -0,0 +1,42 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_DAI_Q6_PDATA_H__
+
+#define __MSM_DAI_Q6_PDATA_H__
+
+#define MSM_MI2S_SD0 (1 << 0)
+#define MSM_MI2S_SD1 (1 << 1)
+#define MSM_MI2S_SD2 (1 << 2)
+#define MSM_MI2S_SD3 (1 << 3)
+#define MSM_MI2S_CAP_RX 0
+#define MSM_MI2S_CAP_TX 1
+
+struct msm_dai_auxpcm_pdata {
+ const char *clk;
+ u16 mode;
+ u16 sync;
+ u16 frame;
+ u16 quant;
+ /* modify slot to arr[4] to specify
+ * the slot number for each channel
+ * in multichannel scenario */
+ u16 slot;
+ u16 data;
+ int pcm_clk_rate;
+};
+
+struct msm_i2s_data {
+ u32 capability; /* RX or TX */
+ u16 sd_lines;
+};
+#endif
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 9b279cc..c634367 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -399,8 +399,6 @@
#endif
static struct attribute *g[] = {
- &touch_event_attr.attr,
- &touch_event_timer_attr.attr,
&state_attr.attr,
#ifdef CONFIG_PM_TRACE
&pm_trace_attr.attr,
@@ -409,6 +407,8 @@
#ifdef CONFIG_PM_SLEEP
&pm_async_attr.attr,
&wakeup_count_attr.attr,
+ &touch_event_attr.attr,
+ &touch_event_timer_attr.attr,
#ifdef CONFIG_PM_DEBUG
&pm_test_attr.attr,
#endif
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 9e545f3..2b14423 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -1813,6 +1813,15 @@
struct hci_cp_auth_requested cp;
hci_remove_link_key(hdev, &conn->dst);
cp.handle = cpu_to_le16(conn->handle);
+ /*Initiates dedicated bonding as pin or key is missing
+ on remote device*/
+ /*In case if remote device is ssp supported,
+ reduce the security level to MEDIUM if it is HIGH*/
+ if (conn->ssp_mode && conn->auth_initiator &&
+ conn->io_capability != 0x03) {
+ conn->pending_sec_level = BT_SECURITY_HIGH;
+ conn->auth_type = HCI_AT_DEDICATED_BONDING_MITM;
+ }
hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED,
sizeof(cp), &cp);
hci_dev_unlock(hdev);
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 80f4bd6..86ae763 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -308,7 +308,7 @@
cmd = list_entry(p, struct pending_cmd, list);
- if (cmd->opcode != opcode)
+ if (opcode > 0 && cmd->opcode != opcode)
continue;
if (index >= 0 && cmd->index != index)
@@ -2475,6 +2475,14 @@
return err;
}
+static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
+{
+ u8 *status = data;
+
+ cmd_status(cmd->sk, cmd->index, cmd->opcode, *status);
+ mgmt_pending_remove(cmd);
+}
+
int mgmt_index_added(u16 index)
{
BT_DBG("%d", index);
@@ -2483,7 +2491,12 @@
int mgmt_index_removed(u16 index)
{
+ u8 status = ENODEV;
+
BT_DBG("%d", index);
+
+ mgmt_pending_foreach(0, index, cmd_status_rsp, &status);
+
return mgmt_event(MGMT_EV_INDEX_REMOVED, index, NULL, 0, NULL);
}
@@ -2522,6 +2535,11 @@
mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, mode_rsp, &match);
+ if (!powered) {
+ u8 status = ENETDOWN;
+ mgmt_pending_foreach(0, index, cmd_status_rsp, &status);
+ }
+
ev.val = powered;
ret = mgmt_event(MGMT_EV_POWERED, index, &ev, sizeof(ev), match.sk);
diff --git a/scripts/gcc-wrapper.py b/scripts/gcc-wrapper.py
index 1497452..16c623c 100755
--- a/scripts/gcc-wrapper.py
+++ b/scripts/gcc-wrapper.py
@@ -44,8 +44,8 @@
"alignment.c:720",
"async.c:122",
"async.c:270",
- "block.c:847",
- "block.c:848",
+ "block.c:885",
+ "block.c:886",
"dir.c:43",
"dm.c:1053",
"dm.c:1080",
diff --git a/sound/soc/codecs/wcd9304.c b/sound/soc/codecs/wcd9304.c
index c55eac0..54f527c 100644
--- a/sound/soc/codecs/wcd9304.c
+++ b/sound/soc/codecs/wcd9304.c
@@ -257,6 +257,23 @@
struct sitar_priv *debug_sitar_priv;
#endif
+static int sitar_get_anc_slot(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+ ucontrol->value.integer.value[0] = sitar->anc_slot;
+ return 0;
+}
+
+static int sitar_put_anc_slot(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+ sitar->anc_slot = ucontrol->value.integer.value[0];
+ return 0;
+}
static int sitar_pa_gain_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -536,6 +553,9 @@
SOC_SINGLE("MICBIAS1 CAPLESS Switch", SITAR_A_MICB_1_CTL, 4, 1, 1),
SOC_SINGLE("MICBIAS2 CAPLESS Switch", SITAR_A_MICB_2_CTL, 4, 1, 1),
+ SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 0, 100, sitar_get_anc_slot,
+ sitar_put_anc_slot),
+
SOC_ENUM("TX1 HPF cut off", cf_dec1_enum),
SOC_SINGLE("TX1 HPF Switch", SITAR_A_CDC_TX1_MUX_CTL, 3, 1, 0),
@@ -629,24 +649,32 @@
};
static const char *sb_tx5_mux_text[] = {
- "ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7",
- "DEC5"
+ "ZERO", "RMIX1", "RMIX2", "RMIX3", "DEC1", "DEC2", "DEC3", "DEC4"
};
static const char *dec1_mux_text[] = {
- "ZERO", "DMIC1", "ADC1", "ADC2", "ADC3", "MBADC", "DMIC4", "ANCFB1",
+ "ZERO", "DMIC1", "ADC1", "ADC2", "ADC3", "MBADC", "DMIC4", "ANC1_FB",
};
static const char *dec2_mux_text[] = {
- "ZERO", "DMIC2", "ADC1", "ADC2", "ADC3", "MBADC", "DMIC3", "ANCFB2",
+ "ZERO", "DMIC2", "ADC1", "ADC2", "ADC3", "MBADC", "DMIC3", "ANC2_FB",
};
static const char *dec3_mux_text[] = {
- "ZERO", "DMIC2", "DMIC3", "DMIC4", "ADC1", "ADC2", "ADC3", "MBADC",
+ "ZERO", "DMIC3", "ADC1", "ADC2", "ADC3", "MBADC", "DMIC2", "DMIC4"
};
static const char *dec4_mux_text[] = {
- "ZERO", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "ADC1", "ADC2", "ADC3",
+ "ZERO", "DMIC4", "ADC1", "ADC2", "ADC3", "DMIC3", "DMIC2", "DMIC1"
+};
+
+static const char const *anc_mux_text[] = {
+ "ZERO", "ADC1", "ADC2", "ADC3", "RSVD1", "RSVD2", "RSVD3",
+ "MBADC", "RSVD4", "DMIC1", "DMIC2", "DMIC3", "DMIC4"
+};
+
+static const char const *anc1_fb_mux_text[] = {
+ "ZERO", "EAR_HPH_L", "EAR_LINE_1",
};
static const char *iir1_inp1_text[] = {
@@ -711,6 +739,15 @@
static const struct soc_enum dec4_mux_enum =
SOC_ENUM_SINGLE(SITAR_A_CDC_CONN_TX_B2_CTL, 3, 8, dec4_mux_text);
+static const struct soc_enum anc1_mux_enum =
+ SOC_ENUM_SINGLE(SITAR_A_CDC_CONN_ANC_B1_CTL, 0, 13, anc_mux_text);
+
+static const struct soc_enum anc2_mux_enum =
+ SOC_ENUM_SINGLE(SITAR_A_CDC_CONN_ANC_B1_CTL, 4, 13, anc_mux_text);
+
+static const struct soc_enum anc1_fb_mux_enum =
+ SOC_ENUM_SINGLE(SITAR_A_CDC_CONN_ANC_B2_CTL, 0, 3, anc1_fb_mux_text);
+
static const struct soc_enum iir1_inp1_mux_enum =
SOC_ENUM_SINGLE(SITAR_A_CDC_CONN_EQ1_B1_CTL, 0, 16, iir1_inp1_text);
@@ -774,6 +811,15 @@
static const struct snd_kcontrol_new iir1_inp1_mux =
SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum);
+static const struct snd_kcontrol_new anc1_mux =
+ SOC_DAPM_ENUM("ANC1 MUX Mux", anc1_mux_enum);
+
+static const struct snd_kcontrol_new anc2_mux =
+ SOC_DAPM_ENUM("ANC2 MUX Mux", anc2_mux_enum);
+
+static const struct snd_kcontrol_new anc1_fb_mux =
+ SOC_DAPM_ENUM("ANC1 FB MUX Mux", anc1_fb_mux_enum);
+
static const struct snd_kcontrol_new dac1_switch[] = {
SOC_DAPM_SINGLE("Switch", SITAR_A_RX_EAR_EN, 5, 1, 0),
};
@@ -804,27 +850,39 @@
{
struct snd_soc_codec *codec = w->codec;
u16 adc_reg;
+ u8 init_bit_shift;
pr_debug("%s %d\n", __func__, event);
if (w->reg == SITAR_A_TX_1_2_EN)
adc_reg = SITAR_A_TX_1_2_TEST_CTL;
+ else if (w->reg == SITAR_A_TX_3_EN)
+ adc_reg = SITAR_A_TX_3_TEST_CTL;
else {
pr_err("%s: Error, invalid adc register\n", __func__);
return -EINVAL;
}
+ if (w->shift == 3)
+ init_bit_shift = 6;
+ else if (w->shift == 7)
+ init_bit_shift = 7;
+ else {
+ pr_err("%s: Error, invalid init bit postion adc register\n",
+ __func__);
+ return -EINVAL;
+ }
+
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
sitar_codec_enable_adc_block(codec, 1);
+ snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift,
+ 1 << init_bit_shift);
break;
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, adc_reg, 1 << w->shift,
- 1 << w->shift);
- usleep_range(1000, 1000);
- snd_soc_update_bits(codec, adc_reg, 1 << w->shift, 0x00);
- usleep_range(1000, 1000);
- snd_soc_update_bits(codec, adc_reg, 0x08, 0x08);
+
+ snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift, 0x00);
+
break;
case SND_SOC_DAPM_POST_PMD:
sitar_codec_enable_adc_block(codec, 0);
@@ -898,7 +956,7 @@
unsigned int dmic;
int ret;
- ret = kstrtouint(strpbrk(w->name, "12"), 10, &dmic);
+ ret = kstrtouint(strpbrk(w->name, "1234"), 10, &dmic);
if (ret < 0) {
pr_err("%s: Invalid DMIC line on the codec\n", __func__);
return -EINVAL;
@@ -950,6 +1008,115 @@
return 0;
}
+static int sitar_codec_enable_anc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ const char *filename;
+ const struct firmware *fw;
+ int i;
+ int ret;
+ int num_anc_slots;
+ struct anc_header *anc_head;
+ struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+ u32 anc_writes_size = 0;
+ int anc_size_remaining;
+ u32 *anc_ptr;
+ u16 reg;
+ u8 mask, val, old_val;
+
+ pr_debug("%s %d\n", __func__, event);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+
+ /* Use the same firmware file as that of WCD9310,
+ * since the register sequences are same for
+ * WCD9310 and WCD9304
+ */
+ filename = "wcd9310/wcd9310_anc.bin";
+
+ ret = request_firmware(&fw, filename, codec->dev);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to acquire ANC data: %d\n",
+ ret);
+ return -ENODEV;
+ }
+
+ if (fw->size < sizeof(struct anc_header)) {
+ dev_err(codec->dev, "Not enough data\n");
+ release_firmware(fw);
+ return -ENOMEM;
+ }
+
+ /* First number is the number of register writes */
+ anc_head = (struct anc_header *)(fw->data);
+ anc_ptr = (u32 *)((u32)fw->data + sizeof(struct anc_header));
+ anc_size_remaining = fw->size - sizeof(struct anc_header);
+ num_anc_slots = anc_head->num_anc_slots;
+
+ if (sitar->anc_slot >= num_anc_slots) {
+ dev_err(codec->dev, "Invalid ANC slot selected\n");
+ release_firmware(fw);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num_anc_slots; i++) {
+
+ if (anc_size_remaining < SITAR_PACKED_REG_SIZE) {
+ dev_err(codec->dev, "Invalid register format\n");
+ release_firmware(fw);
+ return -EINVAL;
+ }
+ anc_writes_size = (u32)(*anc_ptr);
+ anc_size_remaining -= sizeof(u32);
+ anc_ptr += 1;
+
+ if (anc_writes_size * SITAR_PACKED_REG_SIZE
+ > anc_size_remaining) {
+ dev_err(codec->dev, "Invalid register format\n");
+ release_firmware(fw);
+ return -ENOMEM;
+ }
+
+ if (sitar->anc_slot == i)
+ break;
+
+ anc_size_remaining -= (anc_writes_size *
+ SITAR_PACKED_REG_SIZE);
+ anc_ptr += anc_writes_size;
+ }
+ if (i == num_anc_slots) {
+ dev_err(codec->dev, "Selected ANC slot not present\n");
+ release_firmware(fw);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < anc_writes_size; i++) {
+ SITAR_CODEC_UNPACK_ENTRY(anc_ptr[i], reg,
+ mask, val);
+ old_val = snd_soc_read(codec, reg);
+ snd_soc_write(codec, reg, (old_val & ~mask) |
+ (val & mask));
+ }
+
+ release_firmware(fw);
+
+ /* For Sitar, it is required to enable both Feed-forward
+ * and Feed back clocks to enable ANC
+ */
+ snd_soc_write(codec, SITAR_A_CDC_CLK_ANC_CLK_EN_CTL, 0x0F);
+
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_write(codec, SITAR_A_CDC_CLK_ANC_RESET_CTL, 0xFF);
+ snd_soc_write(codec, SITAR_A_CDC_CLK_ANC_CLK_EN_CTL, 0x00);
+ break;
+ }
+ return 0;
+}
+
+
static void sitar_codec_start_hs_polling(struct snd_soc_codec *codec)
{
struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
@@ -1250,7 +1417,7 @@
break;
case SND_SOC_DAPM_POST_PMU:
if (sitar->mbhc_polling_active &&
- sitar->micbias == micb_line) {
+ sitar->mbhc_cfg.micbias == micb_line) {
SITAR_ACQUIRE_LOCK(sitar->codec_resource_lock);
sitar_codec_pause_hs_polling(codec);
sitar_codec_start_hs_polling(codec);
@@ -1709,10 +1876,20 @@
SND_SOC_DAPM_MUX_E("DEC4 MUX", SITAR_A_CDC_CLK_TX_CLK_EN_B1_CTL, 3, 0,
&dec4_mux, sitar_codec_enable_dec, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MUX("ANC1 MUX", SND_SOC_NOPM, 0, 0, &anc1_mux),
+ SND_SOC_DAPM_MUX("ANC2 MUX", SND_SOC_NOPM, 0, 0, &anc2_mux),
+
+ SND_SOC_DAPM_MIXER_E("ANC", SND_SOC_NOPM, 0, 0, NULL, 0,
+ sitar_codec_enable_anc, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("ANC1 FB MUX", SND_SOC_NOPM, 0, 0, &anc1_fb_mux),
+
SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, 0, 0, &sb_tx1_mux),
SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, 0, 0, &sb_tx2_mux),
SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, 0, 0, &sb_tx3_mux),
SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, 0, 0, &sb_tx4_mux),
+ SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, 0, 0, &sb_tx5_mux),
SND_SOC_DAPM_AIF_OUT_E("SLIM TX1", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
0, sitar_codec_enable_slimtx,
@@ -1729,6 +1906,10 @@
0, sitar_codec_enable_slimtx,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT_E("SLIM TX5", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
+ 0, sitar_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
/* Digital Mic Inputs */
SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
sitar_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
@@ -1770,6 +1951,10 @@
{"LINEOUT1 PA", NULL, "LINEOUT1 DAC"},
{"LINEOUT1 DAC", NULL, "DAC2 MUX"},
+ {"ANC1 FB MUX", "EAR_HPH_L", "RX2 MIX1"},
+ {"ANC1 FB MUX", "EAR_LINE_1", "RX3 MIX1"},
+ {"ANC", NULL, "ANC1 FB MUX"},
+
/* Headset (RX MIX1 and RX MIX2) */
{"HEADPHONE", NULL, "HPHL"},
@@ -1801,6 +1986,21 @@
{"RX3 MIX1", NULL, "RX3 MIX1 INP1"},
{"RX3 MIX1", NULL, "RX3 MIX1 INP2"},
+ /* ANC */
+ {"ANC", NULL, "ANC1 MUX"},
+ {"ANC", NULL, "ANC2 MUX"},
+ {"ANC1 MUX", "ADC1", "ADC1"},
+ {"ANC1 MUX", "ADC2", "ADC2"},
+ {"ANC1 MUX", "ADC3", "ADC3"},
+ {"ANC2 MUX", "ADC1", "ADC1"},
+ {"ANC2 MUX", "ADC2", "ADC2"},
+ {"ANC2 MUX", "ADC3", "ADC3"},
+
+ {"ANC", NULL, "CDC_CONN"},
+
+ {"RX2 MIX1", NULL, "ANC"},
+ {"RX3 MIX1", NULL, "ANC"},
+
/* SLIMBUS Connections */
/* Slimbus port 5 is non functional in Sitar 1.0 */
@@ -1841,11 +2041,16 @@
{"SLIM TX2", NULL, "SLIM TX2 MUX"},
{"SLIM TX3", NULL, "SLIM TX3 MUX"},
{"SLIM TX4", NULL, "SLIM TX4 MUX"},
+ {"SLIM TX5", NULL, "SLIM TX5 MUX"},
{"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX2 MUX", "DEC2", "DEC2 MUX"},
{"SLIM TX3 MUX", "DEC3", "DEC3 MUX"},
{"SLIM TX4 MUX", "DEC4", "DEC4 MUX"},
+ {"SLIM TX5 MUX", "DEC1", "DEC1 MUX"},
+ {"SLIM TX5 MUX", "DEC2", "DEC2 MUX"},
+ {"SLIM TX5 MUX", "DEC3", "DEC3 MUX"},
+ {"SLIM TX5 MUX", "DEC4", "DEC4 MUX"},
/* Decimator Inputs */
{"DEC1 MUX", "DMIC1", "DMIC1"},
@@ -1865,7 +2070,7 @@
{"DEC3 MUX", "ADC2", "ADC2"},
{"DEC3 MUX", "ADC3", "ADC3"},
{"DEC3 MUX", "DMIC2", "DMIC2"},
- {"DEC3 MUX", "DMIC3", "DMIC4"},
+ {"DEC3 MUX", "DMIC4", "DMIC4"},
{"DEC3 MUX", NULL, "CDC_CONN"},
{"DEC4 MUX", "DMIC4", "DMIC4"},
{"DEC4 MUX", "ADC1", "ADC1"},
@@ -1992,6 +2197,7 @@
(choice == SITAR_BANDGAP_AUDIO_MODE)) {
sitar_codec_enable_audio_mode_bandgap(codec);
} else if (choice == SITAR_BANDGAP_MBHC_MODE) {
+ snd_soc_update_bits(codec, SITAR_A_BIAS_CURR_CTL_2, 0x0C, 0x08);
snd_soc_update_bits(codec, SITAR_A_BIAS_CENTRAL_BG_CTL, 0x2,
0x2);
snd_soc_update_bits(codec, SITAR_A_BIAS_CENTRAL_BG_CTL, 0x80,
@@ -2329,10 +2535,10 @@
}
} else if (dai->id == AIF1_CAP) {
*tx_num = sitar_dai[dai->id - 1].capture.channels_max;
- while (cnt < *tx_num) {
- tx_slot[cnt] = tx_ch[cnt];
- cnt++;
- }
+ tx_slot[0] = tx_ch[cnt];
+ tx_slot[1] = tx_ch[4 + cnt];
+ tx_slot[2] = tx_ch[2 + cnt];
+ tx_slot[3] = tx_ch[3 + cnt];
}
return 0;
}
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 443114c..88bdcad 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -68,8 +68,9 @@
#define AIF2_PB 3
#define AIF2_CAP 4
#define AIF3_CAP 5
+#define AIF3_PB 6
-#define NUM_CODEC_DAIS 5
+#define NUM_CODEC_DAIS 6
#define TABLA_COMP_DIGITAL_GAIN_OFFSET 3
struct tabla_codec_dai_data {
@@ -244,6 +245,10 @@
u32 cfilt2_cnt;
u32 cfilt3_cnt;
u32 rx_bias_count;
+ s32 dmic_1_2_clk_cnt;
+ s32 dmic_3_4_clk_cnt;
+ s32 dmic_5_6_clk_cnt;
+
enum tabla_bandgap_type bandgap_type;
bool mclk_enabled;
bool clock_active;
@@ -1179,6 +1184,9 @@
static const struct soc_enum rx_mix1_inp2_chain_enum =
SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_RX1_B1_CTL, 4, 12, rx_mix1_text);
+static const struct soc_enum rx_mix1_inp3_chain_enum =
+ SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_RX1_B2_CTL, 0, 12, rx_mix1_text);
+
static const struct soc_enum rx2_mix1_inp1_chain_enum =
SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_RX2_B1_CTL, 0, 12, rx_mix1_text);
@@ -1321,6 +1329,9 @@
static const struct snd_kcontrol_new rx_mix1_inp2_mux =
SOC_DAPM_ENUM("RX1 MIX1 INP2 Mux", rx_mix1_inp2_chain_enum);
+static const struct snd_kcontrol_new rx_mix1_inp3_mux =
+ SOC_DAPM_ENUM("RX1 MIX1 INP3 Mux", rx_mix1_inp3_chain_enum);
+
static const struct snd_kcontrol_new rx2_mix1_inp1_mux =
SOC_DAPM_ENUM("RX2 MIX1 INP1 Mux", rx2_mix1_inp1_chain_enum);
@@ -2027,8 +2038,9 @@
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
- u16 tx_dmic_ctl_reg;
- u8 dmic_clk_sel, dmic_clk_en;
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ u8 dmic_clk_en;
+ s32 *dmic_clk_cnt;
unsigned int dmic;
int ret;
@@ -2041,20 +2053,31 @@
switch (dmic) {
case 1:
case 2:
- dmic_clk_sel = 0x02;
dmic_clk_en = 0x01;
+ dmic_clk_cnt = &(tabla->dmic_1_2_clk_cnt);
+
+ pr_debug("%s() event %d DMIC%d dmic_1_2_clk_cnt %d\n",
+ __func__, event, dmic, *dmic_clk_cnt);
+
break;
case 3:
case 4:
- dmic_clk_sel = 0x08;
dmic_clk_en = 0x04;
+ dmic_clk_cnt = &(tabla->dmic_3_4_clk_cnt);
+
+ pr_debug("%s() event %d DMIC%d dmic_3_4_clk_cnt %d\n",
+ __func__, event, dmic, *dmic_clk_cnt);
break;
case 5:
case 6:
- dmic_clk_sel = 0x20;
dmic_clk_en = 0x10;
+ dmic_clk_cnt = &(tabla->dmic_5_6_clk_cnt);
+
+ pr_debug("%s() event %d DMIC%d dmic_5_6_clk_cnt %d\n",
+ __func__, event, dmic, *dmic_clk_cnt);
+
break;
default:
@@ -2062,24 +2085,21 @@
return -EINVAL;
}
- tx_dmic_ctl_reg = TABLA_A_CDC_TX1_DMIC_CTL + 8 * (dmic - 1);
-
- pr_debug("%s %d\n", __func__, event);
-
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, TABLA_A_CDC_CLK_DMIC_CTL,
- dmic_clk_sel, dmic_clk_sel);
+ (*dmic_clk_cnt)++;
+ if (*dmic_clk_cnt == 1)
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_DMIC_CTL,
+ dmic_clk_en, dmic_clk_en);
- snd_soc_update_bits(codec, tx_dmic_ctl_reg, 0x1, 0x1);
-
- snd_soc_update_bits(codec, TABLA_A_CDC_CLK_DMIC_CTL,
- dmic_clk_en, dmic_clk_en);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, TABLA_A_CDC_CLK_DMIC_CTL,
- dmic_clk_en, 0);
+
+ (*dmic_clk_cnt)--;
+ if (*dmic_clk_cnt == 0)
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_DMIC_CTL,
+ dmic_clk_en, 0);
break;
}
return 0;
@@ -3211,6 +3231,7 @@
{"RX1 MIX1", NULL, "RX1 MIX1 INP1"},
{"RX1 MIX1", NULL, "RX1 MIX1 INP2"},
+ {"RX1 MIX1", NULL, "RX1 MIX1 INP3"},
{"RX2 MIX1", NULL, "RX2 MIX1 INP1"},
{"RX2 MIX1", NULL, "RX2 MIX1 INP2"},
{"RX3 MIX1", NULL, "RX3 MIX1 INP1"},
@@ -3237,6 +3258,7 @@
{"RX1 MIX1 INP1", "RX2", "SLIM RX2"},
{"RX1 MIX1 INP1", "RX3", "SLIM RX3"},
{"RX1 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX1 MIX1 INP1", "RX5", "SLIM RX5"},
{"RX1 MIX1 INP1", "RX6", "SLIM RX6"},
{"RX1 MIX1 INP1", "RX7", "SLIM RX7"},
{"RX1 MIX1 INP1", "IIR1", "IIR1"},
@@ -3244,13 +3266,22 @@
{"RX1 MIX1 INP2", "RX2", "SLIM RX2"},
{"RX1 MIX1 INP2", "RX3", "SLIM RX3"},
{"RX1 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX1 MIX1 INP2", "RX5", "SLIM RX5"},
{"RX1 MIX1 INP2", "RX6", "SLIM RX6"},
{"RX1 MIX1 INP2", "RX7", "SLIM RX7"},
{"RX1 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX1 MIX1 INP3", "RX1", "SLIM RX1"},
+ {"RX1 MIX1 INP3", "RX2", "SLIM RX2"},
+ {"RX1 MIX1 INP3", "RX3", "SLIM RX3"},
+ {"RX1 MIX1 INP3", "RX4", "SLIM RX4"},
+ {"RX1 MIX1 INP3", "RX5", "SLIM RX5"},
+ {"RX1 MIX1 INP3", "RX6", "SLIM RX6"},
+ {"RX1 MIX1 INP3", "RX7", "SLIM RX7"},
{"RX2 MIX1 INP1", "RX1", "SLIM RX1"},
{"RX2 MIX1 INP1", "RX2", "SLIM RX2"},
{"RX2 MIX1 INP1", "RX3", "SLIM RX3"},
{"RX2 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX2 MIX1 INP1", "RX5", "SLIM RX5"},
{"RX2 MIX1 INP1", "RX6", "SLIM RX6"},
{"RX2 MIX1 INP1", "RX7", "SLIM RX7"},
{"RX2 MIX1 INP1", "IIR1", "IIR1"},
@@ -3258,6 +3289,7 @@
{"RX2 MIX1 INP2", "RX2", "SLIM RX2"},
{"RX2 MIX1 INP2", "RX3", "SLIM RX3"},
{"RX2 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX2 MIX1 INP2", "RX5", "SLIM RX5"},
{"RX2 MIX1 INP2", "RX6", "SLIM RX6"},
{"RX2 MIX1 INP2", "RX7", "SLIM RX7"},
{"RX2 MIX1 INP2", "IIR1", "IIR1"},
@@ -3265,6 +3297,7 @@
{"RX3 MIX1 INP1", "RX2", "SLIM RX2"},
{"RX3 MIX1 INP1", "RX3", "SLIM RX3"},
{"RX3 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX3 MIX1 INP1", "RX5", "SLIM RX5"},
{"RX3 MIX1 INP1", "RX6", "SLIM RX6"},
{"RX3 MIX1 INP1", "RX7", "SLIM RX7"},
{"RX3 MIX1 INP1", "IIR1", "IIR1"},
@@ -3272,6 +3305,7 @@
{"RX3 MIX1 INP2", "RX2", "SLIM RX2"},
{"RX3 MIX1 INP2", "RX3", "SLIM RX3"},
{"RX3 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX3 MIX1 INP2", "RX5", "SLIM RX5"},
{"RX3 MIX1 INP2", "RX6", "SLIM RX6"},
{"RX3 MIX1 INP2", "RX7", "SLIM RX7"},
{"RX3 MIX1 INP2", "IIR1", "IIR1"},
@@ -3279,12 +3313,14 @@
{"RX4 MIX1 INP1", "RX2", "SLIM RX2"},
{"RX4 MIX1 INP1", "RX3", "SLIM RX3"},
{"RX4 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX4 MIX1 INP1", "RX5", "SLIM RX5"},
{"RX4 MIX1 INP1", "RX6", "SLIM RX6"},
{"RX4 MIX1 INP1", "RX7", "SLIM RX7"},
{"RX4 MIX1 INP1", "IIR1", "IIR1"},
{"RX4 MIX1 INP2", "RX1", "SLIM RX1"},
{"RX4 MIX1 INP2", "RX2", "SLIM RX2"},
{"RX4 MIX1 INP2", "RX3", "SLIM RX3"},
+ {"RX4 MIX1 INP2", "RX5", "SLIM RX5"},
{"RX4 MIX1 INP2", "RX4", "SLIM RX4"},
{"RX4 MIX1 INP2", "RX6", "SLIM RX6"},
{"RX4 MIX1 INP2", "RX7", "SLIM RX7"},
@@ -3293,6 +3329,7 @@
{"RX5 MIX1 INP1", "RX2", "SLIM RX2"},
{"RX5 MIX1 INP1", "RX3", "SLIM RX3"},
{"RX5 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX5 MIX1 INP1", "RX5", "SLIM RX5"},
{"RX5 MIX1 INP1", "RX6", "SLIM RX6"},
{"RX5 MIX1 INP1", "RX7", "SLIM RX7"},
{"RX5 MIX1 INP1", "IIR1", "IIR1"},
@@ -3300,6 +3337,7 @@
{"RX5 MIX1 INP2", "RX2", "SLIM RX2"},
{"RX5 MIX1 INP2", "RX3", "SLIM RX3"},
{"RX5 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX5 MIX1 INP2", "RX5", "SLIM RX5"},
{"RX5 MIX1 INP2", "RX6", "SLIM RX6"},
{"RX5 MIX1 INP2", "RX7", "SLIM RX7"},
{"RX5 MIX1 INP2", "IIR1", "IIR1"},
@@ -3307,6 +3345,7 @@
{"RX6 MIX1 INP1", "RX2", "SLIM RX2"},
{"RX6 MIX1 INP1", "RX3", "SLIM RX3"},
{"RX6 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX6 MIX1 INP1", "RX5", "SLIM RX5"},
{"RX6 MIX1 INP1", "RX6", "SLIM RX6"},
{"RX6 MIX1 INP1", "RX7", "SLIM RX7"},
{"RX6 MIX1 INP1", "IIR1", "IIR1"},
@@ -3314,6 +3353,7 @@
{"RX6 MIX1 INP2", "RX2", "SLIM RX2"},
{"RX6 MIX1 INP2", "RX3", "SLIM RX3"},
{"RX6 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX6 MIX1 INP2", "RX5", "SLIM RX5"},
{"RX6 MIX1 INP2", "RX6", "SLIM RX6"},
{"RX6 MIX1 INP2", "RX7", "SLIM RX7"},
{"RX6 MIX1 INP2", "IIR1", "IIR1"},
@@ -3321,6 +3361,7 @@
{"RX7 MIX1 INP1", "RX2", "SLIM RX2"},
{"RX7 MIX1 INP1", "RX3", "SLIM RX3"},
{"RX7 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX7 MIX1 INP1", "RX5", "SLIM RX5"},
{"RX7 MIX1 INP1", "RX6", "SLIM RX6"},
{"RX7 MIX1 INP1", "RX7", "SLIM RX7"},
{"RX7 MIX1 INP1", "IIR1", "IIR1"},
@@ -3328,6 +3369,7 @@
{"RX7 MIX1 INP2", "RX2", "SLIM RX2"},
{"RX7 MIX1 INP2", "RX3", "SLIM RX3"},
{"RX7 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX7 MIX1 INP2", "RX5", "SLIM RX5"},
{"RX7 MIX1 INP2", "RX6", "SLIM RX6"},
{"RX7 MIX1 INP2", "RX7", "SLIM RX7"},
{"RX7 MIX1 INP2", "IIR1", "IIR1"},
@@ -3808,9 +3850,10 @@
pr_err("%s: Invalid\n", __func__);
return -EINVAL;
}
- pr_debug("%s: DAI-ID %x %d %d\n", __func__, dai->id, tx_num, rx_num);
+ pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n",
+ __func__, dai->name, dai->id, tx_num, rx_num);
- if (dai->id == AIF1_PB || dai->id == AIF2_PB) {
+ if (dai->id == AIF1_PB || dai->id == AIF2_PB || dai->id == AIF3_PB) {
for (i = 0; i < rx_num; i++) {
tabla->dai[dai->id - 1].ch_num[i] = rx_slot[i];
tabla->dai[dai->id - 1].ch_act = 0;
@@ -3842,7 +3885,7 @@
pr_err("%s: Invalid\n", __func__);
return -EINVAL;
}
- pr_debug("%s: DAI-ID %x\n", __func__, dai->id);
+
/* for virtual port, codec driver needs to do
* housekeeping, for now should be ok
*/
@@ -3871,11 +3914,20 @@
tx_slot[1] = tx_ch[1 + cnt];
tx_slot[2] = tx_ch[5 + cnt];
tx_slot[3] = tx_ch[3 + cnt];
+
+ } else if (dai->id == AIF3_PB) {
+ *rx_num = tabla_dai[dai->id - 1].playback.channels_max;
+ rx_slot[0] = rx_ch[3];
+ rx_slot[1] = rx_ch[4];
+
} else if (dai->id == AIF3_CAP) {
*tx_num = tabla_dai[dai->id - 1].capture.channels_max;
tx_slot[cnt] = tx_ch[2 + cnt];
tx_slot[cnt + 1] = tx_ch[4 + cnt];
}
+ pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n",
+ __func__, dai->name, dai->id, *tx_num, *rx_num);
+
return 0;
}
@@ -3891,8 +3943,9 @@
u8 tx_fs_rate, rx_fs_rate, rx_state, tx_state;
u32 compander_fs;
- pr_debug("%s: DAI-ID %x rate %d\n", __func__, dai->id,
- params_rate(params));
+ pr_debug("%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__,
+ dai->name, dai->id, params_rate(params),
+ params_channels(params));
switch (params_rate(params)) {
case 8000:
@@ -3989,7 +4042,7 @@
* If current dai is a rx dai, set sample rate to
* all the rx paths that are currently not active
*/
- if (dai->id == AIF1_PB || dai->id == AIF2_PB) {
+ if (dai->id == AIF1_PB || dai->id == AIF2_PB || dai->id == AIF3_PB) {
rx_state = snd_soc_read(codec,
TABLA_A_CDC_CLK_RX_B1_CTL);
@@ -4114,6 +4167,20 @@
},
.ops = &tabla_dai_ops,
},
+ {
+ .name = "tabla_rx3",
+ .id = AIF3_PB,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .rates = WCD9310_RATES,
+ .formats = TABLA_FORMATS,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tabla_dai_ops,
+ },
};
static struct snd_soc_dai_driver tabla_i2s_dai[] = {
@@ -4160,6 +4227,9 @@
/* Execute the callback only if interface type is slimbus */
if (tabla_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS)
return 0;
+
+ pr_debug("%s: %s %d\n", __func__, w->name, event);
+
switch (event) {
case SND_SOC_DAPM_POST_PMU:
for (j = 0; j < ARRAY_SIZE(tabla_dai); j++) {
@@ -4228,7 +4298,8 @@
case SND_SOC_DAPM_POST_PMU:
for (j = 0; j < ARRAY_SIZE(tabla_dai); j++) {
if (tabla_dai[j].id == AIF1_PB ||
- tabla_dai[j].id == AIF2_PB)
+ tabla_dai[j].id == AIF2_PB ||
+ tabla_dai[j].id == AIF3_PB)
continue;
if (!strncmp(w->sname,
tabla_dai[j].capture.stream_name, 13)) {
@@ -4245,7 +4316,8 @@
case SND_SOC_DAPM_POST_PMD:
for (j = 0; j < ARRAY_SIZE(tabla_dai); j++) {
if (tabla_dai[j].id == AIF1_PB ||
- tabla_dai[j].id == AIF2_PB)
+ tabla_dai[j].id == AIF2_PB ||
+ tabla_dai[j].id == AIF3_PB)
continue;
if (!strncmp(w->sname,
tabla_dai[j].capture.stream_name, 13)) {
@@ -4284,9 +4356,16 @@
SND_SOC_DAPM_AIF_IN_E("SLIM RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
0, tabla_codec_enable_slimrx,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("SLIM RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
+ 0, tabla_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_AIF_IN("SLIM RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_IN("SLIM RX4", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN_E("SLIM RX4", "AIF3 Playback", 0, SND_SOC_NOPM, 0,
+ 0, tabla_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("SLIM RX5", "AIF3 Playback", 0, SND_SOC_NOPM, 0,
+ 0, tabla_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_AIF_IN_E("SLIM RX6", "AIF2 Playback", 0, SND_SOC_NOPM, 0,
0, tabla_codec_enable_slimrx,
@@ -4395,6 +4474,8 @@
&rx_mix1_inp1_mux),
SND_SOC_DAPM_MUX("RX1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
&rx_mix1_inp2_mux),
+ SND_SOC_DAPM_MUX("RX1 MIX1 INP3", SND_SOC_NOPM, 0, 0,
+ &rx_mix1_inp3_mux),
SND_SOC_DAPM_MUX("RX2 MIX1 INP1", SND_SOC_NOPM, 0, 0,
&rx2_mix1_inp1_mux),
SND_SOC_DAPM_MUX("RX2 MIX1 INP2", SND_SOC_NOPM, 0, 0,
@@ -7173,6 +7254,22 @@
{TABLA_A_CDC_TX8_MUX_CTL, 0x8, 0x0},
{TABLA_A_CDC_TX9_MUX_CTL, 0x8, 0x0},
{TABLA_A_CDC_TX10_MUX_CTL, 0x8, 0x0},
+
+ /* config Decimator for DMIC CLK_MODE_1(3.072Mhz@12.88Mhz mclk) */
+ {TABLA_A_CDC_TX1_DMIC_CTL, 0x1, 0x1},
+ {TABLA_A_CDC_TX2_DMIC_CTL, 0x1, 0x1},
+ {TABLA_A_CDC_TX3_DMIC_CTL, 0x1, 0x1},
+ {TABLA_A_CDC_TX4_DMIC_CTL, 0x1, 0x1},
+ {TABLA_A_CDC_TX5_DMIC_CTL, 0x1, 0x1},
+ {TABLA_A_CDC_TX6_DMIC_CTL, 0x1, 0x1},
+ {TABLA_A_CDC_TX7_DMIC_CTL, 0x1, 0x1},
+ {TABLA_A_CDC_TX8_DMIC_CTL, 0x1, 0x1},
+ {TABLA_A_CDC_TX9_DMIC_CTL, 0x1, 0x1},
+ {TABLA_A_CDC_TX10_DMIC_CTL, 0x1, 0x1},
+
+ /* config DMIC clk to CLK_MODE_1 (3.072Mhz@12.88Mhz mclk) */
+ {TABLA_A_CDC_CLK_DMIC_CTL, 0x2A, 0x2A},
+
};
static const struct tabla_reg_mask_val tabla_1_x_codec_reg_init_val[] = {
@@ -7519,6 +7616,9 @@
case AIF2_CAP:
ch_cnt = tabla_dai[i].capture.channels_max;
break;
+ case AIF3_PB:
+ ch_cnt = tabla_dai[i].playback.channels_max;
+ break;
case AIF3_CAP:
ch_cnt = tabla_dai[i].capture.channels_max;
break;
diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile
index b3cffd1..43c678d 100644
--- a/sound/soc/msm/Makefile
+++ b/sound/soc/msm/Makefile
@@ -75,3 +75,12 @@
# for MDM 9615 sound card driver
snd-soc-mdm9615-objs := mdm9615.o
obj-$(CONFIG_SND_SOC_MDM9615) += snd-soc-mdm9615.o
+
+# for MSM 8974 sound card driver
+obj-$(CONFIG_SND_SOC_MSM_QDSP6V2_INTF) += qdsp6v2/
+snd-soc-msm8974-objs := msm8974.o
+obj-$(CONFIG_SND_SOC_MSM8974) += snd-soc-msm8974.o
+
+snd-soc-qdsp6v2-objs := msm-dai-fe.o msm-dai-stub.o
+obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o
+
diff --git a/sound/soc/msm/apq8064.c b/sound/soc/msm/apq8064.c
index 0c72880..72c7e47 100644
--- a/sound/soc/msm/apq8064.c
+++ b/sound/soc/msm/apq8064.c
@@ -66,7 +66,8 @@
SLIM_1_TX_1 = 146, /* BT-SCO and USB RX */
SLIM_3_RX_1 = 151, /* External echo-cancellation ref */
SLIM_3_RX_2 = 152, /* External echo-cancellation ref */
- SLIM_3_TX_1 = 147, /* HDMI RX */
+ SLIM_3_TX_1 = 153, /* HDMI RX */
+ SLIM_3_TX_2 = 154, /* HDMI RX */
SLIM_4_TX_1 = 148, /* In-call recording RX */
SLIM_4_TX_2 = 149, /* In-call recording RX */
SLIM_4_RX_1 = 150, /* In-call music delivery TX */
@@ -984,6 +985,7 @@
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int ret = 0;
unsigned int rx_ch[2] = {SLIM_3_RX_1, SLIM_3_RX_2};
+ unsigned int tx_ch[2] = {SLIM_3_TX_1, SLIM_3_TX_2};
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
pr_debug("%s: slim_3_rx_ch %d, sch %d %d\n",
@@ -999,7 +1001,16 @@
goto end;
}
} else {
- pr_err("%s: SLIMBUS_3_TX not defined for this DAI\n", __func__);
+ pr_debug("%s: MDM RX -> SLIMBUS_3_TX -> APQ HDMI ch: %d, %d\n",
+ __func__, tx_ch[0], tx_ch[1]);
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai, 2, tx_ch, 0, 0);
+ if (ret < 0) {
+ pr_err("%s: Erorr %d setting SLIM_3 TX channel map\n",
+ __func__, ret);
+
+ goto end;
+ }
}
end:
@@ -1238,6 +1249,22 @@
return 0;
}
+static int msm_slim_3_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s()\n", __func__);
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ return 0;
+}
+
static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -1800,6 +1827,18 @@
.be_hw_params_fixup = msm_slim_3_rx_be_hw_params_fixup,
.ops = &msm_slimbus_3_be_ops,
},
+ {
+ .name = LPASS_BE_SLIMBUS_3_TX,
+ .stream_name = "Slimbus3 Capture",
+ .cpu_dai_name = "msm-dai-q6.16391",
+ .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_SLIMBUS_3_TX,
+ .be_hw_params_fixup = msm_slim_3_tx_be_hw_params_fixup,
+ .ops = &msm_slimbus_3_be_ops,
+ },
};
struct snd_soc_card snd_soc_card_msm = {
diff --git a/sound/soc/msm/msm-dai-q6.c b/sound/soc/msm/msm-dai-q6.c
index a62541a..965559f 100644
--- a/sound/soc/msm/msm-dai-q6.c
+++ b/sound/soc/msm/msm-dai-q6.c
@@ -604,7 +604,9 @@
case SLIMBUS_3_RX:
case SLIMBUS_0_TX:
case SLIMBUS_1_TX:
+ case SLIMBUS_2_RX:
case SLIMBUS_2_TX:
+ case SLIMBUS_3_TX:
case SLIMBUS_4_RX:
case SLIMBUS_4_TX:
rc = msm_dai_q6_slim_bus_hw_params(params, dai,
@@ -1127,17 +1129,16 @@
static int msm_dai_q6_set_channel_map(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot)
-
{
int rc = 0;
struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
unsigned int i = 0;
- dev_dbg(dai->dev, "enter %s, id = %d\n", __func__,
- dai->id);
+ dev_dbg(dai->dev, "%s: dai_id = %d\n", __func__, dai->id);
switch (dai->id) {
case SLIMBUS_0_RX:
case SLIMBUS_1_RX:
+ case SLIMBUS_2_RX:
case SLIMBUS_3_RX:
case SLIMBUS_4_RX:
/* channel number to be between 128 and 255. For RX port
@@ -1164,6 +1165,7 @@
case SLIMBUS_0_TX:
case SLIMBUS_1_TX:
case SLIMBUS_2_TX:
+ case SLIMBUS_3_TX:
case SLIMBUS_4_TX:
/* channel number to be between 128 and 255. For RX port
* use channel numbers from 138 to 144, for TX port
@@ -1472,6 +1474,22 @@
.remove = msm_dai_q6_dai_remove,
};
+static struct snd_soc_dai_driver msm_dai_q6_slimbus_2_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
static struct snd_soc_dai_driver msm_dai_q6_slimbus_2_tx_dai = {
.capture = {
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
@@ -1534,6 +1552,7 @@
break;
case SLIMBUS_0_TX:
case SLIMBUS_4_TX:
+ case SLIMBUS_3_TX:
rc = snd_soc_register_dai(&pdev->dev,
&msm_dai_q6_slimbus_tx_dai);
break;
@@ -1545,6 +1564,10 @@
rc = snd_soc_register_dai(&pdev->dev,
&msm_dai_q6_slimbus_1_tx_dai);
break;
+ case SLIMBUS_2_RX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_slimbus_2_rx_dai);
+ break;
case SLIMBUS_2_TX:
rc = snd_soc_register_dai(&pdev->dev,
&msm_dai_q6_slimbus_2_tx_dai);
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 7fbb592..217158c 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -110,6 +110,43 @@
/* This array is indexed by back-end DAI ID defined in msm-pcm-routing.h
* If new back-end is defined, add new back-end DAI ID at the end of enum
*/
+
+union srs_trumedia_params_u {
+ struct srs_trumedia_params srs_params;
+ unsigned short int raw_params[1];
+};
+static union srs_trumedia_params_u msm_srs_trumedia_params[2];
+static int srs_port_id = -1;
+
+static void srs_send_params(int port_id, unsigned int techs,
+ int param_block_idx) {
+ pr_debug("SRS %s: called, port_id = %d, techs flags = %u,"
+ " paramblockidx %d", __func__, port_id, techs,
+ param_block_idx);
+ /* force all if techs is set to 1 */
+ if (techs == 1)
+ techs = 0xFFFFFFFF;
+
+ if (techs & (1 << SRS_ID_WOWHD))
+ srs_trumedia_open(port_id, SRS_ID_WOWHD,
+ (void *)&msm_srs_trumedia_params[param_block_idx].srs_params.wowhd);
+ if (techs & (1 << SRS_ID_CSHP))
+ srs_trumedia_open(port_id, SRS_ID_CSHP,
+ (void *)&msm_srs_trumedia_params[param_block_idx].srs_params.cshp);
+ if (techs & (1 << SRS_ID_HPF))
+ srs_trumedia_open(port_id, SRS_ID_HPF,
+ (void *)&msm_srs_trumedia_params[param_block_idx].srs_params.hpf);
+ if (techs & (1 << SRS_ID_PEQ))
+ srs_trumedia_open(port_id, SRS_ID_PEQ,
+ (void *)&msm_srs_trumedia_params[param_block_idx].srs_params.peq);
+ if (techs & (1 << SRS_ID_HL))
+ srs_trumedia_open(port_id, SRS_ID_HL,
+ (void *)&msm_srs_trumedia_params[param_block_idx].srs_params.hl);
+ if (techs & (1 << SRS_ID_GLOBAL))
+ srs_trumedia_open(port_id, SRS_ID_GLOBAL,
+ (void *)&msm_srs_trumedia_params[param_block_idx].srs_params.global);
+}
+
static struct msm_pcm_routing_bdai_data msm_bedais[MSM_BACKEND_DAI_MAX] = {
{ PRIMARY_I2S_RX, 0, 0, 0, 0, 0},
{ PRIMARY_I2S_TX, 0, 0, 0, 0, 0},
@@ -135,6 +172,7 @@
{ SLIMBUS_4_RX, 0, 0, 0, 0, 0},
{ SLIMBUS_4_TX, 0, 0, 0, 0, 0},
{ SLIMBUS_3_RX, 0, 0, 0, 0, 0},
+ { SLIMBUS_3_TX, 0, 0, 0, 0, 0},
{ SLIMBUS_EXTPROC_RX, 0, 0, 0, 0, 0},
{ SLIMBUS_EXTPROC_RX, 0, 0, 0, 0, 0},
{ SLIMBUS_EXTPROC_RX, 0, 0, 0, 0, 0},
@@ -749,6 +787,104 @@
return 0;
}
+static int msm_routing_get_srs_trumedia_control(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_routing_set_srs_trumedia_control_(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned int techs = 0;
+ unsigned short offset, value, max, index;
+
+ max = sizeof(msm_srs_trumedia_params) >> 1;
+ index = (unsigned short)((ucontrol->value.integer.value[0] &
+ SRS_PARAM_INDEX_MASK) >> 31);
+ if (SRS_CMD_UPLOAD ==
+ (ucontrol->value.integer.value[0] & SRS_CMD_UPLOAD)) {
+ techs = ucontrol->value.integer.value[0] & 0xFF;
+ pr_debug("SRS %s: send params request, flags = %u",
+ __func__, techs);
+ if (srs_port_id >= 0 && techs)
+ srs_send_params(srs_port_id, techs, index);
+ return 0;
+ }
+ offset = (unsigned short)((ucontrol->value.integer.value[0] &
+ SRS_PARAM_OFFSET_MASK) >> 16);
+ value = (unsigned short)(ucontrol->value.integer.value[0] &
+ SRS_PARAM_VALUE_MASK);
+ if (offset < max) {
+ msm_srs_trumedia_params[index].raw_params[offset] = value;
+ pr_debug("SRS %s: index set... (max %d, requested %d,"
+ " val %d, paramblockidx %d)", __func__, max, offset,
+ value, index);
+ } else {
+ pr_err("SRS %s: index out of bounds! (max %d, requested %d)",
+ __func__, max, offset);
+ }
+ if (offset == 4) {
+ int i;
+ for (i = 0; i < max; i++) {
+ if (i == 0) {
+ pr_debug("SRS %s: global block start",
+ __func__);
+ }
+ if (i ==
+ (sizeof(struct srs_trumedia_params_GLOBAL) >> 1)) {
+ break;
+ pr_debug("SRS %s: wowhd block start at"
+ " offset %d word offset %d", __func__,
+ i, i>>1);
+ }
+ pr_debug("SRS %s: param_index %d index %d val %d",
+ __func__, index, i,
+ msm_srs_trumedia_params[index].raw_params[i]);
+ }
+ }
+ return 0;
+}
+
+static int msm_routing_set_srs_trumedia_control(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol) {
+ int ret;
+
+ pr_debug("SRS control normal called");
+ mutex_lock(&routing_lock);
+ srs_port_id = SLIMBUS_0_RX;
+ ret = msm_routing_set_srs_trumedia_control_(kcontrol, ucontrol);
+ mutex_unlock(&routing_lock);
+ return ret;
+}
+
+static int msm_routing_set_srs_trumedia_control_I2S(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol) {
+ int ret;
+
+ pr_debug("SRS control I2S called");
+ mutex_lock(&routing_lock);
+ srs_port_id = PRIMARY_I2S_RX;
+ ret = msm_routing_set_srs_trumedia_control_(kcontrol, ucontrol);
+ mutex_unlock(&routing_lock);
+ return ret;
+}
+
+static int msm_routing_set_srs_trumedia_control_HDMI(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol) {
+ int ret;
+
+ pr_debug("SRS control HDMI called");
+ mutex_lock(&routing_lock);
+ srs_port_id = HDMI_RX;
+ ret = msm_routing_set_srs_trumedia_control_(kcontrol, ucontrol);
+ mutex_unlock(&routing_lock);
+ return ret;
+}
+
static void msm_send_eq_values(int eq_idx)
{
int result;
@@ -1273,6 +1409,9 @@
SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX,
MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("SLIM_3_TX", MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
};
static const struct snd_kcontrol_new sbus_0_rx_port_mixer_controls[] = {
@@ -1330,6 +1469,9 @@
SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_HDMI_RX,
MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("SLIM_3_TX", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_BACKEND_DAI_SLIMBUS_3_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
};
static const struct snd_kcontrol_new sec_i2s_rx_port_mixer_controls[] = {
@@ -1378,6 +1520,63 @@
msm_routing_set_compressed_vol_mixer, compressed_rx_vol_gain),
};
+static const struct snd_kcontrol_new lpa_SRS_trumedia_controls[] = {
+ {.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SRS TruMedia",
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_soc_info_volsw, \
+ .get = msm_routing_get_srs_trumedia_control,
+ .put = msm_routing_set_srs_trumedia_control,
+ .private_value = ((unsigned long)&(struct soc_mixer_control)
+ {.reg = SND_SOC_NOPM,
+ .shift = 0,
+ .rshift = 0,
+ .max = 0xFFFFFFFF,
+ .platform_max = 0xFFFFFFFF,
+ .invert = 0
+ })
+ }
+};
+
+static const struct snd_kcontrol_new lpa_SRS_trumedia_controls_HDMI[] = {
+ {.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SRS TruMedia HDMI",
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_soc_info_volsw, \
+ .get = msm_routing_get_srs_trumedia_control,
+ .put = msm_routing_set_srs_trumedia_control_HDMI,
+ .private_value = ((unsigned long)&(struct soc_mixer_control)
+ {.reg = SND_SOC_NOPM,
+ .shift = 0,
+ .rshift = 0,
+ .max = 0xFFFFFFFF,
+ .platform_max = 0xFFFFFFFF,
+ .invert = 0
+ })
+ }
+};
+
+static const struct snd_kcontrol_new lpa_SRS_trumedia_controls_I2S[] = {
+ {.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SRS TruMedia I2S",
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_soc_info_volsw, \
+ .get = msm_routing_get_srs_trumedia_control,
+ .put = msm_routing_set_srs_trumedia_control_I2S,
+ .private_value = ((unsigned long)&(struct soc_mixer_control)
+ {.reg = SND_SOC_NOPM,
+ .shift = 0,
+ .rshift = 0,
+ .max = 0xFFFFFFFF,
+ .platform_max = 0xFFFFFFFF,
+ .invert = 0
+ })
+ }
+};
+
static const struct snd_kcontrol_new eq_enable_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia1 EQ Enable", SND_SOC_NOPM,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_eq_enable_mixer,
@@ -1633,6 +1832,7 @@
SND_SOC_DAPM_AIF_IN("SLIMBUS_1_TX", "Slimbus1 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("STUB_1_TX", "Stub1 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("SLIMBUS_3_RX", "Slimbus3 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_3_TX", "Slimbus3 Capture", 0, 0, 0, 0),
/* Switch Definitions */
SND_SOC_DAPM_SWITCH("SLIMBUS_DL_HL", SND_SOC_NOPM, 0, 0,
@@ -1911,6 +2111,7 @@
{"Voice Stub Tx Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
{"Voice Stub Tx Mixer", "STUB_1_TX_HL", "STUB_1_TX"},
{"Voice Stub Tx Mixer", "MI2S_TX", "MI2S_TX"},
+ {"Voice Stub Tx Mixer", "SLIM_3_TX", "SLIMBUS_3_TX"},
{"VOICE_STUB_UL", NULL, "Voice Stub Tx Mixer"},
{"STUB_RX Mixer", "Voice Stub", "VOICE_STUB_DL"},
@@ -1920,6 +2121,7 @@
{"INTERNAL_BT_SCO_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
{"MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
{"MI2S_RX", NULL, "MI2S_RX_Voice Mixer"},
+ {"HDMI_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
{"SLIMBUS_3_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
{"SLIMBUS_3_RX", NULL, "SLIMBUS_3_RX_Voice Mixer"},
@@ -1934,6 +2136,7 @@
{"HDMI_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
+ {"HDMI_RX Port Mixer", "SLIM_3_TX", "SLIMBUS_3_TX"},
{"HDMI", NULL, "HDMI_RX Port Mixer"},
{"SEC_I2S_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
@@ -1980,8 +2183,10 @@
mutex_lock(&routing_lock);
for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) {
- if (fe_dai_map[i][session_type] != INVALID_SESSION)
+ if (fe_dai_map[i][session_type] != INVALID_SESSION) {
adm_close(bedai->port_id);
+ srs_port_id = -1;
+ }
}
bedai->active = 0;
@@ -2047,6 +2252,8 @@
msm_pcm_routing_build_matrix(i,
fe_dai_map[i][session_type], path_type);
+ srs_port_id = bedai->port_id;
+ srs_send_params(srs_port_id, 1, 0);
}
}
@@ -2115,6 +2322,18 @@
compressed_vol_mixer_controls,
ARRAY_SIZE(compressed_vol_mixer_controls));
+ snd_soc_add_platform_controls(platform,
+ lpa_SRS_trumedia_controls,
+ ARRAY_SIZE(lpa_SRS_trumedia_controls));
+
+ snd_soc_add_platform_controls(platform,
+ lpa_SRS_trumedia_controls_HDMI,
+ ARRAY_SIZE(lpa_SRS_trumedia_controls_HDMI));
+
+ snd_soc_add_platform_controls(platform,
+ lpa_SRS_trumedia_controls_I2S,
+ ARRAY_SIZE(lpa_SRS_trumedia_controls_I2S));
+
return 0;
}
diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h
index 5f5c12a..e1a0e6c 100644
--- a/sound/soc/msm/msm-pcm-routing.h
+++ b/sound/soc/msm/msm-pcm-routing.h
@@ -39,6 +39,7 @@
#define LPASS_BE_SLIMBUS_1_TX "(Backend) SLIMBUS_1_TX"
#define LPASS_BE_STUB_1_TX "(Backend) STUB_1_TX"
#define LPASS_BE_SLIMBUS_3_RX "(Backend) SLIMBUS_3_RX"
+#define LPASS_BE_SLIMBUS_3_TX "(Backend) SLIMBUS_3_TX"
#define LPASS_BE_SLIMBUS_4_RX "(Backend) SLIMBUS_4_RX"
#define LPASS_BE_SLIMBUS_4_TX "(Backend) SLIMBUS_4_TX"
@@ -90,6 +91,7 @@
MSM_BACKEND_DAI_SLIMBUS_4_RX,
MSM_BACKEND_DAI_SLIMBUS_4_TX,
MSM_BACKEND_DAI_SLIMBUS_3_RX,
+ MSM_BACKEND_DAI_SLIMBUS_3_TX,
MSM_BACKEND_DAI_EXTPROC_RX,
MSM_BACKEND_DAI_EXTPROC_TX,
MSM_BACKEND_DAI_EXTPROC_EC_TX,
diff --git a/sound/soc/msm/msm8930.c b/sound/soc/msm/msm8930.c
index 2762bd6..063fe6b 100644
--- a/sound/soc/msm/msm8930.c
+++ b/sound/soc/msm/msm8930.c
@@ -42,9 +42,14 @@
#define DEFAULT_PMIC_SPK_GAIN 0x0D
#define SITAR_EXT_CLK_RATE 12288000
-#define SITAR_MBHC_DEF_BUTTONS 3
+#define SITAR_MBHC_DEF_BUTTONS 8
#define SITAR_MBHC_DEF_RLOADS 5
+#define GPIO_AUX_PCM_DOUT 63
+#define GPIO_AUX_PCM_DIN 64
+#define GPIO_AUX_PCM_SYNC 65
+#define GPIO_AUX_PCM_CLK 66
+
static int msm8930_spk_control;
static int msm8930_slim_0_rx_ch = 1;
static int msm8930_slim_0_tx_ch = 1;
@@ -307,11 +312,11 @@
{"MIC BIAS2 Internal1", NULL, "Headset Mic"},
/* Microphone path */
- {"AMIC1", NULL, "MIC BIAS2 Internal1"},
- {"MIC BIAS2 Internal1", NULL, "ANCLeft Headset Mic"},
+ {"AMIC1", NULL, "MIC BIAS2 External"},
+ {"MIC BIAS2 External", NULL, "ANCLeft Headset Mic"},
- {"AMIC3", NULL, "MIC BIAS2 Internal1"},
- {"MIC BIAS2 Internal1", NULL, "ANCRight Headset Mic"},
+ {"AMIC3", NULL, "MIC BIAS2 External"},
+ {"MIC BIAS2 External", NULL, "ANCRight Headset Mic"},
{"HEADPHONE", NULL, "LDO_H"},
@@ -321,17 +326,17 @@
*/
/**
- * Digital Mic1. Front Bottom left Digital Mic on Fluid and MTP.
+ * Digital Mic1. Front Bottom left Mic on Fluid and MTP.
* Digital Mic GM5 on CDP mainboard.
- * Conncted to DMIC2 Input on Sitar codec.
+ * Conncted to DMIC1 Input on Sitar codec.
*/
{"DMIC1", NULL, "MIC BIAS1 External"},
{"MIC BIAS1 External", NULL, "Digital Mic1"},
/**
- * Digital Mic2. Front Bottom right Digital Mic on Fluid and MTP.
+ * Digital Mic2. Back top MIC on Fluid.
* Digital Mic GM6 on CDP mainboard.
- * Conncted to DMIC1 Input on Sitar codec.
+ * Conncted to DMIC2 Input on Sitar codec.
*/
{"DMIC2", NULL, "MIC BIAS1 External"},
{"MIC BIAS1 External", NULL, "Digital Mic2"},
@@ -526,7 +531,7 @@
#undef S
#define S(X, Y) ((SITAR_MBHC_CAL_PLUG_TYPE_PTR(sitar_cal)->X) = (Y))
S(v_no_mic, 30);
- S(v_hs_max, 1550);
+ S(v_hs_max, 1650);
#undef S
#define S(X, Y) ((SITAR_MBHC_CAL_BTN_DET_PTR(sitar_cal)->X) = (Y))
S(c[0], 62);
@@ -668,7 +673,7 @@
}
err = snd_soc_jack_new(codec, "Button Jack",
- SND_JACK_BTN_0, &button_jack);
+ SITAR_JACK_BUTTON_MASK, &button_jack);
if (err) {
pr_err("failed to create new jack\n");
return err;
@@ -812,6 +817,80 @@
return 0;
}
+static int msm8930_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ /* PCM only supports mono output with 8khz sample rate */
+ rate->min = rate->max = 8000;
+ channels->min = channels->max = 1;
+
+ return 0;
+}
+
+static int msm8930_aux_pcm_get_gpios(void)
+{
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+
+ ret = gpio_request(GPIO_AUX_PCM_DOUT, "AUX PCM DOUT");
+ if (ret < 0) {
+ pr_err("%s: Failed to request gpio(%d): AUX PCM DOUT",
+ __func__, GPIO_AUX_PCM_DOUT);
+
+ goto fail_dout;
+ }
+
+ ret = gpio_request(GPIO_AUX_PCM_DIN, "AUX PCM DIN");
+ if (ret < 0) {
+ pr_err("%s: Failed to request gpio(%d): AUX PCM DIN",
+ __func__, GPIO_AUX_PCM_DIN);
+ goto fail_din;
+ }
+
+ ret = gpio_request(GPIO_AUX_PCM_SYNC, "AUX PCM SYNC");
+ if (ret < 0) {
+ pr_err("%s: Failed to request gpio(%d): AUX PCM SYNC",
+ __func__, GPIO_AUX_PCM_SYNC);
+ goto fail_sync;
+ }
+
+ ret = gpio_request(GPIO_AUX_PCM_CLK, "AUX PCM CLK");
+ if (ret < 0) {
+ pr_err("%s: Failed to request gpio(%d): AUX PCM CLK",
+ __func__, GPIO_AUX_PCM_CLK);
+ goto fail_clk;
+ }
+
+ return 0;
+
+fail_clk:
+ gpio_free(GPIO_AUX_PCM_SYNC);
+fail_sync:
+ gpio_free(GPIO_AUX_PCM_DIN);
+fail_din:
+ gpio_free(GPIO_AUX_PCM_DOUT);
+fail_dout:
+
+ return ret;
+}
+
+static int msm8930_aux_pcm_free_gpios(void)
+{
+ gpio_free(GPIO_AUX_PCM_DIN);
+ gpio_free(GPIO_AUX_PCM_DOUT);
+ gpio_free(GPIO_AUX_PCM_SYNC);
+ gpio_free(GPIO_AUX_PCM_CLK);
+
+ return 0;
+}
+
static int msm8930_startup(struct snd_pcm_substream *substream)
{
pr_debug("%s(): substream = %s stream = %d\n", __func__,
@@ -819,6 +898,26 @@
return 0;
}
+static int msm8930_auxpcm_startup(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ pr_debug("%s(): substream = %s\n", __func__, substream->name);
+ ret = msm8930_aux_pcm_get_gpios();
+ if (ret < 0) {
+ pr_err("%s: Aux PCM GPIO request failed\n", __func__);
+ return -EINVAL;
+ }
+ return 0;
+
+}
+
+static void msm8930_auxpcm_shutdown(struct snd_pcm_substream *substream)
+{
+ pr_debug("%s(): substream = %s\n", __func__, substream->name);
+ msm8930_aux_pcm_free_gpios();
+}
+
static void msm8930_shutdown(struct snd_pcm_substream *substream)
{
pr_debug("%s(): substream = %s stream = %d\n", __func__,
@@ -831,6 +930,11 @@
.shutdown = msm8930_shutdown,
};
+static struct snd_soc_ops msm8930_auxpcm_be_ops = {
+ .startup = msm8930_auxpcm_startup,
+ .shutdown = msm8930_auxpcm_shutdown,
+};
+
/* Digital audio interface glue - connects codec <---> CPU */
static struct snd_soc_dai_link msm8930_dai[] = {
/* FrontEnd DAI Links */
@@ -1061,6 +1165,30 @@
.no_pcm = 1,
.be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
},
+ /* AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_AUXPCM_RX,
+ .stream_name = "AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6.2",
+ .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_AUXPCM_RX,
+ .be_hw_params_fixup = msm8930_auxpcm_be_params_fixup,
+ .ops = &msm8930_auxpcm_be_ops,
+ },
+ {
+ .name = LPASS_BE_AUXPCM_TX,
+ .stream_name = "AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6.3",
+ .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_AUXPCM_TX,
+ .be_hw_params_fixup = msm8930_auxpcm_be_params_fixup,
+ },
/* Incall Music BACK END DAI Link */
{
.name = LPASS_BE_VOICE_PLAYBACK_TX,
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
new file mode 100644
index 0000000..d47910b
--- /dev/null
+++ b/sound/soc/msm/msm8974.c
@@ -0,0 +1,752 @@
+/* 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/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc-dsp.h>
+#include <sound/pcm.h>
+#include <sound/jack.h>
+#include <asm/mach-types.h>
+#include <mach/socinfo.h>
+#include <qdsp6v2/msm-pcm-routing-v2.h>
+#include "../codecs/wcd9310.h"
+
+/* 8974 machine driver */
+
+#define PM8921_GPIO_BASE NR_GPIO_IRQS
+#define PM8921_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8921_GPIO_BASE)
+
+#define MSM8974_SPK_ON 1
+#define MSM8974_SPK_OFF 0
+
+#define MSM_SLIM_0_RX_MAX_CHANNELS 2
+#define MSM_SLIM_0_TX_MAX_CHANNELS 4
+
+#define BTSCO_RATE_8KHZ 8000
+#define BTSCO_RATE_16KHZ 16000
+
+#define BOTTOM_SPK_AMP_POS 0x1
+#define BOTTOM_SPK_AMP_NEG 0x2
+#define TOP_SPK_AMP_POS 0x4
+#define TOP_SPK_AMP_NEG 0x8
+
+#define GPIO_AUX_PCM_DOUT 43
+#define GPIO_AUX_PCM_DIN 44
+#define GPIO_AUX_PCM_SYNC 45
+#define GPIO_AUX_PCM_CLK 46
+
+#define TABLA_EXT_CLK_RATE 12288000
+
+#define TABLA_MBHC_DEF_BUTTONS 8
+#define TABLA_MBHC_DEF_RLOADS 5
+
+/* Shared channel numbers for Slimbus ports that connect APQ to MDM. */
+enum {
+ SLIM_1_RX_1 = 145, /* BT-SCO and USB TX */
+ SLIM_1_TX_1 = 146, /* BT-SCO and USB RX */
+ SLIM_2_RX_1 = 147, /* HDMI RX */
+ SLIM_3_RX_1 = 148, /* In-call recording RX */
+ SLIM_3_RX_2 = 149, /* In-call recording RX */
+ SLIM_4_TX_1 = 150, /* In-call musid delivery TX */
+};
+
+static u32 top_spk_pamp_gpio = PM8921_GPIO_PM_TO_SYS(18);
+static u32 bottom_spk_pamp_gpio = PM8921_GPIO_PM_TO_SYS(19);
+static int msm_spk_control;
+static int msm_ext_bottom_spk_pamp;
+static int msm_ext_top_spk_pamp;
+static int msm_slim_0_rx_ch = 1;
+static int msm_slim_0_tx_ch = 1;
+
+static int msm_btsco_rate = BTSCO_RATE_8KHZ;
+static int msm_headset_gpios_configured;
+
+static struct snd_soc_jack hs_jack;
+static struct snd_soc_jack button_jack;
+
+static int msm_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+ bool dapm);
+
+static struct tabla_mbhc_config mbhc_cfg = {
+ .headset_jack = &hs_jack,
+ .button_jack = &button_jack,
+ .read_fw_bin = false,
+ .calibration = NULL,
+ .micbias = TABLA_MICBIAS2,
+ .mclk_cb_fn = msm_enable_codec_ext_clk,
+ .mclk_rate = TABLA_EXT_CLK_RATE,
+ .gpio = 0, /* MBHC GPIO is not configured */
+ .gpio_irq = 0,
+ .gpio_level_insert = 1,
+};
+
+static void msm_enable_ext_spk_amp_gpio(u32 spk_amp_gpio)
+{
+ int ret = 0;
+
+ struct pm_gpio param = {
+ .direction = PM_GPIO_DIR_OUT,
+ .output_buffer = PM_GPIO_OUT_BUF_CMOS,
+ .output_value = 1,
+ .pull = PM_GPIO_PULL_NO,
+ .vin_sel = PM_GPIO_VIN_S4,
+ .out_strength = PM_GPIO_STRENGTH_MED,
+ .
+ function = PM_GPIO_FUNC_NORMAL,
+ };
+
+ if (spk_amp_gpio == bottom_spk_pamp_gpio) {
+
+ ret = gpio_request(bottom_spk_pamp_gpio, "BOTTOM_SPK_AMP");
+ if (ret) {
+ pr_err("%s: Error requesting BOTTOM SPK AMP GPIO %u\n",
+ __func__, bottom_spk_pamp_gpio);
+ return;
+ }
+ ret = pm8xxx_gpio_config(bottom_spk_pamp_gpio, ¶m);
+ if (ret)
+ pr_err("%s: Failed to configure Bottom Spk Ampl"
+ " gpio %u\n", __func__, bottom_spk_pamp_gpio);
+ else {
+ pr_debug("%s: enable Bottom spkr amp gpio\n", __func__);
+ gpio_direction_output(bottom_spk_pamp_gpio, 1);
+ }
+
+ } else if (spk_amp_gpio == top_spk_pamp_gpio) {
+
+ ret = gpio_request(top_spk_pamp_gpio, "TOP_SPK_AMP");
+ if (ret) {
+ pr_err("%s: Error requesting GPIO %d\n", __func__,
+ top_spk_pamp_gpio);
+ return;
+ }
+ ret = pm8xxx_gpio_config(top_spk_pamp_gpio, ¶m);
+ if (ret)
+ pr_err("%s: Failed to configure Top Spk Ampl"
+ " gpio %u\n", __func__, top_spk_pamp_gpio);
+ else {
+ pr_debug("%s: enable Top spkr amp gpio\n", __func__);
+ gpio_direction_output(top_spk_pamp_gpio, 1);
+ }
+ } else {
+ pr_err("%s: ERROR : Invalid External Speaker Ampl GPIO."
+ " gpio = %u\n", __func__, spk_amp_gpio);
+ return;
+ }
+}
+
+static void msm_ext_spk_power_amp_on(u32 spk)
+{
+ if (spk & (BOTTOM_SPK_AMP_POS | BOTTOM_SPK_AMP_NEG)) {
+
+ if ((msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_POS) &&
+ (msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_NEG)) {
+
+ pr_debug("%s() External Bottom Speaker Ampl already "
+ "turned on. spk = 0x%08x\n", __func__, spk);
+ return;
+ }
+
+ msm_ext_bottom_spk_pamp |= spk;
+
+ if ((msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_POS) &&
+ (msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_NEG)) {
+
+ msm_enable_ext_spk_amp_gpio(bottom_spk_pamp_gpio);
+ pr_debug("%s: slepping 4 ms after turning on external "
+ " Bottom Speaker Ampl\n", __func__);
+ usleep_range(4000, 4000);
+ }
+
+ } else if (spk & (TOP_SPK_AMP_POS | TOP_SPK_AMP_NEG)) {
+
+ if ((msm_ext_top_spk_pamp & TOP_SPK_AMP_POS) &&
+ (msm_ext_top_spk_pamp & TOP_SPK_AMP_NEG)) {
+
+ pr_debug("%s() External Top Speaker Ampl already"
+ "turned on. spk = 0x%08x\n", __func__, spk);
+ return;
+ }
+
+ msm_ext_top_spk_pamp |= spk;
+
+ if ((msm_ext_top_spk_pamp & TOP_SPK_AMP_POS) &&
+ (msm_ext_top_spk_pamp & TOP_SPK_AMP_NEG)) {
+
+ msm_enable_ext_spk_amp_gpio(top_spk_pamp_gpio);
+ pr_debug("%s: sleeping 4 ms after turning on "
+ " external Top Speaker Ampl\n", __func__);
+ usleep_range(4000, 4000);
+ }
+ } else {
+
+ pr_err("%s: ERROR : Invalid External Speaker Ampl. spk = 0x%08x\n",
+ __func__, spk);
+ return;
+ }
+}
+
+static void msm_ext_spk_power_amp_off(u32 spk)
+{
+ if (spk & (BOTTOM_SPK_AMP_POS | BOTTOM_SPK_AMP_NEG)) {
+
+ if (!msm_ext_bottom_spk_pamp)
+ return;
+
+ gpio_direction_output(bottom_spk_pamp_gpio, 0);
+ gpio_free(bottom_spk_pamp_gpio);
+ msm_ext_bottom_spk_pamp = 0;
+
+ pr_debug("%s: sleeping 4 ms after turning off external Bottom"
+ " Speaker Ampl\n", __func__);
+
+ usleep_range(4000, 4000);
+
+ } else if (spk & (TOP_SPK_AMP_POS | TOP_SPK_AMP_NEG)) {
+
+ if (!msm_ext_top_spk_pamp)
+ return;
+
+ gpio_direction_output(top_spk_pamp_gpio, 0);
+ gpio_free(top_spk_pamp_gpio);
+ msm_ext_top_spk_pamp = 0;
+
+ pr_debug("%s: sleeping 4 ms after turning off external Top"
+ " Spkaker Ampl\n", __func__);
+
+ usleep_range(4000, 4000);
+ } else {
+
+ pr_err("%s: ERROR : Invalid Ext Spk Ampl. spk = 0x%08x\n",
+ __func__, spk);
+ return;
+ }
+}
+
+static void msm_ext_control(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ pr_debug("%s: msm_spk_control = %d", __func__, msm_spk_control);
+ if (msm_spk_control == MSM8974_SPK_ON) {
+ snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Pos");
+ snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Neg");
+ snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Pos");
+ snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Neg");
+ } else {
+ snd_soc_dapm_disable_pin(dapm, "Ext Spk Bottom Pos");
+ snd_soc_dapm_disable_pin(dapm, "Ext Spk Bottom Neg");
+ snd_soc_dapm_disable_pin(dapm, "Ext Spk Top Pos");
+ snd_soc_dapm_disable_pin(dapm, "Ext Spk Top Neg");
+ }
+
+ snd_soc_dapm_sync(dapm);
+}
+
+static int msm_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_spk_control = %d", __func__, msm_spk_control);
+ ucontrol->value.integer.value[0] = msm_spk_control;
+ return 0;
+}
+static int msm_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ pr_debug("%s()\n", __func__);
+ if (msm_spk_control == ucontrol->value.integer.value[0])
+ return 0;
+
+ msm_spk_control = ucontrol->value.integer.value[0];
+ msm_ext_control(codec);
+ return 1;
+}
+static int msm_spkramp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ pr_debug("%s() %x\n", __func__, SND_SOC_DAPM_EVENT_ON(event));
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ if (!strncmp(w->name, "Ext Spk Bottom Pos", 18))
+ msm_ext_spk_power_amp_on(BOTTOM_SPK_AMP_POS);
+ else if (!strncmp(w->name, "Ext Spk Bottom Neg", 18))
+ msm_ext_spk_power_amp_on(BOTTOM_SPK_AMP_NEG);
+ else if (!strncmp(w->name, "Ext Spk Top Pos", 15))
+ msm_ext_spk_power_amp_on(TOP_SPK_AMP_POS);
+ else if (!strncmp(w->name, "Ext Spk Top Neg", 15))
+ msm_ext_spk_power_amp_on(TOP_SPK_AMP_NEG);
+ else {
+ pr_err("%s() Invalid Speaker Widget = %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+
+ } else {
+ if (!strncmp(w->name, "Ext Spk Bottom Pos", 18))
+ msm_ext_spk_power_amp_off(BOTTOM_SPK_AMP_POS);
+ else if (!strncmp(w->name, "Ext Spk Bottom Neg", 18))
+ msm_ext_spk_power_amp_off(BOTTOM_SPK_AMP_NEG);
+ else if (!strncmp(w->name, "Ext Spk Top Pos", 15))
+ msm_ext_spk_power_amp_off(TOP_SPK_AMP_POS);
+ else if (!strncmp(w->name, "Ext Spk Top Neg", 15))
+ msm_ext_spk_power_amp_off(TOP_SPK_AMP_NEG);
+ else {
+ pr_err("%s() Invalid Speaker Widget = %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int msm_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+ bool dapm)
+{
+ return 0;
+}
+
+static int msm_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget msm_dapm_widgets[] = {
+
+ SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0,
+ msm_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SPK("Ext Spk Bottom Pos", msm_spkramp_event),
+ SND_SOC_DAPM_SPK("Ext Spk Bottom Neg", msm_spkramp_event),
+
+ SND_SOC_DAPM_SPK("Ext Spk Top Pos", msm_spkramp_event),
+ SND_SOC_DAPM_SPK("Ext Spk Top Neg", msm_spkramp_event),
+
+ SND_SOC_DAPM_MIC("Handset Mic", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic1", NULL),
+ SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL),
+
+ SND_SOC_DAPM_MIC("Digital Mic1", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic2", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic3", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic4", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic5", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic6", NULL),
+
+};
+
+static const struct snd_soc_dapm_route common_audio_map[] = {
+
+ {"RX_BIAS", NULL, "MCLK"},
+ {"LDO_H", NULL, "MCLK"},
+
+ /* Speaker path */
+ {"Ext Spk Bottom Pos", NULL, "LINEOUT1"},
+ {"Ext Spk Bottom Neg", NULL, "LINEOUT3"},
+
+ {"Ext Spk Top Pos", NULL, "LINEOUT2"},
+ {"Ext Spk Top Neg", NULL, "LINEOUT4"},
+
+ /* Microphone path */
+ {"AMIC1", NULL, "MIC BIAS1 Internal1"},
+ {"MIC BIAS1 Internal1", NULL, "Handset Mic"},
+
+ {"AMIC2", NULL, "MIC BIAS2 External"},
+ {"MIC BIAS2 External", NULL, "Headset Mic"},
+
+ /**
+ * AMIC3 and AMIC4 inputs are connected to ANC microphones
+ * These mics are biased differently on CDP and FLUID
+ * routing entries below are based on bias arrangement
+ * on FLUID.
+ */
+ {"AMIC3", NULL, "MIC BIAS3 Internal1"},
+ {"MIC BIAS3 Internal1", NULL, "ANCRight Headset Mic"},
+
+ {"AMIC4", NULL, "MIC BIAS1 Internal2"},
+ {"MIC BIAS1 Internal2", NULL, "ANCLeft Headset Mic"},
+
+ {"HEADPHONE", NULL, "LDO_H"},
+
+ /**
+ * The digital Mic routes are setup considering
+ * fluid as default device.
+ */
+
+ /**
+ * Digital Mic1. Front Bottom left Digital Mic on Fluid and MTP.
+ * Digital Mic GM5 on CDP mainboard.
+ * Conncted to DMIC2 Input on Tabla codec.
+ */
+ {"DMIC2", NULL, "MIC BIAS1 External"},
+ {"MIC BIAS1 External", NULL, "Digital Mic1"},
+
+ /**
+ * Digital Mic2. Front Bottom right Digital Mic on Fluid and MTP.
+ * Digital Mic GM6 on CDP mainboard.
+ * Conncted to DMIC1 Input on Tabla codec.
+ */
+ {"DMIC1", NULL, "MIC BIAS1 External"},
+ {"MIC BIAS1 External", NULL, "Digital Mic2"},
+
+ /**
+ * Digital Mic3. Back Bottom Digital Mic on Fluid.
+ * Digital Mic GM1 on CDP mainboard.
+ * Conncted to DMIC4 Input on Tabla codec.
+ */
+ {"DMIC4", NULL, "MIC BIAS3 External"},
+ {"MIC BIAS3 External", NULL, "Digital Mic3"},
+
+ /**
+ * Digital Mic4. Back top Digital Mic on Fluid.
+ * Digital Mic GM2 on CDP mainboard.
+ * Conncted to DMIC3 Input on Tabla codec.
+ */
+ {"DMIC3", NULL, "MIC BIAS3 External"},
+ {"MIC BIAS3 External", NULL, "Digital Mic4"},
+
+ /**
+ * Digital Mic5. Front top Digital Mic on Fluid.
+ * Digital Mic GM3 on CDP mainboard.
+ * Conncted to DMIC5 Input on Tabla codec.
+ */
+ {"DMIC5", NULL, "MIC BIAS4 External"},
+ {"MIC BIAS4 External", NULL, "Digital Mic5"},
+
+ /* Tabla digital Mic6 - back bottom digital Mic on Liquid and
+ * bottom mic on CDP. FLUID/MTP do not have dmic6 installed.
+ */
+ {"DMIC6", NULL, "MIC BIAS4 External"},
+ {"MIC BIAS4 External", NULL, "Digital Mic6"},
+};
+
+static const char *spk_function[] = {"Off", "On"};
+static const char *slim0_rx_ch_text[] = {"One", "Two"};
+static const char *slim0_tx_ch_text[] = {"One", "Two", "Three", "Four"};
+
+static const struct soc_enum msm_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+ SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
+ SOC_ENUM_SINGLE_EXT(4, slim0_tx_ch_text),
+};
+
+static const char *btsco_rate_text[] = {"8000", "16000"};
+static const struct soc_enum msm_btsco_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, btsco_rate_text),
+};
+
+static int msm_slim_0_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_slim_0_rx_ch = %d\n", __func__,
+ msm_slim_0_rx_ch);
+ ucontrol->value.integer.value[0] = msm_slim_0_rx_ch - 1;
+ return 0;
+}
+
+static int msm_slim_0_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_slim_0_rx_ch = ucontrol->value.integer.value[0] + 1;
+
+ pr_debug("%s: msm_slim_0_rx_ch = %d\n", __func__,
+ msm_slim_0_rx_ch);
+ return 1;
+}
+
+static int msm_slim_0_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_slim_0_tx_ch = %d\n", __func__,
+ msm_slim_0_tx_ch);
+ ucontrol->value.integer.value[0] = msm_slim_0_tx_ch - 1;
+ return 0;
+}
+
+static int msm_slim_0_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_slim_0_tx_ch = ucontrol->value.integer.value[0] + 1;
+
+ pr_debug("%s: msm_slim_0_tx_ch = %d\n", __func__,
+ msm_slim_0_tx_ch);
+ return 1;
+}
+
+static int msm_btsco_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_btsco_rate = %d", __func__,
+ msm_btsco_rate);
+ ucontrol->value.integer.value[0] = msm_btsco_rate;
+ return 0;
+}
+
+static int msm_btsco_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_btsco_rate = BTSCO_RATE_8KHZ;
+ break;
+ case 1:
+ msm_btsco_rate = BTSCO_RATE_16KHZ;
+ break;
+ default:
+ msm_btsco_rate = BTSCO_RATE_8KHZ;
+ break;
+ }
+ pr_debug("%s: msm_btsco_rate = %d\n", __func__,
+ msm_btsco_rate);
+ return 0;
+}
+
+static const struct snd_kcontrol_new tabla_msm_controls[] = {
+ SOC_ENUM_EXT("Speaker Function", msm_enum[0], msm_get_spk,
+ msm_set_spk),
+ SOC_ENUM_EXT("SLIM_0_RX Channels", msm_enum[1],
+ msm_slim_0_rx_ch_get, msm_slim_0_rx_ch_put),
+ SOC_ENUM_EXT("SLIM_0_TX Channels", msm_enum[2],
+ msm_slim_0_tx_ch_get, msm_slim_0_tx_ch_put),
+};
+
+static const struct snd_kcontrol_new int_btsco_rate_mixer_controls[] = {
+ SOC_ENUM_EXT("Internal BTSCO SampleRate", msm_btsco_enum[0],
+ msm_btsco_rate_get, msm_btsco_rate_put),
+};
+
+static struct snd_soc_dsp_link lpa_fe_media = {
+ .playback = true,
+ .trigger = {
+ SND_SOC_DSP_TRIGGER_POST,
+ SND_SOC_DSP_TRIGGER_POST
+ },
+};
+static struct snd_soc_dsp_link fe_media = {
+ .playback = true,
+ .capture = true,
+ .trigger = {
+ SND_SOC_DSP_TRIGGER_POST,
+ SND_SOC_DSP_TRIGGER_POST
+ },
+};
+static int msm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ /* PCM only supports mono output with 8khz sample rate */
+ rate->min = rate->max = 8000;
+ channels->min = channels->max = 1;
+
+ return 0;
+}
+static int msm_aux_pcm_get_gpios(void)
+{
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+
+ ret = gpio_request(GPIO_AUX_PCM_DOUT, "AUX PCM DOUT");
+ if (ret < 0) {
+ pr_err("%s: Failed to request gpio(%d): AUX PCM DOUT",
+ __func__, GPIO_AUX_PCM_DOUT);
+ goto fail_dout;
+ }
+
+ ret = gpio_request(GPIO_AUX_PCM_DIN, "AUX PCM DIN");
+ if (ret < 0) {
+ pr_err("%s: Failed to request gpio(%d): AUX PCM DIN",
+ __func__, GPIO_AUX_PCM_DIN);
+ goto fail_din;
+ }
+
+ ret = gpio_request(GPIO_AUX_PCM_SYNC, "AUX PCM SYNC");
+ if (ret < 0) {
+ pr_err("%s: Failed to request gpio(%d): AUX PCM SYNC",
+ __func__, GPIO_AUX_PCM_SYNC);
+ goto fail_sync;
+ }
+ ret = gpio_request(GPIO_AUX_PCM_CLK, "AUX PCM CLK");
+ if (ret < 0) {
+ pr_err("%s: Failed to request gpio(%d): AUX PCM CLK",
+ __func__, GPIO_AUX_PCM_CLK);
+ goto fail_clk;
+ }
+
+ return 0;
+
+fail_clk:
+ gpio_free(GPIO_AUX_PCM_SYNC);
+fail_sync:
+ gpio_free(GPIO_AUX_PCM_DIN);
+fail_din:
+ gpio_free(GPIO_AUX_PCM_DOUT);
+fail_dout:
+
+ return ret;
+}
+static int msm_aux_pcm_free_gpios(void)
+{
+ gpio_free(GPIO_AUX_PCM_DIN);
+ gpio_free(GPIO_AUX_PCM_DOUT);
+ gpio_free(GPIO_AUX_PCM_SYNC);
+ gpio_free(GPIO_AUX_PCM_CLK);
+
+ return 0;
+}
+
+static int msm_auxpcm_startup(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ pr_debug("%s(): substream = %s\n", __func__, substream->name);
+ ret = msm_aux_pcm_get_gpios();
+ if (ret < 0) {
+ pr_err("%s: Aux PCM GPIO request failed\n", __func__);
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static void msm_auxpcm_shutdown(struct snd_pcm_substream *substream)
+{
+
+ pr_debug("%s(): substream = %s\n", __func__, substream->name);
+ msm_aux_pcm_free_gpios();
+}
+static struct snd_soc_ops msm_auxpcm_be_ops = {
+ .startup = msm_auxpcm_startup,
+ .shutdown = msm_auxpcm_shutdown,
+};
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link msm_dai[] = {
+ /* FrontEnd DAI Links */
+ {
+ .name = "MSM8974 Media1",
+ .stream_name = "MultiMedia1",
+ .cpu_dai_name = "MultiMedia1",
+ .platform_name = "msm-pcm-dsp",
+ .dynamic = 1,
+ .dsp_link = &fe_media,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
+ },
+ {
+ .name = "MSM8974 LPA",
+ .stream_name = "LPA",
+ .cpu_dai_name = "MultiMedia3",
+ .platform_name = "msm-pcm-lpa",
+ .dynamic = 1,
+ .dsp_link = &lpa_fe_media,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
+ },
+
+ /* AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_AUXPCM_RX,
+ .stream_name = "AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6.4106",
+ .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_AUXPCM_RX,
+ .be_hw_params_fixup = msm_auxpcm_be_params_fixup,
+ .ops = &msm_auxpcm_be_ops,
+ },
+ {
+ .name = LPASS_BE_AUXPCM_TX,
+ .stream_name = "AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6.4107",
+ .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_AUXPCM_TX,
+ .be_hw_params_fixup = msm_auxpcm_be_params_fixup,
+ },
+
+};
+
+struct snd_soc_card snd_soc_card_msm = {
+ .name = "msm8974-taiko-snd-card",
+ .dai_link = msm_dai,
+ .num_links = ARRAY_SIZE(msm_dai),
+};
+
+static struct platform_device *msm_snd_device;
+
+static void msm_free_headset_mic_gpios(void)
+{
+ if (msm_headset_gpios_configured) {
+ gpio_free(PM8921_GPIO_PM_TO_SYS(23));
+ gpio_free(PM8921_GPIO_PM_TO_SYS(35));
+ }
+}
+
+static int __init msm_audio_init(void)
+{
+ int ret = 0;
+ if (!machine_is_copper_sim()) {
+ pr_err("%s: Not the right machine type\n", __func__);
+ return -ENODEV;
+ }
+ msm_snd_device = platform_device_alloc("soc-audio", 0);
+ if (!msm_snd_device) {
+ pr_err("Platform device allocation failed\n");
+ kfree(mbhc_cfg.calibration);
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(msm_snd_device, &snd_soc_card_msm);
+ ret = platform_device_add(msm_snd_device);
+ if (ret) {
+ platform_device_put(msm_snd_device);
+ kfree(mbhc_cfg.calibration);
+ return ret;
+ }
+ return ret;
+
+}
+module_init(msm_audio_init);
+
+static void __exit msm_audio_exit(void)
+{
+ if (!machine_is_copper_sim()) {
+ pr_err("%s: Not the right machine type\n", __func__);
+ return ;
+ }
+ msm_free_headset_mic_gpios();
+ platform_device_unregister(msm_snd_device);
+ kfree(mbhc_cfg.calibration);
+}
+module_exit(msm_audio_exit);
+
+MODULE_DESCRIPTION("ALSA SoC msm");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6/q6adm.c b/sound/soc/msm/qdsp6/q6adm.c
index 2ada94f..bf6f743 100644
--- a/sound/soc/msm/qdsp6/q6adm.c
+++ b/sound/soc/msm/qdsp6/q6adm.c
@@ -45,6 +45,189 @@
static struct adm_ctl this_adm;
+int srs_trumedia_open(int port_id, int srs_tech_id, void *srs_params)
+{
+ struct asm_pp_params_command *open = NULL;
+ int ret = 0, sz = 0;
+ int index;
+
+ pr_debug("SRS - %s", __func__);
+ switch (srs_tech_id) {
+ case SRS_ID_GLOBAL: {
+ struct srs_trumedia_params_GLOBAL *glb_params = NULL;
+ sz = sizeof(struct asm_pp_params_command) +
+ sizeof(struct srs_trumedia_params_GLOBAL);
+ open = kzalloc(sz, GFP_KERNEL);
+ open->payload_size = sizeof(struct srs_trumedia_params_GLOBAL) +
+ sizeof(struct asm_pp_param_data_hdr);
+ open->params.param_id = SRS_TRUMEDIA_PARAMS;
+ open->params.param_size =
+ sizeof(struct srs_trumedia_params_GLOBAL);
+ glb_params = (struct srs_trumedia_params_GLOBAL *)((u8 *)open +
+ sizeof(struct asm_pp_params_command));
+ memcpy(glb_params, srs_params,
+ sizeof(struct srs_trumedia_params_GLOBAL));
+ pr_debug("SRS - %s: Global params - 1 = %x, 2 = %x, 3 = %x,"
+ " 4 = %x, 5 = %x, 6 = %x, 7 = %x, 8 = %x\n",
+ __func__, (int)glb_params->v1,
+ (int)glb_params->v2, (int)glb_params->v3,
+ (int)glb_params->v4, (int)glb_params->v5,
+ (int)glb_params->v6, (int)glb_params->v7,
+ (int)glb_params->v8);
+ break;
+ }
+ case SRS_ID_WOWHD: {
+ struct srs_trumedia_params_WOWHD *whd_params = NULL;
+ sz = sizeof(struct asm_pp_params_command) +
+ sizeof(struct srs_trumedia_params_WOWHD);
+ open = kzalloc(sz, GFP_KERNEL);
+ open->payload_size = sizeof(struct srs_trumedia_params_WOWHD) +
+ sizeof(struct asm_pp_param_data_hdr);
+ open->params.param_id = SRS_TRUMEDIA_PARAMS_WOWHD;
+ open->params.param_size =
+ sizeof(struct srs_trumedia_params_WOWHD);
+ whd_params = (struct srs_trumedia_params_WOWHD *)((u8 *)open +
+ sizeof(struct asm_pp_params_command));
+ memcpy(whd_params, srs_params,
+ sizeof(struct srs_trumedia_params_WOWHD));
+ pr_debug("SRS - %s: WOWHD params - 1 = %x, 2 = %x, 3 = %x,"
+ " 4 = %x, 5 = %x, 6 = %x, 7 = %x, 8 = %x, 9 = %x,"
+ " 10 = %x, 11 = %x\n", __func__, (int)whd_params->v1,
+ (int)whd_params->v2, (int)whd_params->v3,
+ (int)whd_params->v4, (int)whd_params->v5,
+ (int)whd_params->v6, (int)whd_params->v7,
+ (int)whd_params->v8, (int)whd_params->v9,
+ (int)whd_params->v10, (int)whd_params->v11);
+ break;
+ }
+ case SRS_ID_CSHP: {
+ struct srs_trumedia_params_CSHP *chp_params = NULL;
+ sz = sizeof(struct asm_pp_params_command) +
+ sizeof(struct srs_trumedia_params_CSHP);
+ open = kzalloc(sz, GFP_KERNEL);
+ open->payload_size = sizeof(struct srs_trumedia_params_CSHP) +
+ sizeof(struct asm_pp_param_data_hdr);
+ open->params.param_id = SRS_TRUMEDIA_PARAMS_CSHP;
+ open->params.param_size =
+ sizeof(struct srs_trumedia_params_CSHP);
+ chp_params = (struct srs_trumedia_params_CSHP *)((u8 *)open +
+ sizeof(struct asm_pp_params_command));
+ memcpy(chp_params, srs_params,
+ sizeof(struct srs_trumedia_params_CSHP));
+ pr_debug("SRS - %s: CSHP params - 1 = %x, 2 = %x, 3 = %x,"
+ " 4 = %x, 5 = %x, 6 = %x, 7 = %x, 8 = %x,"
+ " 9 = %x\n", __func__, (int)chp_params->v1,
+ (int)chp_params->v2, (int)chp_params->v3,
+ (int)chp_params->v4, (int)chp_params->v5,
+ (int)chp_params->v6, (int)chp_params->v7,
+ (int)chp_params->v8, (int)chp_params->v9);
+ break;
+ }
+ case SRS_ID_HPF: {
+ struct srs_trumedia_params_HPF *hpf_params = NULL;
+ sz = sizeof(struct asm_pp_params_command) +
+ sizeof(struct srs_trumedia_params_HPF);
+ open = kzalloc(sz, GFP_KERNEL);
+ open->payload_size = sizeof(struct srs_trumedia_params_HPF) +
+ sizeof(struct asm_pp_param_data_hdr);
+ open->params.param_id = SRS_TRUMEDIA_PARAMS_HPF;
+ open->params.param_size =
+ sizeof(struct srs_trumedia_params_HPF);
+ hpf_params = (struct srs_trumedia_params_HPF *)((u8 *)open +
+ sizeof(struct asm_pp_params_command));
+ memcpy(hpf_params, srs_params,
+ sizeof(struct srs_trumedia_params_HPF));
+ pr_debug("SRS - %s: HPF params - 1 = %x\n", __func__,
+ (int)hpf_params->v1);
+ break;
+ }
+ case SRS_ID_PEQ: {
+ struct srs_trumedia_params_PEQ *peq_params = NULL;
+ sz = sizeof(struct asm_pp_params_command) +
+ sizeof(struct srs_trumedia_params_PEQ);
+ open = kzalloc(sz, GFP_KERNEL);
+ open->payload_size = sizeof(struct srs_trumedia_params_PEQ) +
+ sizeof(struct asm_pp_param_data_hdr);
+ open->params.param_id = SRS_TRUMEDIA_PARAMS_PEQ;
+ open->params.param_size =
+ sizeof(struct srs_trumedia_params_PEQ);
+ peq_params = (struct srs_trumedia_params_PEQ *)((u8 *)open +
+ sizeof(struct asm_pp_params_command));
+ memcpy(peq_params, srs_params,
+ sizeof(struct srs_trumedia_params_PEQ));
+ pr_debug("SRS - %s: PEQ params - 1 = %x 2 = %x, 3 = %x,"
+ " 4 = %x\n", __func__, (int)peq_params->v1,
+ (int)peq_params->v2, (int)peq_params->v3,
+ (int)peq_params->v4);
+ break;
+ }
+ case SRS_ID_HL: {
+ struct srs_trumedia_params_HL *hl_params = NULL;
+ sz = sizeof(struct asm_pp_params_command) +
+ sizeof(struct srs_trumedia_params_HL);
+ open = kzalloc(sz, GFP_KERNEL);
+ open->payload_size = sizeof(struct srs_trumedia_params_HL) +
+ sizeof(struct asm_pp_param_data_hdr);
+ open->params.param_id = SRS_TRUMEDIA_PARAMS_HL;
+ open->params.param_size = sizeof(struct srs_trumedia_params_HL);
+ hl_params = (struct srs_trumedia_params_HL *)((u8 *)open +
+ sizeof(struct asm_pp_params_command));
+ memcpy(hl_params, srs_params,
+ sizeof(struct srs_trumedia_params_HL));
+ pr_debug("SRS - %s: HL params - 1 = %x, 2 = %x, 3 = %x, 4 = %x,"
+ " 5 = %x, 6 = %x, 7 = %x\n", __func__,
+ (int)hl_params->v1, (int)hl_params->v2,
+ (int)hl_params->v3, (int)hl_params->v4,
+ (int)hl_params->v5, (int)hl_params->v6,
+ (int)hl_params->v7);
+ break;
+ }
+ default:
+ goto fail_cmd;
+ }
+
+ open->payload = NULL;
+ open->params.module_id = SRS_TRUMEDIA_MODULE_ID;
+ open->params.reserved = 0;
+ open->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ open->hdr.pkt_size = sz;
+ open->hdr.src_svc = APR_SVC_ADM;
+ open->hdr.src_domain = APR_DOMAIN_APPS;
+ open->hdr.src_port = port_id;
+ open->hdr.dest_svc = APR_SVC_ADM;
+ open->hdr.dest_domain = APR_DOMAIN_ADSP;
+ index = afe_get_port_index(port_id);
+ open->hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
+ open->hdr.token = port_id;
+ open->hdr.opcode = ADM_CMD_SET_PARAMS;
+ pr_debug("SRS - %s: Command was sent now check Q6 - port id = %d,"
+ " size %d, module id %x, param id %x.\n", __func__,
+ open->hdr.dest_port, open->payload_size,
+ open->params.module_id, open->params.param_id);
+
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)open);
+ if (ret < 0) {
+ pr_err("SRS - %s: ADM enable for port %d failed\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ /* Wait for the callback with copp id */
+ ret = wait_event_timeout(this_adm.wait, 1,
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("SRS - %s: ADM open failed for port %d\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+fail_cmd:
+ kfree(open);
+ return ret;
+}
+
static int32_t adm_callback(struct apr_client_data *data, void *priv)
{
uint32_t *payload;
diff --git a/sound/soc/msm/qdsp6/q6afe.c b/sound/soc/msm/qdsp6/q6afe.c
index 9f058b9..ce81cca 100644
--- a/sound/soc/msm/qdsp6/q6afe.c
+++ b/sound/soc/msm/qdsp6/q6afe.c
@@ -147,6 +147,7 @@
case HDMI_RX:
case SLIMBUS_0_RX:
case SLIMBUS_1_RX:
+ case SLIMBUS_2_RX:
case SLIMBUS_3_RX:
case INT_BT_SCO_RX:
case INT_BT_A2DP_RX:
@@ -166,6 +167,7 @@
case SLIMBUS_0_TX:
case SLIMBUS_1_TX:
case SLIMBUS_2_TX:
+ case SLIMBUS_3_TX:
case INT_FM_TX:
case VOICE_RECORD_RX:
case INT_BT_SCO_TX:
@@ -206,8 +208,10 @@
case SLIMBUS_0_TX:
case SLIMBUS_1_RX:
case SLIMBUS_1_TX:
+ case SLIMBUS_2_RX:
case SLIMBUS_2_TX:
case SLIMBUS_3_RX:
+ case SLIMBUS_3_TX:
case INT_BT_SCO_RX:
case INT_BT_SCO_TX:
case INT_BT_A2DP_RX:
@@ -272,8 +276,10 @@
case SLIMBUS_0_TX: return IDX_SLIMBUS_0_TX;
case SLIMBUS_1_RX: return IDX_SLIMBUS_1_RX;
case SLIMBUS_1_TX: return IDX_SLIMBUS_1_TX;
+ case SLIMBUS_2_RX: return IDX_SLIMBUS_2_RX;
case SLIMBUS_2_TX: return IDX_SLIMBUS_2_TX;
case SLIMBUS_3_RX: return IDX_SLIMBUS_3_RX;
+ case SLIMBUS_3_TX: return IDX_SLIMBUS_3_TX;
case INT_BT_SCO_RX: return IDX_INT_BT_SCO_RX;
case INT_BT_SCO_TX: return IDX_INT_BT_SCO_TX;
case INT_BT_A2DP_RX: return IDX_INT_BT_A2DP_RX;
@@ -307,8 +313,10 @@
case SLIMBUS_0_TX:
case SLIMBUS_1_RX:
case SLIMBUS_1_TX:
+ case SLIMBUS_2_RX:
case SLIMBUS_2_TX:
case SLIMBUS_3_RX:
+ case SLIMBUS_3_TX:
case SLIMBUS_4_RX:
case SLIMBUS_4_TX:
ret_size = SIZEOF_CFG_CMD(afe_port_slimbus_sch_cfg);
diff --git a/sound/soc/msm/qdsp6v2/Makefile b/sound/soc/msm/qdsp6v2/Makefile
index 434197a..6f765d1 100644
--- a/sound/soc/msm/qdsp6v2/Makefile
+++ b/sound/soc/msm/qdsp6v2/Makefile
@@ -1 +1,4 @@
-obj-y := q6adm.o q6afe.o q6asm.o q6audio-v2.o
+snd-soc-qdsp6v2-objs += msm-dai-q6-v2.o msm-pcm-q6-v2.o msm-pcm-routing-v2.o msm-compr-q6-v2.o msm-multi-ch-pcm-q6-v2.o
+snd-soc-qdsp6v2-objs += msm-pcm-lpa-v2.o msm-pcm-afe-v2.o
+obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o
+obj-y += q6adm.o q6afe.o q6asm.o q6audio-v2.o
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
new file mode 100644
index 0000000..daba79d
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
@@ -0,0 +1,666 @@
+/* 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/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/android_pmem.h>
+
+#include "msm-compr-q6-v2.h"
+#include "msm-pcm-routing-v2.h"
+
+struct snd_msm {
+ struct msm_audio *prtd;
+ unsigned volume;
+};
+static struct snd_msm compressed_audio = {NULL, 0x2000} ;
+
+static struct audio_locks the_locks;
+
+static struct snd_pcm_hardware msm_compr_hardware_playback = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 1200 * 1024 * 2,
+ .period_bytes_min = 4800,
+ .period_bytes_max = 1200 * 1024,
+ .periods_min = 2,
+ .periods_max = 512,
+ .fifo_size = 0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static void compr_event_handler(uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv)
+{
+ struct compr_audio *compr = priv;
+ struct msm_audio *prtd = &compr->prtd;
+ struct snd_pcm_substream *substream = prtd->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_aio_write_param param;
+ struct audio_buffer *buf = NULL;
+ int i = 0;
+
+ pr_debug("%s opcode =%08x\n", __func__, opcode);
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE_V2: {
+ uint32_t *ptrmem = (uint32_t *)¶m;
+ pr_debug("ASM_DATA_EVENT_WRITE_DONE\n");
+ pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
+ prtd->pcm_irq_pos += prtd->pcm_count;
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ atomic_inc(&prtd->out_count);
+ wake_up(&the_locks.write_wait);
+ if (!atomic_read(&prtd->start)) {
+ atomic_set(&prtd->pending_buffer, 1);
+ break;
+ } else
+ atomic_set(&prtd->pending_buffer, 0);
+
+ if (runtime->status->hw_ptr >= runtime->control->appl_ptr)
+ break;
+ buf = prtd->audio_client->port[IN].buf;
+ pr_debug("%s:writing %d bytes of buffer[%d] to dsp 2\n",
+ __func__, prtd->pcm_count, prtd->out_head);
+ pr_debug("%s:writing buffer[%d] from 0x%08x\n",
+ __func__, prtd->out_head,
+ ((unsigned int)buf[0].phys
+ + (prtd->out_head * prtd->pcm_count)));
+
+ param.paddr = (unsigned long)buf[0].phys
+ + (prtd->out_head * prtd->pcm_count);
+ param.len = prtd->pcm_count;
+ param.msw_ts = 0;
+ param.lsw_ts = 0;
+ param.flags = NO_TIMESTAMP;
+ param.uid = (unsigned long)buf[0].phys
+ + (prtd->out_head * prtd->pcm_count);
+ for (i = 0; i < sizeof(struct audio_aio_write_param)/4;
+ i++, ++ptrmem)
+ pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
+ if (q6asm_async_write(prtd->audio_client,
+ ¶m) < 0)
+ pr_err("%s:q6asm_async_write failed\n",
+ __func__);
+ else
+ prtd->out_head =
+ (prtd->out_head + 1) & (runtime->periods - 1);
+ break;
+ }
+ case ASM_DATA_EVENT_RENDERED_EOS:
+ pr_debug("ASM_DATA_CMDRSP_EOS\n");
+ prtd->cmd_ack = 1;
+ wake_up(&the_locks.eos_wait);
+ break;
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case ASM_SESSION_CMD_RUN_V2: {
+ if (!atomic_read(&prtd->pending_buffer))
+ break;
+ pr_debug("%s:writing %d bytes"
+ " of buffer[%d] to dsp\n",
+ __func__, prtd->pcm_count, prtd->out_head);
+ buf = prtd->audio_client->port[IN].buf;
+ pr_debug("%s:writing buffer[%d] from 0x%08x\n",
+ __func__, prtd->out_head,
+ ((unsigned int)buf[0].phys
+ + (prtd->out_head * prtd->pcm_count)));
+ param.paddr = (unsigned long)buf[prtd->out_head].phys;
+ param.len = prtd->pcm_count;
+ param.msw_ts = 0;
+ param.lsw_ts = 0;
+ param.flags = NO_TIMESTAMP;
+ param.uid = (unsigned long)buf[prtd->out_head].phys;
+ if (q6asm_async_write(prtd->audio_client,
+ ¶m) < 0)
+ pr_err("%s:q6asm_async_write failed\n",
+ __func__);
+ else
+ prtd->out_head =
+ (prtd->out_head + 1)
+ & (runtime->periods - 1);
+ atomic_set(&prtd->pending_buffer, 0);
+ }
+ break;
+ case ASM_STREAM_CMD_FLUSH:
+ pr_debug("ASM_STREAM_CMD_FLUSH\n");
+ prtd->cmd_ack = 1;
+ wake_up(&the_locks.eos_wait);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
+ break;
+ }
+}
+
+static int msm_compr_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct compr_audio *compr = runtime->private_data;
+ struct msm_audio *prtd = &compr->prtd;
+ struct asm_aac_cfg aac_cfg;
+ int ret;
+
+ pr_debug("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+ prtd->out_head = 0;
+ atomic_set(&prtd->out_count, runtime->periods);
+
+ if (prtd->enabled)
+ return 0;
+
+ switch (compr->info.codec_param.codec.id) {
+ case SND_AUDIOCODEC_MP3:
+ /* No media format block for mp3 */
+ break;
+ case SND_AUDIOCODEC_AAC:
+ pr_debug("SND_AUDIOCODEC_AAC\n");
+ memset(&aac_cfg, 0x0, sizeof(struct asm_aac_cfg));
+ aac_cfg.aot = AAC_ENC_MODE_EAAC_P;
+ aac_cfg.format = 0x03;
+ aac_cfg.ch_cfg = runtime->channels;
+ aac_cfg.sample_rate = runtime->rate;
+ ret = q6asm_media_format_block_aac(prtd->audio_client,
+ &aac_cfg);
+ if (ret < 0)
+ pr_err("%s: CMD Format block failed\n", __func__);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ prtd->enabled = 1;
+ prtd->cmd_ack = 0;
+
+ return 0;
+}
+
+static int msm_compr_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct compr_audio *compr = runtime->private_data;
+ struct msm_audio *prtd = &compr->prtd;
+
+ pr_debug("%s\n", __func__);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ prtd->pcm_irq_pos = 0;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("%s: Trigger start\n", __func__);
+ q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+ atomic_set(&prtd->start, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ atomic_set(&prtd->start, 0);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
+ q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ atomic_set(&prtd->start, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static void populate_codec_list(struct compr_audio *compr,
+ struct snd_pcm_runtime *runtime)
+{
+ pr_debug("%s\n", __func__);
+ /* MP3 Block */
+ compr->info.compr_cap.num_codecs = 1;
+ compr->info.compr_cap.min_fragment_size = runtime->hw.period_bytes_min;
+ compr->info.compr_cap.max_fragment_size = runtime->hw.period_bytes_max;
+ compr->info.compr_cap.min_fragments = runtime->hw.periods_min;
+ compr->info.compr_cap.max_fragments = runtime->hw.periods_max;
+ compr->info.compr_cap.codecs[0] = SND_AUDIOCODEC_MP3;
+ compr->info.compr_cap.codecs[1] = SND_AUDIOCODEC_AAC;
+ /* Add new codecs here */
+}
+
+static int msm_compr_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct compr_audio *compr;
+ struct msm_audio *prtd;
+ int ret = 0;
+ struct asm_softpause_params softpause = {
+ .enable = SOFT_PAUSE_ENABLE,
+ .period = SOFT_PAUSE_PERIOD,
+ .step = SOFT_PAUSE_STEP,
+ .rampingcurve = SOFT_PAUSE_CURVE_LINEAR,
+ };
+ struct asm_softvolume_params softvol = {
+ .period = SOFT_VOLUME_PERIOD,
+ .step = SOFT_VOLUME_STEP,
+ .rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
+ };
+
+ /* Capture path */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return -EINVAL;
+
+ pr_debug("%s\n", __func__);
+ compr = kzalloc(sizeof(struct compr_audio), GFP_KERNEL);
+ if (compr == NULL) {
+ pr_err("Failed to allocate memory for msm_audio\n");
+ return -ENOMEM;
+ }
+ prtd = &compr->prtd;
+ prtd->substream = substream;
+ prtd->audio_client = q6asm_audio_client_alloc(
+ (app_cb)compr_event_handler, compr);
+ if (!prtd->audio_client) {
+ pr_info("%s: Could not allocate memory\n", __func__);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ runtime->hw = msm_compr_hardware_playback;
+
+ pr_info("%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);
+
+ prtd->cmd_ack = 1;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_info("snd_pcm_hw_constraint_list failed\n");
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ pr_info("snd_pcm_hw_constraint_integer failed\n");
+
+ prtd->dsp_cnt = 0;
+ atomic_set(&prtd->pending_buffer, 1);
+ compr->codec = FORMAT_MP3;
+ populate_codec_list(compr, runtime);
+ runtime->private_data = compr;
+ compressed_audio.prtd = &compr->prtd;
+ ret = compressed_set_volume(compressed_audio.volume);
+ if (ret < 0)
+ pr_err("%s : Set Volume failed : %d", __func__, ret);
+
+ ret = q6asm_set_softpause(compressed_audio.prtd->audio_client,
+ &softpause);
+ if (ret < 0)
+ pr_err("%s: Send SoftPause Param failed ret=%d\n",
+ __func__, ret);
+ ret = q6asm_set_softvolume(compressed_audio.prtd->audio_client,
+ &softvol);
+ if (ret < 0)
+ pr_err("%s: Send SoftVolume Param failed ret=%d\n",
+ __func__, ret);
+
+ return 0;
+}
+
+int compressed_set_volume(unsigned volume)
+{
+ int rc = 0;
+ if (compressed_audio.prtd && compressed_audio.prtd->audio_client) {
+ rc = q6asm_set_volume(compressed_audio.prtd->audio_client,
+ volume);
+ if (rc < 0) {
+ pr_err("%s: Send Volume command failed"
+ " rc=%d\n", __func__, rc);
+ }
+ }
+ compressed_audio.volume = volume;
+ return rc;
+}
+
+static int msm_compr_playback_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct compr_audio *compr = runtime->private_data;
+ struct msm_audio *prtd = &compr->prtd;
+ int dir = 0;
+
+ pr_debug("%s\n", __func__);
+
+ dir = IN;
+ atomic_set(&prtd->pending_buffer, 0);
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ compressed_audio.prtd = NULL;
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return 0;
+}
+
+static int msm_compr_close(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_compr_playback_close(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = EINVAL;
+ return ret;
+}
+static int msm_compr_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_compr_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = EINVAL;
+ return ret;
+}
+
+static snd_pcm_uframes_t msm_compr_pointer(struct snd_pcm_substream *substream)
+{
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct compr_audio *compr = runtime->private_data;
+ struct msm_audio *prtd = &compr->prtd;
+
+ if (prtd->pcm_irq_pos >= prtd->pcm_size)
+ prtd->pcm_irq_pos = 0;
+
+ pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
+ return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static int msm_compr_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ int result = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct compr_audio *compr = runtime->private_data;
+ struct msm_audio *prtd = &compr->prtd;
+
+ pr_debug("%s\n", __func__);
+ prtd->mmap_flag = 1;
+ if (runtime->dma_addr && runtime->dma_bytes) {
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ result = remap_pfn_range(vma, vma->vm_start,
+ runtime->dma_addr >> PAGE_SHIFT,
+ runtime->dma_bytes,
+ vma->vm_page_prot);
+ } else {
+ pr_err("Physical address or size of buf is NULL");
+ return -EINVAL;
+ }
+ return result;
+}
+
+static int msm_compr_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct compr_audio *compr = runtime->private_data;
+ struct msm_audio *prtd = &compr->prtd;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct audio_buffer *buf;
+ int dir, ret;
+
+ pr_debug("%s\n", __func__);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ return -EINVAL;
+
+ ret = q6asm_open_write(prtd->audio_client, compr->codec);
+ if (ret < 0) {
+ pr_err("%s: Session out open failed\n", __func__);
+ return -ENOMEM;
+ }
+ ret = q6asm_set_io_mode(prtd->audio_client, ASYNC_IO_MODE);
+ if (ret < 0) {
+ pr_err("%s: Set IO mode failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ ret = q6asm_audio_client_buf_alloc_contiguous(dir,
+ prtd->audio_client,
+ runtime->hw.period_bytes_min,
+ runtime->hw.periods_max);
+ if (ret < 0) {
+ pr_err("Audio Start: Buffer Allocation failed "
+ "rc = %d\n", ret);
+ return -ENOMEM;
+ }
+ buf = prtd->audio_client->port[dir].buf;
+
+ pr_debug("%s:buf = %p\n", __func__, buf);
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+ dma_buf->area = buf[0].data;
+ dma_buf->addr = buf[0].phys;
+ dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ if (!dma_buf->area)
+ return -ENOMEM;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ return 0;
+}
+
+static int msm_compr_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ int rc = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct compr_audio *compr = runtime->private_data;
+ struct msm_audio *prtd = &compr->prtd;
+ uint64_t timestamp;
+ uint64_t temp;
+
+ switch (cmd) {
+ case SNDRV_COMPRESS_TSTAMP: {
+ struct snd_compr_tstamp tstamp;
+ pr_debug("SNDRV_COMPRESS_TSTAMP\n");
+
+ memset(&tstamp, 0x0, sizeof(struct snd_compr_tstamp));
+ timestamp = q6asm_get_session_time(prtd->audio_client);
+ if (timestamp < 0) {
+ pr_err("%s: Get Session Time return value =%lld\n",
+ __func__, timestamp);
+ return -EAGAIN;
+ }
+ temp = (timestamp * 2 * runtime->channels);
+ temp = temp * (runtime->rate/1000);
+ temp = div_u64(temp, 1000);
+ tstamp.sampling_rate = runtime->rate;
+ tstamp.rendered = (size_t)(temp & 0xFFFFFFFF);
+ tstamp.decoded = (size_t)((temp >> 32) & 0xFFFFFFFF);
+ tstamp.timestamp = timestamp;
+ pr_debug("%s: bytes_consumed:lsb = %d, msb = %d,"
+ "timestamp = %lld,\n",
+ __func__, tstamp.rendered, tstamp.decoded,
+ tstamp.timestamp);
+ if (copy_to_user((void *) arg, &tstamp,
+ sizeof(struct snd_compr_tstamp)))
+ return -EFAULT;
+ return 0;
+ }
+ case SNDRV_COMPRESS_GET_CAPS:
+ pr_debug("SNDRV_COMPRESS_GET_CAPS\n");
+ if (copy_to_user((void *) arg, &compr->info.compr_cap,
+ sizeof(struct snd_compr_caps))) {
+ rc = -EFAULT;
+ pr_err("%s: ERROR: copy to user\n", __func__);
+ return rc;
+ }
+ return 0;
+ case SNDRV_COMPRESS_SET_PARAMS:
+ pr_debug("SNDRV_COMPRESS_SET_PARAMS: ");
+ if (copy_from_user(&compr->info.codec_param, (void *) arg,
+ sizeof(struct snd_compr_params))) {
+ rc = -EFAULT;
+ pr_err("%s: ERROR: copy from user\n", __func__);
+ return rc;
+ }
+ switch (compr->info.codec_param.codec.id) {
+ case SND_AUDIOCODEC_MP3:
+ /* For MP3 we dont need any other parameter */
+ pr_debug("SND_AUDIOCODEC_MP3\n");
+ compr->codec = FORMAT_MP3;
+ break;
+ case SND_AUDIOCODEC_AAC:
+ pr_debug("SND_AUDIOCODEC_AAC\n");
+ compr->codec = FORMAT_MPEG4_AAC;
+ break;
+ default:
+ pr_debug("FORMAT_LINEAR_PCM\n");
+ compr->codec = FORMAT_LINEAR_PCM;
+ break;
+ }
+ return 0;
+ case SNDRV_PCM_IOCTL1_RESET:
+ prtd->cmd_ack = 0;
+ rc = q6asm_cmd(prtd->audio_client, CMD_FLUSH);
+ if (rc < 0)
+ pr_err("%s: flush cmd failed rc=%d\n", __func__, rc);
+ rc = wait_event_timeout(the_locks.eos_wait,
+ prtd->cmd_ack, 5 * HZ);
+ if (rc < 0)
+ pr_err("Flush cmd timeout\n");
+ prtd->pcm_irq_pos = 0;
+ break;
+ default:
+ break;
+ }
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static struct snd_pcm_ops msm_compr_ops = {
+ .open = msm_compr_open,
+ .hw_params = msm_compr_hw_params,
+ .close = msm_compr_close,
+ .ioctl = msm_compr_ioctl,
+ .prepare = msm_compr_prepare,
+ .trigger = msm_compr_trigger,
+ .pointer = msm_compr_pointer,
+ .mmap = msm_compr_mmap,
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_compr_ops,
+ .pcm_new = msm_asoc_pcm_new,
+};
+
+static __devinit int msm_compr_probe(struct platform_device *pdev)
+{
+ pr_info("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_compr_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_compr_driver = {
+ .driver = {
+ .name = "msm-compr-dsp",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_compr_probe,
+ .remove = __devexit_p(msm_compr_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ init_waitqueue_head(&the_locks.enable_wait);
+ init_waitqueue_head(&the_locks.eos_wait);
+ init_waitqueue_head(&the_locks.write_wait);
+ init_waitqueue_head(&the_locks.read_wait);
+
+ return platform_driver_register(&msm_compr_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_compr_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h
new file mode 100644
index 0000000..2183690
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_COMPR_H
+#define _MSM_COMPR_H
+#include <sound/apr_audio-v2.h>
+#include <sound/q6asm-v2.h>
+#include <sound/snd_compress_params.h>
+#include <sound/compress_offload.h>
+#include <sound/compress_driver.h>
+
+#include "msm-pcm-q6-v2.h"
+
+struct compr_info {
+ struct snd_compr_caps compr_cap;
+ struct snd_compr_codec_caps codec_caps;
+ struct snd_compr_params codec_param;
+};
+
+struct compr_audio {
+ struct msm_audio prtd;
+ struct compr_info info;
+ uint32_t codec;
+};
+
+#endif /*_MSM_COMPR_H*/
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
new file mode 100644
index 0000000..1605062
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -0,0 +1,1229 @@
+/* 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/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/msm-dai-q6-v2.h>
+#include <sound/pcm_params.h>
+#include <mach/clk.h>
+
+enum {
+ STATUS_PORT_STARTED, /* track if AFE port has started */
+ STATUS_MAX
+};
+
+struct msm_dai_q6_dai_data {
+ DECLARE_BITMAP(status_mask, STATUS_MAX);
+ u32 rate;
+ u32 channels;
+ union afe_port_config port_config;
+};
+
+static struct clk *pcm_clk;
+static DEFINE_MUTEX(aux_pcm_mutex);
+static int aux_pcm_count;
+static struct msm_dai_auxpcm_pdata *auxpcm_plat_data;
+
+static u8 num_of_bits_set(u8 sd_line_mask)
+{
+ u8 num_bits_set = 0;
+
+ while (sd_line_mask) {
+ num_bits_set++;
+ sd_line_mask = sd_line_mask & (sd_line_mask - 1);
+ }
+ return num_bits_set;
+}
+
+static int msm_dai_q6_cdc_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai, int stream)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ dai_data->channels = params_channels(params);
+ switch (dai_data->channels) {
+ case 2:
+ dai_data->port_config.i2s.mono_stereo = MSM_AFE_STEREO;
+ break;
+ case 1:
+ dai_data->port_config.i2s.mono_stereo = MSM_AFE_MONO;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ dai_data->rate = params_rate(params);
+ dai_data->port_config.i2s.sample_rate = dai_data->rate;
+ dai_data->port_config.i2s.i2s_cfg_minor_version =
+ AFE_API_VERSION_I2S_CONFIG;
+ dai_data->port_config.i2s.data_format = AFE_LINEAR_PCM_DATA;
+ dev_dbg(dai->dev, " channel %d sample rate %d entered\n",
+ dai_data->channels, dai_data->rate);
+
+ /* Q6 only supports 16 as now */
+ dai_data->port_config.i2s.bit_width = 16;
+ dai_data->port_config.i2s.channel_mode = 1;
+ return 0;
+}
+
+static int msm_dai_q6_i2s_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai, int stream)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct msm_i2s_data *i2s_pdata =
+ (struct msm_i2s_data *) dai->dev->platform_data;
+
+ dai_data->channels = params_channels(params);
+ if (num_of_bits_set(i2s_pdata->sd_lines) == 1) {
+ switch (dai_data->channels) {
+ case 2:
+ dai_data->port_config.i2s.mono_stereo = MSM_AFE_STEREO;
+ break;
+ case 1:
+ dai_data->port_config.i2s.mono_stereo = MSM_AFE_MONO;
+ break;
+ default:
+ pr_warn("greater than stereo has not been validated");
+ break;
+ }
+ }
+ dai_data->rate = params_rate(params);
+ dai_data->port_config.i2s.sample_rate = dai_data->rate;
+ dai_data->port_config.i2s.i2s_cfg_minor_version =
+ AFE_API_VERSION_I2S_CONFIG;
+ dai_data->port_config.i2s.data_format = AFE_LINEAR_PCM_DATA;
+ /* Q6 only supports 16 as now */
+ dai_data->port_config.i2s.bit_width = 16;
+ dai_data->port_config.i2s.channel_mode = 1;
+
+ return 0;
+}
+
+static int msm_dai_q6_i2s_platform_data_validation(
+ struct snd_soc_dai *dai)
+{
+ u8 num_of_sd_lines;
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct msm_i2s_data *i2s_pdata =
+ (struct msm_i2s_data *)dai->dev->platform_data;
+ struct snd_soc_dai_driver *dai_driver =
+ (struct snd_soc_dai_driver *)dai->driver;
+
+ num_of_sd_lines = num_of_bits_set(i2s_pdata->sd_lines);
+
+ switch (num_of_sd_lines) {
+ case 1:
+ switch (i2s_pdata->sd_lines) {
+ case MSM_MI2S_SD0:
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_SD0;
+ break;
+ case MSM_MI2S_SD1:
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_SD1;
+ break;
+ case MSM_MI2S_SD2:
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_SD2;
+ break;
+ case MSM_MI2S_SD3:
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_SD3;
+ break;
+ default:
+ pr_err("%s: invalid SD line\n",
+ __func__);
+ goto error_invalid_data;
+ }
+ break;
+ case 2:
+ switch (i2s_pdata->sd_lines) {
+ case MSM_MI2S_SD0 | MSM_MI2S_SD1:
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_QUAD01;
+ break;
+ case MSM_MI2S_SD2 | MSM_MI2S_SD3:
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_QUAD23;
+ break;
+ default:
+ pr_err("%s: invalid SD line\n",
+ __func__);
+ goto error_invalid_data;
+ }
+ break;
+ case 3:
+ switch (i2s_pdata->sd_lines) {
+ case MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2:
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_6CHS;
+ break;
+ default:
+ pr_err("%s: invalid SD lines\n",
+ __func__);
+ goto error_invalid_data;
+ }
+ break;
+ case 4:
+ switch (i2s_pdata->sd_lines) {
+ case MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2 | MSM_MI2S_SD3:
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_8CHS;
+ break;
+ default:
+ pr_err("%s: invalid SD lines\n",
+ __func__);
+ goto error_invalid_data;
+ }
+ break;
+ default:
+ pr_err("%s: invalid SD lines\n", __func__);
+ goto error_invalid_data;
+ }
+ if (i2s_pdata->capability == MSM_MI2S_CAP_RX)
+ dai_driver->playback.channels_max = num_of_sd_lines << 1;
+
+ return 0;
+
+error_invalid_data:
+ return -EINVAL;
+}
+
+static int msm_dai_q6_cdc_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ dai_data->port_config.i2s.ws_src = 1; /* CPU is master */
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ dai_data->port_config.i2s.ws_src = 0; /* CPU is slave */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int msm_dai_q6_slim_bus_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai, int stream)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ dai_data->channels = params_channels(params);
+ dai_data->rate = params_rate(params);
+
+ /* Q6 only supports 16 as now */
+ dai_data->port_config.slim_sch.sb_cfg_minor_version =
+ AFE_API_VERSION_SLIMBUS_CONFIG;
+ dai_data->port_config.slim_sch.bit_width = 16;
+ dai_data->port_config.slim_sch.data_format = 0;
+ dai_data->port_config.slim_sch.num_channels = dai_data->channels;
+ dai_data->port_config.slim_sch.sample_rate = dai_data->rate;
+
+ dev_dbg(dai->dev, "%s:slimbus_dev_id[%hu] bit_wd[%hu] format[%hu]\n"
+ "num_channel %hu shared_ch_mapping[0] %hu\n"
+ "slave_port_mapping[1] %hu slave_port_mapping[2] %hu\n"
+ "sample_rate %d\n", __func__,
+ dai_data->port_config.slim_sch.slimbus_dev_id,
+ dai_data->port_config.slim_sch.bit_width,
+ dai_data->port_config.slim_sch.data_format,
+ dai_data->port_config.slim_sch.num_channels,
+ dai_data->port_config.slim_sch.shared_ch_mapping[0],
+ dai_data->port_config.slim_sch.shared_ch_mapping[1],
+ dai_data->port_config.slim_sch.shared_ch_mapping[2],
+ dai_data->rate);
+
+ return 0;
+}
+
+static int msm_dai_q6_bt_fm_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai, int stream)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ dai_data->channels = params_channels(params);
+ dai_data->rate = params_rate(params);
+
+ dev_dbg(dai->dev, "channels %d sample rate %d entered\n",
+ dai_data->channels, dai_data->rate);
+
+ memset(&dai_data->port_config, 0, sizeof(dai_data->port_config));
+
+ return 0;
+}
+static int msm_dai_q6_auxpcm_hw_params(
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct msm_dai_auxpcm_pdata *auxpcm_pdata =
+ (struct msm_dai_auxpcm_pdata *) dai->dev->platform_data;
+
+ if (params_channels(params) != 1) {
+ dev_err(dai->dev, "AUX PCM supports only mono stream\n");
+ return -EINVAL;
+ }
+ dai_data->channels = params_channels(params);
+
+ if (params_rate(params) != 8000) {
+ dev_err(dai->dev, "AUX PCM supports only 8KHz sampling rate\n");
+ return -EINVAL;
+ }
+ dai_data->rate = params_rate(params);
+
+ dai_data->port_config.pcm.pcm_cfg_minor_version =
+ AFE_API_VERSION_PCM_CONFIG;
+ dai_data->port_config.pcm.aux_mode = auxpcm_pdata->mode;
+ dai_data->port_config.pcm.sync_src = auxpcm_pdata->sync;
+ dai_data->port_config.pcm.frame_setting = auxpcm_pdata->frame;
+ dai_data->port_config.pcm.quantype = auxpcm_pdata->quant;
+ dai_data->port_config.pcm.ctrl_data_out_enable = auxpcm_pdata->data;
+ dai_data->port_config.pcm.sample_rate = dai_data->rate;
+ dai_data->port_config.pcm.num_channels = dai_data->channels;
+ dai_data->port_config.pcm.bit_width = 16;
+ dai_data->port_config.pcm.slot_number_mapping[0] = auxpcm_pdata->slot;
+
+ return 0;
+}
+
+static int msm_dai_q6_afe_rtproxy_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ dai_data->rate = params_rate(params);
+ dai_data->port_config.rtproxy.num_channels = params_channels(params);
+ dai_data->port_config.rtproxy.sample_rate = params_rate(params);
+
+ pr_debug("channel %d entered,dai_id: %d,rate: %d\n",
+ dai_data->port_config.rtproxy.num_channels, dai->id, dai_data->rate);
+
+ dai_data->port_config.rtproxy.rt_proxy_cfg_minor_version =
+ AFE_API_VERSION_RT_PROXY_CONFIG;
+ dai_data->port_config.rtproxy.bit_width = 16; /* Q6 only supports 16 */
+ dai_data->port_config.rtproxy.interleaved = 1;
+ dai_data->port_config.rtproxy.frame_size = params_period_bytes(params);
+ dai_data->port_config.rtproxy.jitter_allowance =
+ dai_data->port_config.rtproxy.frame_size/2;
+ dai_data->port_config.rtproxy.low_water_mark = 0;
+ dai_data->port_config.rtproxy.high_water_mark = 0;
+
+ return 0;
+}
+
+/* Current implementation assumes hw_param is called once
+ * This may not be the case but what to do when ADM and AFE
+ * port are already opened and parameter changes
+ */
+static int msm_dai_q6_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int rc = 0;
+
+ switch (dai->id) {
+ case PRIMARY_I2S_TX:
+ case PRIMARY_I2S_RX:
+ case SECONDARY_I2S_RX:
+ rc = msm_dai_q6_cdc_hw_params(params, dai, substream->stream);
+ break;
+ case MI2S_RX:
+ rc = msm_dai_q6_i2s_hw_params(params, dai, substream->stream);
+ break;
+ case SLIMBUS_0_RX:
+ case SLIMBUS_1_RX:
+ case SLIMBUS_0_TX:
+ case SLIMBUS_1_TX:
+ rc = msm_dai_q6_slim_bus_hw_params(params, dai,
+ substream->stream);
+ break;
+ case INT_BT_SCO_RX:
+ case INT_BT_SCO_TX:
+ case INT_FM_RX:
+ case INT_FM_TX:
+ rc = msm_dai_q6_bt_fm_hw_params(params, dai, substream->stream);
+ break;
+ case RT_PROXY_DAI_001_TX:
+ case RT_PROXY_DAI_001_RX:
+ case RT_PROXY_DAI_002_TX:
+ case RT_PROXY_DAI_002_RX:
+ rc = msm_dai_q6_afe_rtproxy_hw_params(params, dai);
+ break;
+ case VOICE_PLAYBACK_TX:
+ case VOICE_RECORD_RX:
+ case VOICE_RECORD_TX:
+ rc = 0;
+ break;
+ default:
+ dev_err(dai->dev, "invalid AFE port ID\n");
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static void msm_dai_q6_auxpcm_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int rc = 0;
+
+ mutex_lock(&aux_pcm_mutex);
+
+ if (aux_pcm_count == 0) {
+ dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count is 0. Just"
+ " return\n", __func__, dai->id);
+ mutex_unlock(&aux_pcm_mutex);
+ return;
+ }
+
+ aux_pcm_count--;
+
+ if (aux_pcm_count > 0) {
+ dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count = %d\n",
+ __func__, dai->id, aux_pcm_count);
+ mutex_unlock(&aux_pcm_mutex);
+ return;
+ } else if (aux_pcm_count < 0) {
+ dev_err(dai->dev, "%s(): ERROR: dai->id %d"
+ " aux_pcm_count = %d < 0\n",
+ __func__, dai->id, aux_pcm_count);
+ aux_pcm_count = 0;
+ mutex_unlock(&aux_pcm_mutex);
+ return;
+ }
+
+ pr_debug("%s: dai->id = %d aux_pcm_count = %d\n", __func__,
+ dai->id, aux_pcm_count);
+
+ rc = afe_close(PCM_RX); /* can block */
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close PCM_RX AFE port\n");
+
+ rc = afe_close(PCM_TX);
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AUX PCM TX port\n");
+
+ mutex_unlock(&aux_pcm_mutex);
+}
+
+static void msm_dai_q6_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ switch (dai->id) {
+ case VOICE_PLAYBACK_TX:
+ case VOICE_RECORD_TX:
+ case VOICE_RECORD_RX:
+ pr_debug("%s, stop pseudo port:%d\n",
+ __func__, dai->id);
+ rc = afe_stop_pseudo_port(dai->id);
+ break;
+ default:
+ rc = afe_close(dai->id); /* can block */
+ break;
+ }
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+ pr_debug("%s: dai_data->status_mask = %ld\n", __func__,
+ *dai_data->status_mask);
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+ }
+}
+
+static int msm_dai_q6_auxpcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ mutex_lock(&aux_pcm_mutex);
+
+ if (aux_pcm_count == 2) {
+ dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count is 2. Just"
+ " return.\n", __func__, dai->id);
+ mutex_unlock(&aux_pcm_mutex);
+ return 0;
+ } else if (aux_pcm_count > 2) {
+ dev_err(dai->dev, "%s(): ERROR: dai->id %d"
+ " aux_pcm_count = %d > 2\n",
+ __func__, dai->id, aux_pcm_count);
+ mutex_unlock(&aux_pcm_mutex);
+ return 0;
+ }
+
+ aux_pcm_count++;
+ if (aux_pcm_count == 2) {
+ dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count = %d after "
+ " increment\n", __func__, dai->id, aux_pcm_count);
+ mutex_unlock(&aux_pcm_mutex);
+ return 0;
+ }
+
+ pr_debug("%s:dai->id:%d aux_pcm_count = %d. opening afe\n",
+ __func__, dai->id, aux_pcm_count);
+
+ rc = afe_q6_interface_prepare();
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to open AFE APR\n");
+
+ /*
+ * For AUX PCM Interface the below sequence of clk
+ * settings and afe_open is a strict requirement.
+ *
+ * Also using afe_open instead of afe_port_start_nowait
+ * to make sure the port is open before deasserting the
+ * clock line. This is required because pcm register is
+ * not written before clock deassert. Hence the hw does
+ * not get updated with new setting if the below clock
+ * assert/deasset and afe_open sequence is not followed.
+ */
+
+ afe_open(PCM_RX, &dai_data->port_config, dai_data->rate);
+
+ afe_open(PCM_TX, &dai_data->port_config, dai_data->rate);
+
+ mutex_unlock(&aux_pcm_mutex);
+
+ return rc;
+}
+
+static int msm_dai_q6_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ /* PORT START should be set if prepare called in active state */
+ rc = afe_q6_interface_prepare();
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to open AFE APR\n");
+ }
+ return rc;
+}
+
+static int msm_dai_q6_auxpcm_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ int rc = 0;
+
+ pr_debug("%s:port:%d cmd:%d aux_pcm_count= %d",
+ __func__, dai->id, cmd, aux_pcm_count);
+
+ switch (cmd) {
+
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ /* afe_open will be called from prepare */
+ return 0;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ return 0;
+
+ default:
+ rc = -EINVAL;
+ }
+
+ return rc;
+
+}
+
+static int msm_dai_q6_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ /* Start/stop port without waiting for Q6 AFE response. Need to have
+ * native q6 AFE driver propagates AFE response in order to handle
+ * port start/stop command error properly if error does arise.
+ */
+ pr_debug("%s:port:%d cmd:%d dai_data->status_mask = %ld",
+ __func__, dai->id, cmd, *dai_data->status_mask);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ switch (dai->id) {
+ case VOICE_PLAYBACK_TX:
+ case VOICE_RECORD_TX:
+ case VOICE_RECORD_RX:
+ afe_pseudo_port_start_nowait(dai->id);
+ break;
+ default:
+ afe_port_start_nowait(dai->id,
+ &dai_data->port_config, dai_data->rate);
+ break;
+ }
+ set_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ switch (dai->id) {
+ case VOICE_PLAYBACK_TX:
+ case VOICE_RECORD_TX:
+ case VOICE_RECORD_RX:
+ afe_pseudo_port_stop_nowait(dai->id);
+ break;
+ default:
+ afe_port_stop_nowait(dai->id);
+ break;
+ }
+ clear_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
+ }
+ break;
+
+ default:
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+static int msm_dai_q6_dai_auxpcm_probe(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data;
+ int rc = 0;
+
+ struct msm_dai_auxpcm_pdata *auxpcm_pdata =
+ (struct msm_dai_auxpcm_pdata *) dai->dev->platform_data;
+
+ mutex_lock(&aux_pcm_mutex);
+
+ if (!auxpcm_plat_data)
+ auxpcm_plat_data = auxpcm_pdata;
+ else if (auxpcm_plat_data != auxpcm_pdata) {
+
+ dev_err(dai->dev, "AUX PCM RX and TX devices does not have"
+ " same platform data\n");
+ return -EINVAL;
+ }
+
+ /*
+ * The clk name for AUX PCM operation is passed as platform
+ * data to the cpu driver, since cpu drive is unaware of any
+ * boarc specific configuration.
+ */
+ if (!pcm_clk)
+ pcm_clk = clk_get(dai->dev, auxpcm_pdata->clk);
+
+ mutex_unlock(&aux_pcm_mutex);
+
+ dai_data = kzalloc(sizeof(struct msm_dai_q6_dai_data), GFP_KERNEL);
+
+ if (!dai_data) {
+ dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
+ dai->id);
+ rc = -ENOMEM;
+ } else
+ dev_set_drvdata(dai->dev, dai_data);
+
+ pr_err("%s : probe done for dai->id %d\n", __func__, dai->id);
+ return rc;
+}
+
+static int msm_dai_q6_dai_auxpcm_remove(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data;
+ int rc;
+
+ dai_data = dev_get_drvdata(dai->dev);
+
+ mutex_lock(&aux_pcm_mutex);
+
+ if (aux_pcm_count == 0) {
+ dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count is 0. clean"
+ " up and return\n", __func__, dai->id);
+ goto done;
+ }
+
+ aux_pcm_count--;
+
+ if (aux_pcm_count > 0) {
+ dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count = %d\n",
+ __func__, dai->id, aux_pcm_count);
+ goto done;
+ } else if (aux_pcm_count < 0) {
+ dev_err(dai->dev, "%s(): ERROR: dai->id %d"
+ " aux_pcm_count = %d < 0\n",
+ __func__, dai->id, aux_pcm_count);
+ goto done;
+ }
+
+ dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count = %d."
+ "closing afe\n",
+ __func__, dai->id, aux_pcm_count);
+
+ rc = afe_close(PCM_RX); /* can block */
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AUX PCM RX AFE port\n");
+
+ rc = afe_close(PCM_TX);
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AUX PCM TX AFE port\n");
+
+done:
+ kfree(dai_data);
+ snd_soc_unregister_dai(dai->dev);
+
+ mutex_unlock(&aux_pcm_mutex);
+
+ return 0;
+}
+static int msm_dai_q6_dai_i2s_probe(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data;
+ int rc = 0;
+
+ dai_data = kzalloc(sizeof(struct msm_dai_q6_dai_data),
+ GFP_KERNEL);
+
+ if (!dai_data) {
+ dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
+ dai->id);
+ rc = -ENOMEM;
+ goto rtn;
+ } else
+ dev_set_drvdata(dai->dev, dai_data);
+
+ rc = msm_dai_q6_i2s_platform_data_validation(dai);
+ if (rc != 0) {
+ pr_err("%s: The msm_dai_q6_i2s_platform_data_validation failed\n",
+ __func__);
+ kfree(dai_data);
+ }
+rtn:
+ return rc;
+}
+
+static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data;
+ int rc = 0;
+
+ dai_data = kzalloc(sizeof(struct msm_dai_q6_dai_data),
+ GFP_KERNEL);
+
+ if (!dai_data) {
+ dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
+ dai->id);
+ rc = -ENOMEM;
+ } else
+ dev_set_drvdata(dai->dev, dai_data);
+
+ return rc;
+}
+
+static int msm_dai_q6_dai_remove(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data;
+ int rc;
+
+ dai_data = dev_get_drvdata(dai->dev);
+
+ /* If AFE port is still up, close it */
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ switch (dai->id) {
+ case VOICE_PLAYBACK_TX:
+ case VOICE_RECORD_TX:
+ case VOICE_RECORD_RX:
+ pr_debug("%s, stop pseudo port:%d\n",
+ __func__, dai->id);
+ rc = afe_stop_pseudo_port(dai->id);
+ break;
+ default:
+ rc = afe_close(dai->id); /* can block */
+ }
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+ }
+ kfree(dai_data);
+ snd_soc_unregister_dai(dai->dev);
+
+ return 0;
+}
+
+static int msm_dai_q6_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ int rc = 0;
+
+ dev_dbg(dai->dev, "enter %s, id = %d fmt[%d]\n", __func__,
+ dai->id, fmt);
+ switch (dai->id) {
+ case PRIMARY_I2S_TX:
+ case PRIMARY_I2S_RX:
+ case MI2S_RX:
+ case SECONDARY_I2S_RX:
+ rc = msm_dai_q6_cdc_set_fmt(dai, fmt);
+ break;
+ default:
+ dev_err(dai->dev, "invalid cpu_dai set_fmt\n");
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static int msm_dai_q6_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+
+{
+ int rc = 0;
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ unsigned int i = 0;
+
+ dev_dbg(dai->dev, "enter %s, id = %d\n", __func__,
+ dai->id);
+ switch (dai->id) {
+ case SLIMBUS_0_RX:
+ case SLIMBUS_1_RX:
+ /* channel number to be between 128 and 255. For RX port
+ * use channel numbers from 138 to 144, for TX port
+ * use channel numbers from 128 to 137
+ * For ports between MDM-APQ use channel numbers from 145
+ */
+ if (!rx_slot)
+ return -EINVAL;
+ for (i = 0; i < rx_num; i++) {
+ dai_data->port_config.slim_sch.shared_ch_mapping[i] =
+ rx_slot[i];
+ pr_debug("%s: find number of channels[%d] ch[%d]\n",
+ __func__, i,
+ rx_slot[i]);
+ }
+ dai_data->port_config.slim_sch.num_channels = rx_num;
+ pr_debug("%s:SLIMBUS_0_RX cnt[%d] ch[%d %d]\n", __func__,
+ rx_num, dai_data->port_config.slim_sch.shared_ch_mapping[0],
+ dai_data->port_config.slim_sch.shared_ch_mapping[1]);
+
+ break;
+ case SLIMBUS_0_TX:
+ case SLIMBUS_1_TX:
+ /* channel number to be between 128 and 255. For RX port
+ * use channel numbers from 138 to 144, for TX port
+ * use channel numbers from 128 to 137
+ * For ports between MDM-APQ use channel numbers from 145
+ */
+ if (!tx_slot)
+ return -EINVAL;
+ for (i = 0; i < tx_num; i++) {
+ dai_data->port_config.slim_sch.shared_ch_mapping[i] =
+ tx_slot[i];
+ pr_debug("%s: find number of channels[%d] ch[%d]\n",
+ __func__, i, tx_slot[i]);
+ }
+ dai_data->port_config.slim_sch.num_channels = tx_num;
+ pr_debug("%s:SLIMBUS_0_TX cnt[%d] ch[%d %d]\n", __func__,
+ tx_num, dai_data->port_config.slim_sch.shared_ch_mapping[0],
+ dai_data->port_config.slim_sch.shared_ch_mapping[1]);
+ break;
+ default:
+ dev_err(dai->dev, "invalid cpu_dai set_fmt\n");
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_ops = {
+ .prepare = msm_dai_q6_prepare,
+ .trigger = msm_dai_q6_trigger,
+ .hw_params = msm_dai_q6_hw_params,
+ .shutdown = msm_dai_q6_shutdown,
+ .set_fmt = msm_dai_q6_set_fmt,
+ .set_channel_map = msm_dai_q6_set_channel_map,
+};
+
+static struct snd_soc_dai_ops msm_dai_q6_auxpcm_ops = {
+ .prepare = msm_dai_q6_auxpcm_prepare,
+ .trigger = msm_dai_q6_auxpcm_trigger,
+ .hw_params = msm_dai_q6_auxpcm_hw_params,
+ .shutdown = msm_dai_q6_auxpcm_shutdown,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_i2s_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 4,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_i2s_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_i2s_tx_dai = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_afe_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_afe_tx_dai = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_voice_playback_tx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_slimbus_tx_dai = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_incall_record_dai = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_bt_sco_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 16000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_bt_sco_tx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 16000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_fm_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_fm_tx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_aux_pcm_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 8000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_auxpcm_ops,
+ .probe = msm_dai_q6_dai_auxpcm_probe,
+ .remove = msm_dai_q6_dai_auxpcm_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_aux_pcm_tx_dai = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 8000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_auxpcm_ops,
+ .probe = msm_dai_q6_dai_auxpcm_probe,
+ .remove = msm_dai_q6_dai_auxpcm_remove,
+};
+
+
+static struct snd_soc_dai_driver msm_dai_q6_slimbus_1_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_slimbus_1_tx_dai = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static __devinit int msm_dai_q6_dev_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+
+ dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
+
+ switch (pdev->id) {
+ case PRIMARY_I2S_RX:
+ case SECONDARY_I2S_RX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_i2s_rx_dai);
+ break;
+ case PRIMARY_I2S_TX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_i2s_tx_dai);
+ break;
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_aux_pcm_rx_dai);
+ break;
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_aux_pcm_tx_dai);
+ break;
+ case MI2S_RX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_i2s_rx_dai);
+ break;
+ case SLIMBUS_0_RX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_slimbus_rx_dai);
+ break;
+ case SLIMBUS_0_TX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_slimbus_tx_dai);
+ break;
+
+ case SLIMBUS_1_RX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_slimbus_1_rx_dai);
+ break;
+ case SLIMBUS_1_TX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_slimbus_1_tx_dai);
+ break;
+ case INT_BT_SCO_RX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_bt_sco_rx_dai);
+ break;
+ case INT_BT_SCO_TX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_bt_sco_tx_dai);
+ break;
+ case INT_FM_RX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_fm_rx_dai);
+ break;
+ case INT_FM_TX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_fm_tx_dai);
+ break;
+ case RT_PROXY_DAI_001_RX:
+ case RT_PROXY_DAI_002_RX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_afe_rx_dai);
+ break;
+ case RT_PROXY_DAI_001_TX:
+ case RT_PROXY_DAI_002_TX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_afe_tx_dai);
+ break;
+ case VOICE_PLAYBACK_TX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_voice_playback_tx_dai);
+ break;
+ case VOICE_RECORD_RX:
+ case VOICE_RECORD_TX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_incall_record_dai);
+ break;
+ default:
+ rc = -ENODEV;
+ break;
+ }
+ return rc;
+}
+
+static __devexit int msm_dai_q6_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dai(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_dai_q6_driver = {
+ .probe = msm_dai_q6_dev_probe,
+ .remove = msm_dai_q6_dev_remove,
+ .driver = {
+ .name = "msm-dai-q6",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_dai_q6_init(void)
+{
+ return platform_driver_register(&msm_dai_q6_driver);
+}
+module_init(msm_dai_q6_init);
+
+static void __exit msm_dai_q6_exit(void)
+{
+ platform_driver_unregister(&msm_dai_q6_driver);
+}
+module_exit(msm_dai_q6_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM DSP DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-multi-ch-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-multi-ch-pcm-q6-v2.c
new file mode 100644
index 0000000..cab689d
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-multi-ch-pcm-q6-v2.c
@@ -0,0 +1,777 @@
+/* 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/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/android_pmem.h>
+#include <asm/dma.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+
+#include "msm-pcm-q6-v2.h"
+#include "msm-pcm-routing-v2.h"
+
+static struct audio_locks the_locks;
+
+struct snd_msm {
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+};
+
+struct snd_msm_volume {
+ struct msm_audio *prtd;
+ unsigned volume;
+};
+static struct snd_msm_volume multi_ch_pcm_audio = {NULL, 0x2000};
+
+#define PLAYBACK_NUM_PERIODS 8
+#define PLAYBACK_PERIOD_SIZE 4032
+#define CAPTURE_NUM_PERIODS 16
+#define CAPTURE_PERIOD_SIZE 320
+
+static struct snd_pcm_hardware msm_pcm_hardware_capture = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = CAPTURE_NUM_PERIODS * CAPTURE_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_PERIOD_SIZE,
+ .periods_min = CAPTURE_NUM_PERIODS,
+ .periods_max = CAPTURE_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+static struct snd_pcm_hardware msm_pcm_hardware_playback = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 6,
+ .buffer_bytes_max = PLAYBACK_NUM_PERIODS * PLAYBACK_PERIOD_SIZE,
+ .period_bytes_min = PLAYBACK_PERIOD_SIZE,
+ .period_bytes_max = PLAYBACK_PERIOD_SIZE,
+ .periods_min = PLAYBACK_NUM_PERIODS,
+ .periods_max = PLAYBACK_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static uint32_t in_frame_info[CAPTURE_NUM_PERIODS][2];
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static void event_handler(uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv)
+{
+ struct msm_audio *prtd = priv;
+ struct snd_pcm_substream *substream = prtd->substream;
+ uint32_t *ptrmem = (uint32_t *)payload;
+ int i = 0;
+ uint32_t idx = 0;
+ uint32_t size = 0;
+
+ pr_debug("%s\n", __func__);
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE_V2: {
+ pr_debug("ASM_DATA_EVENT_WRITE_DONE\n");
+ pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
+ prtd->pcm_irq_pos += prtd->pcm_count;
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ atomic_inc(&prtd->out_count);
+ wake_up(&the_locks.write_wait);
+ if (!atomic_read(&prtd->start))
+ break;
+ if (!prtd->mmap_flag)
+ break;
+ if (q6asm_is_cpu_buf_avail_nolock(IN,
+ prtd->audio_client,
+ &size, &idx)) {
+ pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
+ __func__, prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count, 0, 0, NO_TIMESTAMP);
+ }
+ break;
+ }
+ case ASM_DATA_EVENT_RENDERED_EOS:
+ pr_debug("ASM_DATA_CMDRSP_EOS\n");
+ prtd->cmd_ack = 1;
+ wake_up(&the_locks.eos_wait);
+ break;
+ case ASM_DATA_EVENT_READ_DONE_V2: {
+ pr_debug("ASM_DATA_EVENT_READ_DONE\n");
+ pr_debug("token = 0x%08x\n", token);
+ for (i = 0; i < 8; i++, ++ptrmem)
+ pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
+ in_frame_info[token][0] = payload[2];
+ in_frame_info[token][1] = payload[3];
+ prtd->pcm_irq_pos += in_frame_info[token][0];
+ pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos);
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ if (atomic_read(&prtd->in_count) <= prtd->periods)
+ atomic_inc(&prtd->in_count);
+ wake_up(&the_locks.read_wait);
+ if (prtd->mmap_flag
+ && q6asm_is_cpu_buf_avail_nolock(OUT,
+ prtd->audio_client,
+ &size, &idx))
+ q6asm_read_nolock(prtd->audio_client);
+ break;
+ }
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case ASM_SESSION_CMD_RUN_V2:
+ if (substream->stream
+ != SNDRV_PCM_STREAM_PLAYBACK) {
+ atomic_set(&prtd->start, 1);
+ break;
+ }
+ if (prtd->mmap_flag) {
+ pr_debug("%s:writing %d bytes"
+ " of buffer to dsp\n",
+ __func__,
+ prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count,
+ 0, 0, NO_TIMESTAMP);
+ } else {
+ while (atomic_read(&prtd->out_needed)) {
+ pr_debug("%s:writing %d bytes"
+ " of buffer to dsp\n",
+ __func__,
+ prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count,
+ 0, 0, NO_TIMESTAMP);
+ atomic_dec(&prtd->out_needed);
+ wake_up(&the_locks.write_wait);
+ };
+ }
+ atomic_set(&prtd->start, 1);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
+ break;
+ }
+}
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int ret;
+
+ pr_debug("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+ if (prtd->enabled)
+ return 0;
+
+ ret = q6asm_media_format_block_pcm(prtd->audio_client,
+ runtime->rate, runtime->channels);
+ if (ret < 0)
+ pr_info("%s: CMD Format block failed\n", __func__);
+
+ atomic_set(&prtd->out_count, runtime->periods);
+
+ prtd->enabled = 1;
+ prtd->cmd_ack = 0;
+
+ return 0;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int ret = 0;
+ int i = 0;
+ pr_debug("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+
+ if (prtd->enabled)
+ return 0;
+
+ pr_debug("Samp_rate = %d\n", prtd->samp_rate);
+ pr_debug("Channel = %d\n", prtd->channel_mode);
+ ret = q6asm_enc_cfg_blk_pcm(prtd->audio_client, prtd->samp_rate,
+ prtd->channel_mode);
+ if (ret < 0)
+ pr_debug("%s: cmd cfg pcm was block failed", __func__);
+
+ for (i = 0; i < runtime->periods; i++)
+ q6asm_read(prtd->audio_client);
+ prtd->periods = runtime->periods;
+
+ prtd->enabled = 1;
+
+ return ret;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("%s: Trigger start\n", __func__);
+ q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ atomic_set(&prtd->start, 0);
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ break;
+ prtd->cmd_ack = 0;
+ q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
+ q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ atomic_set(&prtd->start, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+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;
+ struct asm_softpause_params softpause = {
+ .enable = SOFT_PAUSE_ENABLE,
+ .period = SOFT_PAUSE_PERIOD,
+ .step = SOFT_PAUSE_STEP,
+ .rampingcurve = SOFT_PAUSE_CURVE_LINEAR,
+ };
+ struct asm_softvolume_params softvol = {
+ .period = SOFT_VOLUME_PERIOD,
+ .step = SOFT_VOLUME_STEP,
+ .rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
+ };
+
+ pr_debug("%s\n", __func__);
+ prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
+ if (prtd == NULL) {
+ pr_err("Failed to allocate memory for msm_audio\n");
+ return -ENOMEM;
+ }
+ prtd->substream = substream;
+ prtd->audio_client = q6asm_audio_client_alloc(
+ (app_cb)event_handler, prtd);
+ if (!prtd->audio_client) {
+ pr_err("%s: Could not allocate memory\n", __func__);
+ 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_MULTI_CHANNEL_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;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_err("snd_pcm_hw_constraint_list failed\n");
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ pr_err("snd_pcm_hw_constraint_integer failed\n");
+
+ prtd->dsp_cnt = 0;
+ runtime->private_data = prtd;
+ pr_debug("substream->pcm->device = %d\n", substream->pcm->device);
+ pr_debug("soc_prtd->dai_link->be_id = %d\n", soc_prtd->dai_link->be_id);
+ multi_ch_pcm_audio.prtd = prtd;
+ ret = multi_ch_pcm_set_volume(multi_ch_pcm_audio.volume);
+ if (ret < 0)
+ pr_err("%s : Set Volume failed : %d", __func__, ret);
+
+ ret = q6asm_set_softpause(multi_ch_pcm_audio.prtd->audio_client,
+ &softpause);
+ if (ret < 0)
+ pr_err("%s: Send SoftPause Param failed ret=%d\n",
+ __func__, ret);
+ ret = q6asm_set_softvolume(multi_ch_pcm_audio.prtd->audio_client,
+ &softvol);
+ if (ret < 0)
+ pr_err("%s: Send SoftVolume Param failed ret=%d\n",
+ __func__, ret);
+
+ return 0;
+}
+
+int multi_ch_pcm_set_volume(unsigned volume)
+{
+ int rc = 0;
+ pr_err("multi_ch_pcm_set_volume\n");
+
+ if (multi_ch_pcm_audio.prtd && multi_ch_pcm_audio.prtd->audio_client) {
+ pr_err("%s q6asm_set_volume\n", __func__);
+ rc = q6asm_set_volume(multi_ch_pcm_audio.prtd->audio_client,
+ volume);
+ if (rc < 0) {
+ pr_err("%s: Send Volume command failed"
+ " rc=%d\n", __func__, rc);
+ }
+ }
+ multi_ch_pcm_audio.volume = volume;
+ return rc;
+}
+
+
+static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int fbytes = 0;
+ int xfer = 0;
+ char *bufptr = NULL;
+ void *data = NULL;
+ uint32_t idx = 0;
+ uint32_t size = 0;
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ fbytes = frames_to_bytes(runtime, frames);
+ pr_debug("%s: prtd->out_count = %d\n",
+ __func__, atomic_read(&prtd->out_count));
+ ret = wait_event_timeout(the_locks.write_wait,
+ (atomic_read(&prtd->out_count)), 5 * HZ);
+ if (ret < 0) {
+ pr_err("%s: wait_event_timeout failed\n", __func__);
+ goto fail;
+ }
+
+ if (!atomic_read(&prtd->out_count)) {
+ pr_err("%s: pcm stopped out_count 0\n", __func__);
+ return 0;
+ }
+
+ data = q6asm_is_cpu_buf_avail(IN, prtd->audio_client, &size, &idx);
+ bufptr = data;
+ if (bufptr) {
+ pr_debug("%s:fbytes =%d: xfer=%d size=%d\n",
+ __func__, fbytes, xfer, size);
+ xfer = fbytes;
+ if (copy_from_user(bufptr, buf, xfer)) {
+ ret = -EFAULT;
+ goto fail;
+ }
+ buf += xfer;
+ fbytes -= xfer;
+ pr_debug("%s:fbytes = %d: xfer=%d\n", __func__, fbytes, xfer);
+ if (atomic_read(&prtd->start)) {
+ pr_debug("%s:writing %d bytes of buffer to dsp\n",
+ __func__, xfer);
+ ret = q6asm_write(prtd->audio_client, xfer,
+ 0, 0, NO_TIMESTAMP);
+ if (ret < 0) {
+ ret = -EFAULT;
+ goto fail;
+ }
+ } else
+ atomic_inc(&prtd->out_needed);
+ atomic_dec(&prtd->out_count);
+ }
+fail:
+ return ret;
+}
+
+static int msm_pcm_playback_close(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 = runtime->private_data;
+ int dir = 0;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+
+ dir = IN;
+ ret = wait_event_timeout(the_locks.eos_wait,
+ prtd->cmd_ack, 5 * HZ);
+ if (ret < 0)
+ pr_err("%s: CMD_EOS failed\n", __func__);
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ multi_ch_pcm_audio.prtd = NULL;
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return 0;
+}
+
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t hwoff, void __user *buf,
+ snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int fbytes = 0;
+ int xfer;
+ char *bufptr;
+ void *data = NULL;
+ static uint32_t idx;
+ static uint32_t size;
+ uint32_t offset = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = substream->runtime->private_data;
+
+
+ pr_debug("%s\n", __func__);
+ fbytes = frames_to_bytes(runtime, frames);
+
+ pr_debug("appl_ptr %d\n", (int)runtime->control->appl_ptr);
+ pr_debug("hw_ptr %d\n", (int)runtime->status->hw_ptr);
+ pr_debug("avail_min %d\n", (int)runtime->control->avail_min);
+
+ ret = wait_event_timeout(the_locks.read_wait,
+ (atomic_read(&prtd->in_count)), 5 * HZ);
+ if (ret < 0) {
+ pr_debug("%s: wait_event_timeout failed\n", __func__);
+ goto fail;
+ }
+ if (!atomic_read(&prtd->in_count)) {
+ pr_debug("%s: pcm stopped in_count 0\n", __func__);
+ return 0;
+ }
+ pr_debug("Checking if valid buffer is available...%08x\n",
+ (unsigned int) data);
+ data = q6asm_is_cpu_buf_avail(OUT, prtd->audio_client, &size, &idx);
+ bufptr = data;
+ pr_debug("Size = %d\n", size);
+ pr_debug("fbytes = %d\n", fbytes);
+ pr_debug("idx = %d\n", idx);
+ if (bufptr) {
+ xfer = fbytes;
+ if (xfer > size)
+ xfer = size;
+ offset = in_frame_info[idx][1];
+ pr_debug("Offset value = %d\n", offset);
+ if (copy_to_user(buf, bufptr+offset, xfer)) {
+ pr_err("Failed to copy buf to user\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+ fbytes -= xfer;
+ size -= xfer;
+ in_frame_info[idx][1] += xfer;
+ pr_debug("%s:fbytes = %d: size=%d: xfer=%d\n",
+ __func__, fbytes, size, xfer);
+ pr_debug(" Sending next buffer to dsp\n");
+ memset(&in_frame_info[idx], 0,
+ sizeof(uint32_t) * 2);
+ atomic_dec(&prtd->in_count);
+ ret = q6asm_read(prtd->audio_client);
+ if (ret < 0) {
+ pr_err("q6asm read failed\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+ } else
+ pr_err("No valid buffer\n");
+
+ pr_debug("Returning from capture_copy... %d\n", ret);
+fail:
+ return ret;
+}
+
+static int msm_pcm_capture_close(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 = runtime->private_data;
+ int dir = OUT;
+
+ pr_debug("%s\n", __func__);
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ 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);
+ kfree(prtd);
+
+ return 0;
+}
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
+ return ret;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_close(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_close(substream);
+ return ret;
+}
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_prepare(substream);
+ return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ if (prtd->pcm_irq_pos >= prtd->pcm_size)
+ prtd->pcm_irq_pos = 0;
+
+ pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
+ return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static int msm_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ int result = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+ prtd->mmap_flag = 1;
+
+ if (runtime->dma_addr && runtime->dma_bytes) {
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ result = remap_pfn_range(vma, vma->vm_start,
+ runtime->dma_addr >> PAGE_SHIFT,
+ runtime->dma_bytes,
+ vma->vm_page_prot);
+ } else {
+ pr_err("Physical address or size of buf is NULL");
+ return -EINVAL;
+ }
+
+ return result;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct audio_buffer *buf;
+ int dir, ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ dir = OUT;
+
+ ret = q6asm_audio_client_buf_alloc_contiguous(dir,
+ prtd->audio_client,
+ runtime->hw.period_bytes_min,
+ runtime->hw.periods_max);
+ if (ret < 0) {
+ pr_err("Audio Start: Buffer Allocation failed rc = %d\n", ret);
+ return -ENOMEM;
+ }
+ buf = prtd->audio_client->port[dir].buf;
+
+ pr_debug("%s:buf = %p\n", __func__, buf);
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+ dma_buf->area = buf[0].data;
+ dma_buf->addr = buf[0].phys;
+ dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ if (!dma_buf->area)
+ return -ENOMEM;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ return 0;
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .copy = msm_pcm_copy,
+ .hw_params = msm_pcm_hw_params,
+ .close = msm_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+ .mmap = msm_pcm_mmap,
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
+};
+
+static __devinit int msm_pcm_probe(struct platform_device *pdev)
+{
+ pr_info("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-multi-ch-pcm-dsp",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_pcm_probe,
+ .remove = __devexit_p(msm_pcm_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ init_waitqueue_head(&the_locks.enable_wait);
+ init_waitqueue_head(&the_locks.eos_wait);
+ init_waitqueue_head(&the_locks.write_wait);
+ init_waitqueue_head(&the_locks.read_wait);
+
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("Multi channel PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
new file mode 100644
index 0000000..4593784
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
@@ -0,0 +1,581 @@
+/* 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/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/android_pmem.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6adm-v2.h>
+#include <asm/dma.h>
+#include "msm-pcm-afe-v2.h"
+
+#define MIN_PERIOD_SIZE (128 * 2)
+#define MAX_PERIOD_SIZE (128 * 2 * 2 * 6)
+static struct snd_pcm_hardware msm_afe_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = (SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000),
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = MAX_PERIOD_SIZE * 32,
+ .period_bytes_min = MIN_PERIOD_SIZE,
+ .period_bytes_max = MAX_PERIOD_SIZE,
+ .periods_min = 32,
+ .periods_max = 384,
+ .fifo_size = 0,
+};
+static enum hrtimer_restart afe_hrtimer_callback(struct hrtimer *hrt);
+static enum hrtimer_restart afe_hrtimer_rec_callback(struct hrtimer *hrt);
+
+static enum hrtimer_restart afe_hrtimer_callback(struct hrtimer *hrt)
+{
+ struct pcm_afe_info *prtd =
+ container_of(hrt, struct pcm_afe_info, hrt);
+ struct snd_pcm_substream *substream = prtd->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u32 mem_map_handle = 0;
+ if (prtd->start) {
+ pr_debug("sending frame to DSP: poll_time: %d\n",
+ prtd->poll_time);
+ if (prtd->dsp_cnt == runtime->periods)
+ prtd->dsp_cnt = 0;
+ pr_debug("%s: mem_map_handle 0x%x\n", __func__, mem_map_handle);
+ afe_rt_proxy_port_write(
+ (prtd->dma_addr +
+ (prtd->dsp_cnt *
+ snd_pcm_lib_period_bytes(prtd->substream))), mem_map_handle,
+ snd_pcm_lib_period_bytes(prtd->substream));
+ prtd->dsp_cnt++;
+ hrtimer_forward_now(hrt, ns_to_ktime(prtd->poll_time
+ * 1000));
+
+ return HRTIMER_RESTART;
+ } else
+ return HRTIMER_NORESTART;
+}
+static enum hrtimer_restart afe_hrtimer_rec_callback(struct hrtimer *hrt)
+{
+ struct pcm_afe_info *prtd =
+ container_of(hrt, struct pcm_afe_info, hrt);
+ struct snd_pcm_substream *substream = prtd->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u32 mem_map_handle = 0;
+ if (prtd->start) {
+ if (prtd->dsp_cnt == runtime->periods)
+ prtd->dsp_cnt = 0;
+ pr_err("%s: mem_map_handle 0x%x\n", __func__, mem_map_handle);
+ afe_rt_proxy_port_read(
+ (prtd->dma_addr + (prtd->dsp_cnt
+ * snd_pcm_lib_period_bytes(prtd->substream))), mem_map_handle,
+ snd_pcm_lib_period_bytes(prtd->substream));
+ prtd->dsp_cnt++;
+ pr_debug("sending frame rec to DSP: poll_time: %d\n",
+ prtd->poll_time);
+ hrtimer_forward_now(hrt, ns_to_ktime(prtd->poll_time
+ * 1000));
+
+ return HRTIMER_RESTART;
+ } else
+ return HRTIMER_NORESTART;
+}
+static void pcm_afe_process_tx_pkt(uint32_t opcode,
+ uint32_t token, uint32_t *payload,
+ void *priv)
+{
+ struct pcm_afe_info *prtd = priv;
+ unsigned long dsp_flags;
+ struct snd_pcm_substream *substream = NULL;
+ struct snd_pcm_runtime *runtime = NULL;
+ uint16_t event;
+
+ if (prtd == NULL)
+ return;
+ substream = prtd->substream;
+ runtime = substream->runtime;
+ pr_debug("%s\n", __func__);
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+ switch (opcode) {
+ case AFE_EVENT_RT_PROXY_PORT_STATUS: {
+ event = (uint16_t)((0xFFFF0000 & payload[0]) >> 0x10);
+ switch (event) {
+ case AFE_EVENT_RTPORT_START: {
+ prtd->dsp_cnt = 0;
+ prtd->poll_time = ((unsigned long)((
+ snd_pcm_lib_period_bytes
+ (prtd->substream) *
+ 1000 * 1000)/
+ (runtime->rate *
+ runtime->channels * 2)));
+ pr_debug("prtd->poll_time: %d",
+ prtd->poll_time);
+ hrtimer_start(&prtd->hrt,
+ ns_to_ktime(0),
+ HRTIMER_MODE_REL);
+ break;
+ }
+ case AFE_EVENT_RTPORT_STOP:
+ pr_debug("%s: event!=0\n", __func__);
+ prtd->start = 0;
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+ break;
+ case AFE_EVENT_RTPORT_LOW_WM:
+ pr_debug("%s: Underrun\n", __func__);
+ break;
+ case AFE_EVENT_RTPORT_HI_WM:
+ pr_debug("%s: Overrun\n", __func__);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case AFE_PORT_DATA_CMD_RT_PROXY_PORT_WRITE_V2:
+ pr_debug("write done\n");
+ prtd->pcm_irq_pos += snd_pcm_lib_period_bytes
+ (prtd->substream);
+ snd_pcm_period_elapsed(prtd->substream);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+}
+
+static void pcm_afe_process_rx_pkt(uint32_t opcode,
+ uint32_t token, uint32_t *payload,
+ void *priv)
+{
+ struct pcm_afe_info *prtd = priv;
+ unsigned long dsp_flags;
+ struct snd_pcm_substream *substream = NULL;
+ struct snd_pcm_runtime *runtime = NULL;
+ uint16_t event;
+
+ if (prtd == NULL)
+ return;
+ substream = prtd->substream;
+ runtime = substream->runtime;
+ pr_debug("%s\n", __func__);
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+ switch (opcode) {
+ case AFE_EVENT_RT_PROXY_PORT_STATUS: {
+ event = (uint16_t)((0xFFFF0000 & payload[0]) >> 0x10);
+ switch (event) {
+ case AFE_EVENT_RTPORT_START: {
+ prtd->dsp_cnt = 0;
+ prtd->poll_time = ((unsigned long)((
+ snd_pcm_lib_period_bytes(prtd->substream)
+ * 1000 * 1000)/(runtime->rate
+ * runtime->channels * 2)));
+ hrtimer_start(&prtd->hrt,
+ ns_to_ktime(0),
+ HRTIMER_MODE_REL);
+ pr_debug("prtd->poll_time : %d", prtd->poll_time);
+ break;
+ }
+ case AFE_EVENT_RTPORT_STOP:
+ pr_debug("%s: event!=0\n", __func__);
+ prtd->start = 0;
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+ break;
+ case AFE_EVENT_RTPORT_LOW_WM:
+ pr_debug("%s: Underrun\n", __func__);
+ break;
+ case AFE_EVENT_RTPORT_HI_WM:
+ pr_debug("%s: Overrun\n", __func__);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2:
+ pr_debug("Read done\n");
+ prtd->pcm_irq_pos += snd_pcm_lib_period_bytes
+ (prtd->substream);
+ snd_pcm_period_elapsed(prtd->substream);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+}
+
+static int msm_afe_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ int ret = 0;
+
+ pr_debug("%s: sample_rate=%d\n", __func__, runtime->rate);
+
+ pr_debug("%s: dai->id =%x\n", __func__, dai->id);
+ ret = afe_register_get_events(dai->id,
+ pcm_afe_process_tx_pkt, prtd);
+ if (ret < 0) {
+ pr_err("afe-pcm:register for events failed\n");
+ return ret;
+ }
+ pr_debug("%s:success\n", __func__);
+ prtd->prepared++;
+ return ret;
+}
+
+static int msm_afe_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+
+ pr_debug("%s: dai->id =%x\n", __func__, dai->id);
+ ret = afe_register_get_events(dai->id,
+ pcm_afe_process_rx_pkt, prtd);
+ if (ret < 0) {
+ pr_err("afe-pcm:register for events failed\n");
+ return ret;
+ }
+ pr_debug("%s:success\n", __func__);
+ prtd->prepared++;
+ return 0;
+}
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 16000, 48000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static int msm_afe_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = NULL;
+ int ret = 0;
+
+ prtd = kzalloc(sizeof(struct pcm_afe_info), GFP_KERNEL);
+ if (prtd == NULL) {
+ pr_err("Failed to allocate memory for msm_audio\n");
+ return -ENOMEM;
+ } else
+ pr_debug("prtd %x\n", (unsigned int)prtd);
+
+ mutex_init(&prtd->lock);
+ spin_lock_init(&prtd->dsp_lock);
+ prtd->dsp_cnt = 0;
+
+ mutex_lock(&prtd->lock);
+
+ runtime->hw = msm_afe_hardware;
+ prtd->substream = substream;
+ runtime->private_data = prtd;
+ mutex_unlock(&prtd->lock);
+ hrtimer_init(&prtd->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ prtd->hrt.function = afe_hrtimer_callback;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ prtd->hrt.function = afe_hrtimer_rec_callback;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_err("snd_pcm_hw_constraint_list failed\n");
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ pr_err("snd_pcm_hw_constraint_integer failed\n");
+
+ return 0;
+}
+
+static int msm_afe_close(struct snd_pcm_substream *substream)
+{
+ int rc = 0;
+ struct snd_dma_buffer *dma_buf;
+ struct snd_pcm_runtime *runtime;
+ struct pcm_afe_info *prtd;
+ struct snd_soc_pcm_runtime *rtd = NULL;
+ struct snd_soc_dai *dai = NULL;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ if (substream == NULL) {
+ pr_err("substream is NULL\n");
+ return -EINVAL;
+ }
+ rtd = substream->private_data;
+ dai = rtd->cpu_dai;
+ runtime = substream->runtime;
+ prtd = runtime->private_data;
+
+ mutex_lock(&prtd->lock);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = afe_unregister_get_events(dai->id);
+ if (ret < 0)
+ pr_err("AFE unregister for events failed\n");
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = afe_unregister_get_events(dai->id);
+ if (ret < 0)
+ pr_err("AFE unregister for events failed\n");
+ }
+ hrtimer_cancel(&prtd->hrt);
+
+ rc = afe_cmd_memory_unmap(runtime->dma_addr);
+ if (rc < 0)
+ pr_err("AFE memory unmap failed\n");
+
+ pr_debug("release all buffer\n");
+ dma_buf = &substream->dma_buffer;
+ if (dma_buf == NULL) {
+ pr_debug("dma_buf is NULL\n");
+ goto done;
+ }
+ if (dma_buf->area != NULL) {
+ dma_free_coherent(substream->pcm->card->dev,
+ runtime->hw.buffer_bytes_max, dma_buf->area,
+ dma_buf->addr);
+ dma_buf->area = NULL;
+ }
+done:
+ pr_debug("%s: dai->id =%x\n", __func__, dai->id);
+ mutex_unlock(&prtd->lock);
+ prtd->prepared--;
+ kfree(prtd);
+ return 0;
+}
+static int msm_afe_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+
+ prtd->pcm_irq_pos = 0;
+ if (prtd->prepared)
+ return 0;
+ mutex_lock(&prtd->lock);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_afe_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_afe_capture_prepare(substream);
+ mutex_unlock(&prtd->lock);
+ return ret;
+}
+static int msm_afe_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+ prtd->mmap_flag = 1;
+ dma_mmap_coherent(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+ return 0;
+}
+static int msm_afe_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("%s: SNDRV_PCM_TRIGGER_START\n", __func__);
+ prtd->start = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__);
+ prtd->start = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+static int msm_afe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct pcm_afe_info *prtd = runtime->private_data;
+ int rc;
+
+ pr_debug("%s:\n", __func__);
+
+ mutex_lock(&prtd->lock);
+
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+ dma_buf->area = dma_alloc_coherent(dma_buf->dev.dev,
+ runtime->hw.buffer_bytes_max,
+ &dma_buf->addr, GFP_KERNEL);
+
+ pr_debug("%s: dma_buf->area: 0x%p, dma_buf->addr: 0x%x", __func__,
+ (unsigned int *) dma_buf->area, dma_buf->addr);
+ if (!dma_buf->area) {
+ pr_err("%s:MSM AFE memory allocation failed\n", __func__);
+ mutex_unlock(&prtd->lock);
+ return -ENOMEM;
+ }
+ dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max);
+ prtd->dma_addr = (u32) dma_buf->addr;
+
+ mutex_unlock(&prtd->lock);
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ rc = afe_cmd_memory_map(dma_buf->addr, dma_buf->bytes);
+ if (rc < 0)
+ pr_err("fail to map memory to DSP\n");
+
+ return rc;
+}
+static snd_pcm_uframes_t msm_afe_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+
+ if (prtd->pcm_irq_pos >= snd_pcm_lib_buffer_bytes(substream))
+ prtd->pcm_irq_pos = 0;
+
+ pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
+ return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static struct snd_pcm_ops msm_afe_ops = {
+ .open = msm_afe_open,
+ .hw_params = msm_afe_hw_params,
+ .trigger = msm_afe_trigger,
+ .close = msm_afe_close,
+ .prepare = msm_afe_prepare,
+ .mmap = msm_afe_mmap,
+ .pointer = msm_afe_pointer,
+};
+
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return ret;
+}
+
+static int msm_afe_afe_probe(struct snd_soc_platform *platform)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_afe_ops,
+ .pcm_new = msm_asoc_pcm_new,
+ .probe = msm_afe_afe_probe,
+};
+
+static __devinit int msm_afe_probe(struct platform_device *pdev)
+{
+ pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_afe_remove(struct platform_device *pdev)
+{
+ pr_debug("%s\n", __func__);
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_afe_driver = {
+ .driver = {
+ .name = "msm-pcm-afe",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_afe_probe,
+ .remove = __devexit_p(msm_afe_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ pr_debug("%s\n", __func__);
+ return platform_driver_register(&msm_afe_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ pr_debug("%s\n", __func__);
+ platform_driver_unregister(&msm_afe_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("AFE PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h
new file mode 100644
index 0000000..20d6377
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h
@@ -0,0 +1,45 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _MSM_PCM_AFE_H
+#define _MSM_PCM_AFE_H
+#include <sound/apr_audio-v2.h>
+#include <sound/q6afe-v2.h>
+
+
+struct pcm_afe_info {
+ unsigned long dma_addr;
+ struct snd_pcm_substream *substream;
+ unsigned int pcm_irq_pos; /* IRQ position */
+ struct mutex lock;
+ spinlock_t dsp_lock;
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+ uint8_t start;
+ uint32_t dsp_cnt;
+ uint32_t buf_phys;
+ int32_t mmap_flag;
+ int prepared;
+ struct hrtimer hrt;
+ int poll_time;
+};
+
+
+#define MSM_EXT(xname, fp_info, fp_get, fp_put, addr) \
+ {.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .name = xname, \
+ .info = fp_info,\
+ .get = fp_get, .put = fp_put, \
+ .private_value = addr, \
+ }
+
+#endif /*_MSM_PCM_AFE_H*/
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
new file mode 100644
index 0000000..ee92753
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
@@ -0,0 +1,609 @@
+/* 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/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/android_pmem.h>
+#include <sound/snd_compress_params.h>
+#include <sound/compress_offload.h>
+#include <sound/compress_driver.h>
+#include <sound/timer.h>
+
+#include "msm-pcm-q6-v2.h"
+#include "msm-pcm-routing-v2.h"
+
+static struct audio_locks the_locks;
+
+struct snd_msm {
+ struct msm_audio *prtd;
+ unsigned volume;
+};
+static struct snd_msm lpa_audio;
+
+static struct snd_pcm_hardware msm_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 2 * 1024 * 1024,
+ .period_bytes_min = 128 * 1024,
+ .period_bytes_max = 512 * 1024,
+ .periods_min = 4,
+ .periods_max = 16,
+ .fifo_size = 0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static void event_handler(uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv)
+{
+ struct msm_audio *prtd = priv;
+ struct snd_pcm_substream *substream = prtd->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_aio_write_param param;
+ struct audio_buffer *buf = NULL;
+ unsigned long flag = 0;
+ int i = 0;
+
+ pr_debug("%s\n", __func__);
+ spin_lock_irqsave(&the_locks.event_lock, flag);
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE_V2: {
+ uint32_t *ptrmem = (uint32_t *)¶m;
+ pr_debug("ASM_DATA_EVENT_WRITE_DONE_V2\n");
+ pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
+ prtd->pcm_irq_pos += prtd->pcm_count;
+ if (prtd->pcm_irq_pos >= prtd->pcm_size)
+ prtd->pcm_irq_pos = 0;
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ else
+ if (substream->timer_running)
+ snd_timer_interrupt(substream->timer, 1);
+
+ atomic_inc(&prtd->out_count);
+ wake_up(&the_locks.write_wait);
+ if (!atomic_read(&prtd->start)) {
+ atomic_set(&prtd->pending_buffer, 1);
+ break;
+ } else
+ atomic_set(&prtd->pending_buffer, 0);
+ if (runtime->status->hw_ptr >= runtime->control->appl_ptr)
+ break;
+ pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
+ __func__, prtd->pcm_count);
+
+ buf = prtd->audio_client->port[IN].buf;
+ param.paddr = (unsigned long)buf[0].phys
+ + (prtd->out_head * prtd->pcm_count);
+ param.len = prtd->pcm_count;
+ param.msw_ts = 0;
+ param.lsw_ts = 0;
+ param.flags = NO_TIMESTAMP;
+ param.uid = (unsigned long)buf[0].phys
+ + (prtd->out_head * prtd->pcm_count);
+ for (i = 0; i < sizeof(struct audio_aio_write_param)/4;
+ i++, ++ptrmem)
+ pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
+ if (q6asm_async_write(prtd->audio_client,
+ ¶m) < 0)
+ pr_err("%s:q6asm_async_write failed\n",
+ __func__);
+ else
+ prtd->out_head =
+ (prtd->out_head + 1) & (runtime->periods - 1);
+ atomic_set(&prtd->pending_buffer, 0);
+ break;
+ }
+ case ASM_DATA_EVENT_RENDERED_EOS:
+ pr_debug("ASM_DATA_CMDRSP_EOS\n");
+ prtd->cmd_ack = 1;
+ wake_up(&the_locks.eos_wait);
+ break;
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case ASM_SESSION_CMD_RUN_V2: {
+ if (!atomic_read(&prtd->pending_buffer))
+ break;
+ if (runtime->status->hw_ptr >=
+ runtime->control->appl_ptr)
+ break;
+ pr_debug("%s:writing %d bytes"
+ " of buffer to dsp\n",
+ __func__, prtd->pcm_count);
+ buf = prtd->audio_client->port[IN].buf;
+ param.paddr = (unsigned long)buf[prtd->out_head].phys;
+ param.len = prtd->pcm_count;
+ param.msw_ts = 0;
+ param.lsw_ts = 0;
+ param.flags = NO_TIMESTAMP;
+ param.uid = (unsigned long)buf[prtd->out_head].phys;
+ if (q6asm_async_write(prtd->audio_client,
+ ¶m) < 0)
+ pr_err("%s:q6asm_async_write failed\n",
+ __func__);
+ else
+ prtd->out_head =
+ (prtd->out_head + 1)
+ & (runtime->periods - 1);
+ atomic_set(&prtd->pending_buffer, 0);
+ }
+ break;
+ case ASM_STREAM_CMD_FLUSH:
+ pr_debug("ASM_STREAM_CMD_FLUSH\n");
+ prtd->cmd_ack = 1;
+ wake_up(&the_locks.eos_wait);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
+ break;
+ }
+ spin_unlock_irqrestore(&the_locks.event_lock, flag);
+}
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int ret;
+
+ pr_debug("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+ prtd->out_head = 0;
+ if (prtd->enabled)
+ return 0;
+
+ ret = q6asm_media_format_block_pcm(prtd->audio_client, runtime->rate,
+ runtime->channels);
+ if (ret < 0)
+ pr_debug("%s: CMD Format block failed\n", __func__);
+
+ atomic_set(&prtd->out_count, runtime->periods);
+ prtd->enabled = 1;
+ prtd->cmd_ack = 0;
+ return 0;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ pr_debug("%s\n", __func__);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ prtd->pcm_irq_pos = 0;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("SNDRV_PCM_TRIGGER_START\n");
+ q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+ atomic_set(&prtd->start, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ atomic_set(&prtd->start, 0);
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ break;
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
+ q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ atomic_set(&prtd->start, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+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;
+ struct asm_softpause_params softpause = {
+ .enable = SOFT_PAUSE_ENABLE,
+ .period = SOFT_PAUSE_PERIOD,
+ .step = SOFT_PAUSE_STEP,
+ .rampingcurve = SOFT_PAUSE_CURVE_LINEAR,
+ };
+ struct asm_softvolume_params softvol = {
+ .period = SOFT_VOLUME_PERIOD,
+ .step = SOFT_VOLUME_STEP,
+ .rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
+ };
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
+ if (prtd == NULL) {
+ pr_err("Failed to allocate memory for msm_audio\n");
+ return -ENOMEM;
+ }
+ runtime->hw = msm_pcm_hardware;
+ prtd->substream = substream;
+ prtd->audio_client = q6asm_audio_client_alloc(
+ (app_cb)event_handler, prtd);
+ if (!prtd->audio_client) {
+ pr_debug("%s: Could not allocate memory\n", __func__);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_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;
+ }
+ ret = q6asm_set_io_mode(prtd->audio_client, ASYNC_IO_MODE);
+ if (ret < 0) {
+ pr_err("%s: Set IO mode failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ }
+ /* Capture path */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return -EPERM;
+ 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);
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_debug("snd_pcm_hw_constraint_list failed\n");
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ pr_debug("snd_pcm_hw_constraint_integer failed\n");
+
+ prtd->dsp_cnt = 0;
+ atomic_set(&prtd->pending_buffer, 1);
+ runtime->private_data = prtd;
+ lpa_audio.prtd = prtd;
+ lpa_set_volume(lpa_audio.volume);
+ ret = q6asm_set_softpause(lpa_audio.prtd->audio_client, &softpause);
+ if (ret < 0)
+ pr_err("%s: Send SoftPause Param failed ret=%d\n",
+ __func__, ret);
+ ret = q6asm_set_softvolume(lpa_audio.prtd->audio_client, &softvol);
+ if (ret < 0)
+ pr_err("%s: Send SoftVolume Param failed ret=%d\n",
+ __func__, ret);
+
+ return 0;
+}
+
+int lpa_set_volume(unsigned volume)
+{
+ int rc = 0;
+ if (lpa_audio.prtd && lpa_audio.prtd->audio_client) {
+ rc = q6asm_set_volume(lpa_audio.prtd->audio_client, volume);
+ if (rc < 0) {
+ pr_err("%s: Send Volume command failed"
+ " rc=%d\n", __func__, rc);
+ }
+ }
+ lpa_audio.volume = volume;
+ return rc;
+}
+
+static int msm_pcm_playback_close(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 = runtime->private_data;
+ int dir = 0;
+ int rc = 0;
+
+ /*
+ If routing is still enabled, we need to issue EOS to
+ the DSP
+ To issue EOS to dsp, we need to be run state otherwise
+ EOS is not honored.
+ */
+ if (msm_routing_check_backend_enabled(soc_prtd->dai_link->be_id)) {
+ rc = q6asm_run(prtd->audio_client, 0, 0, 0);
+ atomic_set(&prtd->pending_buffer, 0);
+ prtd->cmd_ack = 0;
+ q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
+ pr_debug("%s\n", __func__);
+ rc = wait_event_timeout(the_locks.eos_wait,
+ prtd->cmd_ack, 5 * HZ);
+ if (rc < 0)
+ pr_err("EOS cmd timeout\n");
+ prtd->pcm_irq_pos = 0;
+ }
+
+ dir = IN;
+ atomic_set(&prtd->pending_buffer, 0);
+ lpa_audio.prtd = NULL;
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+
+ pr_debug("%s\n", __func__);
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ pr_debug("%s\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+
+ return 0;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_close(substream);
+ return ret;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_prepare(substream);
+ return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ pr_debug("%s: pcm_irq_pos = %d\n", __func__, prtd->pcm_irq_pos);
+ return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static int msm_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ int result = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+ prtd->mmap_flag = 1;
+
+ if (runtime->dma_addr && runtime->dma_bytes) {
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ result = remap_pfn_range(vma, vma->vm_start,
+ runtime->dma_addr >> PAGE_SHIFT,
+ runtime->dma_bytes,
+ vma->vm_page_prot);
+ } else {
+ pr_err("Physical address or size of buf is NULL");
+ return -EINVAL;
+ }
+ return result;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct audio_buffer *buf;
+ int dir, ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ return -EPERM;
+ ret = q6asm_audio_client_buf_alloc_contiguous(dir,
+ prtd->audio_client,
+ runtime->hw.period_bytes_min,
+ runtime->hw.periods_max);
+ if (ret < 0) {
+ pr_err("Audio Start: Buffer Allocation failed "
+ "rc = %d\n", ret);
+ return -ENOMEM;
+ }
+ buf = prtd->audio_client->port[dir].buf;
+
+ if (buf == NULL || buf[0].data == NULL)
+ return -ENOMEM;
+
+ pr_debug("%s:buf = %p\n", __func__, buf);
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+ dma_buf->area = buf[0].data;
+ dma_buf->addr = buf[0].phys;
+ dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ if (!dma_buf->area)
+ return -ENOMEM;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ return 0;
+}
+
+static int msm_pcm_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ int rc = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ uint64_t timestamp;
+ uint64_t temp;
+
+ switch (cmd) {
+ case SNDRV_COMPRESS_TSTAMP: {
+ struct snd_compr_tstamp tstamp;
+ pr_debug("SNDRV_COMPRESS_TSTAMP\n");
+
+ memset(&tstamp, 0x0, sizeof(struct snd_compr_tstamp));
+ timestamp = q6asm_get_session_time(prtd->audio_client);
+ if (timestamp < 0) {
+ pr_err("%s: Get Session Time return value =%lld\n",
+ __func__, timestamp);
+ return -EAGAIN;
+ }
+ temp = (timestamp * 2 * runtime->channels);
+ temp = temp * (runtime->rate/1000);
+ temp = div_u64(temp, 1000);
+ tstamp.sampling_rate = runtime->rate;
+ tstamp.rendered = (size_t)(temp & 0xFFFFFFFF);
+ tstamp.decoded = (size_t)((temp >> 32) & 0xFFFFFFFF);
+ tstamp.timestamp = timestamp;
+ pr_debug("%s: bytes_consumed:lsb = %d, msb = %d,"
+ "timestamp = %lld,\n",
+ __func__, tstamp.rendered, tstamp.decoded,
+ tstamp.timestamp);
+ if (copy_to_user((void *) arg, &tstamp,
+ sizeof(struct snd_compr_tstamp)))
+ return -EFAULT;
+ return 0;
+ }
+ case SNDRV_PCM_IOCTL1_RESET:
+ prtd->cmd_ack = 0;
+ rc = q6asm_cmd(prtd->audio_client, CMD_FLUSH);
+ if (rc < 0)
+ pr_err("%s: flush cmd failed rc=%d\n", __func__, rc);
+ rc = wait_event_timeout(the_locks.eos_wait,
+ prtd->cmd_ack, 5 * HZ);
+ if (rc < 0)
+ pr_err("Flush cmd timeout\n");
+ prtd->pcm_irq_pos = 0;
+ break;
+ default:
+ break;
+ }
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .hw_params = msm_pcm_hw_params,
+ .close = msm_pcm_close,
+ .ioctl = msm_pcm_ioctl,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+ .mmap = msm_pcm_mmap,
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
+};
+
+static __devinit int msm_pcm_probe(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "%s: dev name %s\n",
+ __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-lpa",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_pcm_probe,
+ .remove = __devexit_p(msm_pcm_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ spin_lock_init(&the_locks.event_lock);
+ init_waitqueue_head(&the_locks.enable_wait);
+ init_waitqueue_head(&the_locks.eos_wait);
+ init_waitqueue_head(&the_locks.write_wait);
+ init_waitqueue_head(&the_locks.read_wait);
+
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
new file mode 100644
index 0000000..f94e6c1
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
@@ -0,0 +1,725 @@
+/* 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/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/android_pmem.h>
+
+#include "msm-pcm-q6-v2.h"
+#include "msm-pcm-routing-v2.h"
+
+static struct audio_locks the_locks;
+
+struct snd_msm {
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+};
+
+#define PLAYBACK_NUM_PERIODS 8
+#define PLAYBACK_PERIOD_SIZE 2048
+#define CAPTURE_NUM_PERIODS 16
+#define CAPTURE_PERIOD_SIZE 512
+
+static struct snd_pcm_hardware msm_pcm_hardware_capture = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = CAPTURE_NUM_PERIODS * CAPTURE_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_PERIOD_SIZE,
+ .periods_min = CAPTURE_NUM_PERIODS,
+ .periods_max = CAPTURE_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+static struct snd_pcm_hardware msm_pcm_hardware_playback = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = PLAYBACK_NUM_PERIODS * PLAYBACK_PERIOD_SIZE,
+ .period_bytes_min = PLAYBACK_PERIOD_SIZE,
+ .period_bytes_max = PLAYBACK_PERIOD_SIZE,
+ .periods_min = PLAYBACK_NUM_PERIODS,
+ .periods_max = PLAYBACK_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static uint32_t in_frame_info[CAPTURE_NUM_PERIODS][2];
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static void event_handler(uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv)
+{
+ struct msm_audio *prtd = priv;
+ struct snd_pcm_substream *substream = prtd->substream;
+ uint32_t *ptrmem = (uint32_t *)payload;
+ uint32_t idx = 0;
+ uint32_t size = 0;
+
+ pr_err("%s\n", __func__);
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE_V2: {
+ pr_debug("ASM_DATA_EVENT_WRITE_DONE_V2\n");
+ pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
+ prtd->pcm_irq_pos += prtd->pcm_count;
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ atomic_inc(&prtd->out_count);
+ wake_up(&the_locks.write_wait);
+ if (!atomic_read(&prtd->start))
+ break;
+ if (!prtd->mmap_flag)
+ break;
+ if (q6asm_is_cpu_buf_avail_nolock(IN,
+ prtd->audio_client,
+ &size, &idx)) {
+ pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
+ __func__, prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count, 0, 0, NO_TIMESTAMP);
+ }
+ break;
+ }
+ case ASM_DATA_EVENT_RENDERED_EOS:
+ pr_debug("ASM_DATA_EVENT_RENDERED_EOS\n");
+ prtd->cmd_ack = 1;
+ wake_up(&the_locks.eos_wait);
+ break;
+ case ASM_DATA_EVENT_READ_DONE_V2: {
+ pr_debug("ASM_DATA_EVENT_READ_DONE_V2\n");
+ pr_debug("token = 0x%08x\n", token);
+ in_frame_info[token][0] = payload[4];
+ in_frame_info[token][1] = payload[5];
+ prtd->pcm_irq_pos += in_frame_info[token][0];
+ pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos);
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ if (atomic_read(&prtd->in_count) <= prtd->periods)
+ atomic_inc(&prtd->in_count);
+ wake_up(&the_locks.read_wait);
+ if (prtd->mmap_flag
+ && q6asm_is_cpu_buf_avail_nolock(OUT,
+ prtd->audio_client,
+ &size, &idx))
+ q6asm_read_nolock(prtd->audio_client);
+ break;
+ }
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case ASM_SESSION_CMD_RUN_V2:
+ if (substream->stream
+ != SNDRV_PCM_STREAM_PLAYBACK) {
+ atomic_set(&prtd->start, 1);
+ break;
+ }
+ if (prtd->mmap_flag) {
+ pr_debug("%s:writing %d bytes"
+ " of buffer to dsp\n",
+ __func__,
+ prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count,
+ 0, 0, NO_TIMESTAMP);
+ } else {
+ while (atomic_read(&prtd->out_needed)) {
+ pr_debug("%s:writing %d bytes"
+ " of buffer to dsp\n",
+ __func__,
+ prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count,
+ 0, 0, NO_TIMESTAMP);
+ atomic_dec(&prtd->out_needed);
+ wake_up(&the_locks.write_wait);
+ };
+ }
+ atomic_set(&prtd->start, 1);
+ break;
+ default:
+ pr_debug("%s:Payload = [0x%x]stat[0x%x]\n",
+ __func__, payload[0], payload[1]);
+ break;
+ }
+ }
+ break;
+ default:
+ pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
+ break;
+ }
+}
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int ret;
+
+ pr_debug("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+ if (prtd->enabled)
+ return 0;
+
+ ret = q6asm_media_format_block_pcm(prtd->audio_client, runtime->rate,
+ runtime->channels);
+ if (ret < 0)
+ pr_info("%s: CMD Format block failed\n", __func__);
+
+ atomic_set(&prtd->out_count, runtime->periods);
+
+ prtd->enabled = 1;
+ prtd->cmd_ack = 0;
+
+ return 0;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int ret = 0;
+ int i = 0;
+ pr_debug("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+
+ if (prtd->enabled)
+ return 0;
+
+ pr_debug("Samp_rate = %d\n", prtd->samp_rate);
+ pr_debug("Channel = %d\n", prtd->channel_mode);
+ ret = q6asm_enc_cfg_blk_pcm(prtd->audio_client, prtd->samp_rate,
+ prtd->channel_mode);
+ if (ret < 0)
+ pr_debug("%s: cmd cfg pcm was block failed", __func__);
+
+ for (i = 0; i < runtime->periods; i++)
+ q6asm_read(prtd->audio_client);
+ prtd->periods = runtime->periods;
+
+ prtd->enabled = 1;
+
+ return ret;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("%s: Trigger start\n", __func__);
+ q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ atomic_set(&prtd->start, 0);
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ break;
+ prtd->cmd_ack = 0;
+ q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
+ q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ atomic_set(&prtd->start, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+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;
+
+ pr_debug("%s\n", __func__);
+ prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
+ if (prtd == NULL) {
+ pr_err("Failed to allocate memory for msm_audio\n");
+ return -ENOMEM;
+ }
+ prtd->substream = substream;
+ prtd->audio_client = q6asm_audio_client_alloc(
+ (app_cb)event_handler, prtd);
+ if (!prtd->audio_client) {
+ pr_info("%s: Could not allocate memory\n", __func__);
+ 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;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_info("snd_pcm_hw_constraint_list failed\n");
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ pr_info("snd_pcm_hw_constraint_integer failed\n");
+
+ prtd->dsp_cnt = 0;
+ runtime->private_data = prtd;
+
+ return 0;
+}
+
+static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int fbytes = 0;
+ int xfer = 0;
+ char *bufptr = NULL;
+ void *data = NULL;
+ uint32_t idx = 0;
+ uint32_t size = 0;
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ fbytes = frames_to_bytes(runtime, frames);
+ pr_debug("%s: prtd->out_count = %d\n",
+ __func__, atomic_read(&prtd->out_count));
+ ret = wait_event_timeout(the_locks.write_wait,
+ (atomic_read(&prtd->out_count)), 5 * HZ);
+ if (ret < 0) {
+ pr_err("%s: wait_event_timeout failed\n", __func__);
+ goto fail;
+ }
+
+ if (!atomic_read(&prtd->out_count)) {
+ pr_err("%s: pcm stopped out_count 0\n", __func__);
+ return 0;
+ }
+
+ data = q6asm_is_cpu_buf_avail(IN, prtd->audio_client, &size, &idx);
+ bufptr = data;
+ if (bufptr) {
+ pr_debug("%s:fbytes =%d: xfer=%d size=%d\n",
+ __func__, fbytes, xfer, size);
+ xfer = fbytes;
+ if (copy_from_user(bufptr, buf, xfer)) {
+ ret = -EFAULT;
+ goto fail;
+ }
+ buf += xfer;
+ fbytes -= xfer;
+ pr_debug("%s:fbytes = %d: xfer=%d\n", __func__, fbytes, xfer);
+ if (atomic_read(&prtd->start)) {
+ pr_debug("%s:writing %d bytes of buffer to dsp\n",
+ __func__, xfer);
+ ret = q6asm_write(prtd->audio_client, xfer,
+ 0, 0, NO_TIMESTAMP);
+ if (ret < 0) {
+ ret = -EFAULT;
+ goto fail;
+ }
+ } else
+ atomic_inc(&prtd->out_needed);
+ atomic_dec(&prtd->out_count);
+ }
+fail:
+ return ret;
+}
+
+static int msm_pcm_playback_close(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 = runtime->private_data;
+ int dir = 0;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+
+ dir = IN;
+ ret = wait_event_timeout(the_locks.eos_wait,
+ prtd->cmd_ack, 5 * HZ);
+ if (ret < 0)
+ pr_err("%s: CMD_EOS failed\n", __func__);
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return 0;
+}
+
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t hwoff, void __user *buf,
+ snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int fbytes = 0;
+ int xfer;
+ char *bufptr;
+ void *data = NULL;
+ static uint32_t idx;
+ static uint32_t size;
+ uint32_t offset = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = substream->runtime->private_data;
+
+
+ pr_debug("%s\n", __func__);
+ fbytes = frames_to_bytes(runtime, frames);
+
+ pr_debug("appl_ptr %d\n", (int)runtime->control->appl_ptr);
+ pr_debug("hw_ptr %d\n", (int)runtime->status->hw_ptr);
+ pr_debug("avail_min %d\n", (int)runtime->control->avail_min);
+
+ ret = wait_event_timeout(the_locks.read_wait,
+ (atomic_read(&prtd->in_count)), 5 * HZ);
+ if (ret < 0) {
+ pr_debug("%s: wait_event_timeout failed\n", __func__);
+ goto fail;
+ }
+ if (!atomic_read(&prtd->in_count)) {
+ pr_debug("%s: pcm stopped in_count 0\n", __func__);
+ return 0;
+ }
+ pr_debug("Checking if valid buffer is available...%08x\n",
+ (unsigned int) data);
+ data = q6asm_is_cpu_buf_avail(OUT, prtd->audio_client, &size, &idx);
+ bufptr = data;
+ pr_debug("Size = %d\n", size);
+ pr_debug("fbytes = %d\n", fbytes);
+ pr_debug("idx = %d\n", idx);
+ if (bufptr) {
+ xfer = fbytes;
+ if (xfer > size)
+ xfer = size;
+ offset = in_frame_info[idx][1];
+ pr_debug("Offset value = %d\n", offset);
+ if (copy_to_user(buf, bufptr+offset, xfer)) {
+ pr_err("Failed to copy buf to user\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+ fbytes -= xfer;
+ size -= xfer;
+ in_frame_info[idx][1] += xfer;
+ pr_debug("%s:fbytes = %d: size=%d: xfer=%d\n",
+ __func__, fbytes, size, xfer);
+ pr_debug(" Sending next buffer to dsp\n");
+ memset(&in_frame_info[idx], 0,
+ sizeof(uint32_t) * 2);
+ atomic_dec(&prtd->in_count);
+ ret = q6asm_read(prtd->audio_client);
+ if (ret < 0) {
+ pr_err("q6asm read failed\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+ } else
+ pr_err("No valid buffer\n");
+
+ pr_debug("Returning from capture_copy... %d\n", ret);
+fail:
+ return ret;
+}
+
+static int msm_pcm_capture_close(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 = runtime->private_data;
+ int dir = OUT;
+
+ pr_debug("%s\n", __func__);
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ 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);
+ kfree(prtd);
+
+ return 0;
+}
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
+ return ret;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_close(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_close(substream);
+ return ret;
+}
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_prepare(substream);
+ return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ if (prtd->pcm_irq_pos >= prtd->pcm_size)
+ prtd->pcm_irq_pos = 0;
+
+ pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
+ return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static int msm_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ int result = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+ prtd->mmap_flag = 1;
+
+ if (runtime->dma_addr && runtime->dma_bytes) {
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ result = remap_pfn_range(vma, vma->vm_start,
+ runtime->dma_addr >> PAGE_SHIFT,
+ runtime->dma_bytes,
+ vma->vm_page_prot);
+ } else {
+ pr_err("Physical address or size of buf is NULL");
+ return -EINVAL;
+ }
+
+ return result;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct audio_buffer *buf;
+ int dir, ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ dir = OUT;
+pr_err("%s: before buf alloc\n", __func__);
+ ret = q6asm_audio_client_buf_alloc_contiguous(dir,
+ prtd->audio_client,
+ runtime->hw.period_bytes_min,
+ runtime->hw.periods_max);
+ if (ret < 0) {
+ pr_err("Audio Start: Buffer Allocation failed "
+ "rc = %d\n", ret);
+ return -ENOMEM;
+ }
+pr_err("%s: after buf alloc\n", __func__);
+ buf = prtd->audio_client->port[dir].buf;
+ if (buf == NULL || buf[0].data == NULL)
+ return -ENOMEM;
+
+ pr_debug("%s:buf = %p\n", __func__, buf);
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+ dma_buf->area = buf[0].data;
+ dma_buf->addr = buf[0].phys;
+ dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ if (!dma_buf->area)
+ return -ENOMEM;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ return 0;
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .copy = msm_pcm_copy,
+ .hw_params = msm_pcm_hw_params,
+ .close = msm_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+ .mmap = msm_pcm_mmap,
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
+};
+
+static __devinit int msm_pcm_probe(struct platform_device *pdev)
+{
+ pr_info("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-dsp",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_pcm_probe,
+ .remove = __devexit_p(msm_pcm_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ init_waitqueue_head(&the_locks.enable_wait);
+ init_waitqueue_head(&the_locks.eos_wait);
+ init_waitqueue_head(&the_locks.write_wait);
+ init_waitqueue_head(&the_locks.read_wait);
+
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
new file mode 100644
index 0000000..44395b7
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2012 Code Aurora Forum. 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
+ * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org.
+ */
+
+#ifndef _MSM_PCM_H
+#define _MSM_PCM_H
+#include <sound/apr_audio-v2.h>
+#include <sound/q6asm-v2.h>
+
+
+
+/* Support unconventional sample rates 12000, 24000 as well */
+#define USE_RATE \
+ (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
+
+extern int copy_count;
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used;
+ unsigned addr;
+};
+
+struct buffer_rec {
+ void *data;
+ unsigned int size;
+ unsigned int read;
+ unsigned int addr;
+};
+
+struct audio_locks {
+ spinlock_t event_lock;
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait;
+ wait_queue_head_t eos_wait;
+ wait_queue_head_t enable_wait;
+};
+
+struct msm_audio {
+ struct snd_pcm_substream *substream;
+ unsigned int pcm_size;
+ unsigned int pcm_count;
+ unsigned int pcm_irq_pos; /* IRQ position */
+ uint16_t source; /* Encoding source bit mask */
+
+ struct audio_client *audio_client;
+
+ uint16_t session_id;
+
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+ uint32_t dsp_cnt;
+
+ int abort; /* set when error, like sample rate mismatch */
+
+ int enabled;
+ int close_ack;
+ int cmd_ack;
+ atomic_t start;
+ atomic_t out_count;
+ atomic_t in_count;
+ atomic_t out_needed;
+ int out_head;
+ int periods;
+ int mmap_flag;
+ atomic_t pending_buffer;
+};
+
+#endif /*_MSM_PCM_H*/
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
new file mode 100644
index 0000000..2eebae5
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -0,0 +1,1834 @@
+/* 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/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6adm-v2.h>
+#include <sound/q6asm-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/tlv.h>
+#include "msm-pcm-routing-v2.h"
+#include "../qdsp6/q6voice.h"
+
+struct msm_pcm_routing_bdai_data {
+ u16 port_id; /* AFE port ID */
+ u8 active; /* track if this backend is enabled */
+ struct snd_pcm_hw_params *hw_params; /* to get freq and channel mode */
+ unsigned long fe_sessions; /* Front-end sessions */
+ unsigned long port_sessions; /* track Tx BE ports -> Rx BE */
+};
+
+#define INVALID_SESSION -1
+#define SESSION_TYPE_RX 0
+#define SESSION_TYPE_TX 1
+
+static struct mutex routing_lock;
+
+static int fm_switch_enable;
+
+#define INT_FM_RX_VOL_MAX_STEPS 100
+#define INT_FM_RX_VOL_GAIN 2000
+
+static int msm_route_fm_vol_control;
+static const DECLARE_TLV_DB_SCALE(fm_rx_vol_gain, 0,
+ INT_FM_RX_VOL_MAX_STEPS, 0);
+
+#define INT_RX_VOL_MAX_STEPS 100
+#define INT_RX_VOL_GAIN 0x2000
+
+static int msm_route_lpa_vol_control;
+static const DECLARE_TLV_DB_SCALE(lpa_rx_vol_gain, 0,
+ INT_RX_VOL_MAX_STEPS, 0);
+
+static int msm_route_multimedia2_vol_control;
+static const DECLARE_TLV_DB_SCALE(multimedia2_rx_vol_gain, 0,
+ INT_RX_VOL_MAX_STEPS, 0);
+
+static int msm_route_compressed_vol_control;
+static const DECLARE_TLV_DB_SCALE(compressed_rx_vol_gain, 0,
+ INT_RX_VOL_MAX_STEPS, 0);
+
+
+
+/* Equal to Frontend after last of the MULTIMEDIA SESSIONS */
+#define MAX_EQ_SESSIONS MSM_FRONTEND_DAI_CS_VOICE
+
+enum {
+ EQ_BAND1 = 0,
+ EQ_BAND2,
+ EQ_BAND3,
+ EQ_BAND4,
+ EQ_BAND5,
+ EQ_BAND6,
+ EQ_BAND7,
+ EQ_BAND8,
+ EQ_BAND9,
+ EQ_BAND10,
+ EQ_BAND11,
+ EQ_BAND12,
+ EQ_BAND_MAX,
+};
+
+struct msm_audio_eq_band {
+ uint16_t band_idx; /* The band index, 0 .. 11 */
+ uint32_t filter_type; /* Filter band type */
+ uint32_t center_freq_hz; /* Filter band center frequency */
+ uint32_t filter_gain; /* Filter band initial gain (dB) */
+ /* Range is +12 dB to -12 dB with 1dB increments. */
+ uint32_t q_factor;
+} __packed;
+
+struct msm_audio_eq_stream_config {
+ uint32_t enable; /* Number of consequtive bands specified */
+ uint32_t num_bands;
+ struct msm_audio_eq_band eq_bands[EQ_BAND_MAX];
+} __packed;
+
+struct msm_audio_eq_stream_config eq_data[MAX_EQ_SESSIONS];
+
+static void msm_send_eq_values(int eq_idx);
+/* This array is indexed by back-end DAI ID defined in msm-pcm-routing.h
+ * If new back-end is defined, add new back-end DAI ID at the end of enum
+ */
+static struct msm_pcm_routing_bdai_data msm_bedais[MSM_BACKEND_DAI_MAX] = {
+ { PRIMARY_I2S_RX, 0, NULL, 0, 0},
+ { PRIMARY_I2S_TX, 0, NULL, 0, 0},
+ { SLIMBUS_0_RX, 0, NULL, 0, 0},
+ { SLIMBUS_0_TX, 0, NULL, 0, 0},
+ { HDMI_RX, 0, NULL, 0, 0},
+ { INT_BT_SCO_RX, 0, NULL, 0, 0},
+ { INT_BT_SCO_TX, 0, NULL, 0, 0},
+ { INT_FM_RX, 0, NULL, 0, 0},
+ { INT_FM_TX, 0, NULL, 0, 0},
+ { RT_PROXY_PORT_001_RX, 0, NULL, 0, 0},
+ { RT_PROXY_PORT_001_TX, 0, NULL, 0, 0},
+ { PCM_RX, 0, NULL, 0, 0},
+ { PCM_TX, 0, NULL, 0, 0},
+ { VOICE_PLAYBACK_TX, 0, NULL, 0, 0},
+ { VOICE_RECORD_RX, 0, NULL, 0, 0},
+ { VOICE_RECORD_TX, 0, NULL, 0, 0},
+ { MI2S_RX, 0, NULL, 0, 0},
+ { SECONDARY_I2S_RX, 0, NULL, 0, 0},
+ { SLIMBUS_1_RX, 0, NULL, 0, 0},
+ { SLIMBUS_1_TX, 0, NULL, 0, 0},
+ { SLIMBUS_INVALID, 0, NULL, 0, 0},
+};
+
+
+/* Track ASM playback & capture sessions of DAI */
+static int fe_dai_map[MSM_FRONTEND_DAI_MM_SIZE][2] = {
+ /* MULTIMEDIA1 */
+ {INVALID_SESSION, INVALID_SESSION},
+ /* MULTIMEDIA2 */
+ {INVALID_SESSION, INVALID_SESSION},
+ /* MULTIMEDIA3 */
+ {INVALID_SESSION, INVALID_SESSION},
+ /* MULTIMEDIA4 */
+ {INVALID_SESSION, INVALID_SESSION},
+};
+
+static void msm_pcm_routing_build_matrix(int fedai_id, int dspst_id,
+ int path_type)
+{
+ int i, port_type;
+ struct route_payload payload;
+
+ payload.num_copps = 0;
+ port_type = (path_type == ADM_PATH_PLAYBACK ?
+ MSM_AFE_PORT_TYPE_RX : MSM_AFE_PORT_TYPE_TX);
+
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ if ((afe_get_port_type(msm_bedais[i].port_id) ==
+ port_type) &&
+ msm_bedais[i].active && (test_bit(fedai_id,
+ &msm_bedais[i].fe_sessions)))
+ payload.copp_ids[payload.num_copps++] =
+ msm_bedais[i].port_id;
+ }
+
+ if (payload.num_copps)
+ adm_matrix_map(dspst_id, path_type,
+ payload.num_copps, payload.copp_ids, 0);
+}
+
+void msm_pcm_routing_reg_phy_stream(int fedai_id, int dspst_id, int stream_type)
+{
+ int i, session_type, path_type, port_type;
+ struct route_payload payload;
+ u32 channels;
+
+ if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* bad ID assigned in machine driver */
+ pr_err("%s: bad MM ID %d\n", __func__, fedai_id);
+ return;
+ }
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
+ session_type = SESSION_TYPE_RX;
+ path_type = ADM_PATH_PLAYBACK;
+ port_type = MSM_AFE_PORT_TYPE_RX;
+ } else {
+ session_type = SESSION_TYPE_TX;
+ path_type = ADM_PATH_LIVE_REC;
+ port_type = MSM_AFE_PORT_TYPE_TX;
+ }
+
+ mutex_lock(&routing_lock);
+
+ payload.num_copps = 0; /* only RX needs to use payload */
+ fe_dai_map[fedai_id][session_type] = dspst_id;
+ /* re-enable EQ if active */
+ if (eq_data[fedai_id].enable)
+ msm_send_eq_values(fedai_id);
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ if ((afe_get_port_type(msm_bedais[i].port_id) ==
+ port_type) && msm_bedais[i].active &&
+ (test_bit(fedai_id,
+ &msm_bedais[i].fe_sessions))) {
+
+ channels = params_channels(msm_bedais[i].hw_params);
+
+ if ((stream_type == SNDRV_PCM_STREAM_PLAYBACK) &&
+ (channels > 2))
+ adm_multi_ch_copp_open(msm_bedais[i].port_id,
+ path_type,
+ params_rate(msm_bedais[i].hw_params),
+ channels,
+ DEFAULT_COPP_TOPOLOGY);
+ else
+ adm_open(msm_bedais[i].port_id,
+ path_type,
+ params_rate(msm_bedais[i].hw_params),
+ params_channels(msm_bedais[i].hw_params),
+ DEFAULT_COPP_TOPOLOGY);
+
+ payload.copp_ids[payload.num_copps++] =
+ msm_bedais[i].port_id;
+ }
+ }
+ if (payload.num_copps)
+ adm_matrix_map(dspst_id, path_type,
+ payload.num_copps, payload.copp_ids, 0);
+
+ mutex_unlock(&routing_lock);
+}
+
+void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type)
+{
+ int i, port_type, session_type;
+
+ if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* bad ID assigned in machine driver */
+ pr_err("%s: bad MM ID\n", __func__);
+ return;
+ }
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
+ port_type = MSM_AFE_PORT_TYPE_RX;
+ session_type = SESSION_TYPE_RX;
+ } else {
+ port_type = MSM_AFE_PORT_TYPE_TX;
+ session_type = SESSION_TYPE_TX;
+ }
+
+ mutex_lock(&routing_lock);
+
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ if ((afe_get_port_type(msm_bedais[i].port_id) ==
+ port_type) && msm_bedais[i].active &&
+ (test_bit(fedai_id,
+ &msm_bedais[i].fe_sessions)))
+ adm_close(msm_bedais[i].port_id);
+ }
+
+ fe_dai_map[fedai_id][session_type] = INVALID_SESSION;
+
+ mutex_unlock(&routing_lock);
+}
+
+/* Check if FE/BE route is set */
+static bool msm_pcm_routing_route_is_set(u16 be_id, u16 fe_id)
+{
+ bool rc = false;
+
+ if (fe_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* recheck FE ID in the mixer control defined in this file */
+ pr_err("%s: bad MM ID\n", __func__);
+ return rc;
+ }
+
+ if (test_bit(fe_id, &msm_bedais[be_id].fe_sessions))
+ rc = true;
+
+ return rc;
+}
+
+static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set)
+{
+ int session_type, path_type;
+ u32 channels;
+
+ pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
+
+ if (val > MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* recheck FE ID in the mixer control defined in this file */
+ pr_err("%s: bad MM ID\n", __func__);
+ return;
+ }
+
+ if (afe_get_port_type(msm_bedais[reg].port_id) ==
+ MSM_AFE_PORT_TYPE_RX) {
+ session_type = SESSION_TYPE_RX;
+ path_type = ADM_PATH_PLAYBACK;
+ } else {
+ session_type = SESSION_TYPE_TX;
+ path_type = ADM_PATH_LIVE_REC;
+ }
+
+ mutex_lock(&routing_lock);
+
+ if (set) {
+ set_bit(val, &msm_bedais[reg].fe_sessions);
+ if (msm_bedais[reg].active && fe_dai_map[val][session_type] !=
+ INVALID_SESSION) {
+
+ channels = params_channels(msm_bedais[reg].hw_params);
+
+ if ((session_type == SESSION_TYPE_RX) && (channels > 2))
+ adm_multi_ch_copp_open(msm_bedais[reg].port_id,
+ path_type,
+ params_rate(msm_bedais[reg].hw_params),
+ channels,
+ DEFAULT_COPP_TOPOLOGY);
+ else
+ adm_open(msm_bedais[reg].port_id,
+ path_type,
+ params_rate(msm_bedais[reg].hw_params),
+ params_channels(msm_bedais[reg].hw_params),
+ DEFAULT_COPP_TOPOLOGY);
+
+ msm_pcm_routing_build_matrix(val,
+ fe_dai_map[val][session_type], path_type);
+ }
+ } else {
+ clear_bit(val, &msm_bedais[reg].fe_sessions);
+ if (msm_bedais[reg].active && fe_dai_map[val][session_type] !=
+ INVALID_SESSION) {
+ adm_close(msm_bedais[reg].port_id);
+ msm_pcm_routing_build_matrix(val,
+ fe_dai_map[val][session_type], path_type);
+ }
+ }
+ mutex_unlock(&routing_lock);
+}
+
+static int msm_routing_get_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (test_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ pr_info("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+
+ if (ucontrol->value.integer.value[0] &&
+ msm_pcm_routing_route_is_set(mc->reg, mc->shift) == false) {
+ msm_pcm_routing_process_audio(mc->reg, mc->shift, 1);
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
+ } else if (!ucontrol->value.integer.value[0] &&
+ msm_pcm_routing_route_is_set(mc->reg, mc->shift) == true) {
+ msm_pcm_routing_process_audio(mc->reg, mc->shift, 0);
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
+ }
+ pr_info("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+ ucontrol->value.integer.value[0]);
+
+ return 1;
+}
+
+static void msm_pcm_routing_process_voice(u16 reg, u16 val, int set)
+{
+ return;
+}
+
+static int msm_routing_get_voice_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ mutex_lock(&routing_lock);
+
+ if (test_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ mutex_unlock(&routing_lock);
+
+ pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_routing_put_voice_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (ucontrol->value.integer.value[0]) {
+ msm_pcm_routing_process_voice(mc->reg, mc->shift, 1);
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
+ } else {
+ msm_pcm_routing_process_voice(mc->reg, mc->shift, 0);
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
+ }
+
+ return 1;
+}
+
+static int msm_routing_get_voice_stub_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ mutex_lock(&routing_lock);
+
+ if (test_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ mutex_unlock(&routing_lock);
+
+ pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_routing_put_voice_stub_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (ucontrol->value.integer.value[0]) {
+ mutex_lock(&routing_lock);
+ set_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions);
+ mutex_unlock(&routing_lock);
+
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
+ } else {
+ mutex_lock(&routing_lock);
+ clear_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions);
+ mutex_unlock(&routing_lock);
+
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
+ }
+
+ pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+ ucontrol->value.integer.value[0]);
+
+ return 1;
+}
+
+static int msm_routing_get_switch_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = fm_switch_enable;
+ pr_debug("%s: FM Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_routing_put_switch_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+ pr_debug("%s: FM Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ if (ucontrol->value.integer.value[0])
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
+ else
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
+ fm_switch_enable = ucontrol->value.integer.value[0];
+ return 1;
+}
+
+static int msm_routing_get_port_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (test_bit(mc->shift, &msm_bedais[mc->reg].port_sessions))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_routing_put_port_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg,
+ mc->shift, ucontrol->value.integer.value[0]);
+
+ if (ucontrol->value.integer.value[0]) {
+ afe_loopback(1, msm_bedais[mc->reg].port_id,
+ msm_bedais[mc->shift].port_id);
+ set_bit(mc->shift,
+ &msm_bedais[mc->reg].port_sessions);
+ } else {
+ afe_loopback(0, msm_bedais[mc->reg].port_id,
+ msm_bedais[mc->shift].port_id);
+ clear_bit(mc->shift,
+ &msm_bedais[mc->reg].port_sessions);
+ }
+
+ return 1;
+}
+
+static int msm_routing_get_fm_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_route_fm_vol_control;
+ return 0;
+}
+
+static int msm_routing_set_fm_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ afe_loopback_gain(INT_FM_TX , ucontrol->value.integer.value[0]);
+
+ msm_route_fm_vol_control = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int msm_routing_get_lpa_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_route_lpa_vol_control;
+ return 0;
+}
+
+static int msm_routing_set_lpa_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (!lpa_set_volume(ucontrol->value.integer.value[0]))
+ msm_route_lpa_vol_control =
+ ucontrol->value.integer.value[0];
+
+ return 0;
+
+}
+
+static int msm_routing_get_multimedia2_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ ucontrol->value.integer.value[0] = msm_route_multimedia2_vol_control;
+ return 0;
+}
+
+static int msm_routing_set_multimedia2_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (!multi_ch_pcm_set_volume(ucontrol->value.integer.value[0]))
+ msm_route_multimedia2_vol_control =
+ ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int msm_routing_get_compressed_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ ucontrol->value.integer.value[0] = msm_route_compressed_vol_control;
+ return 0;
+}
+
+static int msm_routing_set_compressed_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (!compressed_set_volume(ucontrol->value.integer.value[0]))
+ msm_route_compressed_vol_control =
+ ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static void msm_send_eq_values(int eq_idx)
+{
+ int result;
+ struct audio_client *ac =
+ q6asm_get_audio_client(fe_dai_map[eq_idx][SESSION_TYPE_RX]);
+
+ if (ac == NULL) {
+ pr_err("%s: Could not get audio client for session: %d\n",
+ __func__, fe_dai_map[eq_idx][SESSION_TYPE_RX]);
+ goto done;
+ }
+
+ result = q6asm_equalizer(ac, &eq_data[eq_idx]);
+
+ if (result < 0)
+ pr_err("%s: Call to ASM equalizer failed, returned = %d\n",
+ __func__, result);
+done:
+ return;
+}
+
+static int msm_routing_get_eq_enable_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+
+ ucontrol->value.integer.value[0] = eq_data[eq_idx].enable;
+
+ pr_debug("%s: EQ #%d enable %d\n", __func__,
+ eq_idx, eq_data[eq_idx].enable);
+ return 0;
+}
+
+static int msm_routing_put_eq_enable_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int value = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: EQ #%d enable %d\n", __func__,
+ eq_idx, value);
+ eq_data[eq_idx].enable = value;
+
+ msm_send_eq_values(eq_idx);
+ return 0;
+}
+
+static int msm_routing_get_eq_band_count_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+
+ ucontrol->value.integer.value[0] = eq_data[eq_idx].num_bands;
+
+ pr_debug("%s: EQ #%d bands %d\n", __func__,
+ eq_idx, eq_data[eq_idx].num_bands);
+ return eq_data[eq_idx].num_bands;
+}
+
+static int msm_routing_put_eq_band_count_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int value = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: EQ #%d bands %d\n", __func__,
+ eq_idx, value);
+ eq_data[eq_idx].num_bands = value;
+ return 0;
+}
+
+static int msm_routing_get_eq_band_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ ucontrol->value.integer.value[0] =
+ eq_data[eq_idx].eq_bands[band_idx].band_idx;
+ ucontrol->value.integer.value[1] =
+ eq_data[eq_idx].eq_bands[band_idx].filter_type;
+ ucontrol->value.integer.value[2] =
+ eq_data[eq_idx].eq_bands[band_idx].center_freq_hz;
+ ucontrol->value.integer.value[3] =
+ eq_data[eq_idx].eq_bands[band_idx].filter_gain;
+ ucontrol->value.integer.value[4] =
+ eq_data[eq_idx].eq_bands[band_idx].q_factor;
+
+ pr_debug("%s: band_idx = %d\n", __func__,
+ eq_data[eq_idx].eq_bands[band_idx].band_idx);
+ pr_debug("%s: filter_type = %d\n", __func__,
+ eq_data[eq_idx].eq_bands[band_idx].filter_type);
+ pr_debug("%s: center_freq_hz = %d\n", __func__,
+ eq_data[eq_idx].eq_bands[band_idx].center_freq_hz);
+ pr_debug("%s: filter_gain = %d\n", __func__,
+ eq_data[eq_idx].eq_bands[band_idx].filter_gain);
+ pr_debug("%s: q_factor = %d\n", __func__,
+ eq_data[eq_idx].eq_bands[band_idx].q_factor);
+ return 0;
+}
+
+static int msm_routing_put_eq_band_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ eq_data[eq_idx].eq_bands[band_idx].band_idx =
+ ucontrol->value.integer.value[0];
+ eq_data[eq_idx].eq_bands[band_idx].filter_type =
+ ucontrol->value.integer.value[1];
+ eq_data[eq_idx].eq_bands[band_idx].center_freq_hz =
+ ucontrol->value.integer.value[2];
+ eq_data[eq_idx].eq_bands[band_idx].filter_gain =
+ ucontrol->value.integer.value[3];
+ eq_data[eq_idx].eq_bands[band_idx].q_factor =
+ ucontrol->value.integer.value[4];
+ return 0;
+}
+
+static const struct snd_kcontrol_new pri_i2s_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_PRI_I2S_RX ,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new sec_i2s_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SEC_I2S_RX ,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SLIMBUS_0_RX ,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mi2s_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_MI2S_RX ,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+ /* incall music delivery mixer */
+static const struct snd_kcontrol_new incall_music_delivery_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new afe_pcm_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul1_mixer_controls[] = {
+ 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("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul2_mixer_controls[] = {
+ SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new sec_i2s_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SLIMBUS_0_RX ,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new bt_sco_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_INT_BT_SCO_RX ,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new afe_pcm_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new aux_pcm_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new hdmi_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new stub_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_INVALID,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_1_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voice_mixer_controls[] = {
+ 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("SLIM_0_TX_Voice", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voice",
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_CS_VOICE, 1, 0,
+ msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("AFE_PCM_TX_Voice", MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("AUX_PCM_TX_Voice", MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voip_mixer_controls[] = {
+ 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("SLIM_0_TX_Voip", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voip", MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("AFE_PCM_TX_Voip", MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("AUX_PCM_TX_Voip", MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voice_stub_mixer_controls[] = {
+ SOC_SINGLE_EXT("STUB_TX_HL", MSM_BACKEND_DAI_INVALID,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new sbus_0_rx_port_mixer_controls[] = {
+ SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new auxpcm_rx_port_mixer_controls[] = {
+ SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new sbus_1_rx_port_mixer_controls[] = {
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new bt_sco_rx_port_mixer_controls[] = {
+ SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new fm_switch_mixer_controls =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+ 0, 1, 0, msm_routing_get_switch_mixer,
+ msm_routing_put_switch_mixer);
+
+static const struct snd_kcontrol_new int_fm_vol_mixer_controls[] = {
+ SOC_SINGLE_EXT_TLV("Internal FM RX Volume", SND_SOC_NOPM, 0,
+ INT_FM_RX_VOL_GAIN, 0, msm_routing_get_fm_vol_mixer,
+ msm_routing_set_fm_vol_mixer, fm_rx_vol_gain),
+};
+
+static const struct snd_kcontrol_new lpa_vol_mixer_controls[] = {
+ SOC_SINGLE_EXT_TLV("LPA RX Volume", SND_SOC_NOPM, 0,
+ INT_RX_VOL_GAIN, 0, msm_routing_get_lpa_vol_mixer,
+ msm_routing_set_lpa_vol_mixer, lpa_rx_vol_gain),
+};
+
+static const struct snd_kcontrol_new multimedia2_vol_mixer_controls[] = {
+ SOC_SINGLE_EXT_TLV("HIFI2 RX Volume", SND_SOC_NOPM, 0,
+ INT_RX_VOL_GAIN, 0, msm_routing_get_multimedia2_vol_mixer,
+ msm_routing_set_multimedia2_vol_mixer, multimedia2_rx_vol_gain),
+};
+
+static const struct snd_kcontrol_new compressed_vol_mixer_controls[] = {
+ SOC_SINGLE_EXT_TLV("COMPRESSED RX Volume", SND_SOC_NOPM, 0,
+ INT_RX_VOL_GAIN, 0, msm_routing_get_compressed_vol_mixer,
+ msm_routing_set_compressed_vol_mixer, compressed_rx_vol_gain),
+};
+
+static const struct snd_kcontrol_new eq_enable_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1 EQ Enable", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_eq_enable_mixer,
+ msm_routing_put_eq_enable_mixer),
+ SOC_SINGLE_EXT("MultiMedia2 EQ Enable", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_eq_enable_mixer,
+ msm_routing_put_eq_enable_mixer),
+ SOC_SINGLE_EXT("MultiMedia3 EQ Enable", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_eq_enable_mixer,
+ msm_routing_put_eq_enable_mixer),
+};
+
+static const struct snd_kcontrol_new eq_band_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1 EQ Band Count", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 11, 0,
+ msm_routing_get_eq_band_count_audio_mixer,
+ msm_routing_put_eq_band_count_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2 EQ Band Count", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 11, 0,
+ msm_routing_get_eq_band_count_audio_mixer,
+ msm_routing_put_eq_band_count_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3 EQ Band Count", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 11, 0,
+ msm_routing_get_eq_band_count_audio_mixer,
+ msm_routing_put_eq_band_count_audio_mixer),
+};
+
+static const struct snd_kcontrol_new eq_coeff_mixer_controls[] = {
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band1", EQ_BAND1,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band2", EQ_BAND2,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band3", EQ_BAND3,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band4", EQ_BAND4,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band5", EQ_BAND5,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band6", EQ_BAND6,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band7", EQ_BAND7,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band8", EQ_BAND8,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band9", EQ_BAND9,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band10", EQ_BAND10,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band11", EQ_BAND11,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band12", EQ_BAND12,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band1", EQ_BAND1,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band2", EQ_BAND2,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band3", EQ_BAND3,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band4", EQ_BAND4,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band5", EQ_BAND5,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band6", EQ_BAND6,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band7", EQ_BAND7,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band8", EQ_BAND8,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band9", EQ_BAND9,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band10", EQ_BAND10,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band11", EQ_BAND11,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band12", EQ_BAND12,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band1", EQ_BAND1,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band2", EQ_BAND2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band3", EQ_BAND3,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band4", EQ_BAND4,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band5", EQ_BAND5,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band6", EQ_BAND6,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band7", EQ_BAND7,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band8", EQ_BAND8,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band9", EQ_BAND9,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band10", EQ_BAND10,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band11", EQ_BAND11,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band12", EQ_BAND12,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+};
+
+static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
+ /* Frontend AIF */
+ /* Widget name equals to Front-End DAI name<Need confirmation>,
+ * Stream name must contains substring of front-end dai name
+ */
+ SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL4", "MultiMedia4 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("VOIP_DL", "VoIP Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("CS-VOICE_DL1", "CS-VOICE Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("VOIP_UL", "VoIP Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIM0_DL_HL", "SLIMBUS0_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIM0_UL_HL", "SLIMBUS0_HOSTLESS Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("INTFM_DL_HL", "INT_FM_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INTFM_UL_HL", "INT_FM_HOSTLESS Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("HDMI_DL_HL", "HDMI_HOSTLESS Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AUXPCM_DL_HL", "AUXPCM_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AUXPCM_UL_HL", "AUXPCM_HOSTLESS Capture",
+ 0, 0, 0, 0),
+
+ /* Backend AIF */
+ /* Stream name equals to backend dai link stream name
+ */
+ SND_SOC_DAPM_AIF_OUT("PRI_I2S_RX", "Primary I2S Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_I2S_RX", "Secondary I2S Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_0_RX", "Slimbus Playback", 0, 0, 0, 0),
+ 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("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INT_BT_SCO_RX", "Internal BT-SCO Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("INT_BT_SCO_TX", "Internal BT-SCO Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INT_FM_RX", "Internal FM Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("INT_FM_TX", "Internal FM Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PCM_RX", "AFE Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("PCM_TX", "AFE Capture",
+ 0, 0, 0 , 0),
+ /* incall */
+ SND_SOC_DAPM_AIF_OUT("VOICE_PLAYBACK_TX", "Voice Farend Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("INCALL_RECORD_TX", "Voice Uplink Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("INCALL_RECORD_RX", "Voice Downlink Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AUX_PCM_RX", "AUX PCM Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AUX_PCM_TX", "AUX PCM Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("VOICE_STUB_DL", "VOICE_STUB Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("VOICE_STUB_UL", "VOICE_STUB Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("STUB_RX", "Stub Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("STUB_TX", "Stub Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_1_RX", "Slimbus1 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_1_TX", "Slimbus1 Capture", 0, 0, 0, 0),
+
+ /* Switch Definitions */
+ SND_SOC_DAPM_SWITCH("SLIMBUS_DL_HL", SND_SOC_NOPM, 0, 0,
+ &fm_switch_mixer_controls),
+ /* Mixer definitions */
+ SND_SOC_DAPM_MIXER("PRI_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_i2s_rx_mixer_controls, ARRAY_SIZE(pri_i2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ sec_i2s_rx_mixer_controls, ARRAY_SIZE(sec_i2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_rx_mixer_controls, ARRAY_SIZE(slimbus_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("HDMI Mixer", SND_SOC_NOPM, 0, 0,
+ hdmi_mixer_controls, ARRAY_SIZE(hdmi_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ mi2s_rx_mixer_controls, ARRAY_SIZE(mi2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul2_mixer_controls, ARRAY_SIZE(mmul2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ auxpcm_rx_mixer_controls, ARRAY_SIZE(auxpcm_rx_mixer_controls)),
+ /* incall */
+ SND_SOC_DAPM_MIXER("Incall_Music Audio Mixer", SND_SOC_NOPM, 0, 0,
+ incall_music_delivery_mixer_controls,
+ ARRAY_SIZE(incall_music_delivery_mixer_controls)),
+ /* Voice Mixer */
+ SND_SOC_DAPM_MIXER("PRI_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0, pri_rx_voice_mixer_controls,
+ ARRAY_SIZE(pri_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ sec_i2s_rx_voice_mixer_controls,
+ ARRAY_SIZE(sec_i2s_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIM_0_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ slimbus_rx_voice_mixer_controls,
+ ARRAY_SIZE(slimbus_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ bt_sco_rx_voice_mixer_controls,
+ ARRAY_SIZE(bt_sco_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("AFE_PCM_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ afe_pcm_rx_voice_mixer_controls,
+ ARRAY_SIZE(afe_pcm_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("AUX_PCM_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ aux_pcm_rx_voice_mixer_controls,
+ ARRAY_SIZE(aux_pcm_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("HDMI_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ hdmi_rx_voice_mixer_controls,
+ ARRAY_SIZE(hdmi_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Voice_Tx Mixer",
+ SND_SOC_NOPM, 0, 0, tx_voice_mixer_controls,
+ ARRAY_SIZE(tx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Voip_Tx Mixer",
+ SND_SOC_NOPM, 0, 0, tx_voip_mixer_controls,
+ ARRAY_SIZE(tx_voip_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ int_bt_sco_rx_mixer_controls, ARRAY_SIZE(int_bt_sco_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INTERNAL_FM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ int_fm_rx_mixer_controls, ARRAY_SIZE(int_fm_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("AFE_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ afe_pcm_rx_mixer_controls, ARRAY_SIZE(afe_pcm_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Voice Stub Tx Mixer", SND_SOC_NOPM, 0, 0,
+ tx_voice_stub_mixer_controls, ARRAY_SIZE(tx_voice_stub_mixer_controls)),
+ SND_SOC_DAPM_MIXER("STUB_RX Mixer", SND_SOC_NOPM, 0, 0,
+ stub_rx_mixer_controls, ARRAY_SIZE(stub_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_1_RX Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_1_rx_mixer_controls, ARRAY_SIZE(slimbus_1_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, sbus_0_rx_port_mixer_controls,
+ ARRAY_SIZE(sbus_0_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("AUXPCM_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, auxpcm_rx_port_mixer_controls,
+ ARRAY_SIZE(auxpcm_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_1_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+ sbus_1_rx_port_mixer_controls,
+ ARRAY_SIZE(sbus_1_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+ bt_sco_rx_port_mixer_controls,
+ ARRAY_SIZE(bt_sco_rx_port_mixer_controls)),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ {"PRI_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"PRI_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"PRI_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"PRI_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"PRI_I2S_RX", NULL, "PRI_RX Audio Mixer"},
+
+ {"SEC_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SEC_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SEC_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SEC_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SEC_I2S_RX", NULL, "SEC_RX Audio Mixer"},
+
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Audio Mixer"},
+
+ {"HDMI Mixer", "MultiMedia1", "MM_DL1"},
+ {"HDMI Mixer", "MultiMedia2", "MM_DL2"},
+ {"HDMI Mixer", "MultiMedia3", "MM_DL3"},
+ {"HDMI Mixer", "MultiMedia4", "MM_DL4"},
+ {"HDMI", NULL, "HDMI Mixer"},
+
+ /* incall */
+ {"Incall_Music Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"Incall_Music Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"VOICE_PLAYBACK_TX", NULL, "Incall_Music Audio Mixer"},
+
+ {"MultiMedia1 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"},
+ {"MultiMedia1 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
+ {"MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"MI2S_RX", NULL, "MI2S_RX Audio Mixer"},
+
+ {"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
+ {"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia1 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Audio Mixer"},
+
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"INT_FM_RX", NULL, "INTERNAL_FM_RX Audio Mixer"},
+
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"PCM_RX", NULL, "AFE_PCM_RX Audio Mixer"},
+
+ {"MultiMedia1 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"MultiMedia1 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+
+ {"MultiMedia1 Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"MM_UL1", NULL, "MultiMedia1 Mixer"},
+ {"MultiMedia2 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"MM_UL2", NULL, "MultiMedia2 Mixer"},
+
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"AUX_PCM_RX", NULL, "AUX_PCM_RX Audio Mixer"},
+
+ {"PRI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"PRI_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"PRI_I2S_RX", NULL, "PRI_RX_Voice Mixer"},
+
+ {"SEC_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"SEC_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"SEC_I2S_RX", NULL, "SEC_RX_Voice Mixer"},
+
+ {"SLIM_0_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"SLIM_0_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"SLIMBUS_0_RX", NULL, "SLIM_0_RX_Voice Mixer"},
+
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX_Voice Mixer"},
+
+ {"AFE_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"AFE_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"PCM_RX", NULL, "AFE_PCM_RX_Voice Mixer"},
+
+ {"AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"AUX_PCM_RX", NULL, "AUX_PCM_RX_Voice Mixer"},
+
+ {"HDMI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"HDMI_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"HDMI", NULL, "HDMI_RX_Voice Mixer"},
+ {"HDMI", NULL, "HDMI_DL_HL"},
+
+ {"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"},
+ {"Voice_Tx Mixer", "SLIM_0_TX_Voice", "SLIMBUS_0_TX"},
+ {"Voice_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice", "INT_BT_SCO_TX"},
+ {"Voice_Tx Mixer", "AFE_PCM_TX_Voice", "PCM_TX"},
+ {"Voice_Tx Mixer", "AUX_PCM_TX_Voice", "AUX_PCM_TX"},
+ {"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"},
+ {"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"},
+ {"Voip_Tx Mixer", "SLIM_0_TX_Voip", "SLIMBUS_0_TX"},
+ {"Voip_Tx Mixer", "INTERNAL_BT_SCO_TX_Voip", "INT_BT_SCO_TX"},
+ {"Voip_Tx Mixer", "AFE_PCM_TX_Voip", "PCM_TX"},
+ {"Voip_Tx Mixer", "AUX_PCM_TX_Voip", "AUX_PCM_TX"},
+
+ {"VOIP_UL", NULL, "Voip_Tx Mixer"},
+ {"SLIMBUS_DL_HL", "Switch", "SLIM0_DL_HL"},
+ {"SLIMBUS_0_RX", NULL, "SLIMBUS_DL_HL"},
+ {"SLIM0_UL_HL", NULL, "SLIMBUS_0_TX"},
+ {"INT_FM_RX", NULL, "INTFM_DL_HL"},
+ {"INTFM_UL_HL", NULL, "INT_FM_TX"},
+ {"AUX_PCM_RX", NULL, "AUXPCM_DL_HL"},
+ {"AUXPCM_UL_HL", NULL, "AUX_PCM_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Port Mixer"},
+
+ {"AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"AUX_PCM_RX", NULL, "AUXPCM_RX Port Mixer"},
+
+ {"Voice Stub Tx Mixer", "STUB_TX_HL", "STUB_TX"},
+ {"Voice Stub Tx Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+ {"Voice Stub Tx Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"VOICE_STUB_UL", NULL, "Voice Stub Tx Mixer"},
+
+ {"STUB_RX Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"STUB_RX", NULL, "STUB_RX Mixer"},
+ {"SLIMBUS_1_RX Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"SLIMBUS_1_RX", NULL, "SLIMBUS_1_RX Mixer"},
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+
+ {"SLIMBUS_1_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"SLIMBUS_1_RX", NULL, "SLIMBUS_1_RX Port Mixer"},
+ {"INTERNAL_BT_SCO_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+ {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Port Mixer"},
+};
+
+static int msm_pcm_routing_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int be_id = rtd->dai_link->be_id;
+
+ if (be_id >= MSM_BACKEND_DAI_MAX) {
+ pr_err("%s: unexpected be_id %d\n", __func__, be_id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&routing_lock);
+ msm_bedais[be_id].hw_params = params;
+ mutex_unlock(&routing_lock);
+ return 0;
+}
+
+static int msm_pcm_routing_close(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int be_id = rtd->dai_link->be_id;
+ int i, session_type;
+ struct msm_pcm_routing_bdai_data *bedai;
+
+ if (be_id >= MSM_BACKEND_DAI_MAX) {
+ pr_err("%s: unexpected be_id %d\n", __func__, be_id);
+ return -EINVAL;
+ }
+
+ bedai = &msm_bedais[be_id];
+
+ session_type = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ 0 : 1);
+
+ mutex_lock(&routing_lock);
+
+ for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) {
+ if (fe_dai_map[i][session_type] != INVALID_SESSION)
+ adm_close(bedai->port_id);
+ }
+
+ bedai->active = 0;
+ bedai->hw_params = NULL;
+
+ mutex_unlock(&routing_lock);
+
+ return 0;
+}
+
+static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int be_id = rtd->dai_link->be_id;
+ int i, path_type, session_type;
+ struct msm_pcm_routing_bdai_data *bedai;
+ u32 channels;
+
+ if (be_id >= MSM_BACKEND_DAI_MAX) {
+ pr_err("%s: unexpected be_id %d\n", __func__, be_id);
+ return -EINVAL;
+ }
+
+
+ bedai = &msm_bedais[be_id];
+
+ if (bedai->hw_params == NULL) {
+ pr_err("%s: HW param is not configured", __func__);
+ return -EINVAL;
+ }
+
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ path_type = ADM_PATH_PLAYBACK;
+ session_type = SESSION_TYPE_RX;
+ } else {
+ path_type = ADM_PATH_LIVE_REC;
+ session_type = SESSION_TYPE_TX;
+ }
+
+ mutex_lock(&routing_lock);
+
+ if (bedai->active == 1)
+ goto done; /* Ignore prepare if back-end already active */
+
+ /* AFE port is not active at this point. However, still
+ * go ahead setting active flag under the notion that
+ * QDSP6 is able to handle ADM starting before AFE port
+ * is started.
+ */
+ bedai->active = 1;
+
+ for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) {
+ if (fe_dai_map[i][session_type] != INVALID_SESSION) {
+
+ channels = params_channels(bedai->hw_params);
+ if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
+ (channels > 2))
+ adm_multi_ch_copp_open(bedai->port_id,
+ path_type,
+ params_rate(bedai->hw_params),
+ channels,
+ DEFAULT_COPP_TOPOLOGY);
+ else
+ adm_open(bedai->port_id,
+ path_type,
+ params_rate(bedai->hw_params),
+ params_channels(bedai->hw_params),
+ DEFAULT_COPP_TOPOLOGY);
+
+ msm_pcm_routing_build_matrix(i,
+ fe_dai_map[i][session_type], path_type);
+ }
+ }
+
+done:
+ mutex_unlock(&routing_lock);
+
+ return 0;
+}
+
+static struct snd_pcm_ops msm_routing_pcm_ops = {
+ .hw_params = msm_pcm_routing_hw_params,
+ .close = msm_pcm_routing_close,
+ .prepare = msm_pcm_routing_prepare,
+};
+
+static unsigned int msm_routing_read(struct snd_soc_platform *platform,
+ unsigned int reg)
+{
+ dev_dbg(platform->dev, "reg %x\n", reg);
+ return 0;
+}
+
+/* Not used but frame seems to require it */
+static int msm_routing_write(struct snd_soc_platform *platform,
+ unsigned int reg, unsigned int val)
+{
+ dev_dbg(platform->dev, "reg %x val %x\n", reg, val);
+ return 0;
+}
+
+/* Not used but frame seems to require it */
+static int msm_routing_probe(struct snd_soc_platform *platform)
+{
+ snd_soc_dapm_new_controls(&platform->dapm, msm_qdsp6_widgets,
+ ARRAY_SIZE(msm_qdsp6_widgets));
+ snd_soc_dapm_add_routes(&platform->dapm, intercon,
+ ARRAY_SIZE(intercon));
+
+ snd_soc_dapm_new_widgets(&platform->dapm);
+
+ snd_soc_add_platform_controls(platform,
+ int_fm_vol_mixer_controls,
+ ARRAY_SIZE(int_fm_vol_mixer_controls));
+
+ snd_soc_add_platform_controls(platform,
+ lpa_vol_mixer_controls,
+ ARRAY_SIZE(lpa_vol_mixer_controls));
+
+ snd_soc_add_platform_controls(platform,
+ eq_enable_mixer_controls,
+ ARRAY_SIZE(eq_enable_mixer_controls));
+
+ snd_soc_add_platform_controls(platform,
+ eq_band_mixer_controls,
+ ARRAY_SIZE(eq_band_mixer_controls));
+
+ snd_soc_add_platform_controls(platform,
+ eq_coeff_mixer_controls,
+ ARRAY_SIZE(eq_coeff_mixer_controls));
+
+ snd_soc_add_platform_controls(platform,
+ multimedia2_vol_mixer_controls,
+ ARRAY_SIZE(multimedia2_vol_mixer_controls));
+
+ snd_soc_add_platform_controls(platform,
+ compressed_vol_mixer_controls,
+ ARRAY_SIZE(compressed_vol_mixer_controls));
+
+ return 0;
+}
+
+static struct snd_soc_platform_driver msm_soc_routing_platform = {
+ .ops = &msm_routing_pcm_ops,
+ .probe = msm_routing_probe,
+ .read = msm_routing_read,
+ .write = msm_routing_write,
+};
+
+static __devinit int msm_routing_pcm_probe(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_routing_platform);
+}
+
+static int msm_routing_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_routing_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-routing",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_routing_pcm_probe,
+ .remove = __devexit_p(msm_routing_pcm_remove),
+};
+
+int msm_routing_check_backend_enabled(int fedai_id)
+{
+ int i;
+ if (fedai_id >= MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* bad ID assigned in machine driver */
+ pr_err("%s: bad MM ID\n", __func__);
+ return 0;
+ }
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ if ((test_bit(fedai_id,
+ &msm_bedais[i].fe_sessions))) {
+ return msm_bedais[i].active;
+ }
+ }
+ return 0;
+}
+
+static int __init msm_soc_routing_platform_init(void)
+{
+ mutex_init(&routing_lock);
+ return platform_driver_register(&msm_routing_pcm_driver);
+}
+module_init(msm_soc_routing_platform_init);
+
+static void __exit msm_soc_routing_platform_exit(void)
+{
+ platform_driver_unregister(&msm_routing_pcm_driver);
+}
+module_exit(msm_soc_routing_platform_exit);
+
+MODULE_DESCRIPTION("MSM routing platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
new file mode 100644
index 0000000..b971787
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
@@ -0,0 +1,103 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _MSM_PCM_ROUTING_H
+#define _MSM_PCM_ROUTING_H
+#include <sound/apr_audio-v2.h>
+
+#define LPASS_BE_PRI_I2S_RX "(Backend) PRIMARY_I2S_RX"
+#define LPASS_BE_PRI_I2S_TX "(Backend) PRIMARY_I2S_TX"
+#define LPASS_BE_SLIMBUS_0_RX "(Backend) SLIMBUS_0_RX"
+#define LPASS_BE_SLIMBUS_0_TX "(Backend) SLIMBUS_0_TX"
+#define LPASS_BE_HDMI "(Backend) HDMI"
+#define LPASS_BE_INT_BT_SCO_RX "(Backend) INT_BT_SCO_RX"
+#define LPASS_BE_INT_BT_SCO_TX "(Backend) INT_BT_SCO_TX"
+#define LPASS_BE_INT_FM_RX "(Backend) INT_FM_RX"
+#define LPASS_BE_INT_FM_TX "(Backend) INT_FM_TX"
+#define LPASS_BE_AFE_PCM_RX "(Backend) RT_PROXY_DAI_001_RX"
+#define LPASS_BE_AFE_PCM_TX "(Backend) RT_PROXY_DAI_002_TX"
+#define LPASS_BE_AUXPCM_RX "(Backend) AUX_PCM_RX"
+#define LPASS_BE_AUXPCM_TX "(Backend) AUX_PCM_TX"
+#define LPASS_BE_VOICE_PLAYBACK_TX "(Backend) VOICE_PLAYBACK_TX"
+#define LPASS_BE_INCALL_RECORD_RX "(Backend) INCALL_RECORD_TX"
+#define LPASS_BE_INCALL_RECORD_TX "(Backend) INCALL_RECORD_RX"
+#define LPASS_BE_SEC_I2S_RX "(Backend) SECONDARY_I2S_RX"
+
+#define LPASS_BE_MI2S_RX "(Backend) MI2S_RX"
+#define LPASS_BE_STUB_RX "(Backend) STUB_RX"
+#define LPASS_BE_STUB_TX "(Backend) STUB_TX"
+#define LPASS_BE_SLIMBUS_1_RX "(Backend) SLIMBUS_1_RX"
+#define LPASS_BE_SLIMBUS_1_TX "(Backend) SLIMBUS_1_TX"
+
+/* For multimedia front-ends, asm session is allocated dynamically.
+ * Hence, asm session/multimedia front-end mapping has to be maintained.
+ * Due to this reason, additional multimedia front-end must be placed before
+ * non-multimedia front-ends.
+ */
+
+enum {
+ MSM_FRONTEND_DAI_MULTIMEDIA1 = 0,
+ MSM_FRONTEND_DAI_MULTIMEDIA2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3,
+ MSM_FRONTEND_DAI_MULTIMEDIA4,
+ MSM_FRONTEND_DAI_CS_VOICE,
+ MSM_FRONTEND_DAI_VOIP,
+ MSM_FRONTEND_DAI_AFE_RX,
+ MSM_FRONTEND_DAI_AFE_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB,
+ MSM_FRONTEND_DAI_MAX,
+};
+
+#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA4 + 1)
+#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA4
+
+enum {
+ MSM_BACKEND_DAI_PRI_I2S_RX = 0,
+ MSM_BACKEND_DAI_PRI_I2S_TX,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_BACKEND_DAI_INVALID,
+ MSM_BACKEND_DAI_MAX,
+};
+
+/* dai_id: front-end ID,
+ * dspst_id: DSP audio stream ID
+ * stream_type: playback or capture
+ */
+void msm_pcm_routing_reg_phy_stream(int fedai_id, int dspst_id,
+ int stream_type);
+void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type);
+
+int lpa_set_volume(unsigned volume);
+
+int msm_routing_check_backend_enabled(int fedai_id);
+
+int multi_ch_pcm_set_volume(unsigned volume);
+
+int compressed_set_volume(unsigned volume);
+
+#endif /*_MSM_PCM_H*/