Merge "msm: kgsl: Ensure the GMU does not go to sleep in ISR" into msm-4.9
diff --git a/Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt b/Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt
index c77f84b..29b7334 100644
--- a/Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt
+++ b/Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt
@@ -6,17 +6,25 @@
Required properties:
- compatible: Must be "qcom,bimc-bwmon", "qcom,bimc-bwmon2",
- "qcom,bimc-bwmon3" or "qcom,bimc-bwmon4"
+ "qcom,bimc-bwmon3" or "qcom,bimc-bwmon4" or
+ "qcom,bimc-bwmon5"
- reg: Pairs of physical base addresses and region sizes of
memory mapped registers.
- reg-names: Names of the bases for the above registers. Expected
bases are: "base", "global_base"
+ "global_base" should not be specified for
+ "qcom,bimc-bwmon5" compatibles.
- interrupts: Lists the threshold IRQ.
- qcom,mport: The hardware master port that this device can monitor
- qcom,target-dev: The DT device that corresponds to this master port
- qcom,hw-timer-hz: Hardware sampling rate in Hz. This field must be
specified for "qcom,bimc-bwmon4"
+Optional properties:
+- qcom,byte-mid-match: Byte count MID match value
+- qcom,byte-mid-mask: Byte count MID mask value
+- qcom,count-unit: Number of bytes monitor counts in
+
Example:
qcom,cpu-bwmon {
compatible = "qcom,bimc-bwmon";
@@ -26,4 +34,7 @@
qcom,mport = <0>;
qcom,target-dev = <&cpubw>;
qcom,hw-timer-hz = <19200000>;
+ qcom,byte-mid-match = <0x1e00>;
+ qcom,byte-mid-mask = <0x1e00>;
+ qcom,count-unit = <0x100000>;
};
diff --git a/Documentation/devicetree/bindings/display/msm/sde.txt b/Documentation/devicetree/bindings/display/msm/sde.txt
index a3ef34c..c766df8 100644
--- a/Documentation/devicetree/bindings/display/msm/sde.txt
+++ b/Documentation/devicetree/bindings/display/msm/sde.txt
@@ -274,6 +274,29 @@
applied in scenarios where panel interface can
be more tolerant to memory latency such as
command mode panels.
+- qcom,sde-core-ib-ff: A string entry indicating the fudge factor for
+ core ib calculation.
+- qcom,sde-core-clk-ff: A string entry indicating the fudge factor for
+ core clock calculation.
+- qcom,sde-comp-ratio-rt: A string entry indicating the compression ratio
+ for each supported compressed format on realtime interface.
+ The string is composed of one or more of
+ <fourcc code>/<vendor code>/<modifier>/<compression ratio>
+ separated with spaces.
+- qcom,sde-comp-ratio-nrt: A string entry indicating the compression ratio
+ for each supported compressed format on non-realtime interface.
+ The string is composed of one or more of
+ <fourcc code>/<vendor code>/<modifier>/<compression ratio>
+ separated with spaces.
+- qcom,sde-undersized-prefill-lines: A u32 value indicates the size of undersized prefill in lines.
+- qcom,sde-xtra-prefill-lines: A u32 value indicates the extra prefill in lines.
+- qcom,sde-dest-scale-prefill-lines: A u32 value indicates the latency of destination scaler in lines.
+- qcom,sde-macrotile-prefill-lines: A u32 value indicates the latency of macrotile in lines.
+- qcom,sde-yuv-nv12-prefill-lines: A u32 value indicates the latency of yuv/nv12 in lines.
+- qcom,sde-linear-prefill-lines: A u32 value indicates the latency of linear in lines.
+- qcom,sde-downscaling-prefill-lines: A u32 value indicates the latency of downscaling in lines.
+- qcom,sde-max-per-pipe-bw-kbps: Array of u32 value indicates the max per pipe bandwidth in Kbps.
+- qcom,sde-amortizable-threshold: This value indicates the min for traffic shaping in lines.
Bus Scaling Subnodes:
- qcom,sde-reg-bus: Property to provide Bus scaling for register access for
@@ -462,6 +485,21 @@
qcom,sde-max-bw-high-kbps = <9000000>;
qcom,sde-max-bw-low-kbps = <9000000>;
+ qcom,sde-core-ib-ff = "1.1";
+ qcom,sde-core-clk-ff = "1.0";
+ qcom,sde-comp-ratio-rt = "NV12/5/1/1.1 AB24/5/1/1.2 XB24/5/1/1.3";
+ qcom,sde-comp-ratio-nrt = "NV12/5/1/1.1 AB24/5/1/1.2 XB24/5/1/1.3";
+ qcom,sde-undersized-prefill-lines = <4>;
+ qcom,sde-xtra-prefill-lines = <5>;
+ qcom,sde-dest-scale-prefill-lines = <6>;
+ qcom,sde-macrotile-prefill-lines = <7>;
+ qcom,sde-yuv-nv12-prefill-lines = <8>;
+ qcom,sde-linear-prefill-lines = <9>;
+ qcom,sde-downscaling-prefill-lines = <10>;
+ qcom,sde-max-per-pipe-bw-kbps = <2400000 2400000 2400000 2400000
+ 2400000 2400000 2400000 2400000>;
+ qcom,sde-amortizable-threshold = <11>;
+
qcom,sde-sspp-vig-blocks {
qcom,sde-vig-csc-off = <0x320>;
qcom,sde-vig-qseed-off = <0x200>;
diff --git a/Documentation/devicetree/bindings/extcon/extcon-gpio.txt b/Documentation/devicetree/bindings/extcon/extcon-gpio.txt
new file mode 100644
index 0000000..1b3a1d2
--- /dev/null
+++ b/Documentation/devicetree/bindings/extcon/extcon-gpio.txt
@@ -0,0 +1,32 @@
+GPIO Extcon device
+
+This is a virtual device used to generate USB cable states from the USB ID pin
+connected to a GPIO pin.
+
+Required properties:
+- compatible: Should be "extcon-gpio"
+- extcon-id: The unique id of specific external connector.
+ Valid range is 0 (EXTCON_NONE) to 63 (EXTCON_NUM).
+ Refer include/linux/extcon.h for details.
+- gpio: Specify GPIO (see gpio binding)
+- debounce-ms: Debounce time for GPIO IRQ in ms
+- irq-flags: interrupt flags (edge/level). Refer to "include/dt-bindings/interrupt-controller/irq.h"
+- pinctrl-names, pinctrl-0, pinctrl-1,.. pinctrl-n: Refer to "Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt"
+ for these optional properties
+
+Example:
+ extcon_storage_cd {
+ compatible = "extcon-gpio";
+ extcon-id = <62>; /* EXTCON_MECHANICAL */
+ gpio = <&tlmm 126 GPIO_ACTIVE_LOW>;
+ debounce-ms = <200>;
+ irq-flags = <IRQ_TYPE_EDGE_BOTH>;
+ }
+
+ &ufshc_card {
+ extcon = <&extcon_storage_cd>;
+ };
+
+ &sd_card {
+ extcon = <&extcon_storage_cd>;
+ };
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-cci.txt b/Documentation/devicetree/bindings/media/video/msm-cam-cci.txt
index 8e2bdee..c9aaa00 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cam-cci.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-cci.txt
@@ -165,6 +165,15 @@
should contain phandle of respective ir-cut node
- qcom,special-support-sensors: if only some special sensors are supported
on this board, add sensor name in this property.
+- qcom,clock-rates: clock rate in Hz.
+- qcom,clock-cntl-support: Says whether clock control support is present or not
+- qcom,clock-control: The valid fields are "NO_SET_RATE", "INIT_RATE" and
+ "SET_RATE". "NO_SET_RATE" the corresponding clock is enabled without setting
+ the rate assuming some other driver has already set it to appropriate rate.
+ "INIT_RATE" clock rate is not queried assuming some other driver has set
+ the clock rate and ispif will set the the clock to this rate.
+ "SET_RATE" clock is enabled and the rate is set to the value specified
+ in the property qcom,clock-rates.
* Qualcomm Technologies, Inc. MSM ACTUATOR
@@ -205,6 +214,7 @@
sensor
- 0 -> MASTER 0
- 1 -> MASTER 1
+- qcom,clock-rates: clock rate in Hz.
Optional properties:
- qcom,cam-vreg-name : should contain names of all regulators needed by this
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-icp.txt b/Documentation/devicetree/bindings/media/video/msm-cam-icp.txt
new file mode 100644
index 0000000..c560a05
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-icp.txt
@@ -0,0 +1,219 @@
+* Qualcomm Technologies, Inc. MSM Camera ICP
+
+The MSM camera ICP devices are implemented multiple device nodes.
+The root icp device node has properties defined to hint the driver
+about the number of A5,IPE and BPS nodes available during the
+probe sequence. Each node has multiple properties defined
+for interrupts, clocks and regulators.
+
+=======================
+Required Node Structure
+=======================
+ICP root interface node takes care of the handling account for number
+of A5, IPE and BPS devices present on the hardware.
+
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: Should be "qcom,cam-icp".
+
+- compat-hw-name
+
+ Usage: required
+ Value type: <string>
+ Definition: Should be "qcom,a5" or "qcom,ipe".
+
+- num-a5
+ Usage: required
+ Value type: <u32>
+ Definition: Number of supported A5 processors.
+
+- num-ipe
+ Usage: required
+ Value type: <u32>
+ Definition: Number of supported IPE HW blocks.
+
+- num-bps
+ Usage: required
+ Value type: <u32>
+ Definition: Number of supported BPS HW blocks.
+
+Example:
+ qcom,cam-icp {
+ compatible = "qcom,cam-icp";
+ compat-hw-name = "qcom,a5", "qcom,ipe0", "qcom,ipe1", "qcom,bps";
+ num-a5 = <1>;
+ num-ipe = <2>;
+ num-bps = <1>;
+ status = "ok";
+ };
+
+=======================
+Required Node Structure
+=======================
+A5/IPE/BPS Node's provides interface for Image Control Processor driver
+about the A5 register map, interrupt map, clocks, regulators
+and name of firmware image.
+
+- cell-index
+ Usage: required
+ Value type: <u32>
+ Definition: Node instance number.
+
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: Should be "qcom,cam-cdm-intf".
+
+- reg-names
+ Usage: optional
+ Value type: <string>
+ Definition: Name of the register resources.
+
+- reg
+ Usage: optional
+ Value type: <u32>
+ Definition: Register values.
+
+- reg-cam-base
+ Usage: optional
+ Value type: <u32>
+ Definition: Register values.
+
+- interrupt-names
+ Usage: optional
+ Value type: <string>
+ Definition: Name of the interrupt.
+
+- interrupts
+ Usage: optional
+ Value type: <u32>
+ Definition: Interrupt associated with CDM HW.
+
+- regulator-names
+ Usage: required
+ Value type: <string>
+ Definition: Name of the regulator resources for CDM HW.
+
+- camss-supply
+ Usage: required
+ Value type: <phandle>
+ Definition: Regulator reference corresponding to the names listed
+ in "regulator-names".
+
+- clock-names
+ Usage: required
+ Value type: <string>
+ Definition: List of clock names required for CDM HW.
+
+- clocks
+ Usage: required
+ Value type: <phandle>
+ Definition: List of clocks used for CDM HW.
+
+- clock-rates
+ Usage: required
+ Value type: <u32>
+ Definition: List of clocks rates.
+
+- fw_name
+ Usage: optional
+ Value type: <string>
+ Definition: Name of firmware image.
+
+Examples:
+a5: qcom,a5@a10000 {
+ cell-index = <0>;
+ compatible = "qcom,cam_a5";
+ reg = <0xac00000 0x6000>,
+ <0xac10000 0x8000>,
+ <0xac18000 0x3000>;
+ reg-names = "a5_qgic", "a5_sierra", "a5_csr";
+ interrupts = <0 463 0>;
+ interrupt-names = "a5";
+ regulator-names = "camss-vdd";
+ camss-vdd-supply = <&titan_top_gdsc>;
+ clock-names = "gcc_cam_ahb_clk",
+ "gcc_cam_axi_clk",
+ "soc_ahb_clk",
+ "cpas_ahb_clk",
+ "camnoc_axi_clk",
+ "icp_apb_clk",
+ "icp_atb_clk",
+ "icp_clk",
+ "icp_clk_src",
+ "icp_cti_clk",
+ "icp_ts_clk";
+ clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+ <&clock_gcc GCC_CAMERA_AXI_CLK>,
+ <&clock_camcc CAM_CC_SOC_AHB_CLK>,
+ <&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+ <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+ <&clock_camcc CAM_CC_ICP_APB_CLK>,
+ <&clock_camcc CAM_CC_ICP_ATB_CLK>,
+ <&clock_camcc CAM_CC_ICP_CLK>,
+ <&clock_camcc CAM_CC_ICP_CLK_SRC>,
+ <&clock_camcc CAM_CC_ICP_CTI_CLK>,
+ <&clock_camcc CAM_CC_ICP_TS_CLK>;
+
+ clock-rates = <0 0 0 80000000 0 0 0 0 600000000 0 0>;
+ fw_name = "CAMERA_ICP.elf";
+};
+
+qcom,ipe0 {
+ cell-index = <0>;
+ compatible = "qcom,cam_ipe";
+ regulator-names = "ipe0-vdd";
+ ipe0-vdd-supply = <&ipe_0_gdsc>;
+ clock-names = "ipe_0_ahb_clk",
+ "ipe_0_areg_clk",
+ "ipe_0_axi_clk",
+ "ipe_0_clk",
+ "ipe_0_clk_src";
+ clocks = <&clock_camcc CAM_CC_IPE_0_AHB_CLK>,
+ <&clock_camcc CAM_CC_IPE_0_AREG_CLK>,
+ <&clock_camcc CAM_CC_IPE_0_AXI_CLK>,
+ <&clock_camcc CAM_CC_IPE_0_CLK>,
+ <&clock_camcc CAM_CC_IPE_0_CLK_SRC>;
+
+ clock-rates = <80000000 400000000 0 0 600000000>;
+};
+
+qcom,ipe1 {
+ cell-index = <1>;
+ compatible = "qcom,cam_ipe";
+ regulator-names = "ipe1-vdd";
+ ipe1-vdd-supply = <&ipe_1_gdsc>;
+ clock-names = "ipe_1_ahb_clk",
+ "ipe_1_areg_clk",
+ "ipe_1_axi_clk",
+ "ipe_1_clk",
+ "ipe_1_clk_src";
+ clocks = <&clock_camcc CAM_CC_IPE_1_AHB_CLK>,
+ <&clock_camcc CAM_CC_IPE_1_AREG_CLK>,
+ <&clock_camcc CAM_CC_IPE_1_AXI_CLK>,
+ <&clock_camcc CAM_CC_IPE_1_CLK>,
+ <&clock_camcc CAM_CC_IPE_1_CLK_SRC>;
+
+ clock-rates = <80000000 400000000 0 0 600000000>;
+};
+
+bps: qcom,bps {
+ cell-index = <0>;
+ compatible = "qcom,cam_bps";
+ regulator-names = "bps-vdd";
+ bps-vdd-supply = <&bps_gdsc>;
+ clock-names = "bps_ahb_clk",
+ "bps_areg_clk",
+ "bps_axi_clk",
+ "bps_clk",
+ "bps_clk_src";
+ clocks = <&clock_camcc CAM_CC_BPS_AHB_CLK>,
+ <&clock_camcc CAM_CC_BPS_AREG_CLK>,
+ <&clock_camcc CAM_CC_BPS_AXI_CLK>,
+ <&clock_camcc CAM_CC_BPS_CLK>,
+ <&clock_camcc CAM_CC_BPS_CLK_SRC>;
+
+ clock-rates = <80000000 400000000 0 0 600000000>;
+};
+
diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt
index 8a37782..f978c58 100644
--- a/Documentation/devicetree/bindings/mmc/mmc.txt
+++ b/Documentation/devicetree/bindings/mmc/mmc.txt
@@ -52,6 +52,7 @@
- no-sdio: controller is limited to send sdio cmd during initialization
- no-sd: controller is limited to send sd cmd during initialization
- no-mmc: controller is limited to send mmc cmd during initialization
+- extcon: phandle to external connector (Refer Documentation/devicetree/bindings/extcon/extcon-gpio.txt for more details).
*NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
polarity properties, we have to fix the meaning of the "normal" and "inverted"
diff --git a/Documentation/devicetree/bindings/platform/msm/ipa.txt b/Documentation/devicetree/bindings/platform/msm/ipa.txt
index 3a03add..e821feb 100644
--- a/Documentation/devicetree/bindings/platform/msm/ipa.txt
+++ b/Documentation/devicetree/bindings/platform/msm/ipa.txt
@@ -19,10 +19,13 @@
"bam-irq" - string to identify the IPA BAM interrupt.
"a2-bam-irq" - string to identify the A2 BAM interrupt.
- qcom,ipa-hw-ver: Specifies the IPA hardware version.
+- qcom,ipa-ram-mmap: An array of unsigned integers representing addresses and
+ sizes which are used by the driver to access IPA RAM.
Optional:
-- qcom,wan-rx-ring-size: size of WAN rx ring, default is 32
+- qcom,wan-rx-ring-size: size of WAN rx ring, default is 192
+- qcom,lan-rx-ring-size: size of LAN rx ring, default is 192
- qcom,arm-smmu: SMMU is present and ARM SMMU driver is used
- qcom,msm-smmu: SMMU is present and QSMMU driver is used
- qcom,smmu-s1-bypass: Boolean context flag to set SMMU to S1 bypass
@@ -59,12 +62,12 @@
- qcom,bandwidth-vote-for-ipa: Boolean context flag to indicate whether
ipa clock voting is done by bandwidth
voting via msm-bus-scale driver or not
+- qcom,use-64-bit-dma-mask: Boolean context flag to indicate whether
+ using 64bit dma mask or not
- qcom,use-dma-zone: Boolean context flag to indicate whether memory
allocations controlled by IPA driver that do not
specify a struct device * should use GFP_DMA to
workaround IPA HW limitations
-- qcom,use-gsi: Boolean context flag to indicate if the
- transport protocol is GSI
- qcom,use-rg10-limitation-mitigation: Boolean context flag to activate
the mitigation to register group 10
AP access limitation
@@ -123,6 +126,9 @@
- qcom,iova-mapping: specifies the start address and size of iova space.
+- qcom,additional-mapping: specifies any addtional mapping needed for this
+ context bank. The format is <iova pa size>
+
IPA SMP2P sub nodes
-compatible: "qcom,smp2pgpio-map-ipa-1-out" - represents the out gpio from
@@ -198,18 +204,24 @@
ipa_smmu_ap: ipa_smmu_ap {
compatible = "qcom,ipa-smmu-ap-cb";
- iommus = <&anoc2_smmu 0x30>;
- qcom,iova-mapping = <0x10000000 0x40000000>;
+ iommus = <&apps_smmu 0x720>;
+ qcom,iova-mapping = <0x20000000 0x40000000>;
+ qcom,additional-mapping =
+ /* modem tables in IMEM */
+ <0x146bd000 0x146bd000 0x2000>;
};
ipa_smmu_wlan: ipa_smmu_wlan {
compatible = "qcom,ipa-smmu-wlan-cb";
- iommus = <&anoc2_smmu 0x31>;
+ iommus = <&apps_smmu 0x721>;
+ qcom,additional-mapping =
+ /* ipa-uc ram */
+ <0x1e60000 0x1e60000 0x80000>;
};
ipa_smmu_uc: ipa_smmu_uc {
compatible = "qcom,ipa-smmu-uc-cb";
- iommus = <&anoc2_smmu 0x32>;
+ iommus = <&apps_smmu 0x722>;
qcom,iova-mapping = <0x40000000 0x20000000>;
};
};
diff --git a/Documentation/devicetree/bindings/qdsp/msm-ssc-sensors.txt b/Documentation/devicetree/bindings/qdsp/msm-ssc-sensors.txt
new file mode 100644
index 0000000..db7ab75
--- /dev/null
+++ b/Documentation/devicetree/bindings/qdsp/msm-ssc-sensors.txt
@@ -0,0 +1,21 @@
+Qualcomm Technologies, Inc. SSC Driver
+
+msm-ssc-sensors driver implements the mechanism that allows to load SLPI firmware images.
+
+Required properties:
+
+ - compatible: This must be "qcom,msm-ssc-sensors"
+
+Optional properties:
+
+ - qcom,firmware-name: SLPI firmware name, must be "slpi" or "slpi_v1" or "slpi_v2"
+ Firmware name is not required, if sensors driver is sharing processor for execution.
+
+
+Example:
+ The following for sdm845.
+
+ qcom,msm-ssc-sensors {
+ compatible = "qcom,msm-ssc-sensors";
+ qcom,firmware-name = "slpi";
+ };
diff --git a/Makefile b/Makefile
index f834951..f47cd95 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 4
PATCHLEVEL = 9
-SUBLEVEL = 27
+SUBLEVEL = 28
EXTRAVERSION =
NAME = Roaring Lionus
diff --git a/arch/arm/boot/dts/bcm958522er.dts b/arch/arm/boot/dts/bcm958522er.dts
index a21b0fd..417f657 100644
--- a/arch/arm/boot/dts/bcm958522er.dts
+++ b/arch/arm/boot/dts/bcm958522er.dts
@@ -55,6 +55,7 @@
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpioa 15 GPIO_ACTIVE_LOW>;
+ open-source;
priority = <200>;
};
};
diff --git a/arch/arm/boot/dts/bcm958525er.dts b/arch/arm/boot/dts/bcm958525er.dts
index be7f2f8..5279b76 100644
--- a/arch/arm/boot/dts/bcm958525er.dts
+++ b/arch/arm/boot/dts/bcm958525er.dts
@@ -55,6 +55,7 @@
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpioa 15 GPIO_ACTIVE_LOW>;
+ open-source;
priority = <200>;
};
};
diff --git a/arch/arm/boot/dts/bcm958525xmc.dts b/arch/arm/boot/dts/bcm958525xmc.dts
index 959cde9..872882b 100644
--- a/arch/arm/boot/dts/bcm958525xmc.dts
+++ b/arch/arm/boot/dts/bcm958525xmc.dts
@@ -55,6 +55,7 @@
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpioa 31 GPIO_ACTIVE_LOW>;
+ open-source;
priority = <200>;
};
};
diff --git a/arch/arm/boot/dts/bcm958622hr.dts b/arch/arm/boot/dts/bcm958622hr.dts
index ad2aa87..a340e1d 100644
--- a/arch/arm/boot/dts/bcm958622hr.dts
+++ b/arch/arm/boot/dts/bcm958622hr.dts
@@ -55,6 +55,7 @@
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpioa 15 GPIO_ACTIVE_LOW>;
+ open-source;
priority = <200>;
};
};
diff --git a/arch/arm/boot/dts/bcm958623hr.dts b/arch/arm/boot/dts/bcm958623hr.dts
index 4ceb8fe..226b652 100644
--- a/arch/arm/boot/dts/bcm958623hr.dts
+++ b/arch/arm/boot/dts/bcm958623hr.dts
@@ -55,6 +55,7 @@
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpioa 15 GPIO_ACTIVE_LOW>;
+ open-source;
priority = <200>;
};
};
diff --git a/arch/arm/boot/dts/bcm958625hr.dts b/arch/arm/boot/dts/bcm958625hr.dts
index 4420025..a1658d0 100644
--- a/arch/arm/boot/dts/bcm958625hr.dts
+++ b/arch/arm/boot/dts/bcm958625hr.dts
@@ -55,6 +55,7 @@
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpioa 15 GPIO_ACTIVE_LOW>;
+ open-source;
priority = <200>;
};
};
diff --git a/arch/arm/boot/dts/bcm988312hr.dts b/arch/arm/boot/dts/bcm988312hr.dts
index 104afe9..ed05e33 100644
--- a/arch/arm/boot/dts/bcm988312hr.dts
+++ b/arch/arm/boot/dts/bcm988312hr.dts
@@ -55,6 +55,7 @@
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpioa 15 GPIO_ACTIVE_LOW>;
+ open-source;
priority = <200>;
};
};
diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi
index 2e37557..76f4e89 100644
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
@@ -65,13 +65,13 @@
cxo_board {
compatible = "fixed-clock";
#clock-cells = <0>;
- clock-frequency = <19200000>;
+ clock-frequency = <25000000>;
};
pxo_board {
compatible = "fixed-clock";
#clock-cells = <0>;
- clock-frequency = <27000000>;
+ clock-frequency = <25000000>;
};
sleep_clk: sleep_clk {
diff --git a/arch/arm/boot/dts/sun7i-a20-lamobo-r1.dts b/arch/arm/boot/dts/sun7i-a20-lamobo-r1.dts
index 73c05da..e00539a 100644
--- a/arch/arm/boot/dts/sun7i-a20-lamobo-r1.dts
+++ b/arch/arm/boot/dts/sun7i-a20-lamobo-r1.dts
@@ -167,7 +167,7 @@
reg = <8>;
label = "cpu";
ethernet = <&gmac>;
- phy-mode = "rgmii";
+ phy-mode = "rgmii-txid";
fixed-link {
speed = <1000>;
full-duplex;
diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
index 1921325..fc0d3b0 100644
--- a/arch/arm/configs/sdxpoorwills-perf_defconfig
+++ b/arch/arm/configs/sdxpoorwills-perf_defconfig
@@ -174,6 +174,8 @@
# CONFIG_NET_VENDOR_INTEL is not set
CONFIG_KS8851=y
# CONFIG_NET_VENDOR_MICROCHIP is not set
+CONFIG_ECM_IPA=y
+CONFIG_RNDIS_IPA=y
# CONFIG_NET_VENDOR_NATSEMI is not set
# CONFIG_NET_VENDOR_SEEQ is not set
# CONFIG_NET_VENDOR_SMSC is not set
@@ -255,6 +257,8 @@
CONFIG_UIO=y
CONFIG_STAGING=y
CONFIG_GSI=y
+CONFIG_IPA3=y
+CONFIG_IPA_UT=y
CONFIG_SPS=y
CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_USB_BAM=y
diff --git a/arch/arm/configs/sdxpoorwills_defconfig b/arch/arm/configs/sdxpoorwills_defconfig
index 0a875b1..c62aa16 100644
--- a/arch/arm/configs/sdxpoorwills_defconfig
+++ b/arch/arm/configs/sdxpoorwills_defconfig
@@ -166,6 +166,8 @@
# CONFIG_NET_VENDOR_INTEL is not set
CONFIG_KS8851=y
# CONFIG_NET_VENDOR_MICROCHIP is not set
+CONFIG_ECM_IPA=y
+CONFIG_RNDIS_IPA=y
# CONFIG_NET_VENDOR_NATSEMI is not set
# CONFIG_NET_VENDOR_SEEQ is not set
# CONFIG_NET_VENDOR_SMSC is not set
@@ -248,6 +250,9 @@
CONFIG_DMADEVICES=y
CONFIG_UIO=y
CONFIG_STAGING=y
+CONFIG_GSI=y
+CONFIG_IPA3=y
+CONFIG_IPA_UT=y
CONFIG_SPS=y
CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_HWSPINLOCK_QCOM=y
diff --git a/arch/arm/mach-omap2/omap-headsmp.S b/arch/arm/mach-omap2/omap-headsmp.S
index fe36ce2..4c6f14c 100644
--- a/arch/arm/mach-omap2/omap-headsmp.S
+++ b/arch/arm/mach-omap2/omap-headsmp.S
@@ -17,6 +17,7 @@
#include <linux/linkage.h>
#include <linux/init.h>
+#include <asm/assembler.h>
#include "omap44xx.h"
@@ -66,7 +67,7 @@
cmp r0, r4
bne wait_2
ldr r12, =API_HYP_ENTRY
- adr r0, hyp_boot
+ badr r0, hyp_boot
smc #0
hyp_boot:
b omap_secondary_startup
diff --git a/arch/arm64/boot/dts/qcom/pm8998.dtsi b/arch/arm64/boot/dts/qcom/pm8998.dtsi
index b9a6c79..f46b5639 100644
--- a/arch/arm64/boot/dts/qcom/pm8998.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm8998.dtsi
@@ -36,6 +36,7 @@
interrupt-names = "kpdpwr", "resin",
"resin-bark", "kpdpwr-resin-bark";
qcom,pon-dbc-delay = <15625>;
+ qcom,kpdpwr-sw-debounce;
qcom,system-reset;
qcom,store-hard-reset-reason;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
index e3f7036..072e592 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
@@ -500,4 +500,350 @@
cdm-client-names = "vfe";
status = "ok";
};
+
+ qcom,cam-isp {
+ compatible = "qcom,cam-isp";
+ arch-compat = "ife";
+ status = "ok";
+ };
+
+ qcom,csid0@acb3000 {
+ cell-index = <0>;
+ compatible = "qcom,csid170";
+ reg-names = "csid";
+ reg = <0xacb3000 0x1000>;
+ reg-cam-base = <0xb3000>;
+ interrupt-names = "csid";
+ interrupts = <0 464 0>;
+ regulator-names = "camss", "ife0";
+ camss-supply = <&titan_top_gdsc>;
+ ife0-supply = <&ife_0_gdsc>;
+ clock-names = "camera_ahb",
+ "camera_axi",
+ "soc_ahb_clk",
+ "cpas_ahb_clk",
+ "slow_ahb_clk_src",
+ "ife_csid_clk",
+ "ife_csid_clk_src",
+ "ife_cphy_rx_clk",
+ "cphy_rx_clk_src",
+ "ife_clk",
+ "ife_clk_src",
+ "camnoc_axi_clk",
+ "ife_axi_clk";
+ clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+ <&clock_gcc GCC_CAMERA_AXI_CLK>,
+ <&clock_camcc CAM_CC_SOC_AHB_CLK>,
+ <&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+ <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+ <&clock_camcc CAM_CC_IFE_0_CSID_CLK>,
+ <&clock_camcc CAM_CC_IFE_0_CSID_CLK_SRC>,
+ <&clock_camcc CAM_CC_IFE_0_CPHY_RX_CLK>,
+ <&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>,
+ <&clock_camcc CAM_CC_IFE_0_CLK>,
+ <&clock_camcc CAM_CC_IFE_0_CLK_SRC>,
+ <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+ <&clock_camcc CAM_CC_IFE_0_AXI_CLK>;
+ clock-rates = <0 0 0 0 0 0 384000000 0 0 0 40400000 0 0>;
+ src-clock-name = "ife_csid_clk_src";
+ status = "ok";
+ };
+
+ qcom,vfe0@acaf000 {
+ cell-index = <0>;
+ compatible = "qcom,vfe170";
+ reg-names = "ife";
+ reg = <0xacaf000 0x4000>;
+ reg-cam-base = <0xaf000>;
+ interrupt-names = "ife";
+ interrupts = <0 465 0>;
+ regulator-names = "camss", "ife0";
+ camss-supply = <&titan_top_gdsc>;
+ ife0-supply = <&ife_0_gdsc>;
+ clock-names = "camera_ahb",
+ "camera_axi",
+ "soc_ahb_clk",
+ "cpas_ahb_clk",
+ "slow_ahb_clk_src",
+ "ife_clk",
+ "ife_clk_src",
+ "camnoc_axi_clk",
+ "ife_axi_clk";
+ clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+ <&clock_gcc GCC_CAMERA_AXI_CLK>,
+ <&clock_camcc CAM_CC_SOC_AHB_CLK>,
+ <&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+ <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+ <&clock_camcc CAM_CC_IFE_0_CLK>,
+ <&clock_camcc CAM_CC_IFE_0_CLK_SRC>,
+ <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+ <&clock_camcc CAM_CC_IFE_0_AXI_CLK>;
+ clock-rates = <0 0 0 0 0 0 404000000 0 0>;
+ src-clock-name = "ife_clk_src";
+ clock-names-option = "ife_dsp_clk";
+ clocks-option = <&clock_camcc CAM_CC_IFE_0_DSP_CLK>;
+ clock-rates-option = <404000000>;
+ status = "ok";
+ };
+
+ qcom,csid1@acba000 {
+ cell-index = <1>;
+ compatible = "qcom,csid170";
+ reg-names = "csid";
+ reg = <0xacba000 0x1000>;
+ reg-cam-base = <0xba000>;
+ interrupt-names = "csid";
+ interrupts = <0 466 0>;
+ regulator-names = "camss", "ife1";
+ camss-supply = <&titan_top_gdsc>;
+ ife1-supply = <&ife_1_gdsc>;
+ clock-names = "camera_ahb",
+ "camera_axi",
+ "soc_ahb_clk",
+ "cpas_ahb_clk",
+ "slow_ahb_clk_src",
+ "ife_csid_clk",
+ "ife_csid_clk_src",
+ "ife_cphy_rx_clk",
+ "cphy_rx_clk_src",
+ "ife_clk",
+ "ife_clk_src",
+ "camnoc_axi_clk",
+ "ife_axi_clk";
+ clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+ <&clock_gcc GCC_CAMERA_AXI_CLK>,
+ <&clock_camcc CAM_CC_SOC_AHB_CLK>,
+ <&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+ <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+ <&clock_camcc CAM_CC_IFE_1_CSID_CLK>,
+ <&clock_camcc CAM_CC_IFE_1_CSID_CLK_SRC>,
+ <&clock_camcc CAM_CC_IFE_1_CPHY_RX_CLK>,
+ <&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>,
+ <&clock_camcc CAM_CC_IFE_1_CLK>,
+ <&clock_camcc CAM_CC_IFE_1_CLK_SRC>,
+ <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+ <&clock_camcc CAM_CC_IFE_1_AXI_CLK>;
+ clock-rates = <0 0 0 0 0 0 384000000 0 0 0 40400000 0 0>;
+ src-clock-name = "ife_csid_clk_src";
+ status = "ok";
+ };
+
+ qcom,vfe1@acb6000 {
+ cell-index = <1>;
+ compatible = "qcom,vfe170";
+ reg-names = "ife";
+ reg = <0xacb6000 0x4000>;
+ reg-cam-base = <0xb6000>;
+ interrupt-names = "ife";
+ interrupts = <0 467 0>;
+ regulator-names = "camss", "ife1";
+ camss-supply = <&titan_top_gdsc>;
+ ife1-supply = <&ife_1_gdsc>;
+ clock-names = "camera_ahb",
+ "camera_axi",
+ "soc_ahb_clk",
+ "cpas_ahb_clk",
+ "slow_ahb_clk_src",
+ "ife_clk",
+ "ife_clk_src",
+ "camnoc_axi_clk",
+ "ife_axi_clk";
+ clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+ <&clock_gcc GCC_CAMERA_AXI_CLK>,
+ <&clock_camcc CAM_CC_SOC_AHB_CLK>,
+ <&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+ <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+ <&clock_camcc CAM_CC_IFE_1_CLK>,
+ <&clock_camcc CAM_CC_IFE_1_CLK_SRC>,
+ <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+ <&clock_camcc CAM_CC_IFE_1_AXI_CLK>;
+ clock-rates = <0 0 0 0 0 0 404000000 0 0>;
+ src-clock-name = "ife_clk_src";
+ clock-names-option = "ife_dsp_clk";
+ clocks-option = <&clock_camcc CAM_CC_IFE_1_DSP_CLK>;
+ clock-rates-option = <404000000>;
+ status = "ok";
+ };
+
+ qcom,csid-lite@acc8000 {
+ cell-index = <2>;
+ compatible = "qcom,csid-lite170";
+ reg-names = "csid-lite";
+ reg = <0xacc8000 0x1000>;
+ reg-cam-base = <0xc8000>;
+ interrupt-names = "csid-lite";
+ interrupts = <0 468 0>;
+ regulator-names = "camss";
+ camss-supply = <&titan_top_gdsc>;
+ clock-names = "camera_ahb",
+ "camera_axi",
+ "soc_ahb_clk",
+ "cpas_ahb_clk",
+ "slow_ahb_clk_src",
+ "ife_csid_clk",
+ "ife_csid_clk_src",
+ "ife_cphy_rx_clk",
+ "cphy_rx_clk_src",
+ "ife_clk",
+ "ife_clk_src",
+ "camnoc_axi_clk";
+ clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+ <&clock_gcc GCC_CAMERA_AXI_CLK>,
+ <&clock_camcc CAM_CC_SOC_AHB_CLK>,
+ <&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+ <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+ <&clock_camcc CAM_CC_IFE_LITE_CSID_CLK>,
+ <&clock_camcc CAM_CC_IFE_LITE_CSID_CLK_SRC>,
+ <&clock_camcc CAM_CC_IFE_LITE_CPHY_RX_CLK>,
+ <&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>,
+ <&clock_camcc CAM_CC_IFE_LITE_CLK>,
+ <&clock_camcc CAM_CC_IFE_LITE_CLK_SRC>,
+ <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>;
+ clock-rates = <0 0 0 0 0 0 384000000 0 0 0 40400000 0>;
+ src-clock-name = "ife_csid_clk_src";
+ status = "ok";
+ };
+
+ qcom,vfe-lite@acc4000 {
+ cell-index = <2>;
+ compatible = "qcom,vfe-lite170";
+ reg-names = "ife-lite";
+ reg = <0xacc4000 0x4000>;
+ reg-cam-base = <0xc4000>;
+ interrupt-names = "ife-lite";
+ interrupts = <0 469 0>;
+ regulator-names = "camss";
+ camss-supply = <&titan_top_gdsc>;
+ clock-names = "camera_ahb",
+ "camera_axi",
+ "soc_ahb_clk",
+ "cpas_ahb_clk",
+ "slow_ahb_clk_src",
+ "ife_clk",
+ "ife_clk_src",
+ "camnoc_axi_clk";
+ clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+ <&clock_gcc GCC_CAMERA_AXI_CLK>,
+ <&clock_camcc CAM_CC_SOC_AHB_CLK>,
+ <&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+ <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+ <&clock_camcc CAM_CC_IFE_LITE_CLK>,
+ <&clock_camcc CAM_CC_IFE_LITE_CLK_SRC>,
+ <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>;
+ qcom,clock-rates = <0 0 0 0 0 0 404000000 0>;
+ src-clock-name = "ife_clk_src";
+ status = "ok";
+ };
+
+ qcom,cam-icp {
+ compatible = "qcom,cam-icp";
+ compat-hw-name = "qcom,a5",
+ "qcom,ipe0",
+ "qcom,ipe1",
+ "qcom,bps";
+ num-a5 = <1>;
+ num-ipe = <2>;
+ num-bps = <1>;
+ status = "ok";
+ };
+
+ qcom,a5@ac00000 {
+ cell-index = <0>;
+ compatible = "qcom,cam_a5";
+ reg = <0xac00000 0x6000>,
+ <0xac10000 0x8000>,
+ <0xac18000 0x3000>;
+ reg-names = "a5_qgic", "a5_sierra", "a5_csr";
+ reg-cam-base = <0x00000 0x10000 0x18000>;
+ interrupts = <0 463 0>;
+ interrupt-names = "a5";
+ regulator-names = "camss-vdd";
+ camss-vdd-supply = <&titan_top_gdsc>;
+ clock-names = "gcc_cam_ahb_clk",
+ "gcc_cam_axi_clk",
+ "soc_ahb_clk",
+ "cpas_ahb_clk",
+ "camnoc_axi_clk",
+ "icp_apb_clk",
+ "icp_atb_clk",
+ "icp_clk",
+ "icp_clk_src",
+ "icp_cti_clk",
+ "icp_ts_clk";
+ clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+ <&clock_gcc GCC_CAMERA_AXI_CLK>,
+ <&clock_camcc CAM_CC_SOC_AHB_CLK>,
+ <&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+ <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+ <&clock_camcc CAM_CC_ICP_APB_CLK>,
+ <&clock_camcc CAM_CC_ICP_ATB_CLK>,
+ <&clock_camcc CAM_CC_ICP_CLK>,
+ <&clock_camcc CAM_CC_ICP_CLK_SRC>,
+ <&clock_camcc CAM_CC_ICP_CTI_CLK>,
+ <&clock_camcc CAM_CC_ICP_TS_CLK>;
+
+ clock-rates = <0 0 0 80000000 0 0 0 0 600000000 0 0>;
+ fw_name = "CAMERA_ICP.elf";
+ status = "ok";
+ };
+
+ qcom,ipe0 {
+ cell-index = <0>;
+ compatible = "qcom,cam_ipe";
+ regulator-names = "ipe0-vdd";
+ ipe0-vdd-supply = <&ipe_0_gdsc>;
+ clock-names = "ipe_0_ahb_clk",
+ "ipe_0_areg_clk",
+ "ipe_0_axi_clk",
+ "ipe_0_clk",
+ "ipe_0_clk_src";
+ clocks = <&clock_camcc CAM_CC_IPE_0_AHB_CLK>,
+ <&clock_camcc CAM_CC_IPE_0_AREG_CLK>,
+ <&clock_camcc CAM_CC_IPE_0_AXI_CLK>,
+ <&clock_camcc CAM_CC_IPE_0_CLK>,
+ <&clock_camcc CAM_CC_IPE_0_CLK_SRC>;
+
+ clock-rates = <80000000 400000000 0 0 600000000>;
+ status = "ok";
+ };
+
+ qcom,ipe1 {
+ cell-index = <1>;
+ compatible = "qcom,cam_ipe";
+ regulator-names = "ipe1-vdd";
+ ipe1-vdd-supply = <&ipe_1_gdsc>;
+ clock-names = "ipe_1_ahb_clk",
+ "ipe_1_areg_clk",
+ "ipe_1_axi_clk",
+ "ipe_1_clk",
+ "ipe_1_clk_src";
+ clocks = <&clock_camcc CAM_CC_IPE_1_AHB_CLK>,
+ <&clock_camcc CAM_CC_IPE_1_AREG_CLK>,
+ <&clock_camcc CAM_CC_IPE_1_AXI_CLK>,
+ <&clock_camcc CAM_CC_IPE_1_CLK>,
+ <&clock_camcc CAM_CC_IPE_1_CLK_SRC>;
+
+ clock-rates = <80000000 400000000 0 0 600000000>;
+ status = "ok";
+ };
+
+ qcom,bps {
+ cell-index = <0>;
+ compatible = "qcom,cam_bps";
+ regulator-names = "bps-vdd";
+ bps-vdd-supply = <&bps_gdsc>;
+ clock-names = "bps_ahb_clk",
+ "bps_areg_clk",
+ "bps_axi_clk",
+ "bps_clk",
+ "bps_clk_src";
+ clocks = <&clock_camcc CAM_CC_BPS_AHB_CLK>,
+ <&clock_camcc CAM_CC_BPS_AREG_CLK>,
+ <&clock_camcc CAM_CC_BPS_AXI_CLK>,
+ <&clock_camcc CAM_CC_BPS_CLK>,
+ <&clock_camcc CAM_CC_BPS_CLK_SRC>;
+
+ clock-rates = <80000000 400000000 0 0 600000000>;
+ status = "ok";
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
index 106d0c6..7cdc746 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
@@ -122,6 +122,17 @@
status = "ok";
};
+&extcon_storage_cd {
+ gpio = <&tlmm 126 GPIO_ACTIVE_LOW>;
+ debounce-ms = <200>;
+ irq-flags = <IRQ_TYPE_EDGE_BOTH>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&storage_cd>;
+
+ status = "ok";
+};
+
&ufsphy_card {
compatible = "qcom,ufs-phy-qmp-v3";
@@ -164,6 +175,8 @@
50000000 100000000 200000000>;
qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+ extcon = <&extcon_storage_cd>;
+
status = "ok";
};
@@ -481,3 +494,7 @@
};
};
};
+
+&wil6210 {
+ status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
index 77edb85..1b3f2a6 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
@@ -64,7 +64,6 @@
qcom,initial-pwrlevel = <2>;
qcom,gpu-quirk-hfi-use-reg;
- qcom,gpu-quirk-two-pass-use-wfi;
qcom,idle-timeout = <100000000>; //msecs
qcom,no-nap;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
index 3dc45ae..c776624 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
@@ -464,3 +464,7 @@
};
};
};
+
+&wil6210 {
+ status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
index 947262fb..59b3396 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
@@ -112,6 +112,19 @@
};
};
+ storage_cd: storage_cd {
+ mux {
+ pins = "gpio126";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio126";
+ bias-pull-up; /* pull up */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
sdc2_clk_on: sdc2_clk_on {
config {
pins = "sdc2_clk";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
index 1ac661d..9056569 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
@@ -147,3 +147,7 @@
&dsi_sharp_4k_dsc_video_display {
qcom,dsi-display-active;
};
+
+&wil6210 {
+ status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 5ae733b..a911d5e 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -1378,6 +1378,12 @@
status = "disabled";
};
+ extcon_storage_cd: extcon_storage_cd {
+ compatible = "extcon-gpio";
+ extcon-id = <62>; /* EXTCON_MECHANICAL */
+ status = "disabled";
+ };
+
ufsphy_card: ufsphy_card@1da7000 {
reg = <0x1da7000 0xda8>; /* PHY regs */
reg-names = "phy_mem";
@@ -1910,6 +1916,12 @@
status = "ok";
};
+ ssc_sensors: qcom,msm-ssc-sensors {
+ compatible = "qcom,msm-ssc-sensors";
+ status = "ok";
+ qcom,firmware-name = "slpi";
+ };
+
cpuss_dump {
compatible = "qcom,cpuss-dump";
qcom,l1_i_cache0 {
@@ -3740,6 +3752,27 @@
qcom,affinity = <1>;
#thermal-sensor-cells = <0>;
};
+
+ wil6210: qcom,wil6210 {
+ compatible = "qcom,wil6210";
+ qcom,pcie-parent = <&pcie0>;
+ qcom,wigig-en = <&tlmm 39 0>;
+ qcom,msm-bus,name = "wil6210";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <45 512 0 0>,
+ <45 512 600000 800000>; /* ~4.6Gbps (MCS12) */
+ qcom,use-ext-supply;
+ vdd-supply= <&pm8998_s7>;
+ vddio-supply= <&pm8998_s5>;
+ qcom,use-ext-clocks;
+ clocks = <&clock_rpmh RPMH_RF_CLK3>,
+ <&clock_rpmh RPMH_RF_CLK3_A>;
+ clock-names = "rf_clk3_clk", "rf_clk3_pin_clk";
+ qcom,smmu-support;
+ status = "disabled";
+ };
};
&pcie_0_gdsc {
diff --git a/arch/arm64/boot/dts/renesas/r8a7795.dtsi b/arch/arm64/boot/dts/renesas/r8a7795.dtsi
index 8c15040..9536f20 100644
--- a/arch/arm64/boot/dts/renesas/r8a7795.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a7795.dtsi
@@ -553,6 +553,7 @@
phy-mode = "rgmii-id";
#address-cells = <1>;
#size-cells = <0>;
+ status = "disabled";
};
can0: can@e6c30000 {
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index 86bae12..47e0042 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -78,7 +78,6 @@
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
-CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
CONFIG_CPU_BOOST=y
CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
@@ -338,7 +337,6 @@
CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_SPECTRA_CAMERA=y
CONFIG_MSM_VIDC_V4L2=y
-CONFIG_MSM_VIDC_VMEM=y
CONFIG_MSM_VIDC_GOVERNORS=y
CONFIG_MSM_SDE_ROTATOR=y
CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y
@@ -498,7 +496,6 @@
CONFIG_DEVFREQ_GOV_MEMLAT=y
CONFIG_DEVFREQ_SIMPLE_DEV=y
CONFIG_QCOM_DEVFREQ_DEVBW=y
-CONFIG_EXTCON=y
CONFIG_EXTCON_USB_GPIO=y
CONFIG_IIO=y
CONFIG_QCOM_RRADC=y
@@ -507,6 +504,7 @@
CONFIG_ARM_GIC_V3_ACL=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_SENSORS_SSC=y
CONFIG_MSM_TZ_LOG=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index 1436a40..261744d 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -84,7 +84,6 @@
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
-CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
CONFIG_CPU_BOOST=y
CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
@@ -347,7 +346,6 @@
CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_SPECTRA_CAMERA=y
CONFIG_MSM_VIDC_V4L2=y
-CONFIG_MSM_VIDC_VMEM=y
CONFIG_MSM_VIDC_GOVERNORS=y
CONFIG_MSM_SDE_ROTATOR=y
CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y
@@ -519,7 +517,6 @@
CONFIG_DEVFREQ_GOV_MEMLAT=y
CONFIG_DEVFREQ_SIMPLE_DEV=y
CONFIG_QCOM_DEVFREQ_DEVBW=y
-CONFIG_EXTCON=y
CONFIG_EXTCON_USB_GPIO=y
CONFIG_IIO=y
CONFIG_QCOM_RRADC=y
@@ -529,6 +526,7 @@
CONFIG_PHY_XGENE=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_SENSORS_SSC=y
CONFIG_MSM_TZ_LOG=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
@@ -595,6 +593,7 @@
CONFIG_CPU_FREQ_SWITCH_PROFILER=y
CONFIG_LKDTM=y
CONFIG_MEMTEST=y
+CONFIG_PANIC_ON_DATA_CORRUPTION=y
CONFIG_ARM64_PTDUMP=y
CONFIG_PID_IN_CONTEXTIDR=y
CONFIG_CORESIGHT=y
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 875545d..3845f33 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -72,9 +72,8 @@
#define pte_young(pte) (!!(pte_val(pte) & PTE_AF))
#define pte_special(pte) (!!(pte_val(pte) & PTE_SPECIAL))
#define pte_write(pte) (!!(pte_val(pte) & PTE_WRITE))
-#define pte_exec(pte) (!(pte_val(pte) & PTE_UXN))
+#define pte_user_exec(pte) (!(pte_val(pte) & PTE_UXN))
#define pte_cont(pte) (!!(pte_val(pte) & PTE_CONT))
-#define pte_ng(pte) (!!(pte_val(pte) & PTE_NG))
#ifdef CONFIG_ARM64_HW_AFDBM
#define pte_hw_dirty(pte) (pte_write(pte) && !(pte_val(pte) & PTE_RDONLY))
@@ -85,8 +84,12 @@
#define pte_dirty(pte) (pte_sw_dirty(pte) || pte_hw_dirty(pte))
#define pte_valid(pte) (!!(pte_val(pte) & PTE_VALID))
-#define pte_valid_global(pte) \
- ((pte_val(pte) & (PTE_VALID | PTE_NG)) == PTE_VALID)
+/*
+ * Execute-only user mappings do not have the PTE_USER bit set. All valid
+ * kernel mappings have the PTE_UXN bit set.
+ */
+#define pte_valid_not_user(pte) \
+ ((pte_val(pte) & (PTE_VALID | PTE_USER | PTE_UXN)) == (PTE_VALID | PTE_UXN))
#define pte_valid_young(pte) \
((pte_val(pte) & (PTE_VALID | PTE_AF)) == (PTE_VALID | PTE_AF))
@@ -207,7 +210,7 @@
* Only if the new pte is valid and kernel, otherwise TLB maintenance
* or update_mmu_cache() have the necessary barriers.
*/
- if (pte_valid_global(pte)) {
+ if (pte_valid_not_user(pte)) {
dsb(ishst);
isb();
}
@@ -241,7 +244,7 @@
pte_val(pte) &= ~PTE_RDONLY;
else
pte_val(pte) |= PTE_RDONLY;
- if (pte_ng(pte) && pte_exec(pte) && !pte_special(pte))
+ if (pte_user_exec(pte) && !pte_special(pte))
__sync_icache_dcache(pte, addr);
}
diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
index b2fc97a..9c4b57a 100644
--- a/arch/arm64/net/bpf_jit_comp.c
+++ b/arch/arm64/net/bpf_jit_comp.c
@@ -779,14 +779,14 @@
int ret;
ret = build_insn(insn, ctx);
-
- if (ctx->image == NULL)
- ctx->offset[i] = ctx->idx;
-
if (ret > 0) {
i++;
+ if (ctx->image == NULL)
+ ctx->offset[i] = ctx->idx;
continue;
}
+ if (ctx->image == NULL)
+ ctx->offset[i] = ctx->idx;
if (ret)
return ret;
}
diff --git a/arch/mips/kernel/mips-r2-to-r6-emul.c b/arch/mips/kernel/mips-r2-to-r6-emul.c
index bd09853..d8227f2 100644
--- a/arch/mips/kernel/mips-r2-to-r6-emul.c
+++ b/arch/mips/kernel/mips-r2-to-r6-emul.c
@@ -433,8 +433,8 @@
rs = regs->regs[MIPSInst_RS(ir)];
res = (u64)rt * (u64)rs;
rt = res;
- regs->lo = (s64)rt;
- regs->hi = (s64)(res >> 32);
+ regs->lo = (s64)(s32)rt;
+ regs->hi = (s64)(s32)(res >> 32);
MIPS_R2_STATS(muls);
@@ -670,9 +670,9 @@
res += ((((s64)rt) << 32) | (u32)rs);
rt = res;
- regs->lo = (s64)rt;
+ regs->lo = (s64)(s32)rt;
rs = res >> 32;
- regs->hi = (s64)rs;
+ regs->hi = (s64)(s32)rs;
MIPS_R2_STATS(dsps);
@@ -728,9 +728,9 @@
res = ((((s64)rt) << 32) | (u32)rs) - res;
rt = res;
- regs->lo = (s64)rt;
+ regs->lo = (s64)(s32)rt;
rs = res >> 32;
- regs->hi = (s64)rs;
+ regs->hi = (s64)(s32)rs;
MIPS_R2_STATS(dsps);
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 65fba4c..8f01f21 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -388,8 +388,8 @@
be disabled also.
If you have a toolchain which supports mprofile-kernel, then you can
- enable this. Otherwise leave it disabled. If you're not sure, say
- "N".
+ disable this. Otherwise leave it enabled. If you're not sure, say
+ "Y".
config MPROFILE_KERNEL
depends on PPC64 && CPU_LITTLE_ENDIAN
diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index 13f5fad..e7d9eca 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -337,7 +337,7 @@
#define LPCR_DPFD_SH 52
#define LPCR_DPFD (ASM_CONST(7) << LPCR_DPFD_SH)
#define LPCR_VRMASD_SH 47
-#define LPCR_VRMASD (ASM_CONST(1) << LPCR_VRMASD_SH)
+#define LPCR_VRMASD (ASM_CONST(0x1f) << LPCR_VRMASD_SH)
#define LPCR_VRMA_L ASM_CONST(0x0008000000000000)
#define LPCR_VRMA_LP0 ASM_CONST(0x0001000000000000)
#define LPCR_VRMA_LP1 ASM_CONST(0x0000800000000000)
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 1925341..adb52d1 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -15,7 +15,7 @@
endif
CFLAGS_cputable.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
-CFLAGS_init.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
+CFLAGS_prom_init.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
CFLAGS_btext.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
CFLAGS_prom.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S
index 44d2d84..483d8c0 100644
--- a/arch/powerpc/platforms/powernv/opal-wrappers.S
+++ b/arch/powerpc/platforms/powernv/opal-wrappers.S
@@ -146,7 +146,7 @@
opal_tracepoint_return:
std r3,STK_REG(R31)(r1)
mr r4,r3
- ld r0,STK_REG(R23)(r1)
+ ld r3,STK_REG(R23)(r1)
bl __trace_opal_exit
ld r3,STK_REG(R31)(r1)
addi r1,r1,STACKFRAMESIZE
diff --git a/arch/sparc/kernel/head_64.S b/arch/sparc/kernel/head_64.S
index 6aa3da1..9835152 100644
--- a/arch/sparc/kernel/head_64.S
+++ b/arch/sparc/kernel/head_64.S
@@ -935,3 +935,9 @@
retl
mov %o1, %o0
ENDPROC(__retl_o1)
+
+ENTRY(__retl_o1_asi)
+ wr %o5, 0x0, %asi
+ retl
+ mov %o1, %o0
+ENDPROC(__retl_o1_asi)
diff --git a/arch/sparc/lib/GENbzero.S b/arch/sparc/lib/GENbzero.S
index 8e7a843..2fbf629 100644
--- a/arch/sparc/lib/GENbzero.S
+++ b/arch/sparc/lib/GENbzero.S
@@ -8,7 +8,7 @@
98: x,y; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_o1; \
+ .word 98b, __retl_o1_asi;\
.text; \
.align 4;
diff --git a/arch/sparc/lib/NGbzero.S b/arch/sparc/lib/NGbzero.S
index beab29b..33053bd 100644
--- a/arch/sparc/lib/NGbzero.S
+++ b/arch/sparc/lib/NGbzero.S
@@ -8,7 +8,7 @@
98: x,y; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_o1; \
+ .word 98b, __retl_o1_asi;\
.text; \
.align 4;
diff --git a/arch/x86/events/intel/pt.c b/arch/x86/events/intel/pt.c
index c5047b8..df60b58 100644
--- a/arch/x86/events/intel/pt.c
+++ b/arch/x86/events/intel/pt.c
@@ -106,18 +106,24 @@
};
PMU_FORMAT_ATTR(cyc, "config:1" );
+PMU_FORMAT_ATTR(pwr_evt, "config:4" );
+PMU_FORMAT_ATTR(fup_on_ptw, "config:5" );
PMU_FORMAT_ATTR(mtc, "config:9" );
PMU_FORMAT_ATTR(tsc, "config:10" );
PMU_FORMAT_ATTR(noretcomp, "config:11" );
+PMU_FORMAT_ATTR(ptw, "config:12" );
PMU_FORMAT_ATTR(mtc_period, "config:14-17" );
PMU_FORMAT_ATTR(cyc_thresh, "config:19-22" );
PMU_FORMAT_ATTR(psb_period, "config:24-27" );
static struct attribute *pt_formats_attr[] = {
&format_attr_cyc.attr,
+ &format_attr_pwr_evt.attr,
+ &format_attr_fup_on_ptw.attr,
&format_attr_mtc.attr,
&format_attr_tsc.attr,
&format_attr_noretcomp.attr,
+ &format_attr_ptw.attr,
&format_attr_mtc_period.attr,
&format_attr_cyc_thresh.attr,
&format_attr_psb_period.attr,
diff --git a/arch/x86/include/asm/xen/events.h b/arch/x86/include/asm/xen/events.h
index 608a79d..e6911ca 100644
--- a/arch/x86/include/asm/xen/events.h
+++ b/arch/x86/include/asm/xen/events.h
@@ -20,4 +20,15 @@
/* No need for a barrier -- XCHG is a barrier on x86. */
#define xchg_xen_ulong(ptr, val) xchg((ptr), (val))
+extern int xen_have_vector_callback;
+
+/*
+ * Events delivered via platform PCI interrupts are always
+ * routed to vcpu 0 and hence cannot be rebound.
+ */
+static inline bool xen_support_evtchn_rebind(void)
+{
+ return (!xen_hvm_domain() || xen_have_vector_callback);
+}
+
#endif /* _ASM_X86_XEN_EVENTS_H */
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index d1e2556..7249f15 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -1876,6 +1876,7 @@
.irq_ack = irq_chip_ack_parent,
.irq_eoi = ioapic_ack_level,
.irq_set_affinity = ioapic_set_affinity,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
.flags = IRQCHIP_SKIP_SET_WAKE,
};
@@ -1887,6 +1888,7 @@
.irq_ack = irq_chip_ack_parent,
.irq_eoi = ioapic_ir_ack_level,
.irq_set_affinity = ioapic_set_affinity,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
.flags = IRQCHIP_SKIP_SET_WAKE,
};
diff --git a/arch/x86/kernel/kprobes/common.h b/arch/x86/kernel/kprobes/common.h
index c6ee63f..d688826 100644
--- a/arch/x86/kernel/kprobes/common.h
+++ b/arch/x86/kernel/kprobes/common.h
@@ -67,7 +67,7 @@
#endif
/* Ensure if the instruction can be boostable */
-extern int can_boost(kprobe_opcode_t *instruction);
+extern int can_boost(kprobe_opcode_t *instruction, void *addr);
/* Recover instruction if given address is probed */
extern unsigned long recover_probed_instruction(kprobe_opcode_t *buf,
unsigned long addr);
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c
index d9d8d16..b55d07b 100644
--- a/arch/x86/kernel/kprobes/core.c
+++ b/arch/x86/kernel/kprobes/core.c
@@ -166,12 +166,12 @@
* Returns non-zero if opcode is boostable.
* RIP relative instructions are adjusted at copying time in 64 bits mode
*/
-int can_boost(kprobe_opcode_t *opcodes)
+int can_boost(kprobe_opcode_t *opcodes, void *addr)
{
kprobe_opcode_t opcode;
kprobe_opcode_t *orig_opcodes = opcodes;
- if (search_exception_tables((unsigned long)opcodes))
+ if (search_exception_tables((unsigned long)addr))
return 0; /* Page fault may occur on this address. */
retry:
@@ -416,7 +416,7 @@
* __copy_instruction can modify the displacement of the instruction,
* but it doesn't affect boostable check.
*/
- if (can_boost(p->ainsn.insn))
+ if (can_boost(p->ainsn.insn, p->addr))
p->ainsn.boostable = 0;
else
p->ainsn.boostable = -1;
diff --git a/arch/x86/kernel/kprobes/opt.c b/arch/x86/kernel/kprobes/opt.c
index 3bb4c5f..4d74f73 100644
--- a/arch/x86/kernel/kprobes/opt.c
+++ b/arch/x86/kernel/kprobes/opt.c
@@ -178,7 +178,7 @@
while (len < RELATIVEJUMP_SIZE) {
ret = __copy_instruction(dest + len, src + len);
- if (!ret || !can_boost(dest + len))
+ if (!ret || !can_boost(dest + len, src + len))
return -EINVAL;
len += ret;
}
diff --git a/arch/x86/kernel/pci-calgary_64.c b/arch/x86/kernel/pci-calgary_64.c
index 5d400ba..d475179 100644
--- a/arch/x86/kernel/pci-calgary_64.c
+++ b/arch/x86/kernel/pci-calgary_64.c
@@ -296,7 +296,7 @@
/* were we called with bad_dma_address? */
badend = DMA_ERROR_CODE + (EMERGENCY_PAGES * PAGE_SIZE);
- if (unlikely((dma_addr >= DMA_ERROR_CODE) && (dma_addr < badend))) {
+ if (unlikely(dma_addr < badend)) {
WARN(1, KERN_ERR "Calgary: driver tried unmapping bad DMA "
"address 0x%Lx\n", dma_addr);
return;
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index afa7bbb..967e459 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -846,12 +846,6 @@
if (!best)
best = check_cpuid_limit(vcpu, function, index);
- /*
- * Perfmon not yet supported for L2 guest.
- */
- if (is_guest_mode(vcpu) && function == 0xa)
- best = NULL;
-
if (best) {
*eax = best->eax;
*ebx = best->ebx;
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 43b55ef..89b98e0 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -8051,8 +8051,6 @@
case EXIT_REASON_TASK_SWITCH:
return true;
case EXIT_REASON_CPUID:
- if (kvm_register_read(vcpu, VCPU_REGS_RAX) == 0xa)
- return false;
return true;
case EXIT_REASON_HLT:
return nested_cpu_has(vmcs12, CPU_BASED_HLT_EXITING);
@@ -8137,6 +8135,9 @@
return nested_cpu_has2(vmcs12, SECONDARY_EXEC_XSAVES);
case EXIT_REASON_PREEMPTION_TIMER:
return false;
+ case EXIT_REASON_PML_FULL:
+ /* We don't expose PML support to L1. */
+ return false;
default:
return true;
}
@@ -10073,6 +10074,18 @@
}
+ if (enable_pml) {
+ /*
+ * Conceptually we want to copy the PML address and index from
+ * vmcs01 here, and then back to vmcs01 on nested vmexit. But,
+ * since we always flush the log on each vmexit, this happens
+ * to be equivalent to simply resetting the fields in vmcs02.
+ */
+ ASSERT(vmx->pml_pg);
+ vmcs_write64(PML_ADDRESS, page_to_phys(vmx->pml_pg));
+ vmcs_write16(GUEST_PML_INDEX, PML_ENTITY_NUM - 1);
+ }
+
if (nested_cpu_has_ept(vmcs12)) {
kvm_mmu_unload(vcpu);
nested_ept_init_mmu_context(vcpu);
diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c
index a00a6c0..4ea9f29 100644
--- a/arch/x86/pci/xen.c
+++ b/arch/x86/pci/xen.c
@@ -447,7 +447,7 @@
int __init pci_xen_hvm_init(void)
{
- if (!xen_feature(XENFEAT_hvm_pirqs))
+ if (!xen_have_vector_callback || !xen_feature(XENFEAT_hvm_pirqs))
return 0;
#ifdef CONFIG_ACPI
diff --git a/arch/x86/platform/intel-mid/device_libs/platform_mrfld_wdt.c b/arch/x86/platform/intel-mid/device_libs/platform_mrfld_wdt.c
index 3f1f1c7..10bad1e 100644
--- a/arch/x86/platform/intel-mid/device_libs/platform_mrfld_wdt.c
+++ b/arch/x86/platform/intel-mid/device_libs/platform_mrfld_wdt.c
@@ -19,7 +19,7 @@
#include <asm/intel_scu_ipc.h>
#include <asm/io_apic.h>
-#define TANGIER_EXT_TIMER0_MSI 15
+#define TANGIER_EXT_TIMER0_MSI 12
static struct platform_device wdt_dev = {
.name = "intel_mid_wdt",
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
index bdd8556..8f1f7ef 100644
--- a/arch/x86/xen/enlighten.c
+++ b/arch/x86/xen/enlighten.c
@@ -137,6 +137,8 @@
void *xen_initial_gdt;
RESERVE_BRK(shared_info_page_brk, PAGE_SIZE);
+__read_mostly int xen_have_vector_callback;
+EXPORT_SYMBOL_GPL(xen_have_vector_callback);
static int xen_cpu_up_prepare(unsigned int cpu);
static int xen_cpu_up_online(unsigned int cpu);
@@ -1521,7 +1523,10 @@
if (!xen_feature(XENFEAT_auto_translated_physmap))
return;
- BUG_ON(!xen_feature(XENFEAT_hvm_callback_vector));
+ if (!xen_feature(XENFEAT_hvm_callback_vector))
+ return;
+
+ xen_have_vector_callback = 1;
xen_pvh_early_cpu_init(0, false);
xen_pvh_set_cr_flags(0);
@@ -1860,7 +1865,9 @@
xen_vcpu_setup(cpu);
}
- if (xen_pv_domain() || xen_feature(XENFEAT_hvm_safe_pvclock))
+ if (xen_pv_domain() ||
+ (xen_have_vector_callback &&
+ xen_feature(XENFEAT_hvm_safe_pvclock)))
xen_setup_timer(cpu);
rc = xen_smp_intr_init(cpu);
@@ -1876,7 +1883,9 @@
{
xen_smp_intr_free(cpu);
- if (xen_pv_domain() || xen_feature(XENFEAT_hvm_safe_pvclock))
+ if (xen_pv_domain() ||
+ (xen_have_vector_callback &&
+ xen_feature(XENFEAT_hvm_safe_pvclock)))
xen_teardown_timer(cpu);
return 0;
@@ -1915,8 +1924,8 @@
xen_panic_handler_init();
- BUG_ON(!xen_feature(XENFEAT_hvm_callback_vector));
-
+ if (xen_feature(XENFEAT_hvm_callback_vector))
+ xen_have_vector_callback = 1;
xen_hvm_smp_init();
WARN_ON(xen_cpuhp_setup());
xen_unplug_emulated_devices();
@@ -1954,7 +1963,7 @@
return false;
if (!xen_hvm_domain())
return false;
- if (xen_feature(XENFEAT_hvm_pirqs))
+ if (xen_feature(XENFEAT_hvm_pirqs) && xen_have_vector_callback)
return false;
return true;
}
diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c
index 311acad..137afbb 100644
--- a/arch/x86/xen/smp.c
+++ b/arch/x86/xen/smp.c
@@ -765,6 +765,8 @@
void __init xen_hvm_smp_init(void)
{
+ if (!xen_have_vector_callback)
+ return;
smp_ops.smp_prepare_cpus = xen_hvm_smp_prepare_cpus;
smp_ops.smp_send_reschedule = xen_smp_send_reschedule;
smp_ops.cpu_die = xen_cpu_die;
diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c
index 33d8f6a..67356d2 100644
--- a/arch/x86/xen/time.c
+++ b/arch/x86/xen/time.c
@@ -432,6 +432,11 @@
void __init xen_hvm_init_time_ops(void)
{
+ /* vector callback is needed otherwise we cannot receive interrupts
+ * on cpu > 0 and at this point we don't know how many cpus are
+ * available */
+ if (!xen_have_vector_callback)
+ return;
if (!xen_feature(XENFEAT_hvm_safe_pvclock)) {
printk(KERN_INFO "Xen doesn't support pvclock on HVM,"
"disable pv timer\n");
diff --git a/block/blk-integrity.c b/block/blk-integrity.c
index d69c5c7..319f2e4 100644
--- a/block/blk-integrity.c
+++ b/block/blk-integrity.c
@@ -417,7 +417,7 @@
bi->tuple_size = template->tuple_size;
bi->tag_size = template->tag_size;
- blk_integrity_revalidate(disk);
+ disk->queue->backing_dev_info.capabilities |= BDI_CAP_STABLE_WRITES;
}
EXPORT_SYMBOL(blk_integrity_register);
@@ -430,26 +430,11 @@
*/
void blk_integrity_unregister(struct gendisk *disk)
{
- blk_integrity_revalidate(disk);
+ disk->queue->backing_dev_info.capabilities &= ~BDI_CAP_STABLE_WRITES;
memset(&disk->queue->integrity, 0, sizeof(struct blk_integrity));
}
EXPORT_SYMBOL(blk_integrity_unregister);
-void blk_integrity_revalidate(struct gendisk *disk)
-{
- struct blk_integrity *bi = &disk->queue->integrity;
-
- if (!(disk->flags & GENHD_FL_UP))
- return;
-
- if (bi->profile)
- disk->queue->backing_dev_info.capabilities |=
- BDI_CAP_STABLE_WRITES;
- else
- disk->queue->backing_dev_info.capabilities &=
- ~BDI_CAP_STABLE_WRITES;
-}
-
void blk_integrity_add(struct gendisk *disk)
{
if (kobject_init_and_add(&disk->integrity_kobj, &integrity_ktype,
diff --git a/block/partition-generic.c b/block/partition-generic.c
index 71d9ed9..a2437c0 100644
--- a/block/partition-generic.c
+++ b/block/partition-generic.c
@@ -447,7 +447,6 @@
if (disk->fops->revalidate_disk)
disk->fops->revalidate_disk(disk);
- blk_integrity_revalidate(disk);
check_disk_size_change(disk, bdev);
bdev->bd_invalidated = 0;
if (!get_capacity(disk) || !(state = check_partition(disk, bdev)))
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 6266a37..5f8abc3 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -208,4 +208,6 @@
source "drivers/fpga/Kconfig"
+source "drivers/sensors/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 1419893..413dff9 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -176,3 +176,4 @@
obj-$(CONFIG_NVMEM) += nvmem/
obj-$(CONFIG_ESOC) += esoc/
obj-$(CONFIG_FPGA) += fpga/
+obj-$(CONFIG_SENSORS_SSC) += sensors/
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index e595013..a017ccd 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -140,7 +140,7 @@
* Allocates a new struct tpm_chip instance and assigns a free
* device number for it. Must be paired with put_device(&chip->dev).
*/
-struct tpm_chip *tpm_chip_alloc(struct device *dev,
+struct tpm_chip *tpm_chip_alloc(struct device *pdev,
const struct tpm_class_ops *ops)
{
struct tpm_chip *chip;
@@ -159,7 +159,7 @@
rc = idr_alloc(&dev_nums_idr, NULL, 0, TPM_NUM_DEVICES, GFP_KERNEL);
mutex_unlock(&idr_lock);
if (rc < 0) {
- dev_err(dev, "No available tpm device numbers\n");
+ dev_err(pdev, "No available tpm device numbers\n");
kfree(chip);
return ERR_PTR(rc);
}
@@ -169,7 +169,7 @@
chip->dev.class = tpm_class;
chip->dev.release = tpm_dev_release;
- chip->dev.parent = dev;
+ chip->dev.parent = pdev;
chip->dev.groups = chip->groups;
if (chip->dev_num == 0)
@@ -181,7 +181,7 @@
if (rc)
goto out;
- if (!dev)
+ if (!pdev)
chip->flags |= TPM_CHIP_FLAG_VIRTUAL;
cdev_init(&chip->cdev, &tpm_fops);
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 4d183c9..aa4299c 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -518,6 +518,11 @@
}
#endif
+static inline inline u32 tpm2_rc_value(u32 rc)
+{
+ return (rc & BIT(7)) ? rc & 0xff : rc;
+}
+
int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf);
int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash);
int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max);
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 7df55d58..17896d6 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -529,7 +529,7 @@
tpm_buf_destroy(&buf);
if (rc > 0) {
- if ((rc & TPM2_RC_HASH) == TPM2_RC_HASH)
+ if (tpm2_rc_value(rc) == TPM2_RC_HASH)
rc = -EINVAL;
else
rc = -EPERM;
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 925081e..42042c0 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -87,6 +87,8 @@
obj-$(CONFIG_CLK_UNIPHIER) += uniphier/
obj-$(CONFIG_ARCH_U8500) += ux500/
obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/
+ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_X86) += x86/
+endif
obj-$(CONFIG_ARCH_ZX) += zte/
obj-$(CONFIG_ARCH_ZYNQ) += zynq/
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 1b545d6..1f0c111 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1801,6 +1801,15 @@
clk_enable_unlock(flags);
}
+ trace_clk_set_rate(core, core->new_rate);
+
+ /* Enforce vdd requirements for new frequency. */
+ if (core->prepare_count) {
+ rc = clk_vote_rate_vdd(core, core->new_rate);
+ if (rc)
+ goto out;
+ }
+
if (core->new_parent && core->new_parent != core->parent) {
old_parent = __clk_set_parent_before(core, core->new_parent);
trace_clk_set_parent(core, core->new_parent);
@@ -1821,15 +1830,6 @@
if (core->flags & CLK_OPS_PARENT_ENABLE)
clk_core_prepare_enable(parent);
- trace_clk_set_rate(core, core->new_rate);
-
- /* Enforce vdd requirements for new frequency. */
- if (core->prepare_count) {
- rc = clk_vote_rate_vdd(core, core->new_rate);
- if (rc)
- goto out;
- }
-
if (!skip_set_rate && core->ops->set_rate) {
rc = core->ops->set_rate(core->hw, core->new_rate,
best_parent_rate);
@@ -2329,6 +2329,21 @@
}
EXPORT_SYMBOL_GPL(clk_set_flags);
+unsigned long clk_list_frequency(struct clk *clk, unsigned int index)
+{
+ int ret = 0;
+
+ if (!clk || !clk->core->ops->list_rate)
+ return -EINVAL;
+
+ clk_prepare_lock();
+ ret = clk->core->ops->list_rate(clk->core->hw, index, ULONG_MAX);
+ clk_prepare_unlock();
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_list_frequency);
+
/*** debugfs support ***/
#ifdef CONFIG_DEBUG_FS
diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c
index 29421a1..228f716 100644
--- a/drivers/clk/qcom/gcc-sdm845.c
+++ b/drivers/clk/qcom/gcc-sdm845.c
@@ -1698,6 +1698,19 @@
},
};
+static struct clk_branch gcc_gpu_iref_clk = {
+ .halt_reg = 0x8c010,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x8c010,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_gpu_iref_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
static struct clk_branch gcc_gpu_memnoc_gfx_clk = {
.halt_reg = 0x7100c,
.halt_check = BRANCH_VOTED,
@@ -3310,6 +3323,7 @@
[GCC_GPU_CFG_AHB_CLK] = &gcc_gpu_cfg_ahb_clk.clkr,
[GCC_GPU_GPLL0_CLK_SRC] = &gcc_gpu_gpll0_clk_src.clkr,
[GCC_GPU_GPLL0_DIV_CLK_SRC] = &gcc_gpu_gpll0_div_clk_src.clkr,
+ [GCC_GPU_IREF_CLK] = &gcc_gpu_iref_clk.clkr,
[GCC_GPU_MEMNOC_GFX_CLK] = &gcc_gpu_memnoc_gfx_clk.clkr,
[GCC_GPU_SNOC_DVM_GFX_CLK] = &gcc_gpu_snoc_dvm_gfx_clk.clkr,
[GCC_MSS_AXIS2_CLK] = &gcc_mss_axis2_clk.clkr,
diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c
index 924f560..dcde70f 100644
--- a/drivers/clk/rockchip/clk-rk3036.c
+++ b/drivers/clk/rockchip/clk-rk3036.c
@@ -127,7 +127,7 @@
PNAME(mux_pll_src_3plls_p) = { "apll", "dpll", "gpll" };
PNAME(mux_timer_p) = { "xin24m", "pclk_peri_src" };
-PNAME(mux_pll_src_apll_dpll_gpll_usb480m_p) = { "apll", "dpll", "gpll" "usb480m" };
+PNAME(mux_pll_src_apll_dpll_gpll_usb480m_p) = { "apll", "dpll", "gpll", "usb480m" };
PNAME(mux_mmc_src_p) = { "apll", "dpll", "gpll", "xin24m" };
PNAME(mux_i2s_pre_p) = { "i2s_src", "i2s_frac", "ext_i2s", "xin12m" };
diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c
index 660dc20..2474f14 100644
--- a/drivers/crypto/caam/caamhash.c
+++ b/drivers/crypto/caam/caamhash.c
@@ -154,6 +154,7 @@
ctx_len, DMA_FROM_DEVICE);
if (dma_mapping_error(jrdev, state->ctx_dma)) {
dev_err(jrdev, "unable to map ctx\n");
+ state->ctx_dma = 0;
return -ENOMEM;
}
@@ -214,6 +215,7 @@
state->ctx_dma = dma_map_single(jrdev, state->caam_ctx, ctx_len, flag);
if (dma_mapping_error(jrdev, state->ctx_dma)) {
dev_err(jrdev, "unable to map ctx\n");
+ state->ctx_dma = 0;
return -ENOMEM;
}
@@ -620,8 +622,10 @@
struct caam_hash_ctx *ctx = crypto_ahash_ctx(ahash);
struct caam_hash_state *state = ahash_request_ctx(req);
- if (state->ctx_dma)
+ if (state->ctx_dma) {
dma_unmap_single(dev, state->ctx_dma, ctx->ctx_len, flag);
+ state->ctx_dma = 0;
+ }
ahash_unmap(dev, edesc, req, dst_len);
}
@@ -1605,6 +1609,7 @@
state->finup = ahash_finup_first;
state->final = ahash_final_no_ctx;
+ state->ctx_dma = 0;
state->current_buf = 0;
state->buf_dma = 0;
state->buflen_0 = 0;
diff --git a/drivers/devfreq/arm-memlat-mon.c b/drivers/devfreq/arm-memlat-mon.c
index ed83185..5802c21 100644
--- a/drivers/devfreq/arm-memlat-mon.c
+++ b/drivers/devfreq/arm-memlat-mon.c
@@ -47,22 +47,32 @@
unsigned long prev_count;
};
-struct memlat_hwmon_data {
+struct cpu_pmu_stats {
struct event_data events[NUM_EVENTS];
ktime_t prev_ts;
- bool init_pending;
- unsigned long cache_miss_event;
- unsigned long inst_event;
};
-static DEFINE_PER_CPU(struct memlat_hwmon_data, pm_data);
struct cpu_grp_info {
cpumask_t cpus;
+ cpumask_t inited_cpus;
+ unsigned long cache_miss_event;
+ unsigned long inst_event;
+ struct cpu_pmu_stats *cpustats;
struct memlat_hwmon hw;
struct notifier_block arm_memlat_cpu_notif;
+ struct list_head mon_list;
};
-static unsigned long compute_freq(struct memlat_hwmon_data *hw_data,
+#define to_cpustats(cpu_grp, cpu) \
+ (&cpu_grp->cpustats[cpu - cpumask_first(&cpu_grp->cpus)])
+#define to_devstats(cpu_grp, cpu) \
+ (&cpu_grp->hw.core_stats[cpu - cpumask_first(&cpu_grp->cpus)])
+#define to_cpu_grp(hwmon) container_of(hwmon, struct cpu_grp_info, hw)
+
+static LIST_HEAD(memlat_mon_list);
+static DEFINE_MUTEX(list_lock);
+
+static unsigned long compute_freq(struct cpu_pmu_stats *cpustats,
unsigned long cyc_cnt)
{
ktime_t ts;
@@ -70,10 +80,10 @@
unsigned long freq = 0;
ts = ktime_get();
- diff = ktime_to_us(ktime_sub(ts, hw_data->prev_ts));
+ diff = ktime_to_us(ktime_sub(ts, cpustats->prev_ts));
if (!diff)
diff = 1;
- hw_data->prev_ts = ts;
+ cpustats->prev_ts = ts;
freq = cyc_cnt;
do_div(freq, diff);
@@ -99,69 +109,59 @@
static void read_perf_counters(int cpu, struct cpu_grp_info *cpu_grp)
{
- int cpu_idx;
- struct memlat_hwmon_data *hw_data = &per_cpu(pm_data, cpu);
- struct memlat_hwmon *hw = &cpu_grp->hw;
+ struct cpu_pmu_stats *cpustats = to_cpustats(cpu_grp, cpu);
+ struct dev_stats *devstats = to_devstats(cpu_grp, cpu);
unsigned long cyc_cnt;
- if (hw_data->init_pending)
- return;
-
- cpu_idx = cpu - cpumask_first(&cpu_grp->cpus);
-
- hw->core_stats[cpu_idx].inst_count =
- read_event(&hw_data->events[INST_IDX]);
-
- hw->core_stats[cpu_idx].mem_count =
- read_event(&hw_data->events[CM_IDX]);
-
- cyc_cnt = read_event(&hw_data->events[CYC_IDX]);
- hw->core_stats[cpu_idx].freq = compute_freq(hw_data, cyc_cnt);
+ devstats->inst_count = read_event(&cpustats->events[INST_IDX]);
+ devstats->mem_count = read_event(&cpustats->events[CM_IDX]);
+ cyc_cnt = read_event(&cpustats->events[CYC_IDX]);
+ devstats->freq = compute_freq(cpustats, cyc_cnt);
}
static unsigned long get_cnt(struct memlat_hwmon *hw)
{
int cpu;
- struct cpu_grp_info *cpu_grp = container_of(hw,
- struct cpu_grp_info, hw);
+ struct cpu_grp_info *cpu_grp = to_cpu_grp(hw);
- for_each_cpu(cpu, &cpu_grp->cpus)
+ for_each_cpu(cpu, &cpu_grp->inited_cpus)
read_perf_counters(cpu, cpu_grp);
return 0;
}
-static void delete_events(struct memlat_hwmon_data *hw_data)
+static void delete_events(struct cpu_pmu_stats *cpustats)
{
int i;
for (i = 0; i < NUM_EVENTS; i++) {
- hw_data->events[i].prev_count = 0;
- perf_event_release_kernel(hw_data->events[i].pevent);
+ cpustats->events[i].prev_count = 0;
+ perf_event_release_kernel(cpustats->events[i].pevent);
}
}
static void stop_hwmon(struct memlat_hwmon *hw)
{
- int cpu, idx;
- struct memlat_hwmon_data *hw_data;
- struct cpu_grp_info *cpu_grp = container_of(hw,
- struct cpu_grp_info, hw);
+ int cpu;
+ struct cpu_grp_info *cpu_grp = to_cpu_grp(hw);
+ struct dev_stats *devstats;
get_online_cpus();
- for_each_cpu(cpu, &cpu_grp->cpus) {
- hw_data = &per_cpu(pm_data, cpu);
- if (hw_data->init_pending)
- hw_data->init_pending = false;
- else
- delete_events(hw_data);
+ for_each_cpu(cpu, &cpu_grp->inited_cpus) {
+ delete_events(to_cpustats(cpu_grp, cpu));
/* Clear governor data */
- idx = cpu - cpumask_first(&cpu_grp->cpus);
- hw->core_stats[idx].inst_count = 0;
- hw->core_stats[idx].mem_count = 0;
- hw->core_stats[idx].freq = 0;
+ devstats = to_devstats(cpu_grp, cpu);
+ devstats->inst_count = 0;
+ devstats->mem_count = 0;
+ devstats->freq = 0;
}
+ mutex_lock(&list_lock);
+ if (!cpumask_equal(&cpu_grp->cpus, &cpu_grp->inited_cpus))
+ list_del(&cpu_grp->mon_list);
+ mutex_unlock(&list_lock);
+ cpumask_clear(&cpu_grp->inited_cpus);
+
put_online_cpus();
unregister_cpu_notifier(&cpu_grp->arm_memlat_cpu_notif);
@@ -173,7 +173,7 @@
attr = kzalloc(sizeof(struct perf_event_attr), GFP_KERNEL);
if (!attr)
- return ERR_PTR(-ENOMEM);
+ return attr;
attr->type = PERF_TYPE_RAW;
attr->size = sizeof(struct perf_event_attr);
@@ -183,37 +183,38 @@
return attr;
}
-static int set_events(struct memlat_hwmon_data *hw_data, int cpu)
+static int set_events(struct cpu_grp_info *cpu_grp, int cpu)
{
struct perf_event *pevent;
struct perf_event_attr *attr;
int err;
+ struct cpu_pmu_stats *cpustats = to_cpustats(cpu_grp, cpu);
/* Allocate an attribute for event initialization */
attr = alloc_attr();
- if (IS_ERR(attr))
- return PTR_ERR(attr);
+ if (!attr)
+ return -ENOMEM;
- attr->config = hw_data->inst_event;
+ attr->config = cpu_grp->inst_event;
pevent = perf_event_create_kernel_counter(attr, cpu, NULL, NULL, NULL);
if (IS_ERR(pevent))
goto err_out;
- hw_data->events[INST_IDX].pevent = pevent;
- perf_event_enable(hw_data->events[INST_IDX].pevent);
+ cpustats->events[INST_IDX].pevent = pevent;
+ perf_event_enable(cpustats->events[INST_IDX].pevent);
- attr->config = hw_data->cache_miss_event;
+ attr->config = cpu_grp->cache_miss_event;
pevent = perf_event_create_kernel_counter(attr, cpu, NULL, NULL, NULL);
if (IS_ERR(pevent))
goto err_out;
- hw_data->events[CM_IDX].pevent = pevent;
- perf_event_enable(hw_data->events[CM_IDX].pevent);
+ cpustats->events[CM_IDX].pevent = pevent;
+ perf_event_enable(cpustats->events[CM_IDX].pevent);
attr->config = CYC_EV;
pevent = perf_event_create_kernel_counter(attr, cpu, NULL, NULL, NULL);
if (IS_ERR(pevent))
goto err_out;
- hw_data->events[CYC_IDX].pevent = pevent;
- perf_event_enable(hw_data->events[CYC_IDX].pevent);
+ cpustats->events[CYC_IDX].pevent = pevent;
+ perf_event_enable(cpustats->events[CYC_IDX].pevent);
kfree(attr);
return 0;
@@ -228,15 +229,24 @@
unsigned long action, void *hcpu)
{
unsigned long cpu = (unsigned long)hcpu;
- struct memlat_hwmon_data *hw_data = &per_cpu(pm_data, cpu);
+ struct cpu_grp_info *cpu_grp, *tmp;
- if ((action != CPU_ONLINE) || !hw_data->init_pending)
+ if (action != CPU_ONLINE)
return NOTIFY_OK;
- if (set_events(hw_data, cpu))
- pr_warn("Failed to create perf event for CPU%lu\n", cpu);
-
- hw_data->init_pending = false;
+ mutex_lock(&list_lock);
+ list_for_each_entry_safe(cpu_grp, tmp, &memlat_mon_list, mon_list) {
+ if (!cpumask_test_cpu(cpu, &cpu_grp->cpus) ||
+ cpumask_test_cpu(cpu, &cpu_grp->inited_cpus))
+ continue;
+ if (set_events(cpu_grp, cpu))
+ pr_warn("Failed to create perf ev for CPU%lu\n", cpu);
+ else
+ cpumask_set_cpu(cpu, &cpu_grp->inited_cpus);
+ if (cpumask_equal(&cpu_grp->cpus, &cpu_grp->inited_cpus))
+ list_del(&cpu_grp->mon_list);
+ }
+ mutex_unlock(&list_lock);
return NOTIFY_OK;
}
@@ -244,29 +254,32 @@
static int start_hwmon(struct memlat_hwmon *hw)
{
int cpu, ret = 0;
- struct memlat_hwmon_data *hw_data;
- struct cpu_grp_info *cpu_grp = container_of(hw,
- struct cpu_grp_info, hw);
+ struct cpu_grp_info *cpu_grp = to_cpu_grp(hw);
register_cpu_notifier(&cpu_grp->arm_memlat_cpu_notif);
get_online_cpus();
for_each_cpu(cpu, &cpu_grp->cpus) {
- hw_data = &per_cpu(pm_data, cpu);
- ret = set_events(hw_data, cpu);
+ ret = set_events(cpu_grp, cpu);
if (ret) {
if (!cpu_online(cpu)) {
- hw_data->init_pending = true;
ret = 0;
} else {
pr_warn("Perf event init failed on CPU%d\n",
cpu);
break;
}
+ } else {
+ cpumask_set_cpu(cpu, &cpu_grp->inited_cpus);
}
}
+ mutex_lock(&list_lock);
+ if (!cpumask_equal(&cpu_grp->cpus, &cpu_grp->inited_cpus))
+ list_add_tail(&cpu_grp->mon_list, &memlat_mon_list);
+ mutex_unlock(&list_lock);
put_online_cpus();
+
return ret;
}
@@ -328,6 +341,11 @@
if (!hw->core_stats)
return -ENOMEM;
+ cpu_grp->cpustats = devm_kzalloc(dev, hw->num_cores *
+ sizeof(*(cpu_grp->cpustats)), GFP_KERNEL);
+ if (!cpu_grp->cpustats)
+ return -ENOMEM;
+
ret = of_property_read_u32(dev->of_node, "qcom,cachemiss-ev",
&cachemiss_ev);
if (ret) {
@@ -335,6 +353,7 @@
L2DM_EV);
cachemiss_ev = L2DM_EV;
}
+ cpu_grp->cache_miss_event = cachemiss_ev;
ret = of_property_read_u32(dev->of_node, "qcom,inst-ev", &inst_ev);
if (ret) {
@@ -342,12 +361,10 @@
INST_EV);
inst_ev = INST_EV;
}
+ cpu_grp->inst_event = inst_ev;
- for_each_cpu(cpu, &cpu_grp->cpus) {
- hw->core_stats[cpu - cpumask_first(&cpu_grp->cpus)].id = cpu;
- (&per_cpu(pm_data, cpu))->cache_miss_event = cachemiss_ev;
- (&per_cpu(pm_data, cpu))->inst_event = inst_ev;
- }
+ for_each_cpu(cpu, &cpu_grp->cpus)
+ to_devstats(cpu_grp, cpu)->id = cpu;
hw->start_hwmon = &start_hwmon;
hw->stop_hwmon = &stop_hwmon;
diff --git a/drivers/devfreq/bimc-bwmon.c b/drivers/devfreq/bimc-bwmon.c
index 078e198..ffe60de 100644
--- a/drivers/devfreq/bimc-bwmon.c
+++ b/drivers/devfreq/bimc-bwmon.c
@@ -26,14 +26,20 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/spinlock.h>
+#include <linux/log2.h>
+#include <linux/sizes.h>
#include "governor_bw_hwmon.h"
#define GLB_INT_STATUS(m) ((m)->global_base + 0x100)
#define GLB_INT_CLR(m) ((m)->global_base + 0x108)
#define GLB_INT_EN(m) ((m)->global_base + 0x10C)
#define MON_INT_STATUS(m) ((m)->base + 0x100)
+#define MON_INT_STATUS_MASK 0x03
+#define MON2_INT_STATUS_MASK 0xF0
+#define MON2_INT_STATUS_SHIFT 4
#define MON_INT_CLR(m) ((m)->base + 0x108)
#define MON_INT_EN(m) ((m)->base + 0x10C)
+#define MON_INT_ENABLE 0x1
#define MON_EN(m) ((m)->base + 0x280)
#define MON_CLEAR(m) ((m)->base + 0x284)
#define MON_CNT(m) ((m)->base + 0x288)
@@ -54,9 +60,29 @@
#define MON2_ZONE_CNT(m) ((m)->base + 0x2D8)
#define MON2_ZONE_MAX(m, zone) ((m)->base + 0x2E0 + 0x4 * zone)
+#define MON3_INT_STATUS(m) ((m)->base + 0x00)
+#define MON3_INT_CLR(m) ((m)->base + 0x08)
+#define MON3_INT_EN(m) ((m)->base + 0x0C)
+#define MON3_INT_STATUS_MASK 0x0F
+#define MON3_EN(m) ((m)->base + 0x10)
+#define MON3_CLEAR(m) ((m)->base + 0x14)
+#define MON3_MASK(m) ((m)->base + 0x18)
+#define MON3_MATCH(m) ((m)->base + 0x1C)
+#define MON3_SW(m) ((m)->base + 0x20)
+#define MON3_THRES_HI(m) ((m)->base + 0x24)
+#define MON3_THRES_MED(m) ((m)->base + 0x28)
+#define MON3_THRES_LO(m) ((m)->base + 0x2C)
+#define MON3_ZONE_ACTIONS(m) ((m)->base + 0x30)
+#define MON3_ZONE_CNT_THRES(m) ((m)->base + 0x34)
+#define MON3_BYTE_CNT(m) ((m)->base + 0x38)
+#define MON3_WIN_TIMER(m) ((m)->base + 0x3C)
+#define MON3_ZONE_CNT(m) ((m)->base + 0x40)
+#define MON3_ZONE_MAX(m, zone) ((m)->base + 0x44 + 0x4 * zone)
+
enum mon_reg_type {
MON1,
MON2,
+ MON3,
};
struct bwmon_spec {
@@ -64,6 +90,8 @@
bool overflow;
bool throt_adj;
bool hw_sampling;
+ bool has_global_base;
+ enum mon_reg_type reg_type;
};
struct bwmon {
@@ -78,6 +106,10 @@
u32 throttle_adj;
u32 sample_size_ms;
u32 intr_status;
+ u8 count_shift;
+ u32 thres_lim;
+ u32 byte_mask;
+ u32 byte_match;
};
#define to_bwmon(ptr) container_of(ptr, struct bwmon, hw)
@@ -85,9 +117,6 @@
#define ENABLE_MASK BIT(0)
#define THROTTLE_MASK 0x1F
#define THROTTLE_SHIFT 16
-#define INT_ENABLE_V1 0x1
-#define INT_STATUS_MASK 0x03
-#define INT_STATUS_MASK_HWS 0xF0
static DEFINE_SPINLOCK(glb_lock);
@@ -100,6 +129,9 @@
case MON2:
writel_relaxed(ENABLE_MASK | m->throttle_adj, MON2_EN(m));
break;
+ case MON3:
+ writel_relaxed(ENABLE_MASK | m->throttle_adj, MON3_EN(m));
+ break;
}
}
@@ -112,6 +144,9 @@
case MON2:
writel_relaxed(m->throttle_adj, MON2_EN(m));
break;
+ case MON3:
+ writel_relaxed(m->throttle_adj, MON3_EN(m));
+ break;
}
/*
* mon_disable() and mon_irq_clear(),
@@ -136,6 +171,12 @@
else
writel_relaxed(MON_CLEAR_BIT, MON2_CLEAR(m));
break;
+ case MON3:
+ if (clear_all)
+ writel_relaxed(MON_CLEAR_ALL_BIT, MON3_CLEAR(m));
+ else
+ writel_relaxed(MON_CLEAR_BIT, MON3_CLEAR(m));
+ break;
}
/*
* The counter clear and IRQ clear bits are not in the same 4KB
@@ -146,7 +187,9 @@
}
#define SAMPLE_WIN_LIM 0xFFFFF
-static void mon_set_hw_sampling_window(struct bwmon *m, unsigned int sample_ms)
+static __always_inline
+void mon_set_hw_sampling_window(struct bwmon *m, unsigned int sample_ms,
+ enum mon_reg_type type)
{
u32 rate;
@@ -158,7 +201,17 @@
pr_warn("Sample window %u larger than hw limit: %u\n",
rate, SAMPLE_WIN_LIM);
}
- writel_relaxed(rate, MON2_SW(m));
+ switch (type) {
+ case MON1:
+ WARN(1, "Invalid\n");
+ return;
+ case MON2:
+ writel_relaxed(rate, MON2_SW(m));
+ break;
+ case MON3:
+ writel_relaxed(rate, MON3_SW(m));
+ break;
+ }
}
}
@@ -181,15 +234,20 @@
case MON1:
mon_glb_irq_enable(m);
val = readl_relaxed(MON_INT_EN(m));
- val |= INT_ENABLE_V1;
+ val |= MON_INT_ENABLE;
writel_relaxed(val, MON_INT_EN(m));
break;
case MON2:
mon_glb_irq_enable(m);
val = readl_relaxed(MON_INT_EN(m));
- val |= INT_STATUS_MASK_HWS;
+ val |= MON2_INT_STATUS_MASK;
writel_relaxed(val, MON_INT_EN(m));
break;
+ case MON3:
+ val = readl_relaxed(MON3_INT_EN(m));
+ val |= MON3_INT_STATUS_MASK;
+ writel_relaxed(val, MON3_INT_EN(m));
+ break;
}
spin_unlock(&glb_lock);
/*
@@ -219,15 +277,20 @@
case MON1:
mon_glb_irq_disable(m);
val = readl_relaxed(MON_INT_EN(m));
- val &= ~INT_ENABLE_V1;
+ val &= ~MON_INT_ENABLE;
writel_relaxed(val, MON_INT_EN(m));
break;
case MON2:
mon_glb_irq_disable(m);
val = readl_relaxed(MON_INT_EN(m));
- val &= ~INT_STATUS_MASK_HWS;
+ val &= ~MON2_INT_STATUS_MASK;
writel_relaxed(val, MON_INT_EN(m));
break;
+ case MON3:
+ val = readl_relaxed(MON3_INT_EN(m));
+ val &= ~MON3_INT_STATUS_MASK;
+ writel_relaxed(val, MON3_INT_EN(m));
+ break;
}
spin_unlock(&glb_lock);
/*
@@ -247,13 +310,19 @@
mval = readl_relaxed(MON_INT_STATUS(m));
dev_dbg(m->dev, "IRQ status p:%x, g:%x\n", mval,
readl_relaxed(GLB_INT_STATUS(m)));
- mval &= INT_STATUS_MASK;
+ mval &= MON_INT_STATUS_MASK;
break;
case MON2:
mval = readl_relaxed(MON_INT_STATUS(m));
dev_dbg(m->dev, "IRQ status p:%x, g:%x\n", mval,
readl_relaxed(GLB_INT_STATUS(m)));
- mval &= INT_STATUS_MASK_HWS;
+ mval &= MON2_INT_STATUS_MASK;
+ mval >>= MON2_INT_STATUS_SHIFT;
+ break;
+ case MON3:
+ mval = readl_relaxed(MON3_INT_STATUS(m));
+ dev_dbg(m->dev, "IRQ status p:%x\n", mval);
+ mval &= MON3_INT_STATUS_MASK;
break;
}
@@ -287,13 +356,16 @@
{
switch (type) {
case MON1:
- writel_relaxed(INT_STATUS_MASK, MON_INT_CLR(m));
+ writel_relaxed(MON_INT_STATUS_MASK, MON_INT_CLR(m));
mon_glb_irq_clear(m);
break;
case MON2:
- writel_relaxed(INT_STATUS_MASK_HWS, MON_INT_CLR(m));
+ writel_relaxed(MON2_INT_STATUS_MASK, MON_INT_CLR(m));
mon_glb_irq_clear(m);
break;
+ case MON3:
+ writel_relaxed(MON3_INT_STATUS_MASK, MON3_INT_CLR(m));
+ break;
}
}
@@ -350,11 +422,18 @@
return zone_counts;
}
-static unsigned int mbps_to_mb(unsigned long mbps, unsigned int ms)
+#define MB_SHIFT 20
+
+static u32 mbps_to_count(unsigned long mbps, unsigned int ms, u8 shift)
{
mbps *= ms;
- mbps = DIV_ROUND_UP(mbps, MSEC_PER_SEC);
- return mbps;
+
+ if (shift > MB_SHIFT)
+ mbps >>= shift - MB_SHIFT;
+ else
+ mbps <<= MB_SHIFT - shift;
+
+ return DIV_ROUND_UP(mbps, MSEC_PER_SEC);
}
/*
@@ -362,43 +441,60 @@
* Zone 0: byte count < THRES_LO
* Zone 1: THRES_LO < byte count < THRES_MED
* Zone 2: THRES_MED < byte count < THRES_HI
- * Zone 3: byte count > THRES_HI
+ * Zone 3: THRES_LIM > byte count > THRES_HI
*/
-#define THRES_LIM 0x7FFU
-static void set_zone_thres(struct bwmon *m, unsigned int sample_ms)
-{
- struct bw_hwmon *hw = &(m->hw);
- u32 hi, med, lo;
+#define THRES_LIM(shift) (0xFFFFFFFF >> shift)
- hi = mbps_to_mb(hw->up_wake_mbps, sample_ms);
- med = mbps_to_mb(hw->down_wake_mbps, sample_ms);
+static __always_inline
+void set_zone_thres(struct bwmon *m, unsigned int sample_ms,
+ enum mon_reg_type type)
+{
+ struct bw_hwmon *hw = &m->hw;
+ u32 hi, med, lo;
+ u32 zone_cnt_thres = calc_zone_counts(hw);
+
+ hi = mbps_to_count(hw->up_wake_mbps, sample_ms, m->count_shift);
+ med = mbps_to_count(hw->down_wake_mbps, sample_ms, m->count_shift);
lo = 0;
- if (unlikely((hi > THRES_LIM) || (med > hi) || (lo > med))) {
+ if (unlikely((hi > m->thres_lim) || (med > hi) || (lo > med))) {
pr_warn("Zone thres larger than hw limit: hi:%u med:%u lo:%u\n",
hi, med, lo);
- hi = min(hi, THRES_LIM);
+ hi = min(hi, m->thres_lim);
med = min(med, hi - 1);
lo = min(lo, med-1);
}
- writel_relaxed(hi, MON2_THRES_HI(m));
- writel_relaxed(med, MON2_THRES_MED(m));
- writel_relaxed(lo, MON2_THRES_LO(m));
+ switch (type) {
+ case MON1:
+ WARN(1, "Invalid\n");
+ return;
+ case MON2:
+ writel_relaxed(hi, MON2_THRES_HI(m));
+ writel_relaxed(med, MON2_THRES_MED(m));
+ writel_relaxed(lo, MON2_THRES_LO(m));
+ /* Set the zone count thresholds for interrupts */
+ writel_relaxed(zone_cnt_thres, MON2_ZONE_CNT_THRES(m));
+ break;
+ case MON3:
+ writel_relaxed(hi, MON3_THRES_HI(m));
+ writel_relaxed(med, MON3_THRES_MED(m));
+ writel_relaxed(lo, MON3_THRES_LO(m));
+ /* Set the zone count thresholds for interrupts */
+ writel_relaxed(zone_cnt_thres, MON3_ZONE_CNT_THRES(m));
+ break;
+ }
+
dev_dbg(m->dev, "Thres: hi:%u med:%u lo:%u\n", hi, med, lo);
+ dev_dbg(m->dev, "Zone Count Thres: %0x\n", zone_cnt_thres);
}
-static void mon_set_zones(struct bwmon *m, unsigned int sample_ms)
+static __always_inline
+void mon_set_zones(struct bwmon *m, unsigned int sample_ms,
+ enum mon_reg_type type)
{
- struct bw_hwmon *hw = &(m->hw);
- u32 zone_cnt_thres = calc_zone_counts(hw);
-
- mon_set_hw_sampling_window(m, sample_ms);
- set_zone_thres(m, sample_ms);
- /* Set the zone count thresholds for interrupts */
- writel_relaxed(zone_cnt_thres, MON2_ZONE_CNT_THRES(m));
-
- dev_dbg(m->dev, "Zone Count Thres: %0x\n", zone_cnt_thres);
+ mon_set_hw_sampling_window(m, sample_ms, type);
+ set_zone_thres(m, sample_ms, type);
}
static void mon_set_limit(struct bwmon *m, u32 count)
@@ -433,16 +529,28 @@
return count;
}
-static unsigned int get_zone(struct bwmon *m)
+static __always_inline
+unsigned int get_zone(struct bwmon *m, enum mon_reg_type type)
{
u32 zone_counts;
u32 zone;
- zone = get_bitmask_order((m->intr_status & INT_STATUS_MASK_HWS) >> 4);
+ zone = get_bitmask_order(m->intr_status);
if (zone) {
zone--;
} else {
- zone_counts = readl_relaxed(MON2_ZONE_CNT(m));
+ switch (type) {
+ case MON1:
+ WARN(1, "Invalid\n");
+ return 0;
+ case MON2:
+ zone_counts = readl_relaxed(MON2_ZONE_CNT(m));
+ break;
+ case MON3:
+ zone_counts = readl_relaxed(MON3_ZONE_CNT(m));
+ break;
+ }
+
if (zone_counts) {
zone = get_bitmask_order(zone_counts) - 1;
zone /= 8;
@@ -453,15 +561,36 @@
return zone;
}
-static unsigned long mon_get_zone_stats(struct bwmon *m)
+static __always_inline
+unsigned long get_zone_count(struct bwmon *m, unsigned int zone,
+ enum mon_reg_type type)
+{
+ unsigned long count;
+
+ switch (type) {
+ case MON1:
+ WARN(1, "Invalid\n");
+ return 0;
+ case MON2:
+ count = readl_relaxed(MON2_ZONE_MAX(m, zone)) + 1;
+ break;
+ case MON3:
+ count = readl_relaxed(MON3_ZONE_MAX(m, zone)) + 1;
+ break;
+ }
+
+ return count;
+}
+
+static __always_inline
+unsigned long mon_get_zone_stats(struct bwmon *m, enum mon_reg_type type)
{
unsigned int zone;
unsigned long count = 0;
- zone = get_zone(m);
-
- count = readl_relaxed(MON2_ZONE_MAX(m, zone)) + 1;
- count *= SZ_1M;
+ zone = get_zone(m, type);
+ count = get_zone_count(m, zone, type);
+ count <<= m->count_shift;
dev_dbg(m->dev, "Zone%d Max byte count: %08lx\n", zone, count);
@@ -478,7 +607,8 @@
count = mon_get_count1(m);
break;
case MON2:
- count = mon_get_zone_stats(m);
+ case MON3:
+ count = mon_get_zone_stats(m, type);
break;
}
@@ -523,6 +653,11 @@
return __get_bytes_and_clear(hw, MON2);
}
+static unsigned long get_bytes_and_clear3(struct bw_hwmon *hw)
+{
+ return __get_bytes_and_clear(hw, MON3);
+}
+
static unsigned long set_thres(struct bw_hwmon *hw, unsigned long bytes)
{
unsigned long count;
@@ -545,20 +680,33 @@
return count;
}
-static unsigned long set_hw_events(struct bw_hwmon *hw, unsigned int sample_ms)
+static unsigned long
+__set_hw_events(struct bw_hwmon *hw, unsigned int sample_ms,
+ enum mon_reg_type type)
{
struct bwmon *m = to_bwmon(hw);
- mon_disable(m, MON2);
- mon_clear(m, false, MON2);
- mon_irq_clear(m, MON2);
+ mon_disable(m, type);
+ mon_clear(m, false, type);
+ mon_irq_clear(m, type);
- mon_set_zones(m, sample_ms);
- mon_enable(m, MON2);
+ mon_set_zones(m, sample_ms, type);
+ mon_enable(m, type);
return 0;
}
+static unsigned long set_hw_events(struct bw_hwmon *hw, unsigned int sample_ms)
+{
+ return __set_hw_events(hw, sample_ms, MON2);
+}
+
+static unsigned long
+set_hw_events3(struct bw_hwmon *hw, unsigned int sample_ms)
+{
+ return __set_hw_events(hw, sample_ms, MON3);
+}
+
static irqreturn_t
__bwmon_intr_handler(int irq, void *dev, enum mon_reg_type type)
{
@@ -584,6 +732,11 @@
return __bwmon_intr_handler(irq, dev, MON2);
}
+static irqreturn_t bwmon_intr_handler3(int irq, void *dev)
+{
+ return __bwmon_intr_handler(irq, dev, MON3);
+}
+
static irqreturn_t bwmon_intr_thread(int irq, void *dev)
{
struct bwmon *m = dev;
@@ -592,6 +745,25 @@
return IRQ_HANDLED;
}
+static __always_inline
+void mon_set_byte_count_filter(struct bwmon *m, enum mon_reg_type type)
+{
+ if (!m->byte_mask)
+ return;
+
+ switch (type) {
+ case MON1:
+ case MON2:
+ writel_relaxed(m->byte_mask, MON_MASK(m));
+ writel_relaxed(m->byte_match, MON_MATCH(m));
+ break;
+ case MON3:
+ writel_relaxed(m->byte_mask, MON3_MASK(m));
+ writel_relaxed(m->byte_match, MON3_MATCH(m));
+ break;
+ }
+}
+
static __always_inline int __start_bw_hwmon(struct bw_hwmon *hw,
unsigned long mbps, enum mon_reg_type type)
{
@@ -609,6 +781,10 @@
zone_actions = calc_zone_actions();
handler = bwmon_intr_handler2;
break;
+ case MON3:
+ zone_actions = calc_zone_actions();
+ handler = bwmon_intr_handler3;
+ break;
}
ret = request_threaded_irq(m->irq, handler, bwmon_intr_thread,
@@ -630,12 +806,17 @@
mon_set_limit(m, limit);
break;
case MON2:
- mon_set_zones(m, hw->df->profile->polling_ms);
+ mon_set_zones(m, hw->df->profile->polling_ms, type);
/* Set the zone actions to increment appropriate counters */
writel_relaxed(zone_actions, MON2_ZONE_ACTIONS(m));
break;
+ case MON3:
+ mon_set_zones(m, hw->df->profile->polling_ms, type);
+ /* Set the zone actions to increment appropriate counters */
+ writel_relaxed(zone_actions, MON3_ZONE_ACTIONS(m));
}
+ mon_set_byte_count_filter(m, type);
mon_irq_clear(m, type);
mon_irq_enable(m, type);
mon_enable(m, type);
@@ -653,6 +834,11 @@
return __start_bw_hwmon(hw, mbps, MON2);
}
+static int start_bw_hwmon3(struct bw_hwmon *hw, unsigned long mbps)
+{
+ return __start_bw_hwmon(hw, mbps, MON3);
+}
+
static __always_inline
void __stop_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type)
{
@@ -675,6 +861,11 @@
return __stop_bw_hwmon(hw, MON2);
}
+static void stop_bw_hwmon3(struct bw_hwmon *hw)
+{
+ return __stop_bw_hwmon(hw, MON3);
+}
+
static __always_inline
int __suspend_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type)
{
@@ -698,7 +889,13 @@
return __suspend_bw_hwmon(hw, MON2);
}
-static int __resume_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type)
+static int suspend_bw_hwmon3(struct bw_hwmon *hw)
+{
+ return __suspend_bw_hwmon(hw, MON3);
+}
+
+static __always_inline
+int __resume_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type)
{
struct bwmon *m = to_bwmon(hw);
int ret;
@@ -711,6 +908,9 @@
case MON2:
handler = bwmon_intr_handler2;
break;
+ case MON3:
+ handler = bwmon_intr_handler3;
+ break;
}
mon_clear(m, false, type);
@@ -739,6 +939,11 @@
return __resume_bw_hwmon(hw, MON2);
}
+static int resume_bw_hwmon3(struct bw_hwmon *hw)
+{
+ return __resume_bw_hwmon(hw, MON3);
+}
+
/*************************************************************************/
static const struct bwmon_spec spec[] = {
@@ -746,25 +951,40 @@
.wrap_on_thres = true,
.overflow = false,
.throt_adj = false,
- .hw_sampling = false
+ .hw_sampling = false,
+ .has_global_base = true,
+ .reg_type = MON1,
},
[1] = {
.wrap_on_thres = false,
.overflow = true,
.throt_adj = false,
- .hw_sampling = false
+ .hw_sampling = false,
+ .has_global_base = true,
+ .reg_type = MON1,
},
[2] = {
.wrap_on_thres = false,
.overflow = true,
.throt_adj = true,
- .hw_sampling = false
+ .hw_sampling = false,
+ .has_global_base = true,
+ .reg_type = MON1,
},
[3] = {
.wrap_on_thres = false,
.overflow = true,
.throt_adj = true,
- .hw_sampling = true
+ .hw_sampling = true,
+ .has_global_base = true,
+ .reg_type = MON2,
+ },
+ [4] = {
+ .wrap_on_thres = false,
+ .overflow = true,
+ .throt_adj = false,
+ .hw_sampling = true,
+ .reg_type = MON3,
},
};
@@ -773,6 +993,7 @@
{ .compatible = "qcom,bimc-bwmon2", .data = &spec[1] },
{ .compatible = "qcom,bimc-bwmon3", .data = &spec[2] },
{ .compatible = "qcom,bimc-bwmon4", .data = &spec[3] },
+ { .compatible = "qcom,bimc-bwmon5", .data = &spec[4] },
{}
};
@@ -782,20 +1003,13 @@
struct resource *res;
struct bwmon *m;
int ret;
- u32 data;
+ u32 data, count_unit;
m = devm_kzalloc(dev, sizeof(*m), GFP_KERNEL);
if (!m)
return -ENOMEM;
m->dev = dev;
- ret = of_property_read_u32(dev->of_node, "qcom,mport", &data);
- if (ret) {
- dev_err(dev, "mport not found!\n");
- return ret;
- }
- m->mport = data;
-
m->spec = of_device_get_match_data(dev);
if (!m->spec) {
dev_err(dev, "Unknown device type!\n");
@@ -813,15 +1027,26 @@
return -ENOMEM;
}
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "global_base");
- if (!res) {
- dev_err(dev, "global_base not found!\n");
- return -EINVAL;
- }
- m->global_base = devm_ioremap(dev, res->start, resource_size(res));
- if (!m->global_base) {
- dev_err(dev, "Unable map global_base!\n");
- return -ENOMEM;
+ if (m->spec->has_global_base) {
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "global_base");
+ if (!res) {
+ dev_err(dev, "global_base not found!\n");
+ return -EINVAL;
+ }
+ m->global_base = devm_ioremap(dev, res->start,
+ resource_size(res));
+ if (!m->global_base) {
+ dev_err(dev, "Unable map global_base!\n");
+ return -ENOMEM;
+ }
+
+ ret = of_property_read_u32(dev->of_node, "qcom,mport", &data);
+ if (ret) {
+ dev_err(dev, "mport not found!\n");
+ return ret;
+ }
+ m->mport = data;
}
m->irq = platform_get_irq(pdev, 0);
@@ -841,22 +1066,45 @@
dev_err(dev, "HW sampling rate not specified!\n");
return ret;
}
+ }
+ if (of_property_read_u32(dev->of_node, "qcom,count-unit", &count_unit))
+ count_unit = SZ_1M;
+ m->count_shift = order_base_2(count_unit);
+ m->thres_lim = THRES_LIM(m->count_shift);
+
+ switch (m->spec->reg_type) {
+ case MON3:
+ m->hw.start_hwmon = start_bw_hwmon3;
+ m->hw.stop_hwmon = stop_bw_hwmon3;
+ m->hw.suspend_hwmon = suspend_bw_hwmon3;
+ m->hw.resume_hwmon = resume_bw_hwmon3;
+ m->hw.get_bytes_and_clear = get_bytes_and_clear3;
+ m->hw.set_hw_events = set_hw_events3;
+ break;
+ case MON2:
m->hw.start_hwmon = start_bw_hwmon2;
m->hw.stop_hwmon = stop_bw_hwmon2;
m->hw.suspend_hwmon = suspend_bw_hwmon2;
m->hw.resume_hwmon = resume_bw_hwmon2;
m->hw.get_bytes_and_clear = get_bytes_and_clear2;
m->hw.set_hw_events = set_hw_events;
- } else {
+ break;
+ case MON1:
m->hw.start_hwmon = start_bw_hwmon;
m->hw.stop_hwmon = stop_bw_hwmon;
m->hw.suspend_hwmon = suspend_bw_hwmon;
m->hw.resume_hwmon = resume_bw_hwmon;
m->hw.get_bytes_and_clear = get_bytes_and_clear;
m->hw.set_thres = set_thres;
+ break;
}
+ of_property_read_u32(dev->of_node, "qcom,byte-mid-match",
+ &m->byte_match);
+ of_property_read_u32(dev->of_node, "qcom,byte-mid-mask",
+ &m->byte_mask);
+
if (m->spec->throt_adj) {
m->hw.set_throttle_adj = mon_set_throttle_adj;
m->hw.get_throttle_adj = mon_get_throttle_adj;
diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c
index ebed22f..296ec12 100644
--- a/drivers/extcon/extcon-gpio.c
+++ b/drivers/extcon/extcon-gpio.c
@@ -28,6 +28,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
+#include <linux/of_gpio.h>
struct gpio_extcon_data {
struct extcon_dev *edev;
@@ -37,6 +38,7 @@
struct gpio_desc *id_gpiod;
struct gpio_extcon_pdata *pdata;
+ unsigned int *supported_cable;
};
static void gpio_extcon_work(struct work_struct *work)
@@ -91,15 +93,93 @@
return 0;
}
+static int extcon_parse_pinctrl_data(struct device *dev,
+ struct gpio_extcon_pdata *pdata)
+{
+ struct pinctrl *pctrl;
+ int ret = 0;
+
+ /* Try to obtain pinctrl handle */
+ pctrl = devm_pinctrl_get(dev);
+ if (IS_ERR(pctrl)) {
+ ret = PTR_ERR(pctrl);
+ goto out;
+ }
+ pdata->pctrl = pctrl;
+
+ /* Look-up and keep the state handy to be used later */
+ pdata->pins_default = pinctrl_lookup_state(pdata->pctrl,
+ "default");
+ if (IS_ERR(pdata->pins_default)) {
+ ret = PTR_ERR(pdata->pins_default);
+ dev_err(dev, "Can't get default pinctrl state, ret %d\n", ret);
+ }
+out:
+ return ret;
+}
+
+/* Parse platform data */
+static
+struct gpio_extcon_pdata *extcon_populate_pdata(struct device *dev)
+{
+ struct gpio_extcon_pdata *pdata = NULL;
+ struct device_node *np = dev->of_node;
+ enum of_gpio_flags flags;
+ u32 val;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ goto out;
+
+ if (of_property_read_u32(np, "extcon-id", &pdata->extcon_id)) {
+ dev_err(dev, "extcon-id property not found\n");
+ goto out;
+ }
+
+ pdata->gpio = of_get_named_gpio_flags(np, "gpio", 0, &flags);
+ if (gpio_is_valid(pdata->gpio)) {
+ if (flags & OF_GPIO_ACTIVE_LOW)
+ pdata->gpio_active_low = true;
+ } else {
+ dev_err(dev, "gpio property not found or invalid\n");
+ goto out;
+ }
+
+ if (of_property_read_u32(np, "irq-flags", &val)) {
+ dev_err(dev, "irq-flags property not found\n");
+ goto out;
+ }
+ pdata->irq_flags = val;
+
+ if (of_property_read_u32(np, "debounce-ms", &val)) {
+ dev_err(dev, "debounce-ms property not found\n");
+ goto out;
+ }
+ pdata->debounce = val;
+
+ if (extcon_parse_pinctrl_data(dev, pdata)) {
+ dev_err(dev, "failed to parse pinctrl data\n");
+ goto out;
+ }
+
+ return pdata;
+out:
+ return NULL;
+}
+
static int gpio_extcon_probe(struct platform_device *pdev)
{
struct gpio_extcon_pdata *pdata = dev_get_platdata(&pdev->dev);
struct gpio_extcon_data *data;
int ret;
- if (!pdata)
- return -EBUSY;
- if (!pdata->irq_flags || pdata->extcon_id > EXTCON_NONE)
+ if (!pdata) {
+ /* try populating pdata from device tree */
+ pdata = extcon_populate_pdata(&pdev->dev);
+ if (!pdata)
+ return -EBUSY;
+ }
+ if (!pdata->irq_flags || pdata->extcon_id >= EXTCON_NUM)
return -EINVAL;
data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_extcon_data),
@@ -108,13 +188,27 @@
return -ENOMEM;
data->pdata = pdata;
+ ret = pinctrl_select_state(pdata->pctrl, pdata->pins_default);
+ if (ret < 0)
+ dev_err(&pdev->dev, "pinctrl state select failed, ret %d\n",
+ ret);
+
/* Initialize the gpio */
ret = gpio_extcon_init(&pdev->dev, data);
if (ret < 0)
return ret;
+ data->supported_cable = devm_kzalloc(&pdev->dev,
+ sizeof(*data->supported_cable) * 2,
+ GFP_KERNEL);
+ if (!data->supported_cable)
+ return -ENOMEM;
+
+ data->supported_cable[0] = pdata->extcon_id;
+ data->supported_cable[1] = EXTCON_NONE;
/* Allocate the memory of extcon devie and register extcon device */
- data->edev = devm_extcon_dev_allocate(&pdev->dev, &pdata->extcon_id);
+ data->edev = devm_extcon_dev_allocate(&pdev->dev,
+ data->supported_cable);
if (IS_ERR(data->edev)) {
dev_err(&pdev->dev, "failed to allocate extcon device\n");
return -ENOMEM;
@@ -168,12 +262,18 @@
static SIMPLE_DEV_PM_OPS(gpio_extcon_pm_ops, NULL, gpio_extcon_resume);
+static const struct of_device_id extcon_gpio_of_match[] = {
+ { .compatible = "extcon-gpio"},
+ {},
+};
+
static struct platform_driver gpio_extcon_driver = {
.probe = gpio_extcon_probe,
.remove = gpio_extcon_remove,
.driver = {
.name = "extcon-gpio",
.pm = &gpio_extcon_pm_ops,
+ .of_match_table = of_match_ptr(extcon_gpio_of_match),
},
};
diff --git a/drivers/gpu/drm/msm/sde/sde_core_perf.c b/drivers/gpu/drm/msm/sde/sde_core_perf.c
index db2c515..1015da8 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_perf.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_perf.c
@@ -554,8 +554,8 @@
perf->max_core_clk_rate = sde_power_clk_get_max_rate(phandle, clk_name);
if (!perf->max_core_clk_rate) {
- SDE_ERROR("invalid max core clk rate\n");
- goto err;
+ SDE_DEBUG("optional max core clk rate, use default\n");
+ perf->max_core_clk_rate = SDE_PERF_DEFAULT_MAX_CORE_CLK_RATE;
}
return 0;
diff --git a/drivers/gpu/drm/msm/sde/sde_core_perf.h b/drivers/gpu/drm/msm/sde/sde_core_perf.h
index 20e4eb5..c61c9a7 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_perf.h
+++ b/drivers/gpu/drm/msm/sde/sde_core_perf.h
@@ -21,6 +21,10 @@
#include "sde_hw_catalog.h"
#include "sde_power_handle.h"
+#define SDE_PERF_DEFAULT_MAX_CORE_CLK_RATE 320000000
+#define SDE_PERF_DEFAULT_MAX_BUS_AB_QUOTA 2000000000
+#define SDE_PERF_DEFAULT_MAX_BUS_IB_QUOTA 2000000000
+
/**
* struct sde_core_perf_params - definition of performance parameters
* @max_per_pipe_ib: maximum instantaneous bandwidth request
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 1bd7654..4845c43 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -2382,6 +2382,82 @@
return rc;
}
+static int _sde_crtc_excl_rect_overlap_check(struct plane_state pstates[],
+ int cnt, int curr_cnt, struct sde_rect *excl_rect, int z_pos)
+{
+ struct sde_rect dst_rect, intersect;
+ int i, rc = -EINVAL;
+ const struct drm_plane_state *pstate;
+
+ /* start checking from next plane */
+ for (i = curr_cnt; i < cnt; i++) {
+ pstate = pstates[i].drm_pstate;
+ POPULATE_RECT(&dst_rect, pstate->crtc_x, pstate->crtc_y,
+ pstate->crtc_w, pstate->crtc_h, true);
+ sde_kms_rect_intersect(&dst_rect, excl_rect, &intersect);
+
+ if (intersect.w == excl_rect->w && intersect.h == excl_rect->h
+ /* next plane may be on same z-order */
+ && z_pos != pstates[i].stage) {
+ rc = 0;
+ goto end;
+ }
+ }
+
+ SDE_ERROR("excl rect does not find top overlapping rect\n");
+end:
+ return rc;
+}
+
+/* no input validation - caller API has all the checks */
+static int _sde_crtc_excl_dim_layer_check(struct drm_crtc_state *state,
+ struct plane_state pstates[], int cnt)
+{
+ struct sde_crtc_state *cstate = to_sde_crtc_state(state);
+ struct drm_display_mode *mode = &state->adjusted_mode;
+ const struct drm_plane_state *pstate;
+ struct sde_plane_state *sde_pstate;
+ int rc = 0, i;
+
+ /* Check dim layer rect bounds and stage */
+ for (i = 0; i < cstate->num_dim_layers; i++) {
+ if ((CHECK_LAYER_BOUNDS(cstate->dim_layer[i].rect.y,
+ cstate->dim_layer[i].rect.h, mode->vdisplay)) ||
+ (CHECK_LAYER_BOUNDS(cstate->dim_layer[i].rect.x,
+ cstate->dim_layer[i].rect.w, mode->hdisplay)) ||
+ (cstate->dim_layer[i].stage >= SDE_STAGE_MAX) ||
+ (!cstate->dim_layer[i].rect.w) ||
+ (!cstate->dim_layer[i].rect.h)) {
+ SDE_ERROR("invalid dim_layer:{%d,%d,%d,%d}, stage:%d\n",
+ cstate->dim_layer[i].rect.x,
+ cstate->dim_layer[i].rect.y,
+ cstate->dim_layer[i].rect.w,
+ cstate->dim_layer[i].rect.h,
+ cstate->dim_layer[i].stage);
+ SDE_ERROR("display: %dx%d\n", mode->hdisplay,
+ mode->vdisplay);
+ rc = -E2BIG;
+ goto end;
+ }
+ }
+
+ /* this is traversing on sorted z-order pstates */
+ for (i = 0; i < cnt; i++) {
+ pstate = pstates[i].drm_pstate;
+ sde_pstate = to_sde_plane_state(pstate);
+ if (sde_pstate->excl_rect.w && sde_pstate->excl_rect.h) {
+ /* check overlap on all top z-order */
+ rc = _sde_crtc_excl_rect_overlap_check(pstates, cnt,
+ i + 1, &sde_pstate->excl_rect, pstates[i].stage);
+ if (rc)
+ goto end;
+ }
+ }
+
+end:
+ return rc;
+}
+
static int sde_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
@@ -2490,31 +2566,13 @@
}
}
- /* Check dim layer rect bounds and stage */
- for (i = 0; i < cstate->num_dim_layers; i++) {
- if ((CHECK_LAYER_BOUNDS(cstate->dim_layer[i].rect.y,
- cstate->dim_layer[i].rect.h, mode->vdisplay)) ||
- (CHECK_LAYER_BOUNDS(cstate->dim_layer[i].rect.x,
- cstate->dim_layer[i].rect.w, mode->hdisplay)) ||
- (cstate->dim_layer[i].stage >= SDE_STAGE_MAX) ||
- (!cstate->dim_layer[i].rect.w) ||
- (!cstate->dim_layer[i].rect.h)) {
- SDE_ERROR("invalid dim_layer:{%d,%d,%d,%d}, stage:%d\n",
- cstate->dim_layer[i].rect.x,
- cstate->dim_layer[i].rect.y,
- cstate->dim_layer[i].rect.w,
- cstate->dim_layer[i].rect.h,
- cstate->dim_layer[i].stage);
- SDE_ERROR("display: %dx%d\n", mode->hdisplay,
- mode->vdisplay);
- rc = -E2BIG;
- goto end;
- }
- }
-
/* assign mixer stages based on sorted zpos property */
sort(pstates, cnt, sizeof(pstates[0]), pstate_cmp, NULL);
+ rc = _sde_crtc_excl_dim_layer_check(state, pstates, cnt);
+ if (rc)
+ goto end;
+
if (!sde_is_custom_client()) {
int stage_old = pstates[0].stage;
@@ -2709,19 +2767,19 @@
CRTC_PROP_CORE_CLK);
msm_property_install_range(&sde_crtc->property_info,
"core_ab", 0x0, 0, U64_MAX,
- SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA,
+ SDE_PERF_DEFAULT_MAX_BUS_AB_QUOTA,
CRTC_PROP_CORE_AB);
msm_property_install_range(&sde_crtc->property_info,
"core_ib", 0x0, 0, U64_MAX,
- SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA,
+ SDE_PERF_DEFAULT_MAX_BUS_IB_QUOTA,
CRTC_PROP_CORE_IB);
msm_property_install_range(&sde_crtc->property_info,
"mem_ab", 0x0, 0, U64_MAX,
- SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA,
+ SDE_PERF_DEFAULT_MAX_BUS_AB_QUOTA,
CRTC_PROP_MEM_AB);
msm_property_install_range(&sde_crtc->property_info,
"mem_ib", 0x0, 0, U64_MAX,
- SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA,
+ SDE_PERF_DEFAULT_MAX_BUS_IB_QUOTA,
CRTC_PROP_MEM_IB);
msm_property_install_range(&sde_crtc->property_info,
"rot_prefill_bw", 0, 0, U64_MAX,
@@ -2767,13 +2825,40 @@
sde_kms_info_add_keyint(info, "has_src_split", catalog->has_src_split);
if (catalog->perf.max_bw_low)
sde_kms_info_add_keyint(info, "max_bandwidth_low",
- catalog->perf.max_bw_low);
+ catalog->perf.max_bw_low * 1000LL);
if (catalog->perf.max_bw_high)
sde_kms_info_add_keyint(info, "max_bandwidth_high",
- catalog->perf.max_bw_high);
+ catalog->perf.max_bw_high * 1000LL);
if (sde_kms->perf.max_core_clk_rate)
sde_kms_info_add_keyint(info, "max_mdp_clk",
sde_kms->perf.max_core_clk_rate);
+ sde_kms_info_add_keystr(info, "core_ib_ff",
+ catalog->perf.core_ib_ff);
+ sde_kms_info_add_keystr(info, "core_clk_ff",
+ catalog->perf.core_clk_ff);
+ sde_kms_info_add_keystr(info, "comp_ratio_rt",
+ catalog->perf.comp_ratio_rt);
+ sde_kms_info_add_keystr(info, "comp_ratio_nrt",
+ catalog->perf.comp_ratio_nrt);
+ sde_kms_info_add_keyint(info, "dest_scale_prefill_lines",
+ catalog->perf.dest_scale_prefill_lines);
+ sde_kms_info_add_keyint(info, "undersized_prefill_lines",
+ catalog->perf.undersized_prefill_lines);
+ sde_kms_info_add_keyint(info, "macrotile_prefill_lines",
+ catalog->perf.macrotile_prefill_lines);
+ sde_kms_info_add_keyint(info, "yuv_nv12_prefill_lines",
+ catalog->perf.yuv_nv12_prefill_lines);
+ sde_kms_info_add_keyint(info, "linear_prefill_lines",
+ catalog->perf.linear_prefill_lines);
+ sde_kms_info_add_keyint(info, "downscaling_prefill_lines",
+ catalog->perf.downscaling_prefill_lines);
+ sde_kms_info_add_keyint(info, "xtra_prefill_lines",
+ catalog->perf.xtra_prefill_lines);
+ sde_kms_info_add_keyint(info, "amortizable_threshold",
+ catalog->perf.amortizable_threshold);
+ sde_kms_info_add_keyint(info, "min_prefill_lines",
+ catalog->perf.min_prefill_lines);
+
msm_property_set_blob(&sde_crtc->property_info, &sde_crtc->blob_info,
info->data, info->len, CRTC_PROP_INFO);
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index b8ab066..5118a79 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -93,6 +93,27 @@
#define DEFAULT_SBUF_HEADROOM (20)
+/*
+ * Default parameter values
+ */
+#define DEFAULT_MAX_BW_HIGH 7000000
+#define DEFAULT_MAX_BW_LOW 7000000
+#define DEFAULT_UNDERSIZED_PREFILL_LINES 2
+#define DEFAULT_XTRA_PREFILL_LINES 2
+#define DEFAULT_DEST_SCALE_PREFILL_LINES 3
+#define DEFAULT_MACROTILE_PREFILL_LINES 4
+#define DEFAULT_YUV_NV12_PREFILL_LINES 8
+#define DEFAULT_LINEAR_PREFILL_LINES 1
+#define DEFAULT_DOWNSCALING_PREFILL_LINES 1
+#define DEFAULT_CORE_IB_FF "6.0"
+#define DEFAULT_CORE_CLK_FF "1.0"
+#define DEFAULT_COMP_RATIO_RT \
+ "NV12/5/1/1.23 AB24/5/1/1.23 XB24/5/1/1.23"
+#define DEFAULT_COMP_RATIO_NRT \
+ "NV12/5/1/1.25 AB24/5/1/1.25 XB24/5/1/1.25"
+#define DEFAULT_MAX_PER_PIPE_BW 2400000
+#define DEFAULT_AMORTIZABLE_THRESHOLD 25
+
/*************************************************************
* DTSI PROPERTY INDEX
*************************************************************/
@@ -127,6 +148,18 @@
enum {
PERF_MAX_BW_LOW,
PERF_MAX_BW_HIGH,
+ PERF_CORE_IB_FF,
+ PERF_CORE_CLK_FF,
+ PERF_COMP_RATIO_RT,
+ PERF_COMP_RATIO_NRT,
+ PERF_UNDERSIZED_PREFILL_LINES,
+ PERF_DEST_SCALE_PREFILL_LINES,
+ PERF_MACROTILE_PREFILL_LINES,
+ PERF_YUV_NV12_PREFILL_LINES,
+ PERF_LINEAR_PREFILL_LINES,
+ PERF_DOWNSCALING_PREFILL_LINES,
+ PERF_XTRA_PREFILL_LINES,
+ PERF_AMORTIZABLE_THRESHOLD,
PERF_PROP_MAX,
};
@@ -144,6 +177,7 @@
SSPP_RGB_BLOCKS,
SSPP_EXCL_RECT,
SSPP_SMART_DMA,
+ SSPP_MAX_PER_PIPE_BW,
SSPP_PROP_MAX,
};
@@ -320,6 +354,28 @@
static struct sde_prop_type sde_perf_prop[] = {
{PERF_MAX_BW_LOW, "qcom,sde-max-bw-low-kbps", false, PROP_TYPE_U32},
{PERF_MAX_BW_HIGH, "qcom,sde-max-bw-high-kbps", false, PROP_TYPE_U32},
+ {PERF_CORE_IB_FF, "qcom,sde-core-ib-ff", false, PROP_TYPE_STRING},
+ {PERF_CORE_CLK_FF, "qcom,sde-core-clk-ff", false, PROP_TYPE_STRING},
+ {PERF_COMP_RATIO_RT, "qcom,sde-comp-ratio-rt", false,
+ PROP_TYPE_STRING},
+ {PERF_COMP_RATIO_NRT, "qcom,sde-comp-ratio-nrt", false,
+ PROP_TYPE_STRING},
+ {PERF_UNDERSIZED_PREFILL_LINES, "qcom,sde-undersizedprefill-lines",
+ false, PROP_TYPE_U32},
+ {PERF_DEST_SCALE_PREFILL_LINES, "qcom,sde-dest-scaleprefill-lines",
+ false, PROP_TYPE_U32},
+ {PERF_MACROTILE_PREFILL_LINES, "qcom,sde-macrotileprefill-lines",
+ false, PROP_TYPE_U32},
+ {PERF_YUV_NV12_PREFILL_LINES, "qcom,sde-yuv-nv12prefill-lines",
+ false, PROP_TYPE_U32},
+ {PERF_LINEAR_PREFILL_LINES, "qcom,sde-linearprefill-lines",
+ false, PROP_TYPE_U32},
+ {PERF_DOWNSCALING_PREFILL_LINES, "qcom,sde-downscalingprefill-lines",
+ false, PROP_TYPE_U32},
+ {PERF_XTRA_PREFILL_LINES, "qcom,sde-xtra-prefill-lines",
+ false, PROP_TYPE_U32},
+ {PERF_AMORTIZABLE_THRESHOLD, "qcom,sde-amortizable-threshold",
+ false, PROP_TYPE_U32},
};
static struct sde_prop_type sspp_prop[] = {
@@ -339,6 +395,8 @@
{SSPP_EXCL_RECT, "qcom,sde-sspp-excl-rect", false, PROP_TYPE_U32_ARRAY},
{SSPP_SMART_DMA, "qcom,sde-sspp-smart-dma-priority", false,
PROP_TYPE_U32_ARRAY},
+ {SSPP_MAX_PER_PIPE_BW, "qcom,sde-max-per-pipe-bw-kbps", false,
+ PROP_TYPE_U32_ARRAY},
};
static struct sde_prop_type vig_prop[] = {
@@ -1078,6 +1136,12 @@
if (PROP_VALUE_ACCESS(prop_value, SSPP_EXCL_RECT, i) == 1)
set_bit(SDE_SSPP_EXCL_RECT, &sspp->features);
+ if (prop_exists[SSPP_MAX_PER_PIPE_BW])
+ sblk->max_per_pipe_bw = PROP_VALUE_ACCESS(prop_value,
+ SSPP_MAX_PER_PIPE_BW, i);
+ else
+ sblk->max_per_pipe_bw = DEFAULT_MAX_PER_PIPE_BW;
+
for (j = 0; j < sde_cfg->mdp_count; j++) {
sde_cfg->mdp[j].clk_ctrls[sspp->clk_ctrl].reg_off =
PROP_BITVALUE_ACCESS(prop_value,
@@ -2260,6 +2324,7 @@
int rc, len, prop_count[PERF_PROP_MAX];
struct sde_prop_value *prop_value = NULL;
bool prop_exists[PERF_PROP_MAX];
+ const char *str = NULL;
if (!cfg) {
SDE_ERROR("invalid argument\n");
@@ -2285,9 +2350,72 @@
goto freeprop;
cfg->perf.max_bw_low =
- PROP_VALUE_ACCESS(prop_value, PERF_MAX_BW_LOW, 0);
+ prop_exists[PERF_MAX_BW_LOW] ?
+ PROP_VALUE_ACCESS(prop_value, PERF_MAX_BW_LOW, 0) :
+ DEFAULT_MAX_BW_LOW;
cfg->perf.max_bw_high =
- PROP_VALUE_ACCESS(prop_value, PERF_MAX_BW_HIGH, 0);
+ prop_exists[PERF_MAX_BW_HIGH] ?
+ PROP_VALUE_ACCESS(prop_value, PERF_MAX_BW_HIGH, 0) :
+ DEFAULT_MAX_BW_HIGH;
+
+ /*
+ * The following performance parameters (e.g. core_ib_ff) are
+ * mapped directly as device tree string constants.
+ */
+ rc = of_property_read_string(np,
+ sde_perf_prop[PERF_CORE_IB_FF].prop_name, &str);
+ cfg->perf.core_ib_ff = rc ? DEFAULT_CORE_IB_FF : str;
+ rc = of_property_read_string(np,
+ sde_perf_prop[PERF_CORE_CLK_FF].prop_name, &str);
+ cfg->perf.core_clk_ff = rc ? DEFAULT_CORE_CLK_FF : str;
+ rc = of_property_read_string(np,
+ sde_perf_prop[PERF_COMP_RATIO_RT].prop_name, &str);
+ cfg->perf.comp_ratio_rt = rc ? DEFAULT_COMP_RATIO_RT : str;
+ rc = of_property_read_string(np,
+ sde_perf_prop[PERF_COMP_RATIO_NRT].prop_name, &str);
+ cfg->perf.comp_ratio_nrt = rc ? DEFAULT_COMP_RATIO_NRT : str;
+ rc = 0;
+
+ cfg->perf.undersized_prefill_lines =
+ prop_exists[PERF_UNDERSIZED_PREFILL_LINES] ?
+ PROP_VALUE_ACCESS(prop_value,
+ PERF_UNDERSIZED_PREFILL_LINES, 0) :
+ DEFAULT_UNDERSIZED_PREFILL_LINES;
+ cfg->perf.xtra_prefill_lines =
+ prop_exists[PERF_XTRA_PREFILL_LINES] ?
+ PROP_VALUE_ACCESS(prop_value,
+ PERF_XTRA_PREFILL_LINES, 0) :
+ DEFAULT_XTRA_PREFILL_LINES;
+ cfg->perf.dest_scale_prefill_lines =
+ prop_exists[PERF_DEST_SCALE_PREFILL_LINES] ?
+ PROP_VALUE_ACCESS(prop_value,
+ PERF_DEST_SCALE_PREFILL_LINES, 0) :
+ DEFAULT_DEST_SCALE_PREFILL_LINES;
+ cfg->perf.macrotile_prefill_lines =
+ prop_exists[PERF_MACROTILE_PREFILL_LINES] ?
+ PROP_VALUE_ACCESS(prop_value,
+ PERF_MACROTILE_PREFILL_LINES, 0) :
+ DEFAULT_MACROTILE_PREFILL_LINES;
+ cfg->perf.yuv_nv12_prefill_lines =
+ prop_exists[PERF_YUV_NV12_PREFILL_LINES] ?
+ PROP_VALUE_ACCESS(prop_value,
+ PERF_YUV_NV12_PREFILL_LINES, 0) :
+ DEFAULT_YUV_NV12_PREFILL_LINES;
+ cfg->perf.linear_prefill_lines =
+ prop_exists[PERF_LINEAR_PREFILL_LINES] ?
+ PROP_VALUE_ACCESS(prop_value,
+ PERF_LINEAR_PREFILL_LINES, 0) :
+ DEFAULT_LINEAR_PREFILL_LINES;
+ cfg->perf.downscaling_prefill_lines =
+ prop_exists[PERF_DOWNSCALING_PREFILL_LINES] ?
+ PROP_VALUE_ACCESS(prop_value,
+ PERF_DOWNSCALING_PREFILL_LINES, 0) :
+ DEFAULT_DOWNSCALING_PREFILL_LINES;
+ cfg->perf.amortizable_threshold =
+ prop_exists[PERF_AMORTIZABLE_THRESHOLD] ?
+ PROP_VALUE_ACCESS(prop_value,
+ PERF_AMORTIZABLE_THRESHOLD, 0) :
+ DEFAULT_AMORTIZABLE_THRESHOLD;
freeprop:
kfree(prop_value);
@@ -2400,14 +2528,21 @@
case SDE_HW_VER_171:
case SDE_HW_VER_172:
/* update msm8996 target here */
+ sde_cfg->perf.min_prefill_lines = 21;
break;
case SDE_HW_VER_300:
case SDE_HW_VER_301:
+ /* update msm8998 target here */
+ sde_cfg->has_wb_ubwc = true;
+ sde_cfg->perf.min_prefill_lines = 25;
+ break;
case SDE_HW_VER_400:
/* update msm8998 and sdm845 target here */
sde_cfg->has_wb_ubwc = true;
+ sde_cfg->perf.min_prefill_lines = 24;
break;
default:
+ sde_cfg->perf.min_prefill_lines = 0xffff;
break;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index b5f83ad..cfb1b67 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -361,6 +361,7 @@
* @pcc_blk:
* @igc_blk:
* @format_list: Pointer to list of supported formats
+ * @max_per_pipe_bw: maximum allowable bandwidth of this pipe in kBps
*/
struct sde_sspp_sub_blks {
u32 maxlinewidth;
@@ -379,6 +380,7 @@
u32 maxhdeciexp; /* max decimation is 2^value */
u32 maxvdeciexp; /* max decimation is 2^value */
u32 smart_dma_priority;
+ u32 max_per_pipe_bw;
struct sde_src_blk src_blk;
struct sde_scaler_blk scaler_blk;
struct sde_pp_blk csc_blk;
@@ -687,10 +689,36 @@
* struct sde_perf_cfg - performance control settings
* @max_bw_low low threshold of maximum bandwidth (kbps)
* @max_bw_high high threshold of maximum bandwidth (kbps)
+ * @core_ib_ff core instantaneous bandwidth fudge factor
+ * @core_clk_ff core clock fudge factor
+ * @comp_ratio_rt string of 0 or more of <fourcc>/<ven>/<mod>/<comp ratio>
+ * @comp_ratio_nrt string of 0 or more of <fourcc>/<ven>/<mod>/<comp ratio>
+ * @undersized_prefill_lines undersized prefill in lines
+ * @xtra_prefill_lines extra prefill latency in lines
+ * @dest_scale_prefill_lines destination scaler latency in lines
+ * @macrotile_perfill_lines macrotile latency in lines
+ * @yuv_nv12_prefill_lines yuv_nv12 latency in lines
+ * @linear_prefill_lines linear latency in lines
+ * @downscaling_prefill_lines downscaling latency in lines
+ * @amortizable_theshold minimum y position for traffic shaping prefill
+ * @min_prefill_lines minimum pipeline latency in lines
*/
struct sde_perf_cfg {
u32 max_bw_low;
u32 max_bw_high;
+ const char *core_ib_ff;
+ const char *core_clk_ff;
+ const char *comp_ratio_rt;
+ const char *comp_ratio_nrt;
+ u32 undersized_prefill_lines;
+ u32 xtra_prefill_lines;
+ u32 dest_scale_prefill_lines;
+ u32 macrotile_prefill_lines;
+ u32 yuv_nv12_prefill_lines;
+ u32 linear_prefill_lines;
+ u32 downscaling_prefill_lines;
+ u32 amortizable_threshold;
+ u32 min_prefill_lines;
};
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index d38a6b9..01a5535 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -325,11 +325,11 @@
* sde_kms_info_add_keyint - add integer value to 'sde_kms_info'
* @info: Pointer to sde_kms_info structure
* @key: Pointer to key string
- * @value: Signed 32-bit integer value
+ * @value: Signed 64-bit integer value
*/
void sde_kms_info_add_keyint(struct sde_kms_info *info,
const char *key,
- int32_t value);
+ int64_t value);
/**
* sde_kms_info_add_keystr - add string value to 'sde_kms_info'
diff --git a/drivers/gpu/drm/msm/sde/sde_kms_utils.c b/drivers/gpu/drm/msm/sde/sde_kms_utils.c
index 30e12c9..b956be5 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms_utils.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms_utils.c
@@ -24,14 +24,14 @@
void sde_kms_info_add_keyint(struct sde_kms_info *info,
const char *key,
- int32_t value)
+ int64_t value)
{
uint32_t len;
if (info && key) {
len = snprintf(info->data + info->len,
SDE_KMS_INFO_MAX_SIZE - info->len,
- "%s=%d\n",
+ "%s=%lld\n",
key,
value);
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index c408861..cf03a47 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -2886,11 +2886,12 @@
/*
* Check exclusion rect against src rect.
- * Cropping is not required as hardware will consider only the
- * intersecting region with the src rect.
+ * it must intersect with source rect.
*/
sde_kms_rect_intersect(&src, &pstate->excl_rect, &intersect);
- if (!intersect.w || !intersect.h || SDE_FORMAT_IS_YUV(fmt)) {
+ if (intersect.w != pstate->excl_rect.w ||
+ intersect.h != pstate->excl_rect.h ||
+ SDE_FORMAT_IS_YUV(fmt)) {
SDE_ERROR_PLANE(psde,
"invalid excl_rect:{%d,%d,%d,%d} src:{%d,%d,%d,%d}, fmt: %4.4s\n",
pstate->excl_rect.x, pstate->excl_rect.y,
@@ -3191,6 +3192,8 @@
psde->pipe_sblk->maxhdeciexp);
sde_kms_info_add_keyint(info, "max_vertical_deci",
psde->pipe_sblk->maxvdeciexp);
+ sde_kms_info_add_keyint(info, "max_per_pipe_bw",
+ psde->pipe_sblk->max_per_pipe_bw * 1000LL);
msm_property_set_blob(&psde->property_info, &psde->blob_info,
info->data, info->len, PLANE_PROP_INFO);
diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c
index 81df309..7fd496f 100644
--- a/drivers/gpu/drm/sti/sti_gdp.c
+++ b/drivers/gpu/drm/sti/sti_gdp.c
@@ -66,7 +66,9 @@
#define GAM_GDP_ALPHARANGE_255 BIT(5)
#define GAM_GDP_AGC_FULL_RANGE 0x00808080
#define GAM_GDP_PPT_IGNORE (BIT(1) | BIT(0))
-#define GAM_GDP_SIZE_MAX 0x7FF
+
+#define GAM_GDP_SIZE_MAX_WIDTH 3840
+#define GAM_GDP_SIZE_MAX_HEIGHT 2160
#define GDP_NODE_NB_BANK 2
#define GDP_NODE_PER_FIELD 2
@@ -633,8 +635,8 @@
/* src_x are in 16.16 format */
src_x = state->src_x >> 16;
src_y = state->src_y >> 16;
- src_w = clamp_val(state->src_w >> 16, 0, GAM_GDP_SIZE_MAX);
- src_h = clamp_val(state->src_h >> 16, 0, GAM_GDP_SIZE_MAX);
+ src_w = clamp_val(state->src_w >> 16, 0, GAM_GDP_SIZE_MAX_WIDTH);
+ src_h = clamp_val(state->src_h >> 16, 0, GAM_GDP_SIZE_MAX_HEIGHT);
format = sti_gdp_fourcc2format(fb->pixel_format);
if (format == -1) {
@@ -732,8 +734,8 @@
/* src_x are in 16.16 format */
src_x = state->src_x >> 16;
src_y = state->src_y >> 16;
- src_w = clamp_val(state->src_w >> 16, 0, GAM_GDP_SIZE_MAX);
- src_h = clamp_val(state->src_h >> 16, 0, GAM_GDP_SIZE_MAX);
+ src_w = clamp_val(state->src_w >> 16, 0, GAM_GDP_SIZE_MAX_WIDTH);
+ src_h = clamp_val(state->src_h >> 16, 0, GAM_GDP_SIZE_MAX_HEIGHT);
list = sti_gdp_get_free_nodes(gdp);
top_field = list->top_field;
diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c
index a6ed9d5..750733a 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_vm.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c
@@ -66,8 +66,11 @@
if (vmf->flags & FAULT_FLAG_RETRY_NOWAIT)
goto out_unlock;
+ ttm_bo_reference(bo);
up_read(&vma->vm_mm->mmap_sem);
(void) fence_wait(bo->moving, true);
+ ttm_bo_unreserve(bo);
+ ttm_bo_unref(&bo);
goto out_unlock;
}
@@ -120,8 +123,10 @@
if (vmf->flags & FAULT_FLAG_ALLOW_RETRY) {
if (!(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) {
+ ttm_bo_reference(bo);
up_read(&vma->vm_mm->mmap_sem);
(void) ttm_bo_wait_unreserved(bo);
+ ttm_bo_unref(&bo);
}
return VM_FAULT_RETRY;
@@ -166,6 +171,13 @@
ret = ttm_bo_vm_fault_idle(bo, vma, vmf);
if (unlikely(ret != 0)) {
retval = ret;
+
+ if (retval == VM_FAULT_RETRY &&
+ !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) {
+ /* The BO has already been unreserved. */
+ return retval;
+ }
+
goto out_unlock;
}
diff --git a/drivers/gpu/msm/a6xx_reg.h b/drivers/gpu/msm/a6xx_reg.h
index f78ee05..6db0111 100644
--- a/drivers/gpu/msm/a6xx_reg.h
+++ b/drivers/gpu/msm/a6xx_reg.h
@@ -799,7 +799,7 @@
#define A6XX_GMU_GPU_NAP_CTRL 0x1F8E4
#define A6XX_GMU_RPMH_CTRL 0x1F8E8
#define A6XX_GMU_RPMH_HYST_CTRL 0x1F8E9
-#define A6XX_GMU_RPMH_POWER_STATE 0x1F8EC
+#define A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE 0x1F8EC
#define A6XX_GMU_BOOT_KMD_LM_HANDSHAKE 0x1F9F0
/* HFI registers*/
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index fbf0e7b..b7cfadd 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -1266,9 +1266,12 @@
return ret;
/* Put the GPU in a responsive state */
- ret = kgsl_pwrctrl_change_state(device, KGSL_STATE_AWARE);
- if (ret)
- return ret;
+ if (ADRENO_GPUREV(adreno_dev) < 600) {
+ /* No need for newer generation architectures */
+ ret = kgsl_pwrctrl_change_state(device, KGSL_STATE_AWARE);
+ if (ret)
+ return ret;
+ }
ret = adreno_iommu_init(adreno_dev);
if (ret)
@@ -1278,7 +1281,8 @@
adreno_fault_detect_init(adreno_dev);
/* Power down the device */
- kgsl_pwrctrl_change_state(device, KGSL_STATE_SLUMBER);
+ if (ADRENO_GPUREV(adreno_dev) < 600)
+ kgsl_pwrctrl_change_state(device, KGSL_STATE_SLUMBER);
if (gpudev->init != NULL)
gpudev->init(adreno_dev);
@@ -1344,6 +1348,9 @@
{
int i;
+ if (kgsl_gmu_isenabled(device))
+ return false;
+
for (i = 0; i < KGSL_MAX_REGULATORS; i++) {
struct kgsl_regulator *regulator =
&device->pwrctrl.regulators[i];
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
index 9e66ce1..dee2cd1 100644
--- a/drivers/gpu/msm/adreno_a6xx.c
+++ b/drivers/gpu/msm/adreno_a6xx.c
@@ -830,6 +830,10 @@
struct gmu_device *gmu = &device->gmu;
/* Configure registers for idle setting. The setting is cumulative */
+
+ kgsl_gmu_regwrite(device,
+ A6XX_GMU_PWR_COL_INTER_FRAME_CTRL, 0x9C40400);
+
switch (gmu->idle_level) {
case GPU_HW_MIN_VOLT:
kgsl_gmu_regrmw(device, A6XX_GMU_RPMH_CTRL, 0,
@@ -1229,7 +1233,8 @@
if (ret)
dev_err(&gmu->pdev->dev, "OOB set for slumber timed out\n");
else {
- kgsl_gmu_regread(device, A6XX_GMU_RPMH_POWER_STATE, &state);
+ kgsl_gmu_regread(device,
+ A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE, &state);
if (state != GPU_HW_SLUMBER) {
dev_err(&gmu->pdev->dev,
"Failed to prepare for slumber\n");
@@ -2269,7 +2274,7 @@
ADRENO_REG_DEFINE(ADRENO_REG_GMU_HFI_SFR_ADDR,
A6XX_GMU_HFI_SFR_ADDR),
ADRENO_REG_DEFINE(ADRENO_REG_GMU_RPMH_POWER_STATE,
- A6XX_GMU_RPMH_POWER_STATE),
+ A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE),
ADRENO_REG_DEFINE(ADRENO_REG_GMU_GMU2HOST_INTR_CLR,
A6XX_GMU_GMU2HOST_INTR_CLR),
ADRENO_REG_DEFINE(ADRENO_REG_GMU_GMU2HOST_INTR_INFO,
diff --git a/drivers/gpu/msm/kgsl_gmu.c b/drivers/gpu/msm/kgsl_gmu.c
index 0c821cd..54659fc 100644
--- a/drivers/gpu/msm/kgsl_gmu.c
+++ b/drivers/gpu/msm/kgsl_gmu.c
@@ -1332,12 +1332,6 @@
* In v2, this function call shall move ahead
* of hfi_start() to save power.
*/
- ret = gpudev->oob_set(adreno_dev, OOB_CPINIT_SET_MASK,
- OOB_CPINIT_CHECK_MASK, OOB_CPINIT_CLEAR_MASK);
- gpudev->oob_clear(adreno_dev, OOB_CPINIT_CLEAR_MASK);
-
- if (ret)
- goto error_gpu;
if (device->state == KGSL_STATE_INIT ||
device->state == KGSL_STATE_SUSPEND) {
@@ -1379,19 +1373,20 @@
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
unsigned long t;
bool idle = false;
+ unsigned int reg;
if (!test_bit(GMU_CLK_ON, &gmu->flags))
return;
- if (gpudev->hw_isidle) {
- t = jiffies + msecs_to_jiffies(GMU_IDLE_TIMEOUT);
- while (!time_after(jiffies, t)) {
- if (gpudev->hw_isidle(adreno_dev)) {
- idle = true;
- break;
- }
- cpu_relax();
+ t = jiffies + msecs_to_jiffies(GMU_IDLE_TIMEOUT);
+ while (!time_after(jiffies, t)) {
+ adreno_read_gmureg(ADRENO_DEVICE(device),
+ ADRENO_REG_GMU_RPMH_POWER_STATE, ®);
+ if (reg == device->gmu.idle_level) {
+ idle = true;
+ break;
}
+ cpu_relax();
}
gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_NOTIFY_SLUMBER, 0, 0);
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index b99c1df..81853ee 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -2600,7 +2600,7 @@
/* Check for pwm4 */
reg = superio_inb(sioaddr, IT87_SIO_GPIO4_REG);
- if (!(reg & BIT(2)))
+ if (reg & BIT(2))
sio_data->skip_pwm |= BIT(3);
/* Check for pwm2, fan2 */
diff --git a/drivers/input/misc/qpnp-power-on.c b/drivers/input/misc/qpnp-power-on.c
index 62e34cc..339f94c 100644
--- a/drivers/input/misc/qpnp-power-on.c
+++ b/drivers/input/misc/qpnp-power-on.c
@@ -2123,6 +2123,9 @@
return rc;
}
+ if (sys_reset)
+ boot_reason = ffs(pon_sts);
+
index = ffs(pon_sts) - 1;
cold_boot = !qpnp_pon_is_warm_reset();
if (index >= ARRAY_SIZE(qpnp_pon_reason) || index < 0) {
@@ -2350,8 +2353,6 @@
list_add(&pon->list, &spon_dev_list);
spin_unlock_irqrestore(&spon_list_slock, flags);
pon->is_spon = true;
- } else {
- boot_reason = ffs(pon_sts);
}
/* config whether store the hard reset reason */
diff --git a/drivers/leds/leds-ktd2692.c b/drivers/leds/leds-ktd2692.c
index bf23ba1..45296aa 100644
--- a/drivers/leds/leds-ktd2692.c
+++ b/drivers/leds/leds-ktd2692.c
@@ -270,15 +270,15 @@
return -ENXIO;
led->ctrl_gpio = devm_gpiod_get(dev, "ctrl", GPIOD_ASIS);
- if (IS_ERR(led->ctrl_gpio)) {
- ret = PTR_ERR(led->ctrl_gpio);
+ ret = PTR_ERR_OR_ZERO(led->ctrl_gpio);
+ if (ret) {
dev_err(dev, "cannot get ctrl-gpios %d\n", ret);
return ret;
}
led->aux_gpio = devm_gpiod_get(dev, "aux", GPIOD_ASIS);
- if (IS_ERR(led->aux_gpio)) {
- ret = PTR_ERR(led->aux_gpio);
+ ret = PTR_ERR_OR_ZERO(led->aux_gpio);
+ if (ret) {
dev_err(dev, "cannot get aux-gpios %d\n", ret);
return ret;
}
diff --git a/drivers/media/platform/msm/camera/Makefile b/drivers/media/platform/msm/camera/Makefile
index c05c069..2e71d05 100644
--- a/drivers/media/platform/msm/camera/Makefile
+++ b/drivers/media/platform/msm/camera/Makefile
@@ -6,3 +6,5 @@
obj-$(CONFIG_SPECTRA_CAMERA) += cam_cpas/
obj-$(CONFIG_SPECTRA_CAMERA) += cam_cdm/
obj-$(CONFIG_SPECTRA_CAMERA) += cam_isp/
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_module/
+obj-$(CONFIG_SPECTRA_CAMERA) += icp/
diff --git a/drivers/media/platform/msm/camera/cam_core/Makefile b/drivers/media/platform/msm/camera/cam_core/Makefile
index 417de13..60f94d1 100644
--- a/drivers/media/platform/msm/camera/cam_core/Makefile
+++ b/drivers/media/platform/msm/camera/cam_core/Makefile
@@ -1,3 +1,4 @@
ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sync
-obj-$(CONFIG_SPECTRA_CAMERA) += cam_context.o cam_node.o cam_subdev.o
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_context.o cam_context_utils.o cam_node.o cam_subdev.o
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context.c b/drivers/media/platform/msm/camera/cam_core/cam_context.c
index 56b34f5..17b3c7c 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context.c
@@ -358,4 +358,3 @@
return 0;
}
-
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context.h b/drivers/media/platform/msm/camera/cam_core/cam_context.h
index c7329cf..37a5c03 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context.h
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context.h
@@ -22,6 +22,8 @@
/* max request number */
#define CAM_CTX_REQ_MAX 20
+#define CAM_CTX_CFG_MAX 20
+#define CAM_CTX_RES_MAX 20
/**
* enum cam_ctx_state - context top level states
@@ -43,13 +45,29 @@
* @status: Request status
* @request_id: Request id
* @req_priv: Derived request object
+ * @hw_update_entries: Hardware update entries
+ * @num_hw_update_entries: Number of hardware update entries
+ * @in_map_entries: Entries for in fences
+ * @num_in_map_entries: Number of in map entries
+ * @out_map_entries: Entries for out fences
+ * @num_out_map_entries: Number of out map entries
+ * @num_in_acked: Number of in fence acked
+ * @num_out_acked: Number of out fence acked
*
*/
struct cam_ctx_request {
- struct list_head list;
- uint32_t status;
- uint64_t request_id;
- void *req_priv;
+ struct list_head list;
+ uint32_t status;
+ uint64_t request_id;
+ void *req_priv;
+ struct cam_hw_update_entry hw_update_entries[CAM_CTX_CFG_MAX];
+ uint32_t num_hw_update_entries;
+ struct cam_hw_fence_map_entry in_map_entries[CAM_CTX_CFG_MAX];
+ uint32_t num_in_map_entries;
+ struct cam_hw_fence_map_entry out_map_entries[CAM_CTX_CFG_MAX];
+ uint32_t num_out_map_entries;
+ uint32_t num_in_acked;
+ uint32_t num_out_acked;
};
/**
@@ -132,6 +150,7 @@
* @state: Current state for top level state machine
* @state_machine: Top level state machine
* @ctx_priv: Private context pointer
+ * @ctxt_to_hw_map: Context to hardware mapping pointer
*
*/
struct cam_context {
@@ -159,6 +178,7 @@
struct cam_ctx_ops *state_machine;
void *ctx_priv;
+ void *ctxt_to_hw_map;
};
/**
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c b/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
new file mode 100644
index 0000000..21a61ff
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
@@ -0,0 +1,481 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "CTXT-UTILS %s:%d " fmt, __func__, __LINE__
+
+#include <linux/debugfs.h>
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <media/cam_sync.h>
+#include <media/cam_defs.h>
+
+#include "cam_sync_api.h"
+#include "cam_req_mgr_util.h"
+#include "cam_mem_mgr.h"
+#include "cam_node.h"
+#include "cam_context.h"
+
+int cam_context_buf_done_from_hw(struct cam_context *ctx,
+ void *done_event_data, uint32_t bubble_state)
+{
+ int rc = 0;
+ int i, j;
+ struct cam_ctx_request *req;
+ struct cam_hw_done_event_data *done =
+ (struct cam_hw_done_event_data *)done_event_data;
+
+ if (list_empty(&ctx->active_req_list)) {
+ pr_err("Buf done with no active request\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ req = list_first_entry(&ctx->active_req_list,
+ struct cam_ctx_request, list);
+
+ for (i = 0; i < done->num_handles; i++) {
+ for (j = 0; j < req->num_out_map_entries; j++) {
+ if (done->resource_handle[i] ==
+ req->out_map_entries[j].resource_handle)
+ break;
+ }
+
+ if (j == req->num_out_map_entries) {
+ pr_err("Can not find matching lane handle 0x%x\n",
+ done->resource_handle[i]);
+ rc = -EINVAL;
+ continue;
+ }
+
+ cam_sync_signal(req->out_map_entries[j].sync_id,
+ CAM_SYNC_STATE_SIGNALED_SUCCESS);
+ req->num_out_acked++;
+ trace_printk("Sync success req %lld, reset sync id 0x%x\n",
+ req->request_id,
+ req->out_map_entries[j].sync_id);
+
+ req->out_map_entries[j].sync_id = -1;
+ }
+
+ if (req->num_out_acked == req->num_out_map_entries) {
+ list_del_init(&req->list);
+ list_add_tail(&req->list, &ctx->free_req_list);
+ }
+
+end:
+ return rc;
+}
+
+int cam_context_apply_req_to_hw(struct cam_context *ctx,
+ struct cam_req_mgr_apply_request *apply)
+{
+ int rc = 0;
+ struct cam_ctx_request *req;
+ struct cam_hw_config_args cfg;
+
+ if (!ctx->hw_mgr_intf) {
+ pr_err("HW interface is not ready\n");
+ rc = -EFAULT;
+ goto end;
+ }
+
+ if (list_empty(&ctx->pending_req_list)) {
+ pr_err("No available request for Apply id %lld\n",
+ apply->request_id);
+ rc = -EFAULT;
+ goto end;
+ }
+
+ spin_lock(&ctx->lock);
+ req = list_first_entry(&ctx->pending_req_list,
+ struct cam_ctx_request, list);
+ list_del_init(&req->list);
+ spin_unlock(&ctx->lock);
+
+ cfg.ctxt_to_hw_map = ctx->ctxt_to_hw_map;
+ cfg.hw_update_entries = req->hw_update_entries;
+ cfg.num_hw_update_entries = req->num_hw_update_entries;
+ cfg.out_map_entries = req->out_map_entries;
+ cfg.num_out_map_entries = req->num_out_map_entries;
+ cfg.priv = (void *)&req->request_id;
+ list_add_tail(&req->list, &ctx->active_req_list);
+
+ rc = ctx->hw_mgr_intf->hw_config(ctx->hw_mgr_intf->hw_mgr_priv, &cfg);
+ if (rc)
+ list_del_init(&req->list);
+
+end:
+ return rc;
+}
+
+int32_t cam_context_release_dev_to_hw(struct cam_context *ctx,
+ struct cam_release_dev_cmd *cmd)
+{
+ int rc = 0;
+ int i;
+ struct cam_hw_release_args arg;
+ struct cam_ctx_request *req;
+
+ if (!ctx->hw_mgr_intf) {
+ pr_err("HW interface is not ready\n");
+ rc = -EFAULT;
+ goto end;
+ }
+
+ if (ctx->ctxt_to_hw_map) {
+ arg.ctxt_to_hw_map = ctx->ctxt_to_hw_map;
+ ctx->hw_mgr_intf->hw_release(ctx->hw_mgr_intf->hw_mgr_priv,
+ &arg);
+ ctx->ctxt_to_hw_map = NULL;
+ }
+
+ ctx->session_hdl = 0;
+ ctx->dev_hdl = 0;
+ ctx->link_hdl = 0;
+
+ while (!list_empty(&ctx->active_req_list)) {
+ req = list_first_entry(&ctx->active_req_list,
+ struct cam_ctx_request, list);
+ list_del_init(&req->list);
+ pr_warn("signal fence in active list. fence num %d\n",
+ req->num_out_map_entries);
+ for (i = 0; i < req->num_out_map_entries; i++) {
+ if (req->out_map_entries[i].sync_id != -1)
+ cam_sync_signal(req->out_map_entries[i].sync_id,
+ CAM_SYNC_STATE_SIGNALED_ERROR);
+ }
+ list_add_tail(&req->list, &ctx->free_req_list);
+ }
+
+ /* flush the pending queue */
+ while (!list_empty(&ctx->pending_req_list)) {
+ req = list_first_entry(&ctx->pending_req_list,
+ struct cam_ctx_request, list);
+ list_del_init(&req->list);
+ pr_debug("signal fence in pending list. fence num %d\n",
+ req->num_out_map_entries);
+ for (i = 0; i < req->num_out_map_entries; i++)
+ if (req->out_map_entries[i].sync_id != -1)
+ cam_sync_signal(req->out_map_entries[i].sync_id,
+ CAM_SYNC_STATE_SIGNALED_ERROR);
+ list_add_tail(&req->list, &ctx->free_req_list);
+ }
+
+end:
+ return rc;
+}
+
+void cam_context_sync_callback(int32_t sync_obj, int status, void *data)
+{
+ struct cam_context *ctx = data;
+ struct cam_ctx_request *req = NULL;
+ struct cam_req_mgr_apply_request apply;
+
+ spin_lock(&ctx->lock);
+ if (!list_empty(&ctx->pending_req_list))
+ req = list_first_entry(&ctx->pending_req_list,
+ struct cam_ctx_request, list);
+ spin_unlock(&ctx->lock);
+
+ if (!req) {
+ pr_err("No more request obj free\n");
+ return;
+ }
+
+ req->num_in_acked++;
+ if (req->num_in_acked == req->num_in_map_entries) {
+ apply.request_id = req->request_id;
+ trace_printk("async cb for request :%llu",
+ req->request_id);
+ cam_context_apply_req_to_hw(ctx, &apply);
+ }
+}
+
+int32_t cam_context_prepare_dev_to_hw(struct cam_context *ctx,
+ struct cam_config_dev_cmd *cmd)
+{
+ int rc = 0;
+ struct cam_ctx_request *req = NULL;
+ struct cam_hw_prepare_update_args cfg;
+ uint64_t packet_addr;
+ struct cam_packet *packet;
+ size_t len = 0;
+ int32_t i = 0;
+
+ if (!ctx->hw_mgr_intf) {
+ pr_err("HW interface is not ready\n");
+ rc = -EFAULT;
+ goto end;
+ }
+
+ spin_lock(&ctx->lock);
+ if (!list_empty(&ctx->free_req_list)) {
+ req = list_first_entry(&ctx->free_req_list,
+ struct cam_ctx_request, list);
+ list_del_init(&req->list);
+ }
+ spin_unlock(&ctx->lock);
+
+ if (!req) {
+ pr_err("No more request obj free\n");
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ memset(req, 0, sizeof(*req));
+ INIT_LIST_HEAD(&req->list);
+
+ /* for config dev, only memory handle is supported */
+ /* map packet from the memhandle */
+ rc = cam_mem_get_cpu_buf((int32_t) cmd->packet_handle,
+ (uint64_t *) &packet_addr,
+ &len);
+ if (rc != 0) {
+ pr_err("Can not get packet address\n");
+ rc = -EINVAL;
+ goto free_req;
+ }
+
+ packet = (struct cam_packet *) (packet_addr + cmd->offset);
+ pr_debug("pack_handle %llx\n", cmd->packet_handle);
+ pr_debug("packet address is 0x%llx\n", packet_addr);
+ pr_debug("packet with length %zu, offset 0x%llx\n",
+ len, cmd->offset);
+ pr_debug("Packet request id 0x%llx\n",
+ packet->header.request_id);
+ pr_debug("Packet size 0x%x\n", packet->header.size);
+ pr_debug("packet op %d\n", packet->header.op_code);
+
+ /* preprocess the configuration */
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.packet = packet;
+ cfg.ctxt_to_hw_map = ctx->ctxt_to_hw_map;
+ cfg.max_hw_update_entries = CAM_CTX_CFG_MAX;
+ cfg.num_hw_update_entries = req->num_hw_update_entries;
+ cfg.hw_update_entries = req->hw_update_entries;
+ cfg.max_out_map_entries = CAM_CTX_CFG_MAX;
+ cfg.out_map_entries = req->out_map_entries;
+ cfg.max_in_map_entries = CAM_CTX_CFG_MAX;
+ cfg.in_map_entries = req->in_map_entries;
+
+ rc = ctx->hw_mgr_intf->hw_prepare_update(
+ ctx->hw_mgr_intf->hw_mgr_priv, &cfg);
+ if (rc != 0) {
+ pr_err("Prepare config packet failed in HW layer\n");
+ rc = -EFAULT;
+ goto free_req;
+ }
+ req->num_hw_update_entries = cfg.num_hw_update_entries;
+ req->num_out_map_entries = cfg.num_out_map_entries;
+ req->num_in_map_entries = cfg.num_in_map_entries;
+ req->request_id = packet->header.request_id;
+ req->status = 1;
+ req->req_priv = cfg.priv;
+
+ if (req->num_in_map_entries > 0) {
+ spin_lock(&ctx->lock);
+ list_add_tail(&req->list, &ctx->pending_req_list);
+ spin_unlock(&ctx->lock);
+ for (i = 0; i < req->num_in_map_entries; i++) {
+ trace_printk("register in fence callback: %d\n",
+ req->in_map_entries[i].sync_id);
+ rc = cam_sync_register_callback(
+ cam_context_sync_callback,
+ (void *)ctx,
+ req->in_map_entries[i].sync_id);
+ pr_debug("register in fence callback: %d ret = %d\n",
+ req->in_map_entries[i].sync_id, rc);
+ }
+ goto end;
+ }
+
+ return rc;
+
+free_req:
+ spin_lock(&ctx->lock);
+ list_add_tail(&req->list, &ctx->free_req_list);
+ spin_unlock(&ctx->lock);
+end:
+ pr_debug("Config dev successful\n");
+ return rc;
+}
+
+int32_t cam_context_acquire_dev_to_hw(struct cam_context *ctx,
+ struct cam_acquire_dev_cmd *cmd)
+{
+ int rc;
+ struct cam_hw_acquire_args param;
+ struct cam_create_dev_hdl req_hdl_param;
+ struct cam_hw_release_args release;
+
+ if (!ctx->hw_mgr_intf) {
+ pr_err("HW interface is not ready\n");
+ rc = -EFAULT;
+ goto end;
+ }
+
+ pr_debug("acquire cmd: session_hdl 0x%x, num_resources %d\n",
+ cmd->session_handle, cmd->num_resources);
+ pr_debug(" handle type %d, res %lld\n", cmd->handle_type,
+ cmd->resource_hdl);
+
+ if (cmd->num_resources > CAM_CTX_RES_MAX) {
+ pr_err("Too much resources in the acquire\n");
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ /* for now we only support user pointer */
+ if (cmd->handle_type != 1) {
+ pr_err("Only user pointer is supported");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ /* fill in parameters */
+ param.context_data = ctx;
+ param.event_cb = ctx->irq_cb_intf;
+ param.num_acq = cmd->num_resources;
+ param.acquire_info = cmd->resource_hdl;
+
+ pr_debug("ctx %pK: acquire hw resource: hw_intf: 0x%pK, priv 0x%pK",
+ ctx, ctx->hw_mgr_intf, ctx->hw_mgr_intf->hw_mgr_priv);
+ pr_debug("acquire_hw_func 0x%pK\n", ctx->hw_mgr_intf->hw_acquire);
+
+ /* call HW manager to reserve the resource */
+ rc = ctx->hw_mgr_intf->hw_acquire(ctx->hw_mgr_intf->hw_mgr_priv,
+ ¶m);
+ if (rc != 0) {
+ pr_err("Acquire device failed\n");
+ goto end;
+ }
+
+ ctx->ctxt_to_hw_map = param.ctxt_to_hw_map;
+
+ /* if hw resource acquire successful, acquire dev handle */
+ req_hdl_param.session_hdl = cmd->session_handle;
+ /* bridge is not ready for these flags. so false for now */
+ req_hdl_param.v4l2_sub_dev_flag = 0;
+ req_hdl_param.media_entity_flag = 0;
+ req_hdl_param.priv = ctx;
+
+ pr_debug("get device handle from bridge\n");
+ ctx->dev_hdl = cam_create_device_hdl(&req_hdl_param);
+ if (ctx->dev_hdl <= 0) {
+ rc = -EFAULT;
+ pr_err("Can not create device handle\n");
+ goto free_hw;
+ }
+ cmd->dev_handle = ctx->dev_hdl;
+
+ /* store session information */
+ ctx->session_hdl = cmd->session_handle;
+
+ pr_err("dev_handle = %x\n", cmd->dev_handle);
+ return rc;
+
+free_hw:
+ release.ctxt_to_hw_map = ctx->ctxt_to_hw_map;
+ ctx->hw_mgr_intf->hw_release(ctx->hw_mgr_intf->hw_mgr_priv, &release);
+ ctx->ctxt_to_hw_map = NULL;
+end:
+ return rc;
+}
+
+int32_t cam_context_start_dev_to_hw(struct cam_context *ctx,
+ struct cam_start_stop_dev_cmd *cmd)
+{
+ int rc = 0;
+ struct cam_hw_start_args arg;
+
+ if (!ctx->hw_mgr_intf) {
+ pr_err("HW interface is not ready\n");
+ rc = -EFAULT;
+ goto end;
+ }
+
+ if ((cmd->session_handle != ctx->session_hdl) ||
+ (cmd->dev_handle != ctx->dev_hdl)) {
+ pr_err("Invalid session hdl[%d], dev_handle[%d]\n",
+ cmd->session_handle, cmd->dev_handle);
+ rc = -EPERM;
+ goto end;
+ }
+
+ if (ctx->hw_mgr_intf->hw_start) {
+ rc = ctx->hw_mgr_intf->hw_start(ctx->hw_mgr_intf->hw_mgr_priv,
+ &arg);
+ if (rc) {
+ /* HW failure. user need to clean up the resource */
+ pr_err("Start HW failed\n");
+ goto end;
+ }
+ }
+
+ pr_debug("start device success\n");
+end:
+ return rc;
+}
+
+int32_t cam_context_stop_dev_to_hw(struct cam_context *ctx)
+{
+ int rc = 0;
+ uint32_t i;
+ struct cam_hw_stop_args stop;
+ struct cam_ctx_request *req;
+
+ if (!ctx->hw_mgr_intf) {
+ pr_err("HW interface is not ready\n");
+ rc = -EFAULT;
+ goto end;
+ }
+
+ /* stop hw first */
+ if (ctx->ctxt_to_hw_map) {
+ stop.ctxt_to_hw_map = ctx->ctxt_to_hw_map;
+ if (ctx->hw_mgr_intf->hw_stop)
+ ctx->hw_mgr_intf->hw_stop(ctx->hw_mgr_intf->hw_mgr_priv,
+ &stop);
+ }
+
+ /* flush pending and active queue */
+ while (!list_empty(&ctx->pending_req_list)) {
+ req = list_first_entry(&ctx->pending_req_list,
+ struct cam_ctx_request, list);
+ list_del_init(&req->list);
+ pr_debug("signal fence in pending list. fence num %d\n",
+ req->num_out_map_entries);
+ for (i = 0; i < req->num_out_map_entries; i++)
+ if (req->out_map_entries[i].sync_id != -1)
+ cam_sync_signal(req->out_map_entries[i].sync_id,
+ CAM_SYNC_STATE_SIGNALED_ERROR);
+ list_add_tail(&req->list, &ctx->free_req_list);
+ }
+
+ while (!list_empty(&ctx->active_req_list)) {
+ req = list_first_entry(&ctx->active_req_list,
+ struct cam_ctx_request, list);
+ list_del_init(&req->list);
+ pr_debug("signal fence in active list. fence num %d\n",
+ req->num_out_map_entries);
+ for (i = 0; i < req->num_out_map_entries; i++)
+ if (req->out_map_entries[i].sync_id != -1)
+ cam_sync_signal(req->out_map_entries[i].sync_id,
+ CAM_SYNC_STATE_SIGNALED_ERROR);
+ list_add_tail(&req->list, &ctx->free_req_list);
+ }
+
+end:
+ return rc;
+}
+
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context_utils.h b/drivers/media/platform/msm/camera/cam_core/cam_context_utils.h
new file mode 100644
index 0000000..f7982eb
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context_utils.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_CONTEXT_UTILS_H_
+#define _CAM_CONTEXT_UTILS_H_
+
+#include <linux/types.h>
+
+int cam_context_buf_done_from_hw(struct cam_context *ctx,
+ void *done_event_data, uint32_t bubble_state);
+int cam_context_apply_req_to_hw(struct cam_context *ctx,
+ struct cam_req_mgr_apply_request *apply);
+int32_t cam_context_release_dev_to_hw(struct cam_context *ctx,
+ struct cam_release_dev_cmd *cmd);
+int32_t cam_context_prepare_dev_to_hw(struct cam_context *ctx,
+ struct cam_config_dev_cmd *cmd);
+int32_t cam_context_acquire_dev_to_hw(struct cam_context *ctx,
+ struct cam_acquire_dev_cmd *cmd);
+int32_t cam_context_start_dev_to_hw(struct cam_context *ctx,
+ struct cam_start_stop_dev_cmd *cmd);
+int32_t cam_context_stop_dev_to_hw(struct cam_context *ctx);
+
+#endif /* _CAM_CONTEXT_UTILS_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_core_defs.h b/drivers/media/platform/msm/camera/cam_core/cam_core_defs.h
new file mode 100644
index 0000000..3498836
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_core/cam_core_defs.h
@@ -0,0 +1,44 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _CAM_REQ_MGR_CORE_DEFS_H_
+#define _CAM_REQ_MGR_CORE_DEFS_H_
+
+#define CAM_CORE_TRACE_ENABLE 0
+
+#if (CAM_CORE_TRACE_ENABLE == 1)
+ #define CAM_CORE_DBG(fmt, args...) do { \
+ trace_printk("%d: [cam_core_dbg] "fmt"\n", __LINE__, ##args); \
+ pr_debug("%s:%d "fmt"\n", __func__, __LINE__, ##args); \
+ } while (0)
+
+ #define CAM_CORE_WARN(fmt, args...) do { \
+ trace_printk("%d: [cam_core_warn] "fmt"\n", __LINE__, ##args); \
+ pr_warn("%s:%d "fmt"\n", __func__, __LINE__, ##args); \
+ } while (0)
+
+ #define CAM_CORE_ERR(fmt, args...) do { \
+ trace_printk("%d: [cam_core_err] "fmt"\n", __LINE__, ##args); \
+ pr_err("%s:%d "fmt"\n", __func__, __LINE__, ##args);\
+ } while (0)
+#else
+ #define CAM_CORE_DBG(fmt, args...) pr_debug("%s:%d "fmt"\n", \
+ __func__, __LINE__, ##args)
+
+ #define CAM_CORE_WARN(fmt, args...) pr_warn("%s:%d "fmt"\n", \
+ __func__, __LINE__, ##args)
+
+ #define CAM_CORE_ERR(fmt, args...) pr_err("%s:%d "fmt"\n", \
+ __func__, __LINE__, ##args)
+#endif
+
+#endif /* _CAM_REQ_MGR_CORE_DEFS_H_ */
+
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_hw_mgr_intf.h b/drivers/media/platform/msm/camera/cam_core/cam_hw_mgr_intf.h
index db605e7..f72a1d7 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_hw_mgr_intf.h
+++ b/drivers/media/platform/msm/camera/cam_core/cam_hw_mgr_intf.h
@@ -36,6 +36,7 @@
* @offset: Memory offset
* @len: Size of the configuration
* @flags: Flags for the config entry(eg. DMI)
+ * @addr: Address of hardware update entry
*
*/
struct cam_hw_update_entry {
@@ -43,6 +44,7 @@
uint32_t offset;
uint32_t len;
uint32_t flags;
+ uint64_t addr;
};
/**
@@ -137,6 +139,7 @@
* @max_in_map_entries: Maximum input fence mapping supported
* @in_map_entries: Actual input fence mapping list (returned)
* @num_in_map_entries: Number of acutal input fence mapping (returned)
+ * @priv: Private pointer of hw update
*
*/
struct cam_hw_prepare_update_args {
@@ -151,6 +154,7 @@
uint32_t max_in_map_entries;
struct cam_hw_fence_map_entry *in_map_entries;
uint32_t num_in_map_entries;
+ void *priv;
};
/**
@@ -159,12 +163,18 @@
* @ctxt_to_hw_map: HW context from the acquire
* @num_hw_update_entries: Number of hardware update entries
* @hw_update_entries: Hardware update list
+ * @out_map_entries: Out map info
+ * @num_out_map_entries: Number of out map entries
+ * @priv: Private pointer
*
*/
struct cam_hw_config_args {
- void *ctxt_to_hw_map;
- uint32_t num_hw_update_entries;
- struct cam_hw_update_entry *hw_update_entries;
+ void *ctxt_to_hw_map;
+ uint32_t num_hw_update_entries;
+ struct cam_hw_update_entry *hw_update_entries;
+ struct cam_hw_fence_map_entry *out_map_entries;
+ uint32_t num_out_map_entries;
+ void *priv;
};
/**
@@ -189,6 +199,8 @@
* @hw_write: Function pointer for Write hardware registers
* @hw_cmd: Function pointer for any customized commands for the
* hardware manager
+ * @download_fw: Function pointer for firmware downloading
+ * @hw_close: Function pointer for subdev close
*
*/
struct cam_hw_mgr_intf {
@@ -204,6 +216,8 @@
int (*hw_read)(void *hw_priv, void *read_args);
int (*hw_write)(void *hw_priv, void *write_args);
int (*hw_cmd)(void *hw_priv, void *write_args);
+ int (*download_fw)(void *hw_priv, void *fw_download_args);
+ int (*hw_close)(void *hw_priv, void *hw_close_args);
};
#endif /* _CAM_HW_MGR_INTF_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_node.c b/drivers/media/platform/msm/camera/cam_core/cam_node.c
index ef60822..17f6973 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_node.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_node.c
@@ -21,13 +21,16 @@
{
int rc = -EFAULT;
- if (!query)
+ if (!query) {
+ pr_err("%s: Invalid params\n", __func__);
return -EINVAL;
+ }
if (node->hw_mgr_intf.hw_get_caps) {
rc = node->hw_mgr_intf.hw_get_caps(
node->hw_mgr_intf.hw_mgr_priv, query);
}
+
return rc;
}
@@ -47,7 +50,6 @@
list_del_init(&ctx->list);
}
mutex_unlock(&node->list_mutex);
-
if (!ctx) {
rc = -ENOMEM;
goto err;
@@ -254,8 +256,8 @@
memset(node, 0, sizeof(*node));
pr_debug("%s: deinit complete!\n", __func__);
- return 0;
+ return 0;
}
int cam_node_init(struct cam_node *node, struct cam_hw_mgr_intf *hw_mgr_intf,
@@ -274,7 +276,6 @@
strlcpy(node->name, name, sizeof(node->name));
memcpy(&node->hw_mgr_intf, hw_mgr_intf, sizeof(node->hw_mgr_intf));
-
node->crm_node_intf.apply_req = __cam_node_apply_req;
node->crm_node_intf.get_dev_info = __cam_node_get_dev_info;
node->crm_node_intf.link_setup = __cam_node_link_setup;
@@ -318,15 +319,18 @@
rc = -EFAULT;
break;
}
+
rc = __cam_node_handle_query_cap(node, &query);
if (rc) {
pr_err("%s: querycap is failed(rc = %d)\n",
__func__, rc);
break;
}
+
if (copy_to_user((void __user *)cmd->handle, &query,
sizeof(query)))
rc = -EFAULT;
+
break;
}
case CAM_ACQUIRE_DEV: {
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_subdev.c b/drivers/media/platform/msm/camera/cam_core/cam_subdev.c
index 429474b..a89981d 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_subdev.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_subdev.c
@@ -148,6 +148,7 @@
sd->sd_flags =
V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
sd->ent_function = dev_type;
+
rc = cam_register_subdev(sd);
if (rc) {
pr_err("%s: cam_register_subdev() failed for dev: %s!\n",
diff --git a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
index c304eed..16bf555 100644
--- a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
+++ b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
@@ -652,7 +652,7 @@
ctx->session_hdl = 0;
ctx->dev_hdl = 0;
ctx->link_hdl = 0;
- ctx->crm_ctx_intf = NULL;
+ ctx->ctx_crm_intf = NULL;
ctx_isp->frame_id = 0;
/*
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
index 9c61649..6819fd8 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
@@ -2220,7 +2220,7 @@
cam_ife_hw_mgr_do_error_recovery(&recovery_data);
break;
default:
- CDBG("%s: None error. Error type (%d)", __func__,
+ CDBG("%s: None error. Error type (%d)\n", __func__,
evt_payload->error_type);
}
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
index 21494b5..8dbefd8 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
@@ -983,13 +983,10 @@
in_rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5)
list_add_tail(&comp_grp->list,
&ver2_bus_priv->free_dual_comp_grp);
- else if (in_rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0
- && in_rsrc_data->comp_grp_type <=
- CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5)
+ else if (in_rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_0
+ && in_rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_5)
list_add_tail(&comp_grp->list, &ver2_bus_priv->free_comp_grp);
- list_add_tail(&comp_grp->list,
- &ver2_bus_priv->free_comp_grp);
in_rsrc_data->unique_id = 0;
in_rsrc_data->comp_grp_local_idx = 0;
in_rsrc_data->composite_mask = 0;
@@ -997,7 +994,7 @@
comp_grp->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE;
- return -ENODEV;
+ return 0;
}
static int cam_vfe_bus_start_comp_grp(struct cam_isp_resource_node *comp_grp)
@@ -1333,11 +1330,26 @@
static int cam_vfe_bus_release_vfe_out(void *bus_priv,
struct cam_isp_resource_node *vfe_out)
{
+ uint32_t i;
+ struct cam_vfe_bus_ver2_vfe_out_data *rsrc_data = vfe_out->res_priv;
+
if (vfe_out->res_state != CAM_ISP_RESOURCE_STATE_RESERVED) {
pr_err("Error! Invalid resource state:%d\n",
vfe_out->res_state);
}
+ for (i = 0; i < rsrc_data->num_wm; i++)
+ cam_vfe_bus_release_wm(bus_priv, rsrc_data->wm_res[i]);
+ rsrc_data->num_wm = 0;
+
+ if (rsrc_data->comp_grp)
+ cam_vfe_bus_release_comp_grp(bus_priv, rsrc_data->comp_grp);
+ rsrc_data->comp_grp = NULL;
+
+ vfe_out->tasklet_info = NULL;
+ vfe_out->cdm_ops = NULL;
+ rsrc_data->cdm_util_ops = NULL;
+
if (vfe_out->res_state == CAM_ISP_RESOURCE_STATE_RESERVED)
vfe_out->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE;
@@ -1360,7 +1372,7 @@
}
/* Enable IRQ Mask */
- cam_io_w_mb(0x00001F70, common_data->mem_base + 0x2044);
+ cam_io_w_mb(0x00001FE0, common_data->mem_base + 0x2044);
cam_io_w_mb(0x000FFFE7, common_data->mem_base + 0x2048);
cam_io_w_mb(0x000000FF, common_data->mem_base + 0x204c);
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_util.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_util.c
index 019a775..7bc26ec 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_util.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_util.c
@@ -196,6 +196,7 @@
hdl_tbl->hdl[idx].ops = hdl_data->ops;
spin_unlock_bh(&hdl_tbl_lock);
+ pr_debug("%s: handle = %x\n", __func__, handle);
return handle;
}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/Makefile b/drivers/media/platform/msm/camera/cam_sensor_module/Makefile
new file mode 100644
index 0000000..e515a40
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_utils/
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_cci/
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_io/
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_csiphy/
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_actuator/
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor/
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/Makefile b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/Makefile
new file mode 100644
index 0000000..8670d80
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/Makefile
@@ -0,0 +1,8 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_cci
+
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_actuator_dev.o cam_actuator_core.o cam_actuator_soc.o
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
new file mode 100644
index 0000000..648617e
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
@@ -0,0 +1,628 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <cam_sensor_cmn_header.h>
+#include "cam_actuator_core.h"
+#include <cam_sensor_util.h>
+
+int32_t cam_actuator_slaveInfo_pkt_parser(struct cam_actuator_ctrl_t *a_ctrl,
+ uint32_t *cmd_buf)
+{
+ int32_t rc = 0;
+ struct cam_cmd_i2c_info *i2c_info;
+
+ if (!a_ctrl || !cmd_buf) {
+ pr_err("%s:%d Invalid Args\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ i2c_info = (struct cam_cmd_i2c_info *)cmd_buf;
+ a_ctrl->io_master_info.cci_client->i2c_freq_mode =
+ i2c_info->i2c_freq_mode;
+ a_ctrl->io_master_info.cci_client->sid =
+ i2c_info->slave_addr >> 1;
+ CDBG("%s:%d Slave addr: 0x%x Freq Mode: %d\n", __func__,
+ __LINE__, i2c_info->slave_addr, i2c_info->i2c_freq_mode);
+
+ return rc;
+}
+
+int32_t cam_actuator_apply_settings(struct cam_actuator_ctrl_t *a_ctrl,
+ struct i2c_settings_array *i2c_set)
+{
+ struct i2c_settings_list *i2c_list;
+ int32_t rc = 0;
+ uint32_t i, size;
+
+ if (a_ctrl == NULL || i2c_set == NULL) {
+ pr_err("%s:%d Invalid Args\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if (i2c_set->is_settings_valid != 1) {
+ pr_err("%s: %d :Error: Invalid settings\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ list_for_each_entry(i2c_list,
+ &(i2c_set->list_head), list) {
+ if (i2c_list->op_code == CAM_SENSOR_I2C_WRITE_RANDOM) {
+ rc = camera_io_dev_write(&(a_ctrl->io_master_info),
+ &(i2c_list->i2c_settings));
+ if (rc < 0) {
+ pr_err("%s: %d :Error: Failed in Applying i2c write settings\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ } else if (i2c_list->op_code == CAM_SENSOR_I2C_POLL) {
+ size = i2c_list->i2c_settings.size;
+ for (i = 0; i < size; i++) {
+ rc = camera_io_dev_poll(
+ &(a_ctrl->io_master_info),
+ i2c_list->i2c_settings.
+ reg_setting[i].reg_addr,
+ i2c_list->i2c_settings.
+ reg_setting[i].reg_data,
+ i2c_list->i2c_settings.
+ reg_setting[i].data_mask,
+ i2c_list->i2c_settings.addr_type,
+ i2c_list->i2c_settings.data_type,
+ i2c_list->i2c_settings.
+ reg_setting[i].delay);
+ if (rc < 0) {
+ pr_err("%s: %d :Error: Failed in Applying i2c poll settings\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+int32_t cam_actuator_apply_request(struct cam_req_mgr_apply_request *apply)
+{
+ int32_t rc = 0, request_id, del_req_id;
+ struct cam_actuator_ctrl_t *a_ctrl = NULL;
+
+ if (!apply) {
+ pr_err("%s:%d :Error: Invalid Input Args\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ a_ctrl = (struct cam_actuator_ctrl_t *)
+ cam_get_device_priv(apply->dev_hdl);
+ if (!a_ctrl) {
+ pr_err("%s: %d :Error: Device data is NULL\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ request_id = apply->request_id % MAX_PER_FRAME_ARRAY;
+ CDBG("%s:%d Request Id: %lld\n",
+ __func__, __LINE__, apply->request_id);
+
+ if ((apply->request_id ==
+ a_ctrl->i2c_data.per_frame[request_id].request_id) &&
+ (a_ctrl->i2c_data.per_frame[request_id].is_settings_valid)
+ == 1) {
+ rc = cam_actuator_apply_settings(a_ctrl,
+ &a_ctrl->i2c_data.per_frame[request_id]);
+ if (rc < 0) {
+ pr_err("%s:%d Failed in applying the request: %lld\n",
+ __func__, __LINE__, apply->request_id);
+ return rc;
+ }
+ }
+ del_req_id = (request_id +
+ MAX_PER_FRAME_ARRAY - MAX_SYSTEM_PIPELINE_DELAY) %
+ MAX_PER_FRAME_ARRAY;
+
+ if (apply->request_id >
+ a_ctrl->i2c_data.per_frame[del_req_id].request_id) {
+ a_ctrl->i2c_data.per_frame[del_req_id].request_id = 0;
+ rc = delete_request(&a_ctrl->i2c_data.per_frame[del_req_id]);
+ if (rc < 0) {
+ pr_err("%s: %d :Error: Fail deleting the req: %d err: %d\n",
+ __func__, __LINE__, del_req_id, rc);
+ return rc;
+ }
+ } else {
+ CDBG("%s:%d No Valid Req to clean Up\n", __func__, __LINE__);
+ }
+
+ return rc;
+}
+
+int32_t cam_actuator_establish_link(
+ struct cam_req_mgr_core_dev_link_setup *link)
+{
+ struct cam_actuator_ctrl_t *a_ctrl = NULL;
+
+ if (!link) {
+ pr_err("%s:%d Invalid Args\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ a_ctrl = (struct cam_actuator_ctrl_t *)
+ cam_get_device_priv(link->dev_hdl);
+ if (!a_ctrl) {
+ pr_err("%s:%d :Error: Device data is NULL\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ if (link->link_enable) {
+ a_ctrl->bridge_intf.link_hdl = link->link_hdl;
+ a_ctrl->bridge_intf.crm_cb = link->crm_cb;
+ } else {
+ a_ctrl->bridge_intf.link_hdl = -1;
+ a_ctrl->bridge_intf.crm_cb = NULL;
+ }
+
+ return 0;
+}
+
+int32_t cam_actuator_publish_dev_info(struct cam_req_mgr_device_info *info)
+{
+ if (!info) {
+ pr_err("%s:%d Invalid Args\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ info->dev_id = CAM_REQ_MGR_DEVICE_ACTUATOR;
+ strlcpy(info->name, CAM_ACTUATOR_NAME, sizeof(info->name));
+ info->p_delay = 0;
+
+ return 0;
+}
+
+int32_t cam_actuator_i2c_pkt_parse(struct cam_actuator_ctrl_t *a_ctrl,
+ void *arg)
+{
+ int32_t rc = 0;
+ uint64_t generic_ptr;
+ struct cam_control *ioctl_ctrl = NULL;
+ struct cam_packet *csl_packet = NULL;
+ struct cam_config_dev_cmd config;
+ struct i2c_data_settings *i2c_data = NULL;
+ struct i2c_settings_array *i2c_reg_settings = NULL;
+ struct cam_cmd_buf_desc *cmd_desc = NULL;
+ size_t len_of_buff = 0;
+ uint32_t *offset = NULL, *cmd_buf;
+ struct cam_req_mgr_add_request add_req;
+
+ if (!a_ctrl || !arg) {
+ pr_err("%s:%d :Error: Invalid Args\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ ioctl_ctrl = (struct cam_control *)arg;
+ if (copy_from_user(&config, (void __user *) ioctl_ctrl->handle,
+ sizeof(config)))
+ return -EFAULT;
+ rc = cam_mem_get_cpu_buf(config.packet_handle,
+ (uint64_t *)&generic_ptr, &len_of_buff);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: error in converting command Handle %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ if (config.offset > len_of_buff) {
+ pr_err("%s: %d offset is out of bounds: offset: %lld len: %zu\n",
+ __func__, __LINE__, config.offset, len_of_buff);
+ return -EINVAL;
+ }
+
+ csl_packet = (struct cam_packet *)(generic_ptr +
+ config.offset);
+ CDBG("%s:%d Pkt opcode: %d\n",
+ __func__, __LINE__, csl_packet->header.op_code);
+
+ if ((csl_packet->header.op_code & 0xFFFFFF) ==
+ CAM_ACTUATOR_PACKET_OPCODE_INIT) {
+ i2c_data = &(a_ctrl->i2c_data);
+ i2c_reg_settings = &i2c_data->init_settings;
+
+ offset = (uint32_t *)&csl_packet->payload;
+ offset += (csl_packet->cmd_buf_offset / sizeof(uint32_t));
+ cmd_desc = (struct cam_cmd_buf_desc *)(offset);
+
+ if (csl_packet->num_cmd_buf != 2) {
+ pr_err("%s:: %d :Error: cmd Buffers in Init : %d\n",
+ __func__, __LINE__, csl_packet->num_cmd_buf);
+ return -EINVAL;
+ }
+
+ rc = cam_mem_get_cpu_buf(cmd_desc[0].mem_handle,
+ (uint64_t *)&generic_ptr, &len_of_buff);
+ if (rc < 0) {
+ pr_err("%s:%d Failed to get cpu buf\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ cmd_buf = (uint32_t *)generic_ptr;
+ cmd_buf += cmd_desc->offset / sizeof(uint32_t);
+ rc = cam_actuator_slaveInfo_pkt_parser(a_ctrl, cmd_buf);
+ if (rc < 0) {
+ pr_err("%s:%d Failed in parsing the pkt\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ cmd_buf += (sizeof(struct cam_cmd_i2c_info)/sizeof(uint32_t));
+ i2c_data->init_settings.request_id = 0;
+ i2c_reg_settings->is_settings_valid = 1;
+ rc = cam_sensor_i2c_pkt_parser(i2c_reg_settings,
+ &cmd_desc[1], 1);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: actuator pkt parsing failed: %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+ } else if ((csl_packet->header.op_code & 0xFFFFFF) ==
+ CAM_ACTUATOR_PACKET_AUTO_MOVE_LENS) {
+ a_ctrl->act_apply_state =
+ ACT_APPLY_SETTINGS_NOW;
+
+ i2c_data = &(a_ctrl->i2c_data);
+ i2c_reg_settings = &i2c_data->init_settings;
+
+ i2c_data->init_settings.request_id =
+ csl_packet->header.request_id;
+ i2c_reg_settings->is_settings_valid = 1;
+ offset = (uint32_t *)&csl_packet->payload;
+ offset += csl_packet->cmd_buf_offset / sizeof(uint32_t);
+ cmd_desc = (struct cam_cmd_buf_desc *)(offset);
+ rc = cam_sensor_i2c_pkt_parser(i2c_reg_settings,
+ cmd_desc, 1);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: actuator pkt parsing failed: %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+ } else if ((csl_packet->header.op_code & 0xFFFFFF) ==
+ CAM_ACTUATOR_PACKET_MANUAL_MOVE_LENS) {
+ i2c_data = &(a_ctrl->i2c_data);
+ i2c_reg_settings =
+ &i2c_data->per_frame
+ [csl_packet->header.request_id % MAX_PER_FRAME_ARRAY];
+
+ i2c_data->init_settings.request_id =
+ csl_packet->header.request_id;
+ i2c_reg_settings->is_settings_valid = 1;
+ offset = (uint32_t *)&csl_packet->payload;
+ offset += csl_packet->cmd_buf_offset / sizeof(uint32_t);
+ cmd_desc = (struct cam_cmd_buf_desc *)(offset);
+ rc = cam_sensor_i2c_pkt_parser(i2c_reg_settings,
+ cmd_desc, 1);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: actuator pkt parsing failed: %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+ }
+
+ if ((csl_packet->header.op_code & 0xFFFFFF) !=
+ CAM_ACTUATOR_PACKET_OPCODE_INIT) {
+ add_req.link_hdl = a_ctrl->bridge_intf.link_hdl;
+ add_req.req_id = csl_packet->header.request_id;
+ add_req.dev_hdl = a_ctrl->bridge_intf.device_hdl;
+ if (a_ctrl->bridge_intf.crm_cb &&
+ a_ctrl->bridge_intf.crm_cb->add_req)
+ a_ctrl->bridge_intf.crm_cb->add_req(&add_req);
+ CDBG("%s: %d Req Id: %lld added to Bridge\n",
+ __func__, __LINE__, add_req.req_id);
+ }
+
+ return rc;
+}
+
+static int32_t cam_actuator_vreg_control(struct cam_actuator_ctrl_t *a_ctrl,
+ int config)
+{
+ int rc = 0, i, cnt;
+ struct cam_actuator_vreg *vreg_cfg;
+
+ vreg_cfg = &a_ctrl->vreg_cfg;
+ cnt = vreg_cfg->num_vreg;
+ if (!cnt)
+ return 0;
+
+ if (cnt >= MSM_ACTUATOR_MAX_VREGS) {
+ pr_err("%s:%d Regulators more than supported %d\n",
+ __func__, __LINE__, cnt);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cnt; i++) {
+ if (a_ctrl->io_master_info.master_type ==
+ CCI_MASTER) {
+ rc = msm_camera_config_single_vreg(
+ &(a_ctrl->v4l2_dev_str.pdev->dev),
+ &vreg_cfg->cam_vreg[i],
+ (struct regulator **)&vreg_cfg->data[i],
+ config);
+ } else if (a_ctrl->io_master_info.master_type ==
+ I2C_MASTER) {
+ rc = msm_camera_config_single_vreg(
+ &(a_ctrl->io_master_info.client->dev),
+ &vreg_cfg->cam_vreg[i],
+ (struct regulator **)&vreg_cfg->data[i],
+ config);
+ }
+ }
+
+ return rc;
+}
+
+static int32_t cam_actuator_power_up(struct cam_actuator_ctrl_t *a_ctrl)
+{
+ int rc = 0;
+
+ rc = cam_actuator_vreg_control(a_ctrl, 1);
+ if (rc < 0) {
+ pr_err("%s:%d Actuator Reg Failed %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ if (a_ctrl->gconf &&
+ a_ctrl->gconf->gpio_num_info &&
+ a_ctrl->gconf->gpio_num_info->valid[SENSOR_VAF] == 1) {
+ rc = msm_camera_request_gpio_table(
+ a_ctrl->gconf->cam_gpio_req_tbl,
+ a_ctrl->gconf->cam_gpio_req_tbl_size, 1);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: Failed in req gpio: %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+ if (a_ctrl->cam_pinctrl_status) {
+ rc = pinctrl_select_state(
+ a_ctrl->pinctrl_info.pinctrl,
+ a_ctrl->pinctrl_info.gpio_state_active);
+ if (rc < 0)
+ pr_err("%s:%d :Error: cannot set pin to active state: %d",
+ __func__, __LINE__, rc);
+ }
+
+ gpio_set_value_cansleep(
+ a_ctrl->gconf->gpio_num_info->gpio_num[SENSOR_VAF],
+ 1);
+ }
+
+ /* VREG needs some delay to power up */
+ usleep_range(2000, 2050);
+
+ return rc;
+}
+
+static int32_t cam_actuator_power_down(struct cam_actuator_ctrl_t *a_ctrl)
+{
+ int32_t rc = 0;
+
+ rc = cam_actuator_vreg_control(a_ctrl, 0);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return rc;
+ }
+
+ if (a_ctrl->gconf &&
+ a_ctrl->gconf->gpio_num_info &&
+ a_ctrl->gconf->gpio_num_info->
+ valid[SENSOR_VAF] == 1) {
+
+ gpio_set_value_cansleep(
+ a_ctrl->gconf->gpio_num_info->
+ gpio_num[SENSOR_VAF],
+ GPIOF_OUT_INIT_LOW);
+
+ if (a_ctrl->cam_pinctrl_status) {
+ rc = pinctrl_select_state(
+ a_ctrl->pinctrl_info.pinctrl,
+ a_ctrl->pinctrl_info.
+ gpio_state_suspend);
+ if (rc < 0)
+ pr_err("%s:%d cannot set pin to suspend state: %d",
+ __func__, __LINE__, rc);
+
+ devm_pinctrl_put(
+ a_ctrl->pinctrl_info.pinctrl);
+ }
+ a_ctrl->cam_pinctrl_status = 0;
+ rc = msm_camera_request_gpio_table(
+ a_ctrl->gconf->cam_gpio_req_tbl,
+ a_ctrl->gconf->cam_gpio_req_tbl_size,
+ 0);
+ if (rc < 0)
+ pr_err("%s:%d Failed in selecting state: %d\n",
+ __func__, __LINE__, rc);
+ }
+
+ return rc;
+}
+
+int32_t cam_actuator_driver_cmd(struct cam_actuator_ctrl_t *a_ctrl,
+ void *arg)
+{
+ int rc = 0;
+ struct cam_control *cmd = (struct cam_control *)arg;
+
+ if (!a_ctrl || !cmd) {
+ pr_err("%s: %d :Error: Invalid Args\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s:%d Opcode to Actuator: %d\n",
+ __func__, __LINE__, cmd->op_code);
+
+ mutex_lock(&(a_ctrl->actuator_mutex));
+ switch (cmd->op_code) {
+ case CAM_ACQUIRE_DEV: {
+ struct cam_sensor_acquire_dev actuator_acq_dev;
+ struct cam_create_dev_hdl bridge_params;
+
+ if (a_ctrl->bridge_intf.device_hdl != -1) {
+ pr_err("%s:%d Device is already acquired\n",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ goto release_mutex;
+ }
+ rc = copy_from_user(&actuator_acq_dev,
+ (void __user *) cmd->handle,
+ sizeof(actuator_acq_dev));
+ if (rc < 0) {
+ pr_err("%s:%d :Error: Failed Copying from user\n",
+ __func__, __LINE__);
+ goto release_mutex;
+ }
+
+ bridge_params.session_hdl = actuator_acq_dev.session_handle;
+ bridge_params.ops = &a_ctrl->bridge_intf.ops;
+ bridge_params.v4l2_sub_dev_flag = 0;
+ bridge_params.media_entity_flag = 0;
+ bridge_params.priv = a_ctrl;
+
+ actuator_acq_dev.device_handle =
+ cam_create_device_hdl(&bridge_params);
+ a_ctrl->bridge_intf.device_hdl = actuator_acq_dev.device_handle;
+ a_ctrl->bridge_intf.session_hdl =
+ actuator_acq_dev.session_handle;
+
+ CDBG("%s:%d Device Handle: %d\n",
+ __func__, __LINE__, actuator_acq_dev.device_handle);
+ if (copy_to_user((void __user *) cmd->handle, &actuator_acq_dev,
+ sizeof(struct cam_sensor_acquire_dev))) {
+ pr_err("%s:%d :Error: Failed Copy to User\n",
+ __func__, __LINE__);
+ rc = -EFAULT;
+ goto release_mutex;
+ }
+
+ }
+ break;
+ case CAM_RELEASE_DEV: {
+ if (a_ctrl->bridge_intf.device_hdl == -1) {
+ pr_err("%s:%d :Error: link hdl: %d device hdl: %d\n",
+ __func__, __LINE__,
+ a_ctrl->bridge_intf.device_hdl,
+ a_ctrl->bridge_intf.link_hdl);
+ rc = -EINVAL;
+ goto release_mutex;
+ }
+ rc = cam_destroy_device_hdl(a_ctrl->bridge_intf.device_hdl);
+ if (rc < 0)
+ pr_err("%s:%d :Error: destroying the device hdl\n",
+ __func__, __LINE__);
+ a_ctrl->bridge_intf.device_hdl = -1;
+ a_ctrl->bridge_intf.link_hdl = -1;
+ a_ctrl->bridge_intf.session_hdl = -1;
+ }
+ break;
+ case CAM_QUERY_CAP: {
+ struct cam_actuator_query_cap actuator_cap;
+
+ actuator_cap.slot_info = a_ctrl->id;
+ if (copy_to_user((void __user *) cmd->handle, &actuator_cap,
+ sizeof(struct cam_actuator_query_cap))) {
+ pr_err("%s:%d :Error: Failed Copy to User\n",
+ __func__, __LINE__);
+ rc = -EFAULT;
+ goto release_mutex;
+ }
+ }
+ break;
+ case CAM_START_DEV: {
+ rc = cam_actuator_power_up(a_ctrl);
+ if (rc < 0) {
+ pr_err("%s: %d :Error: Actuator Power up failed\n",
+ __func__, __LINE__);
+ goto release_mutex;
+ }
+ rc = camera_io_init(&a_ctrl->io_master_info);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: cci_init failed\n",
+ __func__, __LINE__);
+ cam_actuator_power_down(a_ctrl);
+ }
+
+ rc = cam_actuator_apply_settings(a_ctrl,
+ &a_ctrl->i2c_data.init_settings);
+ if (rc < 0)
+ pr_err("%s: %d :Error: Cannot apply Init settings\n",
+ __func__, __LINE__);
+
+ /* Delete the request even if the apply is failed */
+ rc = delete_request(&a_ctrl->i2c_data.init_settings);
+ if (rc < 0) {
+ pr_err("%s:%d Fail in deleting the Init settings\n",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ goto release_mutex;
+ }
+ }
+ break;
+ case CAM_STOP_DEV: {
+ rc = camera_io_release(&a_ctrl->io_master_info);
+ if (rc < 0)
+ pr_err("%s:%d :Error: Failed in releasing CCI\n",
+ __func__, __LINE__);
+ rc = cam_actuator_power_down(a_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: Actuator Power down failed\n",
+ __func__, __LINE__);
+ goto release_mutex;
+ }
+ }
+ break;
+ case CAM_CONFIG_DEV: {
+ a_ctrl->act_apply_state =
+ ACT_APPLY_SETTINGS_LATER;
+ rc = cam_actuator_i2c_pkt_parse(a_ctrl, arg);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: Failed in actuator Parsing\n",
+ __func__, __LINE__);
+ }
+
+ if (a_ctrl->act_apply_state ==
+ ACT_APPLY_SETTINGS_NOW) {
+ rc = cam_actuator_apply_settings(a_ctrl,
+ &a_ctrl->i2c_data.init_settings);
+ if (rc < 0)
+ pr_err("%s:%d :Error: Cannot apply Update settings\n",
+ __func__, __LINE__);
+
+ /* Delete the request even if the apply is failed */
+ rc = delete_request(&a_ctrl->i2c_data.init_settings);
+ if (rc < 0) {
+ pr_err("%s: %d :Error: Failed in Deleting the Init Pkt: %d\n",
+ __func__, __LINE__, rc);
+ goto release_mutex;
+ }
+ }
+ }
+ break;
+ default:
+ pr_err("%s:%d Invalid Opcode %d\n",
+ __func__, __LINE__, cmd->op_code);
+ }
+
+release_mutex:
+ mutex_unlock(&(a_ctrl->actuator_mutex));
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.h
new file mode 100644
index 0000000..d2cb96d
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_ACTUATOR_CORE_H_
+#define _CAM_ACTUATOR_CORE_H_
+
+#include "cam_actuator_dev.h"
+
+/**
+ * @apply: Req mgr structure for applying request
+ *
+ * This API applies the request that is mentioned
+ */
+int32_t cam_actuator_apply_request(struct cam_req_mgr_apply_request *apply);
+
+/**
+ * @info: Sub device info to req mgr
+ *
+ * This API publish the subdevice info to req mgr
+ */
+int32_t cam_actuator_publish_dev_info(struct cam_req_mgr_device_info *info);
+
+/**
+ * @link: Link setup info
+ *
+ * This API establishes link actuator subdevice with req mgr
+ */
+int32_t cam_actuator_establish_link(
+ struct cam_req_mgr_core_dev_link_setup *link);
+
+/**
+ * @a_ctrl: Actuator ctrl structure
+ * @arg: Camera control command argument
+ *
+ * This API handles the camera control argument reached to actuator
+ */
+int32_t cam_actuator_driver_cmd(struct cam_actuator_ctrl_t *a_ctrl, void *arg);
+
+#endif /* _CAM_ACTUATOR_CORE_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.c
new file mode 100644
index 0000000..3835680
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.c
@@ -0,0 +1,334 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "cam_actuator_dev.h"
+#include "cam_req_mgr_dev.h"
+#include "cam_actuator_soc.h"
+#include "cam_actuator_core.h"
+
+static long cam_actuator_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ int rc = 0;
+ struct cam_actuator_ctrl_t *a_ctrl =
+ v4l2_get_subdevdata(sd);
+
+ switch (cmd) {
+ case VIDIOC_CAM_CONTROL:
+ rc = cam_actuator_driver_cmd(a_ctrl, arg);
+ break;
+ default:
+ pr_err("%s:%d Invalid ioctl cmd\n",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static int32_t cam_actuator_driver_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int32_t rc = 0, i = 0;
+ struct cam_actuator_ctrl_t *a_ctrl;
+
+ if (client == NULL || id == NULL) {
+ pr_err("%s:%d: :Error: Invalid Args client: %pK id: %pK\n",
+ __func__, __LINE__, client, id);
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("%s %s :Error: i2c_check_functionality failed\n",
+ __func__, client->name);
+ rc = -EFAULT;
+ return rc;
+ }
+
+ /* Create sensor control structure */
+ a_ctrl = kzalloc(sizeof(*a_ctrl), GFP_KERNEL);
+ if (!a_ctrl)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, a_ctrl);
+
+ a_ctrl->i2c_data.per_frame =
+ (struct i2c_settings_array *)
+ kzalloc(sizeof(struct i2c_settings_array) *
+ MAX_PER_FRAME_ARRAY, GFP_KERNEL);
+ if (a_ctrl->i2c_data.per_frame == NULL) {
+ rc = -ENOMEM;
+ goto free_ctrl;
+ }
+
+ INIT_LIST_HEAD(&(a_ctrl->i2c_data.init_settings.list_head));
+
+ for (i = 0; i < MAX_PER_FRAME_ARRAY; i++)
+ INIT_LIST_HEAD(&(a_ctrl->i2c_data.per_frame[i].list_head));
+
+ /* Initialize sensor device type */
+ a_ctrl->of_node = client->dev.of_node;
+ a_ctrl->io_master_info.master_type = I2C_MASTER;
+
+ rc = cam_actuator_parse_dt(a_ctrl, &client->dev);
+ if (rc < 0) {
+ pr_err("failed: cam_sensor_parse_dt rc %d", rc);
+ goto free_mem;
+ }
+
+ return rc;
+free_mem:
+ kfree(a_ctrl->i2c_data.per_frame);
+free_ctrl:
+ kfree(a_ctrl);
+ return rc;
+}
+
+static int32_t cam_actuator_platform_remove(struct platform_device *pdev)
+{
+ struct cam_actuator_ctrl_t *a_ctrl;
+ int32_t rc = 0;
+
+ a_ctrl = platform_get_drvdata(pdev);
+ if (!a_ctrl) {
+ pr_err("%s: Actuator device is NULL\n", __func__);
+ return 0;
+ }
+
+ kfree(a_ctrl->io_master_info.cci_client);
+ a_ctrl->io_master_info.cci_client = NULL;
+ kfree(a_ctrl->i2c_data.per_frame);
+ a_ctrl->i2c_data.per_frame = NULL;
+ devm_kfree(&pdev->dev, a_ctrl);
+
+ return rc;
+}
+
+static int32_t cam_actuator_driver_i2c_remove(struct i2c_client *client)
+{
+ struct cam_actuator_ctrl_t *a_ctrl = i2c_get_clientdata(client);
+ int32_t rc = 0;
+
+ /* Handle I2C Devices */
+ if (!a_ctrl) {
+ pr_err("%s: Actuator device is NULL\n", __func__);
+ return -EINVAL;
+ }
+ /*Free Allocated Mem */
+ kfree(a_ctrl->i2c_data.per_frame);
+ a_ctrl->i2c_data.per_frame = NULL;
+ kfree(a_ctrl);
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+static long cam_actuator_init_subdev_do_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, unsigned long arg)
+{
+ struct cam_control cmd_data;
+ int32_t rc = 0;
+
+ if (copy_from_user(&cmd_data, (void __user *)arg,
+ sizeof(cmd_data))) {
+ pr_err("Failed to copy from user_ptr=%pK size=%zu\n",
+ (void __user *)arg, sizeof(cmd_data));
+ return -EFAULT;
+ }
+
+ switch (cmd) {
+ case VIDIOC_CAM_CONTROL:
+ cmd = VIDIOC_CAM_CONTROL;
+ rc = cam_actuator_subdev_ioctl(sd, cmd, &cmd_data);
+ if (rc < 0) {
+ pr_err("%s:%d Failed in actuator suddev handling",
+ __func__, __LINE__);
+ return rc;
+ }
+ break;
+ default:
+ pr_err("%s:%d Invalid compat ioctl: %d\n",
+ __func__, __LINE__, cmd);
+ rc = -EINVAL;
+ }
+
+ if (!rc) {
+ if (copy_to_user((void __user *)arg, &cmd_data,
+ sizeof(cmd_data))) {
+ pr_err("Failed to copy to user_ptr=%pK size=%zu\n",
+ (void __user *)arg, sizeof(cmd_data));
+ rc = -EFAULT;
+ }
+ }
+ return rc;
+}
+
+#endif
+
+static struct v4l2_subdev_core_ops cam_actuator_subdev_core_ops = {
+ .ioctl = cam_actuator_subdev_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl32 = cam_actuator_init_subdev_do_ioctl,
+#endif
+};
+
+static struct v4l2_subdev_ops cam_actuator_subdev_ops = {
+ .core = &cam_actuator_subdev_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops cam_actuator_internal_ops;
+
+static const struct of_device_id cam_actuator_driver_dt_match[] = {
+ {.compatible = "qcom,actuator"},
+ {}
+};
+
+static int32_t cam_actuator_driver_platform_probe(
+ struct platform_device *pdev)
+{
+ int32_t rc = 0, i = 0;
+ struct cam_actuator_ctrl_t *a_ctrl = NULL;
+
+ /* Create sensor control structure */
+ a_ctrl = devm_kzalloc(&pdev->dev,
+ sizeof(struct cam_actuator_ctrl_t), GFP_KERNEL);
+ if (!a_ctrl)
+ return -ENOMEM;
+
+ /* Initialize actuator device type */
+ a_ctrl->of_node = pdev->dev.of_node;
+
+ /*fill in platform device*/
+ a_ctrl->v4l2_dev_str.pdev = pdev;
+
+ a_ctrl->io_master_info.master_type = CCI_MASTER;
+
+ a_ctrl->io_master_info.cci_client = kzalloc(sizeof(
+ struct cam_sensor_cci_client), GFP_KERNEL);
+ if (!(a_ctrl->io_master_info.cci_client))
+ return -ENOMEM;
+
+ a_ctrl->i2c_data.per_frame =
+ (struct i2c_settings_array *)
+ kzalloc(sizeof(struct i2c_settings_array) *
+ MAX_PER_FRAME_ARRAY, GFP_KERNEL);
+ if (a_ctrl->i2c_data.per_frame == NULL)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&(a_ctrl->i2c_data.init_settings.list_head));
+
+ for (i = 0; i < MAX_PER_FRAME_ARRAY; i++)
+ INIT_LIST_HEAD(&(a_ctrl->i2c_data.per_frame[i].list_head));
+
+ rc = cam_actuator_parse_dt(a_ctrl, &(pdev->dev));
+ if (rc < 0) {
+ pr_err("%s:%d :Error: Paring actuator dt failed rc %d",
+ __func__, __LINE__, rc);
+ goto free_ctrl;
+ }
+
+ /* Fill platform device id*/
+ pdev->id = a_ctrl->id;
+
+ a_ctrl->v4l2_dev_str.internal_ops =
+ &cam_actuator_internal_ops;
+ a_ctrl->v4l2_dev_str.ops =
+ &cam_actuator_subdev_ops;
+ strlcpy(a_ctrl->device_name, CAMX_ACTUATOR_DEV_NAME,
+ sizeof(a_ctrl->device_name));
+ a_ctrl->v4l2_dev_str.name =
+ a_ctrl->device_name;
+ a_ctrl->v4l2_dev_str.sd_flags =
+ (V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS);
+ a_ctrl->v4l2_dev_str.ent_function =
+ CAM_ACTUATOR_DEVICE_TYPE;
+ a_ctrl->v4l2_dev_str.token = a_ctrl;
+
+ rc = cam_register_subdev(&(a_ctrl->v4l2_dev_str));
+ if (rc < 0) {
+ pr_err("%s:%d :ERROR: Fail with cam_register_subdev\n",
+ __func__, __LINE__);
+ goto free_mem;
+ }
+
+ a_ctrl->bridge_intf.device_hdl = -1;
+ a_ctrl->bridge_intf.ops.get_dev_info =
+ cam_actuator_publish_dev_info;
+ a_ctrl->bridge_intf.ops.link_setup =
+ cam_actuator_establish_link;
+ a_ctrl->bridge_intf.ops.apply_req =
+ cam_actuator_apply_request;
+
+ platform_set_drvdata(pdev, a_ctrl);
+ v4l2_set_subdevdata(&a_ctrl->v4l2_dev_str.sd, a_ctrl);
+
+ return rc;
+free_mem:
+ kfree(a_ctrl->i2c_data.per_frame);
+free_ctrl:
+ devm_kfree(&pdev->dev, a_ctrl);
+ return rc;
+}
+
+MODULE_DEVICE_TABLE(of, cam_actuator_driver_dt_match);
+
+static struct platform_driver cam_actuator_platform_driver = {
+ .probe = cam_actuator_driver_platform_probe,
+ .driver = {
+ .name = "qcom,actuator",
+ .owner = THIS_MODULE,
+ .of_match_table = cam_actuator_driver_dt_match,
+ },
+ .remove = cam_actuator_platform_remove,
+};
+
+static const struct i2c_device_id i2c_id[] = {
+ {ACTUATOR_DRIVER_I2C, (kernel_ulong_t)NULL},
+ { }
+};
+
+static struct i2c_driver cam_actuator_driver_i2c = {
+ .id_table = i2c_id,
+ .probe = cam_actuator_driver_i2c_probe,
+ .remove = cam_actuator_driver_i2c_remove,
+ .driver = {
+ .name = ACTUATOR_DRIVER_I2C,
+ },
+};
+
+static int __init cam_actuator_driver_init(void)
+{
+ int32_t rc = 0;
+
+ rc = platform_driver_register(&cam_actuator_platform_driver);
+ if (rc < 0) {
+ pr_err("%s platform_driver_register failed rc = %d",
+ __func__, rc);
+ return rc;
+ }
+ rc = i2c_add_driver(&cam_actuator_driver_i2c);
+ if (rc)
+ pr_err("%s:%d :Error: i2c_add_driver failed rc = %d",
+ __func__, __LINE__, rc);
+
+ return rc;
+}
+
+static void __exit cam_actuator_driver_exit(void)
+{
+ platform_driver_unregister(&cam_actuator_platform_driver);
+ i2c_del_driver(&cam_actuator_driver_i2c);
+}
+
+module_init(cam_actuator_driver_init);
+module_exit(cam_actuator_driver_exit);
+MODULE_DESCRIPTION("cam_actuator_driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.h
new file mode 100644
index 0000000..22ef29e
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.h
@@ -0,0 +1,125 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_ACTUATOR_DEV_H_
+#define _CAM_ACTUATOR_DEV_H_
+
+#include <cam_sensor_io.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/irqreturn.h>
+#include <linux/ion.h>
+#include <linux/iommu.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
+#include <cam_cci_dev.h>
+#include <cam_sensor_cmn_header.h>
+#include <cam_subdev.h>
+#include "cam_sensor_util.h"
+#include "cam_sensor_soc_api.h"
+
+#define NUM_MASTERS 2
+#define NUM_QUEUES 2
+
+#define TRUE 1
+#define FALSE 0
+
+#undef CDBG
+#ifdef CAM_SENSOR_DEBUG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+#define ACTUATOR_DRIVER_I2C "i2c_actuator"
+#define CAMX_ACTUATOR_DEV_NAME "cam-actuator-driver"
+
+#define MSM_ACTUATOR_MAX_VREGS (10)
+#define ACTUATOR_MAX_POLL_COUNT 10
+
+
+enum msm_actuator_state_t {
+ ACT_APPLY_SETTINGS_NOW,
+ ACT_APPLY_SETTINGS_LATER,
+};
+
+/**
+ * struct cam_actuator_vreg
+ * @cam_vreg: Regulator structure
+ * @data: Regulator data
+ * @num_vreg: Number of regulators
+ */
+struct cam_actuator_vreg {
+ struct camera_vreg_t *cam_vreg;
+ void *data[MSM_ACTUATOR_MAX_VREGS];
+ int num_vreg;
+};
+
+/**
+ * struct intf_params
+ * @device_hdl: Device Handle
+ * @session_hdl: Session Handle
+ * @ops: KMD operations
+ * @crm_cb: Callback API pointers
+ */
+struct intf_params {
+ int32_t device_hdl;
+ int32_t session_hdl;
+ int32_t link_hdl;
+ struct cam_req_mgr_kmd_ops ops;
+ struct cam_req_mgr_crm_cb *crm_cb;
+};
+
+/**
+ * struct cam_actuator_ctrl_t
+ * @i2c_driver: I2C device info
+ * @pdev: Platform device
+ * @cci_i2c_master: I2C structure
+ * @io_master_info: Information about the communication master
+ * @actuator_mutex: Actuator mutex
+ * @id: Cell Index
+ * @act_apply_state: Actuator settings aRegulator config
+ * @gconf: GPIO config
+ * @pinctrl_info: Pinctrl information
+ * @v4l2_dev_str: V4L2 device structure
+ * @i2c_data: I2C register settings structure
+ * @act_info: Sensor query cap structure
+ * @of_node: Node ptr
+ * @device_name: Device name
+ */
+struct cam_actuator_ctrl_t {
+ struct i2c_driver *i2c_driver;
+ enum cci_i2c_master_t cci_i2c_master;
+ struct camera_io_master io_master_info;
+ struct mutex actuator_mutex;
+ uint32_t id;
+ enum msm_actuator_state_t act_apply_state;
+ struct cam_actuator_vreg vreg_cfg;
+ struct msm_camera_gpio_conf *gconf;
+ struct msm_pinctrl_info pinctrl_info;
+ uint8_t cam_pinctrl_status;
+ struct cam_subdev v4l2_dev_str;
+ struct i2c_data_settings i2c_data;
+ struct cam_actuator_query_cap act_info;
+ struct intf_params bridge_intf;
+ struct device_node *of_node;
+ char device_name[20];
+};
+
+#endif /* _CAM_ACTUATOR_DEV_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_soc.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_soc.c
new file mode 100644
index 0000000..767f3b0
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_soc.c
@@ -0,0 +1,77 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "cam_actuator_soc.h"
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <cam_sensor_cmn_header.h>
+#include <cam_sensor_util.h>
+#include <cam_sensor_io.h>
+#include <cam_req_mgr_util.h>
+
+int32_t cam_actuator_parse_dt(struct cam_actuator_ctrl_t *a_ctrl,
+ struct device *dev)
+{
+ int32_t rc = 0;
+ struct cam_actuator_vreg *vreg_cfg;
+
+ /* Initialize mutex */
+ mutex_init(&(a_ctrl->actuator_mutex));
+
+ rc = of_property_read_u32(a_ctrl->of_node, "cell-index",
+ &(a_ctrl->id));
+ CDBG("cell-index %d, rc %d\n", a_ctrl->id, rc);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: parsing dt for cellindex rc %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(a_ctrl->of_node, "qcom,cci-master",
+ &(a_ctrl->cci_i2c_master));
+ CDBG("qcom,cci-master %d, rc %d\n", a_ctrl->cci_i2c_master, rc);
+ if (rc < 0 || a_ctrl->cci_i2c_master >= MASTER_MAX) {
+ pr_err("%s:%d :Error: Wrong info from dt CCI master as : %d\n",
+ __func__, __LINE__, a_ctrl->cci_i2c_master);
+ return rc;
+ }
+
+ if (of_find_property(a_ctrl->of_node,
+ "qcom,cam-vreg-name", NULL)) {
+ vreg_cfg = &(a_ctrl->vreg_cfg);
+ rc = cam_sensor_get_dt_vreg_data(dev->of_node,
+ &vreg_cfg->cam_vreg, &vreg_cfg->num_vreg);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: parsing regulator dt: %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+ }
+ rc = msm_sensor_driver_get_gpio_data(&(a_ctrl->gconf),
+ a_ctrl->of_node);
+ if (rc < 0) {
+ pr_err("%s:%d No/Error Actuator GPIOs\n",
+ __func__, __LINE__);
+ } else {
+ a_ctrl->cam_pinctrl_status = 1;
+ rc = msm_camera_pinctrl_init(
+ &(a_ctrl->pinctrl_info), dev);
+ if (rc < 0) {
+ pr_err("ERR:%s: Error in reading actuator pinctrl\n",
+ __func__);
+ a_ctrl->cam_pinctrl_status = 0;
+ rc = 0;
+ }
+ }
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_soc.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_soc.h
new file mode 100644
index 0000000..05d51f4
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_soc.h
@@ -0,0 +1,26 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_ACTUATOR_SOC_H_
+#define _CAM_ACTUATOR_SOC_H_
+
+#include "cam_actuator_dev.h"
+
+/**
+ * @a_ctrl: Actuator ctrl structure
+ *
+ * This API parses actuator device tree
+ */
+int cam_actuator_parse_dt(struct cam_actuator_ctrl_t *a_ctrl,
+ struct device *dev);
+
+#endif /* _CAM_ACTUATOR_SOC_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/Makefile b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/Makefile
new file mode 100644
index 0000000..57dfed5
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/Makefile
@@ -0,0 +1,7 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_cci_dev.o cam_cci_core.o cam_cci_soc.o
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_core.c
new file mode 100644
index 0000000..746b786
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_core.c
@@ -0,0 +1,1303 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include "cam_cci_core.h"
+#include "cam_cci_dev.h"
+
+static int32_t cam_cci_convert_type_to_num_bytes(
+ enum camera_sensor_i2c_type type)
+{
+ int32_t num_bytes;
+
+ switch (type) {
+ case CAMERA_SENSOR_I2C_TYPE_BYTE:
+ num_bytes = 1;
+ break;
+ case CAMERA_SENSOR_I2C_TYPE_WORD:
+ num_bytes = 2;
+ break;
+ case CAMERA_SENSOR_I2C_TYPE_3B:
+ num_bytes = 3;
+ break;
+ case CAMERA_SENSOR_I2C_TYPE_DWORD:
+ num_bytes = 4;
+ break;
+ default:
+ pr_err("%s: %d failed: %d\n", __func__, __LINE__, type);
+ num_bytes = 0;
+ break;
+ }
+ return num_bytes;
+}
+
+static void cam_cci_flush_queue(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master)
+{
+ int32_t rc = 0;
+
+ cam_io_w_mb(1 << master, cci_dev->base + CCI_HALT_REQ_ADDR);
+ rc = wait_for_completion_timeout(
+ &cci_dev->cci_master_info[master].reset_complete, CCI_TIMEOUT);
+ if (rc < 0) {
+ pr_err("%s:%d wait failed\n", __func__, __LINE__);
+ } else if (rc == 0) {
+ pr_err("%s:%d wait timeout\n", __func__, __LINE__);
+
+ /* Set reset pending flag to TRUE */
+ cci_dev->cci_master_info[master].reset_pending = TRUE;
+
+ /* Set proper mask to RESET CMD address based on MASTER */
+ if (master == MASTER_0)
+ cam_io_w_mb(CCI_M0_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ else
+ cam_io_w_mb(CCI_M1_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+
+ /* wait for reset done irq */
+ rc = wait_for_completion_timeout(
+ &cci_dev->cci_master_info[master].reset_complete,
+ CCI_TIMEOUT);
+ if (rc <= 0)
+ pr_err("%s:%d wait failed %d\n", __func__, __LINE__,
+ rc);
+ }
+}
+
+static int32_t cam_cci_validate_queue(struct cci_device *cci_dev,
+ uint32_t len,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ int32_t rc = 0;
+ uint32_t read_val = 0;
+ uint32_t reg_offset = master * 0x200 + queue * 0x100;
+
+ read_val = cam_io_r_mb(cci_dev->base +
+ CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset);
+ CDBG("%s line %d CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR %d len %d max %d\n",
+ __func__, __LINE__, read_val, len,
+ cci_dev->cci_i2c_queue_info[master][queue].max_queue_size);
+ if ((read_val + len + 1) > cci_dev->
+ cci_i2c_queue_info[master][queue].max_queue_size) {
+ uint32_t reg_val = 0;
+ uint32_t report_val = CCI_I2C_REPORT_CMD | (1 << 8);
+
+ CDBG("%s:%d CCI_I2C_REPORT_CMD\n", __func__, __LINE__);
+ cam_io_w_mb(report_val,
+ cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset);
+ read_val++;
+ CDBG("%s:%d CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR %d, queue: %d\n",
+ __func__, __LINE__, read_val, queue);
+ cam_io_w_mb(read_val, cci_dev->base +
+ CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset);
+ reg_val = 1 << ((master * 2) + queue);
+ CDBG("%s:%d CCI_QUEUE_START_ADDR\n", __func__, __LINE__);
+ atomic_set(&cci_dev->cci_master_info[master].
+ done_pending[queue], 1);
+ cam_io_w_mb(reg_val, cci_dev->base +
+ CCI_QUEUE_START_ADDR);
+ CDBG("%s line %d wait_for_completion_timeout\n",
+ __func__, __LINE__);
+ atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1);
+ rc = wait_for_completion_timeout(&cci_dev->
+ cci_master_info[master].report_q[queue], CCI_TIMEOUT);
+ if (rc <= 0) {
+ pr_err("%s: wait_for_completion_timeout %d\n",
+ __func__, __LINE__);
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ cam_cci_flush_queue(cci_dev, master);
+ return rc;
+ }
+ rc = cci_dev->cci_master_info[master].status;
+ if (rc < 0)
+ pr_err("%s failed rc %d\n", __func__, rc);
+ }
+
+ return rc;
+}
+
+static int32_t cam_cci_write_i2c_queue(struct cci_device *cci_dev,
+ uint32_t val,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ int32_t rc = 0;
+ uint32_t reg_offset = master * 0x200 + queue * 0x100;
+
+ if (!cci_dev) {
+ pr_err("%s: failed %d", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ rc = cam_cci_validate_queue(cci_dev, 1, master, queue);
+ if (rc < 0) {
+ pr_err("%s: failed %d", __func__, __LINE__);
+ return rc;
+ }
+ CDBG("%s CCI_I2C_M0_Q0_LOAD_DATA_ADDR:val 0x%x:0x%x\n",
+ __func__, CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset, val);
+ cam_io_w_mb(val, cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset);
+ return rc;
+}
+
+static int32_t cam_cci_lock_queue(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue, uint32_t en)
+{
+ uint32_t val;
+
+ if (queue != PRIORITY_QUEUE)
+ return 0;
+
+ val = en ? CCI_I2C_LOCK_CMD : CCI_I2C_UNLOCK_CMD;
+ return cam_cci_write_i2c_queue(cci_dev, val, master, queue);
+}
+
+#ifdef DUMP_CCI_REGISTERS
+static void cam_cci_dump_registers(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master, enum cci_i2c_queue_t queue)
+{
+ uint32_t read_val = 0;
+ uint32_t i = 0;
+ uint32_t reg_offset = 0;
+
+ /* CCI Top Registers */
+ CCI_DBG(" **** %s : %d CCI TOP Registers ****\n", __func__, __LINE__);
+ for (i = 0; i < DEBUG_TOP_REG_COUNT; i++) {
+ reg_offset = DEBUG_TOP_REG_START + i * 4;
+ read_val = cam_io_r_mb(cci_dev->base + reg_offset);
+ CCI_DBG("%s : %d offset = 0x%X value = 0x%X\n",
+ __func__, __LINE__, reg_offset, read_val);
+ }
+
+ /* CCI Master registers */
+ CCI_DBG(" **** %s : %d CCI MASTER%d Registers ****\n",
+ __func__, __LINE__, master);
+ for (i = 0; i < DEBUG_MASTER_REG_COUNT; i++) {
+ if (i == 6)
+ continue;
+ reg_offset = DEBUG_MASTER_REG_START + master*0x100 + i * 4;
+ read_val = cam_io_r_mb(cci_dev->base + reg_offset);
+ CCI_DBG("%s : %d offset = 0x%X value = 0x%X\n",
+ __func__, __LINE__, reg_offset, read_val);
+ }
+
+ /* CCI Master Queue registers */
+ CCI_DBG(" **** %s : %d CCI MASTER%d QUEUE%d Registers ****\n",
+ __func__, __LINE__, master, queue);
+ for (i = 0; i < DEBUG_MASTER_QUEUE_REG_COUNT; i++) {
+ reg_offset = DEBUG_MASTER_QUEUE_REG_START + master*0x200 +
+ queue*0x100 + i * 4;
+ read_val = cam_io_r_mb(cci_dev->base + reg_offset);
+ CCI_DBG("%s : %d offset = 0x%X value = 0x%X\n",
+ __func__, __LINE__, reg_offset, read_val);
+ }
+
+ /* CCI Interrupt registers */
+ CCI_DBG(" **** %s : %d CCI Interrupt Registers ****\n",
+ __func__, __LINE__);
+ for (i = 0; i < DEBUG_INTR_REG_COUNT; i++) {
+ reg_offset = DEBUG_INTR_REG_START + i * 4;
+ read_val = cam_io_r_mb(cci_dev->base + reg_offset);
+ CCI_DBG("%s : %d offset = 0x%X value = 0x%X\n",
+ __func__, __LINE__, reg_offset, read_val);
+ }
+}
+#endif
+
+static uint32_t cam_cci_wait(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ int32_t rc = 0;
+
+ if (!cci_dev) {
+ pr_err("%s: failed %d", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ rc = wait_for_completion_timeout(&cci_dev->
+ cci_master_info[master].report_q[queue], CCI_TIMEOUT);
+ CDBG("%s line %d wait DONE_for_completion_timeout\n",
+ __func__, __LINE__);
+
+ if (rc <= 0) {
+#ifdef DUMP_CCI_REGISTERS
+ cam_cci_dump_registers(cci_dev, master, queue);
+#endif
+ pr_err("%s: %d wait for queue: %d\n",
+ __func__, __LINE__, queue);
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ cam_cci_flush_queue(cci_dev, master);
+ return rc;
+ }
+ rc = cci_dev->cci_master_info[master].status;
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static void cam_cci_load_report_cmd(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ uint32_t reg_offset = master * 0x200 + queue * 0x100;
+ uint32_t read_val = cam_io_r_mb(cci_dev->base +
+ CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset);
+ uint32_t report_val = CCI_I2C_REPORT_CMD | (1 << 8);
+
+ CDBG("%s:%d CCI_I2C_REPORT_CMD curr_w_cnt: %d\n",
+ __func__, __LINE__, read_val);
+ cam_io_w_mb(report_val,
+ cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset);
+ read_val++;
+
+ CDBG("%s:%d CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR %d\n",
+ __func__, __LINE__, read_val);
+ cam_io_w_mb(read_val, cci_dev->base +
+ CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset);
+}
+
+static int32_t cam_cci_wait_report_cmd(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ uint32_t reg_val = 1 << ((master * 2) + queue);
+
+ cam_cci_load_report_cmd(cci_dev, master, queue);
+ atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1);
+ atomic_set(&cci_dev->cci_master_info[master].done_pending[queue], 1);
+ cam_io_w_mb(reg_val, cci_dev->base +
+ CCI_QUEUE_START_ADDR);
+
+ return cam_cci_wait(cci_dev, master, queue);
+}
+
+static int32_t cam_cci_transfer_end(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ int32_t rc = 0;
+
+ if (atomic_read(&cci_dev->cci_master_info[master].q_free[queue]) == 0) {
+ rc = cam_cci_lock_queue(cci_dev, master, queue, 0);
+ if (rc < 0) {
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ return rc;
+ }
+ rc = cam_cci_wait_report_cmd(cci_dev, master, queue);
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ } else {
+ atomic_set(&cci_dev->cci_master_info[master].
+ done_pending[queue], 1);
+ rc = cam_cci_wait(cci_dev, master, queue);
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ rc = cam_cci_lock_queue(cci_dev, master, queue, 0);
+ if (rc < 0) {
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ return rc;
+ }
+ rc = cam_cci_wait_report_cmd(cci_dev, master, queue);
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int32_t cam_cci_get_queue_free_size(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ uint32_t read_val = 0;
+ uint32_t reg_offset = master * 0x200 + queue * 0x100;
+
+ read_val = cam_io_r_mb(cci_dev->base +
+ CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset);
+ CDBG("%s line %d CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR %d max %d\n",
+ __func__, __LINE__, read_val,
+ cci_dev->cci_i2c_queue_info[master][queue].max_queue_size);
+ return (cci_dev->
+ cci_i2c_queue_info[master][queue].max_queue_size) -
+ read_val;
+}
+
+static void cam_cci_process_half_q(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ uint32_t reg_val = 1 << ((master * 2) + queue);
+
+ if (atomic_read(&cci_dev->cci_master_info[master].q_free[queue]) == 0) {
+ cam_cci_load_report_cmd(cci_dev, master, queue);
+ atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1);
+ cam_io_w_mb(reg_val, cci_dev->base +
+ CCI_QUEUE_START_ADDR);
+ }
+}
+
+static int32_t cam_cci_process_full_q(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ int32_t rc = 0;
+
+ if (atomic_read(&cci_dev->cci_master_info[master].q_free[queue]) == 1) {
+ atomic_set(&cci_dev->cci_master_info[master].
+ done_pending[queue], 1);
+ rc = cam_cci_wait(cci_dev, master, queue);
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ } else {
+ rc = cam_cci_wait_report_cmd(cci_dev, master, queue);
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int32_t cam_cci_calc_cmd_len(struct cci_device *cci_dev,
+ struct cam_cci_ctrl *c_ctrl, uint32_t cmd_size,
+ struct cam_sensor_i2c_reg_array *i2c_cmd, uint32_t *pack)
+{
+ uint8_t i;
+ uint32_t len = 0;
+ uint8_t data_len = 0, addr_len = 0;
+ uint8_t pack_max_len;
+ struct cam_sensor_i2c_reg_setting *msg;
+ struct cam_sensor_i2c_reg_array *cmd = i2c_cmd;
+ uint32_t size = cmd_size;
+
+ if (!cci_dev || !c_ctrl) {
+ pr_err("%s: failed %d", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ msg = &c_ctrl->cfg.cci_i2c_write_cfg;
+ *pack = 0;
+
+ if (c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ) {
+ addr_len = cam_cci_convert_type_to_num_bytes(msg->addr_type);
+ len = (size + addr_len) <= (cci_dev->payload_size) ?
+ (size + addr_len):cci_dev->payload_size;
+ } else {
+ addr_len = cam_cci_convert_type_to_num_bytes(msg->addr_type);
+ data_len = cam_cci_convert_type_to_num_bytes(msg->data_type);
+ len = data_len + addr_len;
+ pack_max_len = size < (cci_dev->payload_size-len) ?
+ size : (cci_dev->payload_size-len);
+ for (i = 0; i < pack_max_len;) {
+ if (cmd->delay || ((cmd - i2c_cmd) >= (cmd_size - 1)))
+ break;
+ if (cmd->reg_addr + 1 ==
+ (cmd+1)->reg_addr) {
+ len += data_len;
+ *pack += data_len;
+ } else {
+ break;
+ }
+ i += data_len;
+ cmd++;
+ }
+ }
+
+ if (len > cci_dev->payload_size) {
+ pr_err("%s: %d Len error: %d",
+ __func__, __LINE__, len);
+ return -EINVAL;
+ }
+
+ len += 1; /*add i2c WR command*/
+ len = len/4 + 1;
+
+ return len;
+}
+
+static uint32_t cam_cci_cycles_per_ms(unsigned long clk)
+{
+ uint32_t cycles_per_us;
+
+ if (clk) {
+ cycles_per_us = ((clk/1000)*256)/1000;
+ } else {
+ pr_err("%s:%d, failed: Can use default: %d",
+ __func__, __LINE__, CYCLES_PER_MICRO_SEC_DEFAULT);
+ cycles_per_us = CYCLES_PER_MICRO_SEC_DEFAULT;
+ }
+
+ return cycles_per_us;
+}
+
+uint32_t *cam_cci_get_clk_rates(struct cci_device *cci_dev,
+ struct cam_cci_ctrl *c_ctrl)
+{
+ uint32_t j;
+ int32_t idx;
+ uint32_t cci_clk_src;
+ unsigned long clk;
+ struct cam_cci_clk_params_t *clk_params = NULL;
+ struct device_node *of_node = cci_dev->v4l2_dev_str.pdev->dev.of_node;
+ enum i2c_freq_mode i2c_freq_mode = c_ctrl->cci_info->i2c_freq_mode;
+
+ if (i2c_freq_mode >= I2C_MAX_MODES ||
+ i2c_freq_mode < I2C_STANDARD_MODE) {
+ pr_err("%s:%d Invalid frequency mode: %d\n",
+ __func__, __LINE__, (int32_t)i2c_freq_mode);
+ return NULL;
+ }
+
+ clk_params = &cci_dev->cci_clk_params[i2c_freq_mode];
+ cci_clk_src = clk_params->cci_clk_src;
+
+ idx = of_property_match_string(of_node,
+ "clock-names", CCI_CLK_SRC_NAME);
+ if (idx < 0) {
+ cci_dev->cycles_per_us = CYCLES_PER_MICRO_SEC_DEFAULT;
+ return cci_dev->cci_clk_rates[0];
+ }
+
+ if (cci_clk_src == 0) {
+ clk = cci_dev->cci_clk_rates[0][idx];
+ cci_dev->cycles_per_us = cam_cci_cycles_per_ms(clk);
+ return cci_dev->cci_clk_rates[0];
+ }
+
+ CDBG("%s:%d CCI: 3 cases:%d idx: %d\n", __func__,
+ __LINE__, (int32_t)cci_dev->num_clk_cases, idx);
+ for (j = 0; j < cci_dev->num_clk_cases; j++) {
+ clk = cci_dev->cci_clk_rates[j][idx];
+ if (clk == cci_clk_src) {
+ cci_dev->cycles_per_us = cam_cci_cycles_per_ms(clk);
+ cci_dev->cci_clk_src = cci_clk_src;
+ return cci_dev->cci_clk_rates[j];
+ }
+ }
+
+ return NULL;
+}
+
+static int32_t cam_cci_set_clk_param(struct cci_device *cci_dev,
+ struct cam_cci_ctrl *c_ctrl)
+{
+ struct cam_cci_clk_params_t *clk_params = NULL;
+ enum cci_i2c_master_t master = c_ctrl->cci_info->cci_i2c_master;
+ enum i2c_freq_mode i2c_freq_mode = c_ctrl->cci_info->i2c_freq_mode;
+
+ if ((i2c_freq_mode >= I2C_MAX_MODES) || (i2c_freq_mode < 0)) {
+ pr_err("%s:%d invalid i2c_freq_mode = %d",
+ __func__, __LINE__, i2c_freq_mode);
+ return -EINVAL;
+ }
+
+ clk_params = &cci_dev->cci_clk_params[i2c_freq_mode];
+
+ if (cci_dev->i2c_freq_mode[master] == i2c_freq_mode)
+ return 0;
+ if (master == MASTER_0) {
+ cam_io_w_mb(clk_params->hw_thigh << 16 |
+ clk_params->hw_tlow,
+ cci_dev->base + CCI_I2C_M0_SCL_CTL_ADDR);
+ cam_io_w_mb(clk_params->hw_tsu_sto << 16 |
+ clk_params->hw_tsu_sta,
+ cci_dev->base + CCI_I2C_M0_SDA_CTL_0_ADDR);
+ cam_io_w_mb(clk_params->hw_thd_dat << 16 |
+ clk_params->hw_thd_sta,
+ cci_dev->base + CCI_I2C_M0_SDA_CTL_1_ADDR);
+ cam_io_w_mb(clk_params->hw_tbuf,
+ cci_dev->base + CCI_I2C_M0_SDA_CTL_2_ADDR);
+ cam_io_w_mb(clk_params->hw_scl_stretch_en << 8 |
+ clk_params->hw_trdhld << 4 | clk_params->hw_tsp,
+ cci_dev->base + CCI_I2C_M0_MISC_CTL_ADDR);
+ } else if (master == MASTER_1) {
+ cam_io_w_mb(clk_params->hw_thigh << 16 |
+ clk_params->hw_tlow,
+ cci_dev->base + CCI_I2C_M1_SCL_CTL_ADDR);
+ cam_io_w_mb(clk_params->hw_tsu_sto << 16 |
+ clk_params->hw_tsu_sta,
+ cci_dev->base + CCI_I2C_M1_SDA_CTL_0_ADDR);
+ cam_io_w_mb(clk_params->hw_thd_dat << 16 |
+ clk_params->hw_thd_sta,
+ cci_dev->base + CCI_I2C_M1_SDA_CTL_1_ADDR);
+ cam_io_w_mb(clk_params->hw_tbuf,
+ cci_dev->base + CCI_I2C_M1_SDA_CTL_2_ADDR);
+ cam_io_w_mb(clk_params->hw_scl_stretch_en << 8 |
+ clk_params->hw_trdhld << 4 | clk_params->hw_tsp,
+ cci_dev->base + CCI_I2C_M1_MISC_CTL_ADDR);
+ }
+ cci_dev->i2c_freq_mode[master] = i2c_freq_mode;
+
+ return 0;
+}
+
+static int32_t cam_cci_data_queue(struct cci_device *cci_dev,
+ struct cam_cci_ctrl *c_ctrl, enum cci_i2c_queue_t queue,
+ enum cci_i2c_sync sync_en)
+{
+ uint16_t i = 0, j = 0, k = 0, h = 0, len = 0;
+ int32_t rc = 0, free_size = 0, en_seq_write = 0;
+ uint8_t data[12];
+ struct cam_sensor_i2c_reg_setting *i2c_msg =
+ &c_ctrl->cfg.cci_i2c_write_cfg;
+ struct cam_sensor_i2c_reg_array *i2c_cmd = i2c_msg->reg_setting;
+ enum cci_i2c_master_t master = c_ctrl->cci_info->cci_i2c_master;
+ uint16_t reg_addr = 0, cmd_size = i2c_msg->size;
+ uint32_t read_val = 0, reg_offset, val, delay = 0;
+ uint32_t max_queue_size, queue_size = 0, cmd = 0;
+
+ if (i2c_cmd == NULL) {
+ pr_err("%s:%d Failed line\n", __func__,
+ __LINE__);
+ return -EINVAL;
+ }
+
+ if ((!cmd_size) || (cmd_size > CCI_I2C_MAX_WRITE)) {
+ pr_err("%s:%d failed: invalid cmd_size %d\n",
+ __func__, __LINE__, cmd_size);
+ return -EINVAL;
+ }
+
+ CDBG("%s addr type %d data type %d cmd_size %d\n", __func__,
+ i2c_msg->addr_type, i2c_msg->data_type, cmd_size);
+
+ if (i2c_msg->addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX) {
+ pr_err("%s:%d failed: invalid addr_type 0x%X\n",
+ __func__, __LINE__, i2c_msg->addr_type);
+ return -EINVAL;
+ }
+ if (i2c_msg->data_type >= CAMERA_SENSOR_I2C_TYPE_MAX) {
+ pr_err("%s:%d failed: invalid data_type 0x%X\n",
+ __func__, __LINE__, i2c_msg->data_type);
+ return -EINVAL;
+ }
+ reg_offset = master * 0x200 + queue * 0x100;
+
+ cam_io_w_mb(cci_dev->cci_wait_sync_cfg.cid,
+ cci_dev->base + CCI_SET_CID_SYNC_TIMER_ADDR +
+ cci_dev->cci_wait_sync_cfg.csid *
+ CCI_SET_CID_SYNC_TIMER_OFFSET);
+
+ val = CCI_I2C_SET_PARAM_CMD | c_ctrl->cci_info->sid << 4 |
+ c_ctrl->cci_info->retries << 16 |
+ c_ctrl->cci_info->id_map << 18;
+
+ CDBG("%s CCI_I2C_M0_Q0_LOAD_DATA_ADDR:val 0x%x:0x%x\n",
+ __func__, CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset, val);
+ cam_io_w_mb(val, cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset);
+
+ atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 0);
+
+ max_queue_size = cci_dev->cci_i2c_queue_info[master][queue].
+ max_queue_size;
+
+ if (c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ)
+ queue_size = max_queue_size;
+ else
+ queue_size = max_queue_size/2;
+ reg_addr = i2c_cmd->reg_addr;
+
+ if (sync_en == MSM_SYNC_ENABLE && cci_dev->valid_sync &&
+ cmd_size < max_queue_size) {
+ val = CCI_I2C_WAIT_SYNC_CMD |
+ ((cci_dev->cci_wait_sync_cfg.line) << 4);
+ cam_io_w_mb(val,
+ cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset);
+ }
+
+ rc = cam_cci_lock_queue(cci_dev, master, queue, 1);
+ if (rc < 0) {
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ return rc;
+ }
+
+ while (cmd_size) {
+ uint32_t pack = 0;
+
+ len = cam_cci_calc_cmd_len(cci_dev, c_ctrl, cmd_size,
+ i2c_cmd, &pack);
+ if (len <= 0) {
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ read_val = cam_io_r_mb(cci_dev->base +
+ CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset);
+ CDBG("%s line %d CUR_WORD_CNT_ADDR %d len %d max %d\n",
+ __func__, __LINE__, read_val, len, max_queue_size);
+ /* + 1 - space alocation for Report CMD */
+ if ((read_val + len + 1) > queue_size) {
+ if ((read_val + len + 1) > max_queue_size) {
+ rc = cam_cci_process_full_q(cci_dev,
+ master, queue);
+ if (rc < 0) {
+ pr_err("%s failed line %d\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ continue;
+ }
+ cam_cci_process_half_q(cci_dev, master, queue);
+ }
+
+ CDBG("%s cmd_size %d addr 0x%x data 0x%x\n", __func__,
+ cmd_size, i2c_cmd->reg_addr, i2c_cmd->reg_data);
+ delay = i2c_cmd->delay;
+ i = 0;
+ data[i++] = CCI_I2C_WRITE_CMD;
+
+ /*
+ * in case of multiple command
+ * MSM_CCI_I2C_WRITE : address is not continuous, so update
+ * address for a new packet.
+ * MSM_CCI_I2C_WRITE_SEQ : address is continuous, need to keep
+ * the incremented address for a
+ * new packet
+ */
+ if (c_ctrl->cmd == MSM_CCI_I2C_WRITE ||
+ c_ctrl->cmd == MSM_CCI_I2C_WRITE_ASYNC ||
+ c_ctrl->cmd == MSM_CCI_I2C_WRITE_SYNC ||
+ c_ctrl->cmd == MSM_CCI_I2C_WRITE_SYNC_BLOCK)
+ reg_addr = i2c_cmd->reg_addr;
+
+ if (en_seq_write == 0) {
+ /* either byte or word addr */
+ if (i2c_msg->addr_type == CAMERA_SENSOR_I2C_TYPE_BYTE)
+ data[i++] = reg_addr;
+ else {
+ data[i++] = (reg_addr & 0xFF00) >> 8;
+ data[i++] = reg_addr & 0x00FF;
+ }
+ }
+ /* max of 10 data bytes */
+ do {
+ if (i2c_msg->data_type == CAMERA_SENSOR_I2C_TYPE_BYTE) {
+ data[i++] = i2c_cmd->reg_data;
+ reg_addr++;
+ } else {
+ if ((i + 1) <= cci_dev->payload_size) {
+ data[i++] = (i2c_cmd->reg_data &
+ 0xFF00) >> 8; /* MSB */
+ data[i++] = i2c_cmd->reg_data &
+ 0x00FF; /* LSB */
+ reg_addr++;
+ } else
+ break;
+ }
+ i2c_cmd++;
+ --cmd_size;
+ } while (((c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ) || pack--) &&
+ (cmd_size > 0) && (i <= cci_dev->payload_size));
+ free_size = cam_cci_get_queue_free_size(cci_dev, master,
+ queue);
+ if ((c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ) &&
+ ((i-1) == MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_11) &&
+ cci_dev->support_seq_write && cmd_size > 0 &&
+ free_size > BURST_MIN_FREE_SIZE) {
+ data[0] |= 0xF0;
+ en_seq_write = 1;
+ } else {
+ data[0] |= ((i-1) << 4);
+ en_seq_write = 0;
+ }
+ len = ((i-1)/4) + 1;
+
+ read_val = cam_io_r_mb(cci_dev->base +
+ CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset);
+ for (h = 0, k = 0; h < len; h++) {
+ cmd = 0;
+ for (j = 0; (j < 4 && k < i); j++)
+ cmd |= (data[k++] << (j * 8));
+ CDBG("%s LOAD_DATA_ADDR 0x%x, q: %d, len:%d, cnt: %d\n",
+ __func__, cmd, queue, len, read_val);
+ cam_io_w_mb(cmd, cci_dev->base +
+ CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ master * 0x200 + queue * 0x100);
+
+ read_val += 1;
+ cam_io_w_mb(read_val, cci_dev->base +
+ CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset);
+ }
+
+ if ((delay > 0) && (delay < CCI_MAX_DELAY) &&
+ en_seq_write == 0) {
+ cmd = (uint32_t)((delay * cci_dev->cycles_per_us) /
+ 0x100);
+ cmd <<= 4;
+ cmd |= CCI_I2C_WAIT_CMD;
+ CDBG("%s CCI_I2C_M0_Q0_LOAD_DATA_ADDR 0x%x\n",
+ __func__, cmd);
+ cam_io_w_mb(cmd, cci_dev->base +
+ CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ master * 0x200 + queue * 0x100);
+ read_val += 1;
+ cam_io_w_mb(read_val, cci_dev->base +
+ CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset);
+ }
+ }
+
+ rc = cam_cci_transfer_end(cci_dev, master, queue);
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int32_t cam_cci_read(struct v4l2_subdev *sd,
+ struct cam_cci_ctrl *c_ctrl)
+{
+ int32_t rc = 0;
+ uint32_t val = 0;
+ int32_t read_words = 0, exp_words = 0;
+ int32_t index = 0, first_byte = 0;
+ uint32_t i = 0;
+ enum cci_i2c_master_t master;
+ enum cci_i2c_queue_t queue = QUEUE_1;
+ struct cci_device *cci_dev = NULL;
+ struct cam_cci_read_cfg *read_cfg = NULL;
+
+ cci_dev = v4l2_get_subdevdata(sd);
+ master = c_ctrl->cci_info->cci_i2c_master;
+ read_cfg = &c_ctrl->cfg.cci_i2c_read_cfg;
+
+ if (c_ctrl->cci_info->cci_i2c_master >= MASTER_MAX
+ || c_ctrl->cci_info->cci_i2c_master < 0) {
+ pr_err("%s:%d Invalid I2C master addr\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ mutex_lock(&cci_dev->cci_master_info[master].mutex_q[queue]);
+
+ /*
+ * Todo: If there is a change in frequency of operation
+ * Wait for previos transaction to complete
+ */
+
+ /* Set the I2C Frequency */
+ rc = cam_cci_set_clk_param(cci_dev, c_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d cam_cci_set_clk_param failed rc = %d\n",
+ __func__, __LINE__, rc);
+ goto rel_mutex;
+ }
+
+ /*
+ * Call validate queue to make sure queue is empty before starting.
+ * If this call fails, don't proceed with i2c_read call. This is to
+ * avoid overflow / underflow of queue
+ */
+ rc = cam_cci_validate_queue(cci_dev,
+ cci_dev->cci_i2c_queue_info[master][queue].max_queue_size - 1,
+ master, queue);
+ if (rc < 0) {
+ pr_err("%s:%d Initial validataion failed rc %d\n", __func__,
+ __LINE__, rc);
+ goto rel_mutex;
+ }
+
+ if (c_ctrl->cci_info->retries > CCI_I2C_READ_MAX_RETRIES) {
+ pr_err("%s:%d More than max retries\n", __func__,
+ __LINE__);
+ goto rel_mutex;
+ }
+
+ if (read_cfg->data == NULL) {
+ pr_err("%s:%d Data ptr is NULL\n", __func__,
+ __LINE__);
+ goto rel_mutex;
+ }
+
+ CDBG("%s master %d, queue %d\n", __func__, master, queue);
+ CDBG("%s set param sid 0x%x retries %d id_map %d\n", __func__,
+ c_ctrl->cci_info->sid, c_ctrl->cci_info->retries,
+ c_ctrl->cci_info->id_map);
+ val = CCI_I2C_SET_PARAM_CMD | c_ctrl->cci_info->sid << 4 |
+ c_ctrl->cci_info->retries << 16 |
+ c_ctrl->cci_info->id_map << 18;
+ rc = cam_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto rel_mutex;
+ }
+
+ val = CCI_I2C_LOCK_CMD;
+ rc = cam_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto rel_mutex;
+ }
+
+ if (read_cfg->addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX) {
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ rc = -EINVAL;
+ goto rel_mutex;
+ }
+
+ val = CCI_I2C_WRITE_DISABLE_P_CMD | (read_cfg->addr_type << 4);
+ for (i = 0; i < read_cfg->addr_type; i++) {
+ val |= ((read_cfg->addr >> (i << 3)) & 0xFF) <<
+ ((read_cfg->addr_type - i) << 3);
+ }
+
+ rc = cam_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto rel_mutex;
+ }
+
+ val = CCI_I2C_READ_CMD | (read_cfg->num_byte << 4);
+ rc = cam_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto rel_mutex;
+ }
+
+ val = CCI_I2C_UNLOCK_CMD;
+ rc = cam_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto rel_mutex;
+ }
+
+ val = cam_io_r_mb(cci_dev->base + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR
+ + master * 0x200 + queue * 0x100);
+ CDBG("%s cur word cnt 0x%x\n", __func__, val);
+ cam_io_w_mb(val, cci_dev->base + CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR
+ + master * 0x200 + queue * 0x100);
+
+ val = 1 << ((master * 2) + queue);
+ cam_io_w_mb(val, cci_dev->base + CCI_QUEUE_START_ADDR);
+ CDBG("%s:%d E wait_for_completion_timeout\n", __func__,
+ __LINE__);
+
+ rc = wait_for_completion_timeout(&cci_dev->
+ cci_master_info[master].reset_complete, CCI_TIMEOUT);
+ if (rc <= 0) {
+#ifdef DUMP_CCI_REGISTERS
+ cam_cci_dump_registers(cci_dev, master, queue);
+#endif
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ pr_err("%s: %d wait_for_completion_timeout rc = %d\n",
+ __func__, __LINE__, rc);
+ cam_cci_flush_queue(cci_dev, master);
+ goto rel_mutex;
+ } else {
+ rc = 0;
+ }
+
+ read_words = cam_io_r_mb(cci_dev->base +
+ CCI_I2C_M0_READ_BUF_LEVEL_ADDR + master * 0x100);
+ exp_words = ((read_cfg->num_byte / 4) + 1);
+ if (read_words != exp_words) {
+ pr_err("%s:%d read_words = %d, exp words = %d\n", __func__,
+ __LINE__, read_words, exp_words);
+ memset(read_cfg->data, 0, read_cfg->num_byte);
+ rc = -EINVAL;
+ goto rel_mutex;
+ }
+ index = 0;
+ CDBG("%s index %d num_type %d\n", __func__, index,
+ read_cfg->num_byte);
+ first_byte = 0;
+ do {
+ val = cam_io_r_mb(cci_dev->base +
+ CCI_I2C_M0_READ_DATA_ADDR + master * 0x100);
+ CDBG("%s read val 0x%x\n", __func__, val);
+ for (i = 0; (i < 4) && (index < read_cfg->num_byte); i++) {
+ CDBG("%s i %d index %d\n", __func__, i, index);
+ if (!first_byte) {
+ CDBG("%s sid 0x%x\n", __func__, val & 0xFF);
+ first_byte++;
+ } else {
+ read_cfg->data[index] =
+ (val >> (i * 8)) & 0xFF;
+ CDBG("%s data[%d] 0x%x\n", __func__, index,
+ read_cfg->data[index]);
+ index++;
+ }
+ }
+ } while (--read_words > 0);
+rel_mutex:
+ mutex_unlock(&cci_dev->cci_master_info[master].mutex_q[queue]);
+
+ return rc;
+}
+
+static int32_t cam_cci_i2c_write(struct v4l2_subdev *sd,
+ struct cam_cci_ctrl *c_ctrl, enum cci_i2c_queue_t queue,
+ enum cci_i2c_sync sync_en)
+{
+ int32_t rc = 0;
+ struct cci_device *cci_dev;
+ enum cci_i2c_master_t master;
+
+ cci_dev = v4l2_get_subdevdata(sd);
+
+ if (cci_dev->cci_state != CCI_STATE_ENABLED) {
+ pr_err("%s invalid cci state %d\n",
+ __func__, cci_dev->cci_state);
+ return -EINVAL;
+ }
+ master = c_ctrl->cci_info->cci_i2c_master;
+ CDBG("%s set param sid 0x%x retries %d id_map %d\n", __func__,
+ c_ctrl->cci_info->sid, c_ctrl->cci_info->retries,
+ c_ctrl->cci_info->id_map);
+
+ /* Set the I2C Frequency */
+ rc = cam_cci_set_clk_param(cci_dev, c_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d cam_cci_set_clk_param failed rc = %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+ /*
+ * Call validate queue to make sure queue is empty before starting.
+ * If this call fails, don't proceed with i2c_write call. This is to
+ * avoid overflow / underflow of queue
+ */
+ rc = cam_cci_validate_queue(cci_dev,
+ cci_dev->cci_i2c_queue_info[master][queue].max_queue_size-1,
+ master, queue);
+ if (rc < 0) {
+ pr_err("%s:%d Initial validataion failed rc %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+ if (c_ctrl->cci_info->retries > CCI_I2C_READ_MAX_RETRIES) {
+ pr_err("%s:%d More than max retries\n", __func__,
+ __LINE__);
+ return rc;
+ }
+ rc = cam_cci_data_queue(cci_dev, c_ctrl, queue, sync_en);
+ if (rc < 0) {
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ return rc;
+ }
+
+ return rc;
+}
+
+static void cam_cci_write_async_helper(struct work_struct *work)
+{
+ int rc;
+ struct cci_device *cci_dev;
+ struct cci_write_async *write_async =
+ container_of(work, struct cci_write_async, work);
+ struct cam_sensor_i2c_reg_setting *i2c_msg;
+ enum cci_i2c_master_t master;
+ struct cam_cci_master_info *cci_master_info;
+
+ cci_dev = write_async->cci_dev;
+ i2c_msg = &write_async->c_ctrl.cfg.cci_i2c_write_cfg;
+ master = write_async->c_ctrl.cci_info->cci_i2c_master;
+ cci_master_info = &cci_dev->cci_master_info[master];
+
+ mutex_lock(&cci_master_info->mutex_q[write_async->queue]);
+ rc = cam_cci_i2c_write(&(cci_dev->v4l2_dev_str.sd),
+ &write_async->c_ctrl, write_async->queue, write_async->sync_en);
+ mutex_unlock(&cci_master_info->mutex_q[write_async->queue]);
+ if (rc < 0)
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+
+ kfree(write_async->c_ctrl.cfg.cci_i2c_write_cfg.reg_setting);
+ kfree(write_async);
+}
+
+static int32_t cam_cci_i2c_write_async(struct v4l2_subdev *sd,
+ struct cam_cci_ctrl *c_ctrl, enum cci_i2c_queue_t queue,
+ enum cci_i2c_sync sync_en)
+{
+ int32_t rc = 0;
+ struct cci_write_async *write_async;
+ struct cci_device *cci_dev;
+ struct cam_sensor_i2c_reg_setting *cci_i2c_write_cfg;
+ struct cam_sensor_i2c_reg_setting *cci_i2c_write_cfg_w;
+
+ cci_dev = v4l2_get_subdevdata(sd);
+
+ write_async = kzalloc(sizeof(*write_async), GFP_KERNEL);
+ if (!write_async)
+ return -ENOMEM;
+
+
+ INIT_WORK(&write_async->work, cam_cci_write_async_helper);
+ write_async->cci_dev = cci_dev;
+ write_async->c_ctrl = *c_ctrl;
+ write_async->queue = queue;
+ write_async->sync_en = sync_en;
+
+ cci_i2c_write_cfg = &c_ctrl->cfg.cci_i2c_write_cfg;
+ cci_i2c_write_cfg_w = &write_async->c_ctrl.cfg.cci_i2c_write_cfg;
+
+ if (cci_i2c_write_cfg->size == 0) {
+ kfree(write_async);
+ return -EINVAL;
+ }
+
+ cci_i2c_write_cfg_w->reg_setting =
+ kzalloc(sizeof(struct cam_sensor_i2c_reg_array)*
+ cci_i2c_write_cfg->size, GFP_KERNEL);
+ if (!cci_i2c_write_cfg_w->reg_setting) {
+ pr_err("%s: %d Couldn't allocate memory\n", __func__, __LINE__);
+ kfree(write_async);
+ return -ENOMEM;
+ }
+ memcpy(cci_i2c_write_cfg_w->reg_setting,
+ cci_i2c_write_cfg->reg_setting,
+ (sizeof(struct cam_sensor_i2c_reg_array)*
+ cci_i2c_write_cfg->size));
+
+ cci_i2c_write_cfg_w->addr_type = cci_i2c_write_cfg->addr_type;
+ cci_i2c_write_cfg_w->addr_type = cci_i2c_write_cfg->addr_type;
+ cci_i2c_write_cfg_w->data_type = cci_i2c_write_cfg->data_type;
+ cci_i2c_write_cfg_w->size = cci_i2c_write_cfg->size;
+ cci_i2c_write_cfg_w->delay = cci_i2c_write_cfg->delay;
+
+ queue_work(cci_dev->write_wq[write_async->queue], &write_async->work);
+
+ return rc;
+}
+
+static int32_t cam_cci_read_bytes(struct v4l2_subdev *sd,
+ struct cam_cci_ctrl *c_ctrl)
+{
+ int32_t rc = 0;
+ struct cci_device *cci_dev = NULL;
+ enum cci_i2c_master_t master;
+ struct cam_cci_read_cfg *read_cfg = NULL;
+ uint16_t read_bytes = 0;
+
+ if (!sd || !c_ctrl) {
+ pr_err("%s:%d sd %pK c_ctrl %pK\n", __func__,
+ __LINE__, sd, c_ctrl);
+ return -EINVAL;
+ }
+ if (!c_ctrl->cci_info) {
+ pr_err("%s:%d cci_info NULL\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ cci_dev = v4l2_get_subdevdata(sd);
+ if (!cci_dev) {
+ pr_err("%s:%d cci_dev NULL\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ if (cci_dev->cci_state != CCI_STATE_ENABLED) {
+ pr_err("%s invalid cci state %d\n",
+ __func__, cci_dev->cci_state);
+ return -EINVAL;
+ }
+
+ if (c_ctrl->cci_info->cci_i2c_master >= MASTER_MAX
+ || c_ctrl->cci_info->cci_i2c_master < 0) {
+ pr_err("%s:%d Invalid I2C master addr\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ master = c_ctrl->cci_info->cci_i2c_master;
+ read_cfg = &c_ctrl->cfg.cci_i2c_read_cfg;
+ if ((!read_cfg->num_byte) || (read_cfg->num_byte > CCI_I2C_MAX_READ)) {
+ pr_err("%s:%d read num bytes 0\n", __func__, __LINE__);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+
+ read_bytes = read_cfg->num_byte;
+ do {
+ if (read_bytes > CCI_READ_MAX)
+ read_cfg->num_byte = CCI_READ_MAX;
+ else
+ read_cfg->num_byte = read_bytes;
+ rc = cam_cci_read(sd, c_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d failed rc %d\n", __func__, __LINE__, rc);
+ goto ERROR;
+ }
+ if (read_bytes > CCI_READ_MAX) {
+ read_cfg->addr += CCI_READ_MAX;
+ read_cfg->data += CCI_READ_MAX;
+ read_bytes -= CCI_READ_MAX;
+ } else {
+ read_bytes = 0;
+ }
+ } while (read_bytes);
+
+ERROR:
+ return rc;
+}
+
+static int32_t cam_cci_i2c_set_sync_prms(struct v4l2_subdev *sd,
+ struct cam_cci_ctrl *c_ctrl)
+{
+ int32_t rc = 0;
+ struct cci_device *cci_dev;
+
+ cci_dev = v4l2_get_subdevdata(sd);
+ if (!cci_dev || !c_ctrl) {
+ pr_err("%s:%d failed: invalid params %pK %pK\n", __func__,
+ __LINE__, cci_dev, c_ctrl);
+ rc = -EINVAL;
+ return rc;
+ }
+ cci_dev->cci_wait_sync_cfg = c_ctrl->cfg.cci_wait_sync_cfg;
+ cci_dev->valid_sync = cci_dev->cci_wait_sync_cfg.csid < 0 ? 0 : 1;
+
+ return rc;
+}
+
+static int32_t cam_cci_release(struct v4l2_subdev *sd)
+{
+ uint8_t rc = 0;
+ struct cci_device *cci_dev;
+
+ cci_dev = v4l2_get_subdevdata(sd);
+
+ rc = cam_cci_soc_release(cci_dev);
+ if (rc < 0) {
+ pr_err("%s:%d Failed in releasing the cci: %d\n",
+ __func__, __LINE__, rc);
+ cam_cpas_stop(cci_dev->cpas_handle);
+ return rc;
+ }
+ cam_cpas_stop(cci_dev->cpas_handle);
+
+ return rc;
+}
+
+static int32_t cam_cci_write(struct v4l2_subdev *sd,
+ struct cam_cci_ctrl *c_ctrl)
+{
+ int32_t rc = 0;
+ struct cci_device *cci_dev;
+ enum cci_i2c_master_t master;
+ struct cam_cci_master_info *cci_master_info;
+ uint32_t i;
+
+ cci_dev = v4l2_get_subdevdata(sd);
+ if (!cci_dev || !c_ctrl) {
+ pr_err("%s:%d failed: invalid params %pK %pK\n", __func__,
+ __LINE__, cci_dev, c_ctrl);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ master = c_ctrl->cci_info->cci_i2c_master;
+
+ if (c_ctrl->cci_info->cci_i2c_master >= MASTER_MAX
+ || c_ctrl->cci_info->cci_i2c_master < 0) {
+ pr_err("%s:%d Invalid I2C master addr\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ cci_master_info = &cci_dev->cci_master_info[master];
+
+ switch (c_ctrl->cmd) {
+ case MSM_CCI_I2C_WRITE_SYNC_BLOCK:
+ mutex_lock(&cci_master_info->mutex_q[SYNC_QUEUE]);
+ rc = cam_cci_i2c_write(sd, c_ctrl,
+ SYNC_QUEUE, MSM_SYNC_ENABLE);
+ mutex_unlock(&cci_master_info->mutex_q[SYNC_QUEUE]);
+ break;
+ case MSM_CCI_I2C_WRITE_SYNC:
+ rc = cam_cci_i2c_write_async(sd, c_ctrl,
+ SYNC_QUEUE, MSM_SYNC_ENABLE);
+ break;
+ case MSM_CCI_I2C_WRITE:
+ case MSM_CCI_I2C_WRITE_SEQ:
+ for (i = 0; i < NUM_QUEUES; i++) {
+ if (mutex_trylock(&cci_master_info->mutex_q[i])) {
+ rc = cam_cci_i2c_write(sd, c_ctrl, i,
+ MSM_SYNC_DISABLE);
+ mutex_unlock(&cci_master_info->mutex_q[i]);
+ return rc;
+ }
+ }
+ mutex_lock(&cci_master_info->mutex_q[PRIORITY_QUEUE]);
+ rc = cam_cci_i2c_write(sd, c_ctrl,
+ PRIORITY_QUEUE, MSM_SYNC_DISABLE);
+ mutex_unlock(&cci_master_info->mutex_q[PRIORITY_QUEUE]);
+ break;
+ case MSM_CCI_I2C_WRITE_ASYNC:
+ rc = cam_cci_i2c_write_async(sd, c_ctrl,
+ PRIORITY_QUEUE, MSM_SYNC_DISABLE);
+ break;
+ default:
+ rc = -ENOIOCTLCMD;
+ }
+
+ return rc;
+}
+
+int32_t cam_cci_core_cfg(struct v4l2_subdev *sd,
+ struct cam_cci_ctrl *cci_ctrl)
+{
+ int32_t rc = 0;
+
+ CDBG("%s line %d cmd %d\n", __func__, __LINE__,
+ cci_ctrl->cmd);
+ switch (cci_ctrl->cmd) {
+ case MSM_CCI_INIT:
+ rc = cam_cci_init(sd, cci_ctrl);
+ break;
+ case MSM_CCI_RELEASE:
+ rc = cam_cci_release(sd);
+ break;
+ case MSM_CCI_I2C_READ:
+ rc = cam_cci_read_bytes(sd, cci_ctrl);
+ break;
+ case MSM_CCI_I2C_WRITE:
+ case MSM_CCI_I2C_WRITE_SEQ:
+ case MSM_CCI_I2C_WRITE_SYNC:
+ case MSM_CCI_I2C_WRITE_ASYNC:
+ case MSM_CCI_I2C_WRITE_SYNC_BLOCK:
+ rc = cam_cci_write(sd, cci_ctrl);
+ break;
+ case MSM_CCI_GPIO_WRITE:
+ break;
+ case MSM_CCI_SET_SYNC_CID:
+ rc = cam_cci_i2c_set_sync_prms(sd, cci_ctrl);
+ break;
+
+ default:
+ rc = -ENOIOCTLCMD;
+ }
+
+ cci_ctrl->status = rc;
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_core.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_core.h
new file mode 100644
index 0000000..f6e82dc
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_core.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _CAM_CCI_CORE_H_
+#define _CAM_CCI_CORE_H_
+
+#include <linux/irqreturn.h>
+#include <media/cam_sensor.h>
+#include "cam_cci_dev.h"
+#include "cam_cci_soc.h"
+
+/**
+ * @cci_dev: CCI device structure
+ * @c_ctrl: CCI control structure
+ *
+ * This API gets CCI clk rates
+ */
+uint32_t *cam_cci_get_clk_rates(struct cci_device *cci_dev,
+ struct cam_cci_ctrl *c_ctrl);
+
+/**
+ * @sd: V4L2 sub device
+ * @c_ctrl: CCI control structure
+ *
+ * This API handles I2C operations for CCI
+ */
+int32_t cam_cci_core_cfg(struct v4l2_subdev *sd,
+ struct cam_cci_ctrl *cci_ctrl);
+
+/**
+ * @irq_num: IRQ number
+ * @data: CCI private structure
+ *
+ * This API handles CCI IRQs
+ */
+irqreturn_t cam_cci_irq(int irq_num, void *data);
+
+#endif /* _CAM_CCI_CORE_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.c
new file mode 100644
index 0000000..789522d
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.c
@@ -0,0 +1,276 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "cam_cci_dev.h"
+#include "cam_req_mgr_dev.h"
+#include "cam_cci_soc.h"
+#include "cam_cci_core.h"
+
+#define CCI_MAX_DELAY 1000000
+#define CCI_TIMEOUT msecs_to_jiffies(500)
+
+static struct v4l2_subdev *g_cci_subdev;
+
+struct v4l2_subdev *cam_cci_get_subdev(void)
+{
+ return g_cci_subdev;
+}
+
+static long cam_cci_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ int32_t rc = 0;
+
+ switch (cmd) {
+ case VIDIOC_MSM_CCI_CFG:
+ rc = cam_cci_core_cfg(sd, arg);
+ break;
+ default:
+ pr_err("%s:%d Invalid ioctl cmd: %d\n",
+ __func__, __LINE__, cmd);
+ rc = -ENOIOCTLCMD;
+ }
+
+ return rc;
+}
+
+irqreturn_t cam_cci_irq(int irq_num, void *data)
+{
+ uint32_t irq;
+ struct cci_device *cci_dev = data;
+
+ irq = cam_io_r_mb(cci_dev->base + CCI_IRQ_STATUS_0_ADDR);
+ cam_io_w_mb(irq, cci_dev->base + CCI_IRQ_CLEAR_0_ADDR);
+ cam_io_w_mb(0x1, cci_dev->base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+
+ if (irq & CCI_IRQ_STATUS_0_RST_DONE_ACK_BMSK) {
+ if (cci_dev->cci_master_info[MASTER_0].reset_pending == TRUE) {
+ cci_dev->cci_master_info[MASTER_0].reset_pending =
+ FALSE;
+ complete(&cci_dev->cci_master_info[MASTER_0].
+ reset_complete);
+ }
+ if (cci_dev->cci_master_info[MASTER_1].reset_pending == TRUE) {
+ cci_dev->cci_master_info[MASTER_1].reset_pending =
+ FALSE;
+ complete(&cci_dev->cci_master_info[MASTER_1].
+ reset_complete);
+ }
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK) {
+ cci_dev->cci_master_info[MASTER_0].status = 0;
+ complete(&cci_dev->cci_master_info[MASTER_0].reset_complete);
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK) {
+ struct cam_cci_master_info *cci_master_info;
+
+ cci_master_info = &cci_dev->cci_master_info[MASTER_0];
+ atomic_set(&cci_master_info->q_free[QUEUE_0], 0);
+ cci_master_info->status = 0;
+ if (atomic_read(&cci_master_info->done_pending[QUEUE_0]) == 1) {
+ complete(&cci_master_info->report_q[QUEUE_0]);
+ atomic_set(&cci_master_info->done_pending[QUEUE_0], 0);
+ }
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK) {
+ struct cam_cci_master_info *cci_master_info;
+
+ cci_master_info = &cci_dev->cci_master_info[MASTER_0];
+ atomic_set(&cci_master_info->q_free[QUEUE_1], 0);
+ cci_master_info->status = 0;
+ if (atomic_read(&cci_master_info->done_pending[QUEUE_1]) == 1) {
+ complete(&cci_master_info->report_q[QUEUE_1]);
+ atomic_set(&cci_master_info->done_pending[QUEUE_1], 0);
+ }
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK) {
+ cci_dev->cci_master_info[MASTER_1].status = 0;
+ complete(&cci_dev->cci_master_info[MASTER_1].reset_complete);
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK) {
+ struct cam_cci_master_info *cci_master_info;
+
+ cci_master_info = &cci_dev->cci_master_info[MASTER_1];
+ atomic_set(&cci_master_info->q_free[QUEUE_0], 0);
+ cci_master_info->status = 0;
+ if (atomic_read(&cci_master_info->done_pending[QUEUE_0]) == 1) {
+ complete(&cci_master_info->report_q[QUEUE_0]);
+ atomic_set(&cci_master_info->done_pending[QUEUE_0], 0);
+ }
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK) {
+ struct cam_cci_master_info *cci_master_info;
+
+ cci_master_info = &cci_dev->cci_master_info[MASTER_1];
+ atomic_set(&cci_master_info->q_free[QUEUE_1], 0);
+ cci_master_info->status = 0;
+ if (atomic_read(&cci_master_info->done_pending[QUEUE_1]) == 1) {
+ complete(&cci_master_info->report_q[QUEUE_1]);
+ atomic_set(&cci_master_info->done_pending[QUEUE_1], 0);
+ }
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK) {
+ cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE;
+ cam_io_w_mb(CCI_M0_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK_BMSK) {
+ cci_dev->cci_master_info[MASTER_1].reset_pending = TRUE;
+ cam_io_w_mb(CCI_M1_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M0_ERROR_BMSK) {
+ pr_err("%s:%d MASTER_0 error 0x%x\n", __func__, __LINE__, irq);
+ cci_dev->cci_master_info[MASTER_0].status = -EINVAL;
+ cam_io_w_mb(CCI_M0_HALT_REQ_RMSK,
+ cci_dev->base + CCI_HALT_REQ_ADDR);
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M1_ERROR_BMSK) {
+ pr_err("%s:%d MASTER_1 error 0x%x\n", __func__, __LINE__, irq);
+ cci_dev->cci_master_info[MASTER_1].status = -EINVAL;
+ cam_io_w_mb(CCI_M1_HALT_REQ_RMSK,
+ cci_dev->base + CCI_HALT_REQ_ADDR);
+ }
+ return IRQ_HANDLED;
+}
+
+static int cam_cci_irq_routine(struct v4l2_subdev *sd, u32 status,
+ bool *handled)
+{
+ struct cci_device *cci_dev = v4l2_get_subdevdata(sd);
+ irqreturn_t ret;
+
+ ret = cam_cci_irq(cci_dev->irq->start, cci_dev);
+ *handled = TRUE;
+ return 0;
+}
+
+static struct v4l2_subdev_core_ops cci_subdev_core_ops = {
+ .ioctl = cam_cci_subdev_ioctl,
+ .interrupt_service_routine = cam_cci_irq_routine,
+};
+
+static const struct v4l2_subdev_ops cci_subdev_ops = {
+ .core = &cci_subdev_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops cci_subdev_intern_ops;
+
+static int cam_cci_platform_probe(struct platform_device *pdev)
+{
+ struct cam_cpas_register_params cpas_parms;
+ struct cci_device *new_cci_dev;
+ int rc = 0;
+
+ new_cci_dev = kzalloc(sizeof(struct cci_device),
+ GFP_KERNEL);
+ if (!new_cci_dev)
+ return -ENOMEM;
+
+ new_cci_dev->v4l2_dev_str.pdev = pdev;
+
+ rc = cam_cci_parse_dt_info(pdev, new_cci_dev);
+ if (rc < 0) {
+ pr_err("%s: %d Resource get Failed: %d\n",
+ __func__, __LINE__, rc);
+ goto cci_no_resource;
+ }
+
+ new_cci_dev->v4l2_dev_str.internal_ops =
+ &cci_subdev_intern_ops;
+ new_cci_dev->v4l2_dev_str.ops =
+ &cci_subdev_ops;
+ strlcpy(new_cci_dev->device_name, CAMX_CCI_DEV_NAME,
+ sizeof(new_cci_dev->device_name));
+ new_cci_dev->v4l2_dev_str.name =
+ new_cci_dev->device_name;
+ new_cci_dev->v4l2_dev_str.sd_flags =
+ (V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS);
+ new_cci_dev->v4l2_dev_str.ent_function =
+ CAM_CCI_DEVICE_TYPE;
+ new_cci_dev->v4l2_dev_str.token =
+ new_cci_dev;
+
+ rc = cam_register_subdev(&(new_cci_dev->v4l2_dev_str));
+ if (rc < 0) {
+ pr_err("%s:%d :Error: Fail with cam_register_subdev\n",
+ __func__, __LINE__);
+ goto cci_no_resource;
+ }
+
+ platform_set_drvdata(pdev, &(new_cci_dev->v4l2_dev_str.sd));
+ v4l2_set_subdevdata(&new_cci_dev->v4l2_dev_str.sd, new_cci_dev);
+ g_cci_subdev = &new_cci_dev->v4l2_dev_str.sd;
+
+ cpas_parms.cam_cpas_client_cb = NULL;
+ cpas_parms.cell_index = 0;
+ cpas_parms.dev = &pdev->dev;
+ cpas_parms.userdata = new_cci_dev;
+ strlcpy(cpas_parms.identifier, "cci", CAM_HW_IDENTIFIER_LENGTH);
+ rc = cam_cpas_register_client(&cpas_parms);
+ if (rc) {
+ pr_err("%s:%d CPAS registration failed\n", __func__, __LINE__);
+ goto cci_no_resource;
+ }
+ CDBG("CPAS registration successful handle=%d\n",
+ cpas_parms.client_handle);
+ new_cci_dev->cpas_handle = cpas_parms.client_handle;
+
+ return rc;
+cci_no_resource:
+ kfree(new_cci_dev);
+ return rc;
+}
+
+static int cam_cci_device_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
+ struct cci_device *cci_dev =
+ v4l2_get_subdevdata(subdev);
+
+ cam_cpas_unregister_client(cci_dev->cpas_handle);
+ cam_cci_soc_remove(pdev, cci_dev);
+ devm_kfree(&pdev->dev, cci_dev);
+ return 0;
+}
+
+static const struct of_device_id cam_cci_dt_match[] = {
+ {.compatible = "qcom,cci"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, cam_cci_dt_match);
+
+static struct platform_driver cci_driver = {
+ .probe = cam_cci_platform_probe,
+ .remove = cam_cci_device_remove,
+ .driver = {
+ .name = CAMX_CCI_DEV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = cam_cci_dt_match,
+ },
+};
+
+static int __init cam_cci_init_module(void)
+{
+ return platform_driver_register(&cci_driver);
+}
+
+static void __exit cam_cci_exit_module(void)
+{
+ platform_driver_unregister(&cci_driver);
+}
+
+module_init(cam_cci_init_module);
+module_exit(cam_cci_exit_module);
+MODULE_DESCRIPTION("MSM CCI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.h
new file mode 100644
index 0000000..996fc62
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.h
@@ -0,0 +1,322 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_CCI_DEV_H_
+#define _CAM_CCI_DEV_H_
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/module.h>
+#include <linux/irqreturn.h>
+#include <linux/ion.h>
+#include <linux/iommu.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <media/cam_sensor.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
+#include <cam_sensor_cmn_header.h>
+#include <cam_sensor_soc_api.h>
+#include <cam_io_util.h>
+#include <cam_sensor_util.h>
+#include <cam_subdev.h>
+#include <cam_cpas_api.h>
+#include "cam_cci_hwreg.h"
+
+#define V4L2_IDENT_CCI 50005
+#define CCI_I2C_QUEUE_0_SIZE 128
+#define CCI_I2C_QUEUE_1_SIZE 32
+#define CYCLES_PER_MICRO_SEC_DEFAULT 4915
+#define CCI_MAX_DELAY 1000000
+
+#define CCI_TIMEOUT msecs_to_jiffies(500)
+
+#define NUM_MASTERS 2
+#define NUM_QUEUES 2
+
+#define TRUE 1
+#define FALSE 0
+
+#define CCI_PINCTRL_STATE_DEFAULT "cci_default"
+#define CCI_PINCTRL_STATE_SLEEP "cci_suspend"
+
+#define CCI_NUM_CLK_MAX 16
+#define CCI_NUM_CLK_CASES 5
+#define CCI_CLK_SRC_NAME "cci_src_clk"
+#define MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_10 10
+#define MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_11 11
+#define BURST_MIN_FREE_SIZE 8
+#define MAX_LRME_V4l2_EVENTS 30
+
+/* Max bytes that can be read per CCI read transaction */
+#define CCI_READ_MAX 12
+#define CCI_I2C_READ_MAX_RETRIES 3
+#define CCI_I2C_MAX_READ 8192
+#define CCI_I2C_MAX_WRITE 8192
+
+#define CAMX_CCI_DEV_NAME "cam-cci-driver"
+
+/* Max bytes that can be read per CCI read transaction */
+#define CCI_READ_MAX 12
+#define CCI_I2C_READ_MAX_RETRIES 3
+#define CCI_I2C_MAX_READ 8192
+#define CCI_I2C_MAX_WRITE 8192
+
+#define PRIORITY_QUEUE (QUEUE_0)
+#define SYNC_QUEUE (QUEUE_1)
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+#undef CCI_DBG
+#ifdef MSM_CCI_DEBUG
+#define CCI_DBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CCI_DBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+enum cci_i2c_sync {
+ MSM_SYNC_DISABLE,
+ MSM_SYNC_ENABLE,
+};
+
+enum cam_cci_cmd_type {
+ MSM_CCI_INIT,
+ MSM_CCI_RELEASE,
+ MSM_CCI_SET_SID,
+ MSM_CCI_SET_FREQ,
+ MSM_CCI_SET_SYNC_CID,
+ MSM_CCI_I2C_READ,
+ MSM_CCI_I2C_WRITE,
+ MSM_CCI_I2C_WRITE_SEQ,
+ MSM_CCI_I2C_WRITE_ASYNC,
+ MSM_CCI_GPIO_WRITE,
+ MSM_CCI_I2C_WRITE_SYNC,
+ MSM_CCI_I2C_WRITE_SYNC_BLOCK,
+};
+
+enum cci_i2c_queue_t {
+ QUEUE_0,
+ QUEUE_1,
+ QUEUE_INVALID,
+};
+
+struct cam_cci_wait_sync_cfg {
+ uint16_t cid;
+ int16_t csid;
+ uint16_t line;
+ uint16_t delay;
+};
+
+struct cam_cci_gpio_cfg {
+ uint16_t gpio_queue;
+ uint16_t i2c_queue;
+};
+
+struct cam_cci_read_cfg {
+ uint32_t addr;
+ uint16_t addr_type;
+ uint8_t *data;
+ uint16_t num_byte;
+};
+
+struct cam_cci_i2c_queue_info {
+ uint32_t max_queue_size;
+ uint32_t report_id;
+ uint32_t irq_en;
+ uint32_t capture_rep_data;
+};
+
+struct cam_cci_master_info {
+ uint32_t status;
+ atomic_t q_free[NUM_QUEUES];
+ uint8_t q_lock[NUM_QUEUES];
+ uint8_t reset_pending;
+ struct mutex mutex;
+ struct completion reset_complete;
+ struct mutex mutex_q[NUM_QUEUES];
+ struct completion report_q[NUM_QUEUES];
+ atomic_t done_pending[NUM_QUEUES];
+};
+
+struct cam_cci_clk_params_t {
+ uint16_t hw_thigh;
+ uint16_t hw_tlow;
+ uint16_t hw_tsu_sto;
+ uint16_t hw_tsu_sta;
+ uint16_t hw_thd_dat;
+ uint16_t hw_thd_sta;
+ uint16_t hw_tbuf;
+ uint8_t hw_scl_stretch_en;
+ uint8_t hw_trdhld;
+ uint8_t hw_tsp;
+ uint32_t cci_clk_src;
+};
+
+enum cam_cci_state_t {
+ CCI_STATE_ENABLED,
+ CCI_STATE_DISABLED,
+};
+
+/**
+ * struct cci_device
+ * @pdev: Platform device
+ * @subdev: V4L2 sub device
+ * @base: Base address of CCI device
+ * @hw_version: Hardware version
+ * @ref_count: Reference Count
+ * @cci_state: CCI state machine
+ * @num_clk: Number of CCI clock
+ * @cci_clk: CCI clock structure
+ * @cci_clk_info: CCI clock information
+ * @cam_cci_i2c_queue_info: CCI queue information
+ * @i2c_freq_mode: I2C frequency of operations
+ * @cci_clk_params: CCI hw clk params
+ * @cci_gpio_tbl: CCI GPIO table
+ * @cci_gpio_tbl_size: GPIO table size
+ * @cci_pinctrl: Pinctrl structure
+ * @cci_pinctrl_status: CCI pinctrl status
+ * @cci_clk_src: CCI clk src rate
+ * @cci_vreg: CCI regulator structure
+ * @cci_reg_ptr: CCI individual regulator structure
+ * @regulator_count: Regulator count
+ * @support_seq_write:
+ * Set this flag when sequential write is enabled
+ * @write_wq: Work queue structure
+ * @valid_sync: Is it a valid sync with CSID
+ * @v4l2_dev_str: V4L2 device structure
+ * @cci_wait_sync_cfg: CCI sync config
+ * @cycles_per_us: Cycles per micro sec
+ * @payload_size: CCI packet payload size
+ */
+struct cci_device {
+ struct v4l2_subdev subdev;
+ struct resource *irq;
+ void __iomem *base;
+ uint32_t hw_version;
+ uint8_t ref_count;
+ enum cam_cci_state_t cci_state;
+ size_t num_clk;
+ struct clk **cci_clk;
+ struct msm_cam_clk_info *cci_clk_info;
+ struct cam_cci_i2c_queue_info
+ cci_i2c_queue_info[NUM_MASTERS][NUM_QUEUES];
+ struct cam_cci_master_info cci_master_info[NUM_MASTERS];
+ enum i2c_freq_mode i2c_freq_mode[NUM_MASTERS];
+ struct cam_cci_clk_params_t cci_clk_params[I2C_MAX_MODES];
+ struct gpio *cci_gpio_tbl;
+ uint8_t cci_gpio_tbl_size;
+ struct msm_pinctrl_info cci_pinctrl;
+ uint8_t cci_pinctrl_status;
+ uint32_t cci_clk_src;
+ struct camera_vreg_t *cci_vreg;
+ struct regulator *cci_reg_ptr[MAX_REGULATOR];
+ int32_t regulator_count;
+ uint8_t support_seq_write;
+ struct workqueue_struct *write_wq[MASTER_MAX];
+ struct cam_cci_wait_sync_cfg cci_wait_sync_cfg;
+ uint8_t valid_sync;
+ struct cam_subdev v4l2_dev_str;
+ uint32_t cycles_per_us;
+ uint8_t payload_size;
+ size_t num_clk_cases;
+ uint32_t **cci_clk_rates;
+ char device_name[20];
+ uint32_t cpas_handle;
+};
+
+enum cam_cci_i2c_cmd_type {
+ CCI_I2C_SET_PARAM_CMD = 1,
+ CCI_I2C_WAIT_CMD,
+ CCI_I2C_WAIT_SYNC_CMD,
+ CCI_I2C_WAIT_GPIO_EVENT_CMD,
+ CCI_I2C_TRIG_I2C_EVENT_CMD,
+ CCI_I2C_LOCK_CMD,
+ CCI_I2C_UNLOCK_CMD,
+ CCI_I2C_REPORT_CMD,
+ CCI_I2C_WRITE_CMD,
+ CCI_I2C_READ_CMD,
+ CCI_I2C_WRITE_DISABLE_P_CMD,
+ CCI_I2C_READ_DISABLE_P_CMD,
+ CCI_I2C_WRITE_CMD2,
+ CCI_I2C_WRITE_CMD3,
+ CCI_I2C_REPEAT_CMD,
+ CCI_I2C_INVALID_CMD,
+};
+
+enum cam_cci_gpio_cmd_type {
+ CCI_GPIO_SET_PARAM_CMD = 1,
+ CCI_GPIO_WAIT_CMD,
+ CCI_GPIO_WAIT_SYNC_CMD,
+ CCI_GPIO_WAIT_GPIO_IN_EVENT_CMD,
+ CCI_GPIO_WAIT_I2C_Q_TRIG_EVENT_CMD,
+ CCI_GPIO_OUT_CMD,
+ CCI_GPIO_TRIG_EVENT_CMD,
+ CCI_GPIO_REPORT_CMD,
+ CCI_GPIO_REPEAT_CMD,
+ CCI_GPIO_CONTINUE_CMD,
+ CCI_GPIO_INVALID_CMD,
+};
+
+struct cam_sensor_cci_client {
+ struct v4l2_subdev *cci_subdev;
+ uint32_t freq;
+ enum i2c_freq_mode i2c_freq_mode;
+ enum cci_i2c_master_t cci_i2c_master;
+ uint16_t sid;
+ uint16_t cid;
+ uint32_t timeout;
+ uint16_t retries;
+ uint16_t id_map;
+};
+
+struct cam_cci_ctrl {
+ int32_t status;
+ struct cam_sensor_cci_client *cci_info;
+ enum cam_cci_cmd_type cmd;
+ union {
+ struct cam_sensor_i2c_reg_setting cci_i2c_write_cfg;
+ struct cam_cci_read_cfg cci_i2c_read_cfg;
+ struct cam_cci_wait_sync_cfg cci_wait_sync_cfg;
+ struct cam_cci_gpio_cfg gpio_cfg;
+ } cfg;
+};
+
+struct cci_write_async {
+ struct cci_device *cci_dev;
+ struct cam_cci_ctrl c_ctrl;
+ enum cci_i2c_queue_t queue;
+ struct work_struct work;
+ enum cci_i2c_sync sync_en;
+};
+
+irqreturn_t cam_cci_irq(int irq_num, void *data);
+
+#ifdef CONFIG_SPECTRA_CAMERA
+struct v4l2_subdev *cam_cci_get_subdev(void);
+#else
+static inline struct v4l2_subdev *cam_cci_get_subdev(void)
+{
+ return NULL;
+}
+#endif
+
+#define VIDIOC_MSM_CCI_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 23, struct cam_cci_ctrl *)
+
+#endif /* _CAM_CCI_DEV_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_hwreg.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_hwreg.h
new file mode 100644
index 0000000..c18593e
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_hwreg.h
@@ -0,0 +1,69 @@
+/* Copyright (c) 2012-2015, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_CCI_HWREG_
+#define _CAM_CCI_HWREG_
+
+#define CCI_HW_VERSION_ADDR 0x00000000
+#define CCI_RESET_CMD_ADDR 0x00000004
+#define CCI_RESET_CMD_RMSK 0x0f73f3f7
+#define CCI_M0_RESET_RMSK 0x3F1
+#define CCI_M1_RESET_RMSK 0x3F001
+#define CCI_QUEUE_START_ADDR 0x00000008
+#define CCI_SET_CID_SYNC_TIMER_ADDR 0x00000010
+#define CCI_SET_CID_SYNC_TIMER_OFFSET 0x00000004
+#define CCI_I2C_M0_SCL_CTL_ADDR 0x00000100
+#define CCI_I2C_M0_SDA_CTL_0_ADDR 0x00000104
+#define CCI_I2C_M0_SDA_CTL_1_ADDR 0x00000108
+#define CCI_I2C_M0_SDA_CTL_2_ADDR 0x0000010c
+#define CCI_I2C_M0_READ_DATA_ADDR 0x00000118
+#define CCI_I2C_M0_MISC_CTL_ADDR 0x00000110
+#define CCI_I2C_M0_READ_BUF_LEVEL_ADDR 0x0000011C
+#define CCI_HALT_REQ_ADDR 0x00000034
+#define CCI_M0_HALT_REQ_RMSK 0x1
+#define CCI_M1_HALT_REQ_RMSK 0x2
+#define CCI_I2C_M1_SCL_CTL_ADDR 0x00000200
+#define CCI_I2C_M1_SDA_CTL_0_ADDR 0x00000204
+#define CCI_I2C_M1_SDA_CTL_1_ADDR 0x00000208
+#define CCI_I2C_M1_SDA_CTL_2_ADDR 0x0000020c
+#define CCI_I2C_M1_MISC_CTL_ADDR 0x00000210
+#define CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR 0x00000304
+#define CCI_I2C_M0_Q0_CUR_CMD_ADDR 0x00000308
+#define CCI_I2C_M0_Q0_REPORT_STATUS_ADDR 0x0000030c
+#define CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR 0x00000300
+#define CCI_I2C_M0_Q0_LOAD_DATA_ADDR 0x00000310
+#define CCI_IRQ_MASK_0_ADDR 0x00000c04
+#define CCI_IRQ_MASK_0_RMSK 0x7fff7ff7
+#define CCI_IRQ_CLEAR_0_ADDR 0x00000c08
+#define CCI_IRQ_STATUS_0_ADDR 0x00000c0c
+#define CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK_BMSK 0x4000000
+#define CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK 0x2000000
+#define CCI_IRQ_STATUS_0_RST_DONE_ACK_BMSK 0x1000000
+#define CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK 0x100000
+#define CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK 0x10000
+#define CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK 0x1000
+#define CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK 0x100
+#define CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK 0x10
+#define CCI_IRQ_STATUS_0_I2C_M0_ERROR_BMSK 0x18000EE6
+#define CCI_IRQ_STATUS_0_I2C_M1_ERROR_BMSK 0x60EE6000
+#define CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK 0x1
+#define CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR 0x00000c00
+
+#define DEBUG_TOP_REG_START 0x0
+#define DEBUG_TOP_REG_COUNT 14
+#define DEBUG_MASTER_REG_START 0x100
+#define DEBUG_MASTER_REG_COUNT 8
+#define DEBUG_MASTER_QUEUE_REG_START 0x300
+#define DEBUG_MASTER_QUEUE_REG_COUNT 6
+#define DEBUG_INTR_REG_START 0xC00
+#define DEBUG_INTR_REG_COUNT 7
+#endif /* _CAM_CCI_HWREG_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_soc.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_soc.c
new file mode 100644
index 0000000..59cdfaa
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_soc.c
@@ -0,0 +1,624 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "cam_cci_dev.h"
+#include "cam_cci_core.h"
+
+static int32_t cam_cci_pinctrl_init(struct cci_device *cci_dev)
+{
+ struct msm_pinctrl_info *cci_pctrl = NULL;
+
+ cci_pctrl = &cci_dev->cci_pinctrl;
+ cci_pctrl->pinctrl = devm_pinctrl_get(&cci_dev->v4l2_dev_str.pdev->dev);
+ if (IS_ERR_OR_NULL(cci_pctrl->pinctrl)) {
+ pr_err("%s:%d devm_pinctrl_get cci_pinctrl failed\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ cci_pctrl->gpio_state_active = pinctrl_lookup_state(
+ cci_pctrl->pinctrl,
+ CCI_PINCTRL_STATE_DEFAULT);
+ if (IS_ERR_OR_NULL(cci_pctrl->gpio_state_active)) {
+ pr_err("%s:%d look up state for active state failed\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ cci_pctrl->gpio_state_suspend = pinctrl_lookup_state(
+ cci_pctrl->pinctrl,
+ CCI_PINCTRL_STATE_SLEEP);
+ if (IS_ERR_OR_NULL(cci_pctrl->gpio_state_suspend)) {
+ pr_err("%s:%d look up state for suspend state failed\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int cam_cci_init(struct v4l2_subdev *sd,
+ struct cam_cci_ctrl *c_ctrl)
+{
+ uint8_t i = 0, j = 0;
+ int32_t rc = 0, ret = 0;
+ struct cci_device *cci_dev;
+ enum cci_i2c_master_t master = MASTER_0;
+ uint32_t *clk_rates = NULL;
+ struct cam_ahb_vote ahb_vote;
+ struct cam_axi_vote axi_vote;
+
+ cci_dev = v4l2_get_subdevdata(sd);
+ if (!cci_dev || !c_ctrl) {
+ pr_err("%s:%d failed: invalid params %pK %pK\n", __func__,
+ __LINE__, cci_dev, c_ctrl);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ if (cci_dev->ref_count++) {
+ CDBG("%s ref_count %d\n", __func__, cci_dev->ref_count);
+ master = c_ctrl->cci_info->cci_i2c_master;
+ CDBG("%s:%d master %d\n", __func__, __LINE__, master);
+ if (master < MASTER_MAX && master >= 0) {
+ mutex_lock(&cci_dev->cci_master_info[master].mutex);
+ flush_workqueue(cci_dev->write_wq[master]);
+ /* Re-initialize the completion */
+ reinit_completion(&cci_dev->
+ cci_master_info[master].reset_complete);
+ for (i = 0; i < NUM_QUEUES; i++)
+ reinit_completion(&cci_dev->
+ cci_master_info[master].report_q[i]);
+ /* Set reset pending flag to TRUE */
+ cci_dev->cci_master_info[master].reset_pending = TRUE;
+ /* Set proper mask to RESET CMD address */
+ if (master == MASTER_0)
+ cam_io_w_mb(CCI_M0_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ else
+ cam_io_w_mb(CCI_M1_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ /* wait for reset done irq */
+ rc = wait_for_completion_timeout(
+ &cci_dev->cci_master_info[master].
+ reset_complete,
+ CCI_TIMEOUT);
+ if (rc <= 0)
+ pr_err("%s:%d wait failed %d\n", __func__,
+ __LINE__, rc);
+ mutex_unlock(&cci_dev->cci_master_info[master].mutex);
+ }
+ return 0;
+ }
+
+ ahb_vote.type = CAM_VOTE_ABSOLUTE;
+ ahb_vote.vote.level = CAM_SVS_VOTE;
+ axi_vote.compressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
+ axi_vote.uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
+
+ rc = cam_cpas_start(cci_dev->cpas_handle,
+ &ahb_vote, &axi_vote);
+ if (rc != 0) {
+ pr_err("%s:%d CPAS start failed\n",
+ __func__, __LINE__);
+ }
+
+ ret = cam_cci_pinctrl_init(cci_dev);
+ if (ret < 0) {
+ pr_err("%s:%d Initialization of pinctrl failed\n",
+ __func__, __LINE__);
+ cci_dev->cci_pinctrl_status = 0;
+ } else {
+ cci_dev->cci_pinctrl_status = 1;
+ }
+ rc = msm_camera_request_gpio_table(cci_dev->cci_gpio_tbl,
+ cci_dev->cci_gpio_tbl_size, 1);
+ if (cci_dev->cci_pinctrl_status) {
+ ret = pinctrl_select_state(cci_dev->cci_pinctrl.pinctrl,
+ cci_dev->cci_pinctrl.gpio_state_active);
+ if (ret)
+ pr_err("%s:%d cannot set pin to active state\n",
+ __func__, __LINE__);
+ }
+ if (rc < 0) {
+ CDBG("%s: request gpio failed\n", __func__);
+ goto request_gpio_failed;
+ }
+
+ rc = msm_camera_config_vreg(&cci_dev->v4l2_dev_str.pdev->dev,
+ cci_dev->cci_vreg, cci_dev->regulator_count, NULL, 0,
+ &cci_dev->cci_reg_ptr[0], 1);
+ if (rc < 0) {
+ pr_err("%s:%d cci config_vreg failed\n", __func__, __LINE__);
+ goto clk_enable_failed;
+ }
+
+ rc = msm_camera_enable_vreg(&cci_dev->v4l2_dev_str.pdev->dev,
+ cci_dev->cci_vreg, cci_dev->regulator_count, NULL, 0,
+ &cci_dev->cci_reg_ptr[0], 1);
+ if (rc < 0) {
+ pr_err("%s:%d cci enable_vreg failed\n", __func__, __LINE__);
+ goto reg_enable_failed;
+ }
+
+ clk_rates = cam_cci_get_clk_rates(cci_dev, c_ctrl);
+ if (!clk_rates) {
+ pr_err("%s: clk enable failed\n", __func__);
+ goto reg_enable_failed;
+ }
+
+ for (i = 0; i < cci_dev->num_clk; i++) {
+ cci_dev->cci_clk_info[i].clk_rate =
+ clk_rates[i];
+ }
+ rc = msm_camera_clk_enable(&cci_dev->v4l2_dev_str.pdev->dev,
+ cci_dev->cci_clk_info, cci_dev->cci_clk,
+ cci_dev->num_clk, true);
+ if (rc < 0) {
+ pr_err("%s: clk enable failed\n", __func__);
+ goto reg_enable_failed;
+ }
+
+ /* Re-initialize the completion */
+ reinit_completion(&cci_dev->cci_master_info[master].reset_complete);
+ for (i = 0; i < NUM_QUEUES; i++)
+ reinit_completion(&cci_dev->cci_master_info[master].
+ report_q[i]);
+ rc = msm_camera_enable_irq(cci_dev->irq, true);
+ if (rc < 0) {
+ pr_err("%s: irq enable failed\n", __func__);
+ return -EINVAL;
+ }
+ cci_dev->hw_version = cam_io_r_mb(cci_dev->base +
+ CCI_HW_VERSION_ADDR);
+ CDBG("%s:%d: hw_version = 0x%x\n", __func__, __LINE__,
+ cci_dev->hw_version);
+
+ cci_dev->payload_size =
+ MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_11;
+ cci_dev->support_seq_write = 1;
+
+ for (i = 0; i < NUM_MASTERS; i++) {
+ for (j = 0; j < NUM_QUEUES; j++) {
+ if (j == QUEUE_0)
+ cci_dev->cci_i2c_queue_info[i][j].
+ max_queue_size =
+ CCI_I2C_QUEUE_0_SIZE;
+ else
+ cci_dev->cci_i2c_queue_info[i][j].
+ max_queue_size =
+ CCI_I2C_QUEUE_1_SIZE;
+
+ CDBG("CCI Master[%d] :: Q0 size: %d Q1 size: %d\n", i,
+ cci_dev->cci_i2c_queue_info[i][j].
+ max_queue_size,
+ cci_dev->cci_i2c_queue_info[i][j].
+ max_queue_size);
+ }
+ }
+
+ cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE;
+ cam_io_w_mb(CCI_RESET_CMD_RMSK, cci_dev->base +
+ CCI_RESET_CMD_ADDR);
+ cam_io_w_mb(0x1, cci_dev->base + CCI_RESET_CMD_ADDR);
+ rc = wait_for_completion_timeout(
+ &cci_dev->cci_master_info[MASTER_0].reset_complete,
+ CCI_TIMEOUT);
+ if (rc <= 0) {
+ pr_err("%s: wait_for_completion_timeout %d\n",
+ __func__, __LINE__);
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ goto reset_complete_failed;
+ }
+ for (i = 0; i < MASTER_MAX; i++)
+ cci_dev->i2c_freq_mode[i] = I2C_MAX_MODES;
+ cam_io_w_mb(CCI_IRQ_MASK_0_RMSK,
+ cci_dev->base + CCI_IRQ_MASK_0_ADDR);
+ cam_io_w_mb(CCI_IRQ_MASK_0_RMSK,
+ cci_dev->base + CCI_IRQ_CLEAR_0_ADDR);
+ cam_io_w_mb(0x1, cci_dev->base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+
+ for (i = 0; i < MASTER_MAX; i++) {
+ if (!cci_dev->write_wq[i]) {
+ pr_err("Failed to flush write wq\n");
+ rc = -ENOMEM;
+ goto reset_complete_failed;
+ } else {
+ flush_workqueue(cci_dev->write_wq[i]);
+ }
+ }
+ cci_dev->cci_state = CCI_STATE_ENABLED;
+
+ return 0;
+
+reset_complete_failed:
+ msm_camera_enable_irq(cci_dev->irq, false);
+ msm_camera_clk_enable(&cci_dev->v4l2_dev_str.pdev->dev,
+ cci_dev->cci_clk_info, cci_dev->cci_clk,
+ cci_dev->num_clk, false);
+reg_enable_failed:
+ msm_camera_config_vreg(&cci_dev->v4l2_dev_str.pdev->dev,
+ cci_dev->cci_vreg, cci_dev->regulator_count, NULL, 0,
+ &cci_dev->cci_reg_ptr[0], 0);
+clk_enable_failed:
+ if (cci_dev->cci_pinctrl_status) {
+ ret = pinctrl_select_state(cci_dev->cci_pinctrl.pinctrl,
+ cci_dev->cci_pinctrl.gpio_state_suspend);
+ if (ret)
+ pr_err("%s:%d cannot set pin to suspend state\n",
+ __func__, __LINE__);
+ }
+ msm_camera_request_gpio_table(cci_dev->cci_gpio_tbl,
+ cci_dev->cci_gpio_tbl_size, 0);
+request_gpio_failed:
+ cci_dev->ref_count--;
+ cam_cpas_stop(cci_dev->cpas_handle);
+
+ return rc;
+}
+
+void cam_cci_soc_remove(struct platform_device *pdev,
+ struct cci_device *cci_dev)
+{
+ msm_camera_put_clk_info_and_rates(pdev,
+ &cci_dev->cci_clk_info, &cci_dev->cci_clk,
+ &cci_dev->cci_clk_rates, cci_dev->num_clk_cases,
+ cci_dev->num_clk);
+
+ msm_camera_put_reg_base(pdev, cci_dev->base, "cci", true);
+}
+
+static void cam_cci_init_cci_params(struct cci_device *new_cci_dev)
+{
+ uint8_t i = 0, j = 0;
+
+ for (i = 0; i < NUM_MASTERS; i++) {
+ new_cci_dev->cci_master_info[i].status = 0;
+ mutex_init(&new_cci_dev->cci_master_info[i].mutex);
+ init_completion(&new_cci_dev->
+ cci_master_info[i].reset_complete);
+
+ for (j = 0; j < NUM_QUEUES; j++) {
+ mutex_init(&new_cci_dev->cci_master_info[i].mutex_q[j]);
+ init_completion(&new_cci_dev->
+ cci_master_info[i].report_q[j]);
+ }
+ }
+}
+
+static int32_t cam_cci_init_gpio_params(struct cci_device *cci_dev)
+{
+ int32_t rc = 0, i = 0;
+ uint32_t *val_array = NULL;
+ uint8_t tbl_size = 0;
+ struct device_node *of_node = cci_dev->v4l2_dev_str.pdev->dev.of_node;
+ struct gpio *gpio_tbl = NULL;
+
+ cci_dev->cci_gpio_tbl_size = tbl_size = of_gpio_count(of_node);
+ CDBG("%s gpio count %d\n", __func__, tbl_size);
+ if (!tbl_size) {
+ pr_err("%s:%d gpio count 0\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ gpio_tbl = cci_dev->cci_gpio_tbl =
+ kzalloc(sizeof(struct gpio) * tbl_size, GFP_KERNEL);
+ if (!gpio_tbl) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < tbl_size; i++) {
+ gpio_tbl[i].gpio = of_get_gpio(of_node, i);
+ CDBG("%s gpio_tbl[%d].gpio = %d\n", __func__, i,
+ gpio_tbl[i].gpio);
+ }
+
+ val_array = kcalloc(tbl_size, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!val_array) {
+ rc = -ENOMEM;
+ goto free_gpio_tbl;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,gpio-tbl-flags",
+ val_array, tbl_size);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto free_val_array;
+ }
+ for (i = 0; i < tbl_size; i++) {
+ gpio_tbl[i].flags = val_array[i];
+ CDBG("%s gpio_tbl[%d].flags = %ld\n", __func__, i,
+ gpio_tbl[i].flags);
+ }
+
+ for (i = 0; i < tbl_size; i++) {
+ rc = of_property_read_string_index(of_node,
+ "qcom,gpio-tbl-label", i, &gpio_tbl[i].label);
+ CDBG("%s gpio_tbl[%d].label = %s\n", __func__, i,
+ gpio_tbl[i].label);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto free_val_array;
+ }
+ }
+
+ kfree(val_array);
+ return rc;
+
+free_val_array:
+ kfree(val_array);
+free_gpio_tbl:
+ kfree(cci_dev->cci_gpio_tbl);
+ cci_dev->cci_gpio_tbl = NULL;
+ cci_dev->cci_gpio_tbl_size = 0;
+ return rc;
+}
+
+static void cam_cci_init_default_clk_params(struct cci_device *cci_dev,
+ uint8_t index)
+{
+ /* default clock params are for 100Khz */
+ cci_dev->cci_clk_params[index].hw_thigh = 201;
+ cci_dev->cci_clk_params[index].hw_tlow = 174;
+ cci_dev->cci_clk_params[index].hw_tsu_sto = 204;
+ cci_dev->cci_clk_params[index].hw_tsu_sta = 231;
+ cci_dev->cci_clk_params[index].hw_thd_dat = 22;
+ cci_dev->cci_clk_params[index].hw_thd_sta = 162;
+ cci_dev->cci_clk_params[index].hw_tbuf = 227;
+ cci_dev->cci_clk_params[index].hw_scl_stretch_en = 0;
+ cci_dev->cci_clk_params[index].hw_trdhld = 6;
+ cci_dev->cci_clk_params[index].hw_tsp = 3;
+ cci_dev->cci_clk_params[index].cci_clk_src = 37500000;
+}
+
+static void cam_cci_init_clk_params(struct cci_device *cci_dev)
+{
+ int32_t rc = 0;
+ uint32_t val = 0;
+ uint8_t count = 0;
+ struct device_node *of_node = cci_dev->v4l2_dev_str.pdev->dev.of_node;
+ struct device_node *src_node = NULL;
+
+ for (count = 0; count < I2C_MAX_MODES; count++) {
+
+ if (count == I2C_STANDARD_MODE)
+ src_node = of_find_node_by_name(of_node,
+ "qcom,i2c_standard_mode");
+ else if (count == I2C_FAST_MODE)
+ src_node = of_find_node_by_name(of_node,
+ "qcom,i2c_fast_mode");
+ else if (count == I2C_FAST_PLUS_MODE)
+ src_node = of_find_node_by_name(of_node,
+ "qcom,i2c_fast_plus_mode");
+ else
+ src_node = of_find_node_by_name(of_node,
+ "qcom,i2c_custom_mode");
+
+ rc = of_property_read_u32(src_node, "qcom,hw-thigh", &val);
+ CDBG("%s qcom,hw-thigh %d, rc %d\n", __func__, val, rc);
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_thigh = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-tlow",
+ &val);
+ CDBG("%s qcom,hw-tlow %d, rc %d\n", __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_tlow = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-tsu-sto",
+ &val);
+ CDBG("%s qcom,hw-tsu-sto %d, rc %d\n",
+ __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_tsu_sto = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-tsu-sta",
+ &val);
+ CDBG("%s qcom,hw-tsu-sta %d, rc %d\n",
+ __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_tsu_sta = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-thd-dat",
+ &val);
+ CDBG("%s qcom,hw-thd-dat %d, rc %d\n",
+ __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_thd_dat = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-thd-sta",
+ &val);
+ CDBG("%s qcom,hw-thd-sta %d, rc %d\n", __func__,
+ val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_thd_sta = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-tbuf",
+ &val);
+ CDBG("%s qcom,hw-tbuf %d, rc %d\n", __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_tbuf = val;
+ rc = of_property_read_u32(src_node,
+ "qcom,hw-scl-stretch-en", &val);
+ CDBG("%s qcom,hw-scl-stretch-en %d, rc %d\n",
+ __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_scl_stretch_en = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-trdhld",
+ &val);
+ CDBG("%s qcom,hw-trdhld %d, rc %d\n",
+ __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_trdhld = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-tsp",
+ &val);
+ CDBG("%s qcom,hw-tsp %d, rc %d\n", __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_tsp = val;
+ val = 0;
+ rc = of_property_read_u32(src_node, "qcom,cci-clk-src",
+ &val);
+ CDBG("%s qcom,cci-clk-src %d, rc %d\n",
+ __func__, val, rc);
+ cci_dev->cci_clk_params[count].cci_clk_src = val;
+ } else
+ cam_cci_init_default_clk_params(cci_dev, count);
+
+ of_node_put(src_node);
+ }
+}
+
+int cam_cci_parse_dt_info(struct platform_device *pdev,
+ struct cci_device *new_cci_dev)
+{
+ int rc = 0, i = 0;
+
+ /* Get Clock Info*/
+ rc = msm_camera_get_clk_info_and_rates(pdev,
+ &new_cci_dev->cci_clk_info, &new_cci_dev->cci_clk,
+ &new_cci_dev->cci_clk_rates, &new_cci_dev->num_clk_cases,
+ &new_cci_dev->num_clk);
+ if (rc < 0) {
+ pr_err("%s: cam_cci_get_clk_info() failed", __func__);
+ kfree(new_cci_dev);
+ new_cci_dev = NULL;
+ return -EFAULT;
+ }
+
+ new_cci_dev->ref_count = 0;
+ new_cci_dev->base = msm_camera_get_reg_base(pdev, "cci", true);
+ if (!new_cci_dev->base) {
+ pr_err("%s: no mem resource?\n", __func__);
+ return -ENODEV;
+ }
+ new_cci_dev->irq = msm_camera_get_irq(pdev, "cci");
+ if (!new_cci_dev->irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ return -ENODEV;
+ }
+ CDBG("%s line %d cci irq start %d end %d\n", __func__,
+ __LINE__,
+ (int) new_cci_dev->irq->start,
+ (int) new_cci_dev->irq->end);
+ rc = msm_camera_register_irq(pdev, new_cci_dev->irq,
+ cam_cci_irq, IRQF_TRIGGER_RISING, "cci", new_cci_dev);
+ if (rc < 0) {
+ pr_err("%s: irq request fail\n", __func__);
+ rc = -EBUSY;
+ goto cci_release_mem;
+ }
+
+ msm_camera_enable_irq(new_cci_dev->irq, false);
+ new_cci_dev->v4l2_dev_str.pdev = pdev;
+ cam_cci_init_cci_params(new_cci_dev);
+ cam_cci_init_clk_params(new_cci_dev);
+ rc = cam_cci_init_gpio_params(new_cci_dev);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: In Initializing GPIO params:%d\n",
+ __func__, __LINE__, rc);
+ goto cci_release_mem;
+ }
+
+ rc = cam_sensor_get_dt_vreg_data(new_cci_dev->
+ v4l2_dev_str.pdev->dev.of_node,
+ &(new_cci_dev->cci_vreg), &(new_cci_dev->regulator_count));
+ if (rc < 0) {
+ pr_err("%s: cam_sensor_get_dt_vreg_data fail\n", __func__);
+ rc = -EFAULT;
+ goto cci_release_mem;
+ }
+
+ /* Parse VREG data */
+ if ((new_cci_dev->regulator_count < 0) ||
+ (new_cci_dev->regulator_count > MAX_REGULATOR)) {
+ pr_err("%s: invalid reg count = %d, max is %d\n", __func__,
+ new_cci_dev->regulator_count, MAX_REGULATOR);
+ rc = -EFAULT;
+ goto cci_invalid_vreg_data;
+ }
+
+ rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ if (rc)
+ pr_err("%s: failed to add child nodes, rc=%d\n", __func__, rc);
+ for (i = 0; i < MASTER_MAX; i++) {
+ new_cci_dev->write_wq[i] = create_singlethread_workqueue(
+ "cam_cci_wq");
+ if (!new_cci_dev->write_wq[i])
+ pr_err("Failed to create write wq\n");
+ }
+ CDBG("%s line %d\n", __func__, __LINE__);
+ return 0;
+
+cci_invalid_vreg_data:
+ kfree(new_cci_dev->cci_vreg);
+ new_cci_dev->cci_vreg = NULL;
+cci_release_mem:
+ msm_camera_put_reg_base(pdev, new_cci_dev->base, "cci", true);
+
+ return rc;
+}
+
+int cam_cci_soc_release(struct cci_device *cci_dev)
+{
+ uint8_t i = 0, rc = 0;
+
+ if (!cci_dev->ref_count || cci_dev->cci_state != CCI_STATE_ENABLED) {
+ pr_err("%s invalid ref count %d / cci state %d\n",
+ __func__, cci_dev->ref_count, cci_dev->cci_state);
+ return -EINVAL;
+ }
+ if (--cci_dev->ref_count) {
+ CDBG("%s ref_count Exit %d\n", __func__, cci_dev->ref_count);
+ return 0;
+ }
+ for (i = 0; i < MASTER_MAX; i++)
+ if (cci_dev->write_wq[i])
+ flush_workqueue(cci_dev->write_wq[i]);
+
+ msm_camera_enable_irq(cci_dev->irq, false);
+ msm_camera_clk_enable(&cci_dev->v4l2_dev_str.pdev->dev,
+ cci_dev->cci_clk_info, cci_dev->cci_clk,
+ cci_dev->num_clk, false);
+
+ rc = msm_camera_enable_vreg(&cci_dev->v4l2_dev_str.pdev->dev,
+ cci_dev->cci_vreg, cci_dev->regulator_count, NULL, 0,
+ &cci_dev->cci_reg_ptr[0], 0);
+ if (rc < 0)
+ pr_err("%s:%d cci disable_vreg failed\n", __func__, __LINE__);
+
+ rc = msm_camera_config_vreg(&cci_dev->v4l2_dev_str.pdev->dev,
+ cci_dev->cci_vreg, cci_dev->regulator_count, NULL, 0,
+ &cci_dev->cci_reg_ptr[0], 0);
+ if (rc < 0)
+ pr_err("%s:%d cci unconfig_vreg failed\n", __func__, __LINE__);
+
+ if (cci_dev->cci_pinctrl_status) {
+ rc = pinctrl_select_state(cci_dev->cci_pinctrl.pinctrl,
+ cci_dev->cci_pinctrl.gpio_state_suspend);
+ if (rc)
+ pr_err("%s:%d cannot set pin to active state\n",
+ __func__, __LINE__);
+ }
+ cci_dev->cci_pinctrl_status = 0;
+ msm_camera_request_gpio_table(cci_dev->cci_gpio_tbl,
+ cci_dev->cci_gpio_tbl_size, 0);
+ for (i = 0; i < MASTER_MAX; i++)
+ cci_dev->i2c_freq_mode[i] = I2C_MAX_MODES;
+ cci_dev->cci_state = CCI_STATE_DISABLED;
+ cci_dev->cycles_per_us = 0;
+ cci_dev->cci_clk_src = 0;
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_soc.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_soc.h
new file mode 100644
index 0000000..ca4bbe0
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_soc.h
@@ -0,0 +1,51 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_CCI_SOC_H_
+#define _CAM_CCI_SOC_H_
+
+#include "cam_cci_core.h"
+
+/**
+ * @sd: V4L2 sub device
+ * @c_ctrl: CCI control structure
+ *
+ * This API initializes the CCI and acquires SOC resources
+ */
+int cam_cci_init(struct v4l2_subdev *sd,
+ struct cam_cci_ctrl *c_ctrl);
+
+/**
+ * @cci_dev: CCI device structure
+ *
+ * This API releases the CCI and its SOC resources
+ */
+int cam_cci_soc_release(struct cci_device *cci_dev);
+
+/**
+ * @pdev: Platform device
+ * @new_cci_dev: CCI device structure
+ *
+ * This API parses CCI device tree
+ */
+int cam_cci_parse_dt_info(struct platform_device *pdev,
+ struct cci_device *new_cci_dev);
+
+/**
+ * @pdev: Platform device
+ * @cci_dev: CCI device structure
+ *
+ * This API puts all SOC resources
+ */
+void cam_cci_soc_remove(struct platform_device *pdev,
+ struct cci_device *cci_dev);
+#endif /* _CAM_CCI_SOC_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/Makefile b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/Makefile
new file mode 100644
index 0000000..0337b32
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/Makefile
@@ -0,0 +1,8 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_cci
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_csiphy_soc.o cam_csiphy_dev.o cam_csiphy_core.o
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
new file mode 100644
index 0000000..8dc65f5
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
@@ -0,0 +1,500 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include "cam_csiphy_core.h"
+#include "cam_csiphy_dev.h"
+#include "cam_csiphy_soc.h"
+#include <cam_mem_mgr.h>
+
+void cam_csiphy_query_cap(struct csiphy_device *csiphy_dev,
+ struct cam_csiphy_query_cap *csiphy_cap)
+{
+ csiphy_cap->slot_info = csiphy_dev->v4l2_dev_str.pdev->id;
+ csiphy_cap->version = csiphy_dev->hw_version;
+ csiphy_cap->clk_lane = csiphy_dev->clk_lane;
+}
+
+void cam_csiphy_reset(struct csiphy_device *csiphy_dev)
+{
+ int32_t i;
+ uint32_t size =
+ csiphy_dev->ctrl_reg->csiphy_reg.csiphy_reset_array_size;
+
+ for (i = 0; i < size; i++) {
+ cam_io_w(
+ csiphy_dev->ctrl_reg->
+ csiphy_reset_reg[i].reg_data,
+ csiphy_dev->base +
+ csiphy_dev->ctrl_reg->
+ csiphy_reset_reg[i].reg_addr);
+
+ usleep_range(csiphy_dev->ctrl_reg->
+ csiphy_reset_reg[i].delay * 100,
+ csiphy_dev->ctrl_reg->
+ csiphy_reset_reg[i].delay * 100 + 1000);
+ }
+}
+
+int32_t cam_cmd_buf_parser(struct csiphy_device *csiphy_dev,
+ struct cam_config_dev_cmd *cfg_dev)
+{
+ int32_t rc = 0;
+ uint64_t generic_ptr;
+ struct cam_packet *csl_packet = NULL;
+ struct cam_cmd_buf_desc *cmd_desc = NULL;
+ uint32_t *cmd_buf = NULL;
+ struct cam_csiphy_info *cam_cmd_csiphy_info = NULL;
+ size_t len;
+
+ if (!cfg_dev || !csiphy_dev) {
+ pr_err("%s:%d Invalid Args\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ csiphy_dev->csiphy_info =
+ kzalloc(sizeof(struct cam_csiphy_info), GFP_KERNEL);
+ if (!csiphy_dev->csiphy_info)
+ return -ENOMEM;
+
+ rc = cam_mem_get_cpu_buf((int32_t) cfg_dev->packet_handle,
+ (uint64_t *)&generic_ptr, &len);
+ if (rc < 0) {
+ pr_err("%s:%d :ERROR: Failed to get packet Mem address: %d\n",
+ __func__, __LINE__, rc);
+ kfree(csiphy_dev->csiphy_info);
+ csiphy_dev->csiphy_info = NULL;
+ return rc;
+ }
+
+ if (cfg_dev->offset > len) {
+ pr_err("%s: %d offset is out of bounds: offset: %lld len: %zu\n",
+ __func__, __LINE__, cfg_dev->offset, len);
+ kfree(csiphy_dev->csiphy_info);
+ csiphy_dev->csiphy_info = NULL;
+ return -EINVAL;
+ }
+
+ csl_packet = (struct cam_packet *)(generic_ptr + cfg_dev->offset);
+
+ cmd_desc = (struct cam_cmd_buf_desc *)
+ ((uint32_t *)&csl_packet->payload +
+ csl_packet->cmd_buf_offset / 4);
+
+ rc = cam_mem_get_cpu_buf(cmd_desc->mem_handle,
+ (uint64_t *)&generic_ptr, &len);
+ if (rc < 0) {
+ pr_err("%s:%d :ERROR: Failed to get cmd buf Mem address : %d\n",
+ __func__, __LINE__, rc);
+ kfree(csiphy_dev->csiphy_info);
+ csiphy_dev->csiphy_info = NULL;
+ return rc;
+ }
+
+ cmd_buf = (uint32_t *)generic_ptr;
+ cmd_buf += cmd_desc->offset / 4;
+ cam_cmd_csiphy_info = (struct cam_csiphy_info *)cmd_buf;
+
+ csiphy_dev->csiphy_info->lane_cnt = cam_cmd_csiphy_info->lane_cnt;
+ csiphy_dev->csiphy_info->lane_mask = cam_cmd_csiphy_info->lane_mask;
+ csiphy_dev->csiphy_info->csiphy_3phase =
+ cam_cmd_csiphy_info->csiphy_3phase;
+ csiphy_dev->csiphy_info->combo_mode = cam_cmd_csiphy_info->combo_mode;
+ csiphy_dev->csiphy_info->settle_time = cam_cmd_csiphy_info->settle_time;
+ csiphy_dev->csiphy_info->data_rate = cam_cmd_csiphy_info->data_rate;
+
+ return rc;
+}
+
+void cam_csiphy_cphy_irq_config(struct csiphy_device *csiphy_dev)
+{
+ int32_t i;
+
+ for (i = 0; i < csiphy_dev->num_irq_registers; i++)
+ cam_io_w(csiphy_dev->ctrl_reg->
+ csiphy_irq_reg[i].reg_data,
+ csiphy_dev->base +
+ csiphy_dev->ctrl_reg->
+ csiphy_irq_reg[i].reg_addr);
+}
+
+void cam_csiphy_cphy_irq_disable(struct csiphy_device *csiphy_dev)
+{
+ int32_t i;
+
+ for (i = 0; i < csiphy_dev->num_irq_registers; i++)
+ cam_io_w(0x0,
+ csiphy_dev->base +
+ csiphy_dev->ctrl_reg->
+ csiphy_irq_reg[i].reg_addr);
+}
+
+irqreturn_t cam_csiphy_irq(int irq_num, void *data)
+{
+ uint32_t irq;
+ uint8_t i;
+ struct csiphy_device *csiphy_dev =
+ (struct csiphy_device *)data;
+
+ if (!csiphy_dev) {
+ pr_err("%s:%d Invalid Args\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < csiphy_dev->num_irq_registers; i++) {
+ irq = cam_io_r(
+ csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_interrupt_status0_addr + 0x4*i);
+ cam_io_w(irq,
+ csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_interrupt_clear0_addr + 0x4*i);
+ pr_err_ratelimited(
+ "%s CSIPHY%d_IRQ_STATUS_ADDR%d = 0x%x\n",
+ __func__, csiphy_dev->v4l2_dev_str.pdev->id, i, irq);
+ cam_io_w(0x0,
+ csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_interrupt_clear0_addr + 0x4*i);
+ }
+ cam_io_w(0x1, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->
+ csiphy_reg.mipi_csiphy_glbl_irq_cmd_addr);
+ cam_io_w(0x0, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->
+ csiphy_reg.mipi_csiphy_glbl_irq_cmd_addr);
+
+ return IRQ_HANDLED;
+}
+
+int32_t cam_csiphy_config_dev(struct csiphy_device *csiphy_dev)
+{
+ int32_t rc = 0;
+ uint32_t lane_enable = 0, mask = 1, size = 0;
+ uint16_t lane_mask = 0, i = 0, cfg_size = 0;
+ uint8_t settle_cnt, lane_cnt, lane_pos = 0;
+ void __iomem *csiphybase;
+ struct csiphy_reg_t (*reg_array)[MAX_SETTINGS_PER_LANE];
+
+ if (csiphy_dev->csiphy_info == NULL) {
+ pr_err("%s:%d csiphy_info is NULL, No/Fail CONFIG_DEV ?\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ lane_cnt = csiphy_dev->csiphy_info->lane_cnt;
+ lane_mask = csiphy_dev->csiphy_info->lane_mask & 0x1f;
+ settle_cnt = (csiphy_dev->csiphy_info->settle_time / 200000000);
+ csiphybase = csiphy_dev->base;
+
+ if (!csiphybase) {
+ pr_err("%s: csiphybase NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < MAX_DPHY_DATA_LN; i++) {
+ if (mask == 0x2) {
+ if (lane_mask & mask)
+ lane_enable |= 0x80;
+ i--;
+ } else if (lane_mask & mask) {
+ lane_enable |= 0x1 << (i<<1);
+ }
+ mask <<= 1;
+ }
+
+ if (!csiphy_dev->csiphy_info->csiphy_3phase) {
+ if (csiphy_dev->csiphy_info->combo_mode == 1)
+ reg_array =
+ csiphy_dev->ctrl_reg->csiphy_2ph_combo_mode_reg;
+ else
+ reg_array =
+ csiphy_dev->ctrl_reg->csiphy_2ph_reg;
+ csiphy_dev->num_irq_registers = 11;
+ cfg_size = csiphy_dev->ctrl_reg->csiphy_reg.
+ csiphy_2ph_config_array_size;
+ } else {
+ if (csiphy_dev->csiphy_info->combo_mode == 1)
+ reg_array =
+ csiphy_dev->ctrl_reg->csiphy_2ph_3ph_mode_reg;
+ else
+ reg_array =
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg;
+ csiphy_dev->num_irq_registers = 20;
+ cfg_size = csiphy_dev->ctrl_reg->csiphy_reg.
+ csiphy_3ph_config_array_size;
+ }
+
+ size = csiphy_dev->ctrl_reg->csiphy_reg.csiphy_common_array_size;
+
+ for (i = 0; i < size; i++) {
+ switch (csiphy_dev->ctrl_reg->
+ csiphy_common_reg[i].csiphy_param_type) {
+ case CSIPHY_LANE_ENABLE:
+ cam_io_w(lane_enable,
+ csiphy_dev->base +
+ csiphy_dev->ctrl_reg->
+ csiphy_common_reg[i].reg_addr);
+ break;
+ case CSIPHY_DEFAULT_PARAMS:
+ cam_io_w(csiphy_dev->ctrl_reg->
+ csiphy_common_reg[i].reg_data,
+ csiphy_dev->base +
+ csiphy_dev->ctrl_reg->
+ csiphy_common_reg[i].reg_addr);
+ break;
+ default:
+ break;
+ }
+ }
+
+ while (lane_mask & 0x1f) {
+ if (!(lane_mask & 0x1)) {
+ lane_pos++;
+ lane_mask >>= 1;
+ continue;
+ }
+
+ for (i = 0; i < cfg_size; i++) {
+ switch (reg_array[lane_pos][i].csiphy_param_type) {
+ case CSIPHY_LANE_ENABLE:
+ cam_io_w(lane_enable,
+ csiphy_dev->base +
+ reg_array[lane_pos][i].reg_addr);
+ break;
+ case CSIPHY_DEFAULT_PARAMS:
+ cam_io_w(reg_array[lane_pos][i].reg_data,
+ csiphy_dev->base +
+ reg_array[lane_pos][i].reg_addr);
+ break;
+ case CSIPHY_SETTLE_CNT_LOWER_BYTE:
+ cam_io_w(settle_cnt & 0xFF,
+ csiphy_dev->base +
+ reg_array[lane_pos][i].reg_addr);
+ break;
+ case CSIPHY_SETTLE_CNT_HIGHER_BYTE:
+ cam_io_w((settle_cnt >> 8) & 0xFF,
+ csiphy_dev->base +
+ reg_array[lane_pos][i].reg_addr);
+ break;
+ default:
+ CDBG("%s: %d Do Nothing\n", __func__, __LINE__);
+ break;
+ }
+ usleep_range(reg_array[lane_pos][i].delay*1000,
+ reg_array[lane_pos][i].delay*1000 + 1000);
+ }
+ lane_mask >>= 1;
+ lane_pos++;
+ }
+
+ cam_csiphy_cphy_irq_config(csiphy_dev);
+
+ return rc;
+}
+
+int32_t cam_csiphy_core_cfg(void *phy_dev,
+ void *arg)
+{
+ struct csiphy_device *csiphy_dev =
+ (struct csiphy_device *)phy_dev;
+ struct cam_control *cmd = (struct cam_control *)arg;
+ int32_t rc = 0;
+
+ if (!csiphy_dev || !cmd) {
+ pr_err("%s:%d Invalid input args\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s:%d Opcode received: %d\n", __func__, __LINE__,
+ cmd->op_code);
+ mutex_lock(&csiphy_dev->mutex);
+ switch (cmd->op_code) {
+ case CAM_ACQUIRE_DEV: {
+ struct cam_sensor_acquire_dev csiphy_acq_dev;
+ struct cam_csiphy_acquire_dev_info csiphy_acq_params;
+
+ struct cam_create_dev_hdl bridge_params;
+
+ rc = copy_from_user(&csiphy_acq_dev,
+ (void __user *)cmd->handle,
+ sizeof(csiphy_acq_dev));
+ if (rc < 0) {
+ pr_err("%s:%d :ERROR: Failed copying from User\n",
+ __func__, __LINE__);
+ goto release_mutex;
+ }
+
+ csiphy_acq_params.combo_mode = 0;
+
+ if (csiphy_dev->acquire_count == 2) {
+ pr_err("%s:%d CSIPHY device do not allow more than 2 acquires\n",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ goto release_mutex;
+ }
+
+ bridge_params.ops = NULL;
+ bridge_params.session_hdl = csiphy_acq_dev.session_handle;
+ bridge_params.v4l2_sub_dev_flag = 0;
+ bridge_params.media_entity_flag = 0;
+ bridge_params.priv = csiphy_dev;
+
+ csiphy_acq_dev.device_handle =
+ cam_create_device_hdl(&bridge_params);
+ csiphy_dev->bridge_intf.
+ device_hdl[csiphy_acq_params.combo_mode] =
+ csiphy_acq_dev.device_handle;
+ csiphy_dev->bridge_intf.
+ session_hdl[csiphy_acq_params.combo_mode] =
+ csiphy_acq_dev.session_handle;
+
+ if (copy_to_user((void __user *)cmd->handle,
+ &csiphy_acq_dev,
+ sizeof(struct cam_sensor_acquire_dev))) {
+ pr_err("%s:%d :ERROR: Failed copying from User\n",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ goto release_mutex;
+ }
+ if (csiphy_acq_params.combo_mode == 1)
+ csiphy_dev->is_acquired_dev_combo_mode = 1;
+ csiphy_dev->acquire_count++;
+ }
+ break;
+ case CAM_QUERY_CAP: {
+ struct cam_csiphy_query_cap csiphy_cap;
+
+ cam_csiphy_query_cap(csiphy_dev, &csiphy_cap);
+ if (copy_to_user((void __user *)cmd->handle,
+ &csiphy_cap, sizeof(struct cam_csiphy_query_cap))) {
+ pr_err("%s:%d :ERROR: Failed copying from User\n",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ goto release_mutex;
+ }
+ }
+ break;
+ case CAM_STOP_DEV: {
+ rc = cam_csiphy_soc_release(csiphy_dev);
+ if (rc < 0) {
+ pr_err("%s:%d Failed in csiphy release\n",
+ __func__, __LINE__);
+ cam_cpas_stop(csiphy_dev->cpas_handle);
+ goto release_mutex;
+ }
+ rc = cam_cpas_stop(csiphy_dev->cpas_handle);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: de-voting CPAS: %d\n",
+ __func__, __LINE__, rc);
+ goto release_mutex;
+ }
+ }
+ break;
+ case CAM_RELEASE_DEV: {
+ struct cam_release_dev_cmd release;
+
+ if (!csiphy_dev->acquire_count) {
+ pr_err("%s:%d No valid devices to release\n",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ goto release_mutex;
+ }
+
+ if (copy_from_user(&release, (void __user *) cmd->handle,
+ sizeof(release))) {
+ rc = -EFAULT;
+ goto release_mutex;
+ }
+
+ rc = cam_destroy_device_hdl(release.dev_handle);
+ if (rc < 0)
+ pr_err("%s:%d :ERROR: destroying the device hdl\n",
+ __func__, __LINE__);
+ if (release.dev_handle ==
+ csiphy_dev->bridge_intf.device_hdl[0]) {
+ csiphy_dev->bridge_intf.device_hdl[0] = -1;
+ csiphy_dev->bridge_intf.link_hdl[0] = -1;
+ csiphy_dev->bridge_intf.session_hdl[0] = -1;
+ } else {
+ csiphy_dev->bridge_intf.device_hdl[1] = -1;
+ csiphy_dev->bridge_intf.link_hdl[1] = -1;
+ csiphy_dev->bridge_intf.
+ session_hdl[1] = -1;
+ }
+ csiphy_dev->acquire_count--;
+ }
+ break;
+ case CAM_CONFIG_DEV: {
+ struct cam_config_dev_cmd config;
+
+ if (copy_from_user(&config, (void __user *)cmd->handle,
+ sizeof(config))) {
+ rc = -EFAULT;
+ } else {
+ rc = cam_cmd_buf_parser(csiphy_dev, &config);
+ if (rc < 0) {
+ pr_err("%s:%d Fail in cmd buf parser\n",
+ __func__, __LINE__);
+ goto release_mutex;
+ }
+ }
+ break;
+ }
+ case CAM_START_DEV: {
+ struct cam_ahb_vote ahb_vote;
+ struct cam_axi_vote axi_vote;
+
+ ahb_vote.type = CAM_VOTE_ABSOLUTE;
+ ahb_vote.vote.level = CAM_SVS_VOTE;
+ axi_vote.compressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
+ axi_vote.uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
+
+ rc = cam_cpas_start(csiphy_dev->cpas_handle,
+ &ahb_vote, &axi_vote);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: voting CPAS: %d\n",
+ __func__, __LINE__, rc);
+ goto release_mutex;
+ }
+
+ rc = cam_csiphy_enable_hw(csiphy_dev);
+ if (rc != 0) {
+ pr_err("%s: %d cam_csiphy_enable_hw failed\n",
+ __func__, __LINE__);
+ cam_cpas_stop(csiphy_dev->cpas_handle);
+ goto release_mutex;
+ }
+ rc = cam_csiphy_config_dev(csiphy_dev);
+ if (rc < 0) {
+ pr_err("%s: %d cam_csiphy_config_dev failed\n",
+ __func__, __LINE__);
+ cam_cpas_stop(csiphy_dev->cpas_handle);
+ goto release_mutex;
+ }
+ }
+ break;
+ default:
+ pr_err("%s:%d :Error: Invalid Opcode: %d\n",
+ __func__, __LINE__, cmd->op_code);
+ rc = -EINVAL;
+ goto release_mutex;
+ }
+
+release_mutex:
+ mutex_unlock(&csiphy_dev->mutex);
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.h
new file mode 100644
index 0000000..6eeeea4
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.h
@@ -0,0 +1,52 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_CSIPHY_CORE_H_
+#define _CAM_CSIPHY_CORE_H_
+
+#include <linux/irqreturn.h>
+#include "cam_csiphy_dev.h"
+#include <cam_mem_mgr.h>
+#include <cam_req_mgr_util.h>
+#include <cam_io_util.h>
+
+/**
+ * @csiphy_dev: CSIPhy device structure
+ *
+ * This API programs CSIPhy IRQ registers
+ */
+void cam_csiphy_cphy_irq_config(struct csiphy_device *csiphy_dev);
+
+/**
+ * @csiphy_dev: CSIPhy device structure
+ *
+ * This API resets CSIPhy hardware
+ */
+void cam_csiphy_reset(struct csiphy_device *csiphy_dev);
+
+/**
+ * @csiphy_dev: CSIPhy device structure
+ * @arg: Camera control command argument
+ *
+ * This API handles the camera control argument reached to CSIPhy
+ */
+int cam_csiphy_core_cfg(void *csiphy_dev, void *arg);
+
+/**
+ * @irq_num: IRQ number
+ * @data: CSIPhy device structure
+ *
+ * This API handles CSIPhy IRQs
+ */
+irqreturn_t cam_csiphy_irq(int irq_num, void *data);
+
+#endif /* _CAM_CSIPHY_CORE_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.c
new file mode 100644
index 0000000..f2ece9d
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.c
@@ -0,0 +1,239 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "cam_csiphy_dev.h"
+#include "cam_req_mgr_dev.h"
+#include "cam_csiphy_soc.h"
+#include "cam_csiphy_core.h"
+#include <media/cam_sensor.h>
+
+#undef CDBG
+#ifdef CAM_CSIPHY_DEV_DEBUG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+static long cam_csiphy_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct csiphy_device *csiphy_dev = v4l2_get_subdevdata(sd);
+ int rc = 0;
+
+ switch (cmd) {
+ case VIDIOC_CAM_CONTROL:
+ rc = cam_csiphy_core_cfg(csiphy_dev, arg);
+ if (rc != 0) {
+ pr_err("%s: %d :ERROR: in configuring the device\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ break;
+ default:
+ pr_err("%s:%d :ERROR: Wrong ioctl\n", __func__, __LINE__);
+ break;
+ }
+
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+static long cam_csiphy_subdev_compat_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, unsigned long arg)
+{
+ int32_t rc = 0;
+ struct cam_control cmd_data;
+
+ if (copy_from_user(&cmd_data, (void __user *)arg,
+ sizeof(cmd_data))) {
+ pr_err("Failed to copy from user_ptr=%pK size=%zu\n",
+ (void __user *)arg, sizeof(cmd_data));
+ return -EFAULT;
+ }
+
+ /* All the arguments converted to 64 bit here
+ * Passed to the api in core.c
+ */
+ switch (cmd) {
+ case VIDIOC_CAM_CONTROL:
+ rc = cam_csiphy_subdev_ioctl(sd, cmd, &cmd_data);
+ break;
+ default:
+ pr_err("%s:%d Invalid compat ioctl cmd: %d\n",
+ __func__, __LINE__, cmd);
+ rc = -EINVAL;
+ }
+
+ if (!rc) {
+ if (copy_to_user((void __user *)arg, &cmd_data,
+ sizeof(cmd_data))) {
+ pr_err("Failed to copy to user_ptr=%pK size=%zu\n",
+ (void __user *)arg, sizeof(cmd_data));
+ rc = -EFAULT;
+ }
+ }
+
+ return rc;
+}
+#endif
+
+static struct v4l2_subdev_core_ops csiphy_subdev_core_ops = {
+ .ioctl = cam_csiphy_subdev_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl32 = cam_csiphy_subdev_compat_ioctl,
+#endif
+};
+
+static const struct v4l2_subdev_ops csiphy_subdev_ops = {
+ .core = &csiphy_subdev_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops csiphy_subdev_intern_ops;
+
+static int32_t cam_csiphy_platform_probe(struct platform_device *pdev)
+{
+ struct cam_cpas_register_params cpas_parms;
+ struct csiphy_device *new_csiphy_dev;
+ int32_t rc = 0;
+
+ new_csiphy_dev = devm_kzalloc(&pdev->dev,
+ sizeof(struct csiphy_device), GFP_KERNEL);
+ if (!new_csiphy_dev)
+ return -ENOMEM;
+
+ new_csiphy_dev->ctrl_reg = kzalloc(sizeof(struct csiphy_ctrl_t),
+ GFP_KERNEL);
+ if (!new_csiphy_dev->ctrl_reg) {
+ devm_kfree(&pdev->dev, new_csiphy_dev);
+ return -ENOMEM;
+ }
+
+ mutex_init(&new_csiphy_dev->mutex);
+ new_csiphy_dev->v4l2_dev_str.pdev = pdev;
+
+ new_csiphy_dev->ref_count = 0;
+
+ rc = cam_csiphy_parse_dt_info(pdev, new_csiphy_dev);
+ if (rc < 0) {
+ pr_err("%s:%d :ERROR: dt paring failed: %d\n",
+ __func__, __LINE__, rc);
+ goto csiphy_no_resource;
+ }
+
+ new_csiphy_dev->v4l2_dev_str.internal_ops =
+ &csiphy_subdev_intern_ops;
+ new_csiphy_dev->v4l2_dev_str.ops =
+ &csiphy_subdev_ops;
+ strlcpy(new_csiphy_dev->device_name, CAMX_CSIPHY_DEV_NAME,
+ sizeof(new_csiphy_dev->device_name));
+ new_csiphy_dev->v4l2_dev_str.name =
+ new_csiphy_dev->device_name;
+ new_csiphy_dev->v4l2_dev_str.sd_flags =
+ (V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS);
+ new_csiphy_dev->v4l2_dev_str.ent_function =
+ CAM_CSIPHY_DEVICE_TYPE;
+ new_csiphy_dev->v4l2_dev_str.token =
+ new_csiphy_dev;
+
+ rc = cam_register_subdev(&(new_csiphy_dev->v4l2_dev_str));
+ if (rc < 0) {
+ pr_err("%s:%d :ERROR: In cam_register_subdev\n",
+ __func__, __LINE__);
+ goto csiphy_no_resource;
+ }
+
+ platform_set_drvdata(pdev, &(new_csiphy_dev->v4l2_dev_str.sd));
+ v4l2_set_subdevdata(&(new_csiphy_dev->v4l2_dev_str.sd), new_csiphy_dev);
+
+ new_csiphy_dev->bridge_intf.device_hdl[0] = -1;
+ new_csiphy_dev->bridge_intf.device_hdl[1] = -1;
+ new_csiphy_dev->bridge_intf.ops.get_dev_info =
+ NULL;
+ new_csiphy_dev->bridge_intf.ops.link_setup =
+ NULL;
+ new_csiphy_dev->bridge_intf.ops.apply_req =
+ NULL;
+
+ new_csiphy_dev->acquire_count = 0;
+ new_csiphy_dev->is_acquired_dev_combo_mode = 0;
+
+ cpas_parms.cam_cpas_client_cb = NULL;
+ cpas_parms.cell_index = pdev->id;
+ cpas_parms.dev = &pdev->dev;
+ cpas_parms.userdata = new_csiphy_dev;
+
+ strlcpy(cpas_parms.identifier, "csiphy", CAM_HW_IDENTIFIER_LENGTH);
+ rc = cam_cpas_register_client(&cpas_parms);
+ if (rc) {
+ pr_err("%s:%d CPAS registration failed\n",
+ __func__, __LINE__);
+ goto csiphy_no_resource;
+ }
+ CDBG("CPAS registration successful handle=%d\n",
+ cpas_parms.client_handle);
+ new_csiphy_dev->cpas_handle = cpas_parms.client_handle;
+
+ return rc;
+csiphy_no_resource:
+ mutex_destroy(&new_csiphy_dev->mutex);
+ kfree(new_csiphy_dev->ctrl_reg);
+ devm_kfree(&pdev->dev, new_csiphy_dev);
+ return rc;
+}
+
+
+static int32_t cam_csiphy_device_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *subdev =
+ platform_get_drvdata(pdev);
+ struct csiphy_device *csiphy_dev =
+ v4l2_get_subdevdata(subdev);
+
+ cam_cpas_unregister_client(csiphy_dev->cpas_handle);
+ cam_csiphy_soc_release(csiphy_dev);
+ kfree(csiphy_dev->ctrl_reg);
+ devm_kfree(&pdev->dev, csiphy_dev);
+
+ return 0;
+}
+
+static const struct of_device_id cam_csiphy_dt_match[] = {
+ {.compatible = "qcom,csiphy"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, cam_csiphy_dt_match);
+
+static struct platform_driver csiphy_driver = {
+ .probe = cam_csiphy_platform_probe,
+ .remove = cam_csiphy_device_remove,
+ .driver = {
+ .name = CAMX_CSIPHY_DEV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = cam_csiphy_dt_match,
+ },
+};
+
+static int32_t __init cam_csiphy_init_module(void)
+{
+ return platform_driver_register(&csiphy_driver);
+}
+
+static void __exit cam_csiphy_exit_module(void)
+{
+ platform_driver_unregister(&csiphy_driver);
+}
+
+module_init(cam_csiphy_init_module);
+module_exit(cam_csiphy_exit_module);
+MODULE_DESCRIPTION("CAM CSIPHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.h
new file mode 100644
index 0000000..9049e4e
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.h
@@ -0,0 +1,209 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_CSIPHY_DEV_H_
+#define _CAM_CSIPHY_DEV_H_
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/irqreturn.h>
+#include <linux/ion.h>
+#include <linux/iommu.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
+#include <media/cam_defs.h>
+#include <cam_sensor_cmn_header.h>
+#include <cam_req_mgr_interface.h>
+#include <cam_subdev.h>
+#include <cam_sensor_soc_api.h>
+#include <cam_io_util.h>
+#include <cam_cpas_api.h>
+
+#define MAX_CSIPHY 3
+#define MAX_DPHY_DATA_LN 4
+#define MAX_LRME_V4l2_EVENTS 30
+#define CSIPHY_NUM_CLK_MAX 16
+#define MAX_CSIPHY_REG_ARRAY 70
+#define MAX_CSIPHY_CMN_REG_ARRAY 5
+
+#define MAX_LANES 5
+#define MAX_SETTINGS_PER_LANE 20
+
+#define MAX_REGULATOR 5
+#define CAMX_CSIPHY_DEV_NAME "cam-csiphy-driver"
+
+#define CSIPHY_POWER_UP 0
+#define CSIPHY_POWER_DOWN 1
+
+#define CSIPHY_DEFAULT_PARAMS 0
+#define CSIPHY_LANE_ENABLE 1
+#define CSIPHY_SETTLE_CNT_LOWER_BYTE 2
+#define CSIPHY_SETTLE_CNT_HIGHER_BYTE 3
+#define CSIPHY_DNP_PARAMS 4
+
+#define ENABLE_IRQ false
+
+#undef CDBG
+#ifdef CAM_CSIPHY_CORE_DEBUG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+/**
+ * struct csiphy_reg_parms_t
+ * @mipi_csiphy_glbl_irq_cmd_addr: CSIPhy irq addr
+ * @mipi_csiphy_interrupt_status0_addr:
+ * CSIPhy interrupt status addr
+ * @mipi_csiphy_interrupt_mask0_addr:
+ * CSIPhy interrupt mask addr
+ * @mipi_csiphy_interrupt_mask_val:
+ * CSIPhy interrupt mask val
+ * @mipi_csiphy_interrupt_clear0_addr:
+ * CSIPhy interrupt clear addr
+ * @csiphy_version: CSIPhy Version
+ * @csiphy_common_array_size: CSIPhy common array size
+ * @csiphy_reset_array_size: CSIPhy reset array size
+ */
+struct csiphy_reg_parms_t {
+/*MIPI CSI PHY registers*/
+ uint32_t mipi_csiphy_glbl_irq_cmd_addr;
+ uint32_t mipi_csiphy_interrupt_status0_addr;
+ uint32_t mipi_csiphy_interrupt_mask0_addr;
+ uint32_t mipi_csiphy_interrupt_mask_val;
+ uint32_t mipi_csiphy_interrupt_mask_addr;
+ uint32_t mipi_csiphy_interrupt_clear0_addr;
+ uint32_t csiphy_version;
+ uint32_t csiphy_common_array_size;
+ uint32_t csiphy_reset_array_size;
+ uint32_t csiphy_2ph_config_array_size;
+ uint32_t csiphy_3ph_config_array_size;
+};
+
+/**
+ * struct intf_params
+ * @device_hdl: Device Handle
+ * @session_hdl: Session Handle
+ * @ops: KMD operations
+ * @crm_cb: Callback API pointers
+ */
+struct intf_params {
+ int32_t device_hdl[2];
+ int32_t session_hdl[2];
+ int32_t link_hdl[2];
+ struct cam_req_mgr_kmd_ops ops;
+ struct cam_req_mgr_crm_cb *crm_cb;
+};
+
+/**
+ * struct csiphy_reg_t
+ * @reg_addr: Register address
+ * @reg_data: Register data
+ * @delay: Delay
+ * @csiphy_param_type: CSIPhy parameter type
+ */
+struct csiphy_reg_t {
+ int32_t reg_addr;
+ int32_t reg_data;
+ int32_t delay;
+ uint32_t csiphy_param_type;
+};
+
+/**
+ * struct csiphy_ctrl_t
+ * @csiphy_reg: Register address
+ * @csiphy_common_reg: Common register set
+ * @csiphy_reset_reg: Reset register set
+ * @csiphy_2ph_reg: 2phase register set
+ * @csiphy_2ph_combo_mode_reg:
+ * 2phase combo register set
+ * @csiphy_3ph_reg: 3phase register set
+ * @csiphy_2ph_3ph_mode_reg:
+ * 2 phase 3phase combo register set
+ */
+struct csiphy_ctrl_t {
+ struct csiphy_reg_parms_t csiphy_reg;
+ struct csiphy_reg_t *csiphy_common_reg;
+ struct csiphy_reg_t *csiphy_irq_reg;
+ struct csiphy_reg_t *csiphy_reset_reg;
+ struct csiphy_reg_t (*csiphy_2ph_reg)[MAX_SETTINGS_PER_LANE];
+ struct csiphy_reg_t (*csiphy_2ph_combo_mode_reg)[MAX_SETTINGS_PER_LANE];
+ struct csiphy_reg_t (*csiphy_3ph_reg)[MAX_SETTINGS_PER_LANE];
+ struct csiphy_reg_t (*csiphy_2ph_3ph_mode_reg)[MAX_SETTINGS_PER_LANE];
+};
+
+/**
+ * struct csiphy_device
+ * @pdev: Platform device
+ * @irq: Interrupt structure
+ * @base: Base address
+ * @hw_version: Hardware Version
+ * @csiphy_state: CSIPhy state
+ * @ctrl_reg: CSIPhy control registers
+ * @num_clk: Number of clocks
+ * @csiphy_max_clk: Max timer clock rate
+ * @num_vreg: Number of regulators
+ * @csiphy_clk: Clock structure
+ * @csiphy_clk_info: Clock information structure
+ * @csiphy_vreg: Regulator structure
+ * @csiphy_reg_ptr: Regulator structure
+ * @csiphy_3p_clk_info: 3Phase clock information
+ * @csiphy_3p_clk: 3Phase clocks structure
+ * @csiphy_clk_index: Timer Src clk index
+ * @csi_3phase: Is it a 3Phase mode
+ * @ref_count: Reference count
+ * @clk_lane: Clock lane
+ * @acquire_count: Acquire device count
+ * @is_acquired_dev_combo_mode:
+ * Flag that mentions whether already acquired
+ * device is for combo mode
+ */
+struct csiphy_device {
+ struct resource *irq;
+ void __iomem *base;
+ struct mutex mutex;
+ uint32_t hw_version;
+ uint32_t csiphy_state;
+ struct csiphy_ctrl_t *ctrl_reg;
+ size_t num_clk;
+ uint32_t csiphy_max_clk;
+ int32_t num_vreg;
+ struct clk **csiphy_clk;
+ struct msm_cam_clk_info *csiphy_clk_info;
+ struct camera_vreg_t *csiphy_vreg;
+ struct regulator *csiphy_reg_ptr[MAX_REGULATOR];
+ struct msm_cam_clk_info csiphy_3p_clk_info[2];
+ struct clk *csiphy_3p_clk[2];
+ uint32_t csiphy_clk_index;
+ unsigned char csi_3phase;
+ int32_t ref_count;
+ uint16_t lane_mask[MAX_CSIPHY];
+ uint8_t is_csiphy_3phase_hw;
+ uint8_t num_irq_registers;
+ struct cam_subdev v4l2_dev_str;
+ struct cam_csiphy_info *csiphy_info;
+ struct intf_params bridge_intf;
+ uint32_t clk_lane;
+ uint32_t acquire_count;
+ char device_name[20];
+ uint32_t is_acquired_dev_combo_mode;
+ uint32_t cpas_handle;
+};
+
+#endif /* _CAM_CSIPHY_DEV_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_soc.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_soc.c
new file mode 100644
index 0000000..540ec76
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_soc.c
@@ -0,0 +1,274 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "cam_csiphy_soc.h"
+#include "cam_csiphy_core.h"
+#include "include/cam_csiphy_1_0_hwreg.h"
+#include "cam_sensor_util.h"
+
+int32_t cam_csiphy_enable_hw(struct csiphy_device *csiphy_dev)
+{
+ int32_t rc = 0;
+ long clk_rate = 0;
+
+ if (csiphy_dev->ref_count++) {
+ pr_err("%s:%d csiphy refcount = %d\n", __func__,
+ __LINE__, csiphy_dev->ref_count);
+ return rc;
+ }
+
+ rc = msm_camera_config_vreg(&csiphy_dev->v4l2_dev_str.pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->num_vreg, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 1);
+ if (rc < 0) {
+ pr_err("%s:%d failed regulator get\n", __func__, __LINE__);
+ goto csiphy_config_regulator_fail;
+ }
+
+ rc = msm_camera_enable_vreg(&csiphy_dev->v4l2_dev_str.pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->num_vreg, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 1);
+ if (rc < 0) {
+ pr_err("%s:%d failed to enable regulators\n", __func__, rc);
+ goto csiphy_regulator_fail;
+ }
+
+ /*Enable clocks*/
+ rc = msm_camera_clk_enable(&csiphy_dev->v4l2_dev_str.pdev->dev,
+ csiphy_dev->csiphy_clk_info, csiphy_dev->csiphy_clk,
+ csiphy_dev->num_clk, true);
+ if (rc < 0) {
+ pr_err("%s: csiphy clk enable failed\n", __func__);
+ csiphy_dev->ref_count--;
+ goto csiphy_regulator_fail;
+ }
+
+ clk_rate = msm_camera_clk_set_rate(&csiphy_dev->v4l2_dev_str.pdev->dev,
+ csiphy_dev->csiphy_clk[csiphy_dev->csiphy_clk_index],
+ clk_rate);
+ if (clk_rate < 0) {
+ pr_err("csiphy_clk_set_rate failed\n");
+ goto csiphy_clk_enable_fail;
+ }
+
+ rc = msm_camera_enable_irq(csiphy_dev->irq, ENABLE_IRQ);
+ if (rc < 0) {
+ pr_err("%s:%d :ERROR: irq enable failed\n",
+ __func__, __LINE__);
+ goto csiphy_clk_enable_fail;
+ return -EINVAL;
+ }
+
+ cam_csiphy_reset(csiphy_dev);
+
+ return rc;
+csiphy_clk_enable_fail:
+ msm_camera_clk_enable(&csiphy_dev->v4l2_dev_str.pdev->dev,
+ csiphy_dev->csiphy_clk_info, csiphy_dev->csiphy_clk,
+ csiphy_dev->num_clk, false);
+csiphy_regulator_fail:
+ msm_camera_enable_vreg(&csiphy_dev->v4l2_dev_str.pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->num_vreg, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 0);
+csiphy_config_regulator_fail:
+ msm_camera_config_vreg(&csiphy_dev->v4l2_dev_str.pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->num_vreg, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 0);
+
+ return rc;
+}
+
+int32_t cam_csiphy_disable_hw(struct platform_device *pdev)
+{
+ struct csiphy_device *csiphy_dev =
+ platform_get_drvdata(pdev);
+
+ /*Disable regulators*/
+ msm_camera_enable_vreg(&csiphy_dev->v4l2_dev_str.pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->num_vreg, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 0);
+
+ /*Disable clocks*/
+ msm_camera_clk_enable(&csiphy_dev->v4l2_dev_str.pdev->dev,
+ csiphy_dev->csiphy_clk_info, csiphy_dev->csiphy_clk,
+ csiphy_dev->num_clk, false);
+
+ /*Disable IRQ*/
+ msm_camera_enable_irq(csiphy_dev->irq, false);
+
+ return 0;
+
+}
+
+int32_t cam_csiphy_parse_dt_info(struct platform_device *pdev,
+ struct csiphy_device *csiphy_dev)
+{
+ int32_t rc = 0, i = 0;
+ uint32_t clk_cnt = 0;
+ char *csi_3p_clk_name = "csi_phy_3p_clk";
+ char *csi_3p_clk_src_name = "csiphy_3p_clk_src";
+
+ if (pdev->dev.of_node) {
+ of_property_read_u32((&pdev->dev)->of_node,
+ "cell-index", &pdev->id);
+ CDBG("%s: device id = %d\n", __func__, pdev->id);
+ }
+
+ csiphy_dev->is_csiphy_3phase_hw = 0;
+ if (of_device_is_compatible(csiphy_dev->v4l2_dev_str.pdev->dev.of_node,
+ "qcom,csiphy-v1.0")) {
+ csiphy_dev->ctrl_reg->csiphy_2ph_reg = csiphy_2ph_v1_0_reg;
+ csiphy_dev->ctrl_reg->csiphy_2ph_combo_mode_reg =
+ csiphy_2ph_v1_0_combo_mode_reg;
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg = csiphy_3ph_v1_0_reg;
+ csiphy_dev->ctrl_reg->csiphy_2ph_3ph_mode_reg =
+ csiphy_3ph_v1_0_combo_mode_reg;
+ csiphy_dev->ctrl_reg->csiphy_irq_reg = csiphy_irq_reg_1_0;
+ csiphy_dev->ctrl_reg->csiphy_common_reg = csiphy_common_reg_1_0;
+ csiphy_dev->ctrl_reg->csiphy_reset_reg = csiphy_reset_reg_1_0;
+ csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v1_0;
+ csiphy_dev->hw_version = CSIPHY_VERSION_V10;
+ csiphy_dev->is_csiphy_3phase_hw = CSI_3PHASE_HW;
+ csiphy_dev->clk_lane = 0;
+ } else {
+ pr_err("%s:%d, invalid hw version : 0x%x\n", __func__, __LINE__,
+ csiphy_dev->hw_version);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ rc = msm_camera_get_clk_info(csiphy_dev->v4l2_dev_str.pdev,
+ &csiphy_dev->csiphy_clk_info,
+ &csiphy_dev->csiphy_clk,
+ &csiphy_dev->num_clk);
+ if (rc < 0) {
+ pr_err("%s:%d failed clock get\n", __func__, __LINE__);
+ return rc;
+ }
+
+ if (csiphy_dev->num_clk > CSIPHY_NUM_CLK_MAX) {
+ pr_err("%s: invalid clk count=%zu, max is %d\n", __func__,
+ csiphy_dev->num_clk, CSIPHY_NUM_CLK_MAX);
+ goto clk_mem_ovf_err;
+ }
+
+ for (i = 0; i < csiphy_dev->num_clk; i++) {
+ if (!strcmp(csiphy_dev->csiphy_clk_info[i].clk_name,
+ csi_3p_clk_src_name)) {
+ csiphy_dev->csiphy_3p_clk_info[0].clk_name =
+ csiphy_dev->csiphy_clk_info[i].clk_name;
+ csiphy_dev->csiphy_3p_clk_info[0].clk_rate =
+ csiphy_dev->csiphy_clk_info[i].clk_rate;
+ csiphy_dev->csiphy_3p_clk[0] =
+ csiphy_dev->csiphy_clk[i];
+ continue;
+ } else if (!strcmp(csiphy_dev->csiphy_clk_info[i].clk_name,
+ csi_3p_clk_name)) {
+ csiphy_dev->csiphy_3p_clk_info[1].clk_name =
+ csiphy_dev->csiphy_clk_info[i].clk_name;
+ csiphy_dev->csiphy_3p_clk_info[1].clk_rate =
+ csiphy_dev->csiphy_clk_info[i].clk_rate;
+ csiphy_dev->csiphy_3p_clk[1] =
+ csiphy_dev->csiphy_clk[i];
+ continue;
+ }
+
+ if (!strcmp(csiphy_dev->csiphy_clk_info[clk_cnt].clk_name,
+ "csiphy_timer_src_clk")) {
+ csiphy_dev->csiphy_max_clk =
+ csiphy_dev->csiphy_clk_info[clk_cnt].clk_rate;
+ csiphy_dev->csiphy_clk_index = clk_cnt;
+ }
+ CDBG("%s: clk_rate[%d] = %ld\n", __func__, clk_cnt,
+ csiphy_dev->csiphy_clk_info[clk_cnt].clk_rate);
+ clk_cnt++;
+ }
+
+ rc = cam_sensor_get_dt_vreg_data(pdev->dev.of_node,
+ &(csiphy_dev->csiphy_vreg), &(csiphy_dev->num_vreg));
+ if (rc < 0) {
+ pr_err("%s:%d Reg get failed\n", __func__, __LINE__);
+ csiphy_dev->num_vreg = 0;
+ }
+
+ csiphy_dev->base = msm_camera_get_reg_base(pdev, "csiphy", true);
+ if (!csiphy_dev->base) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto csiphy_no_resource;
+ }
+
+ csiphy_dev->irq = msm_camera_get_irq(pdev, "csiphy");
+ if (!csiphy_dev->irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto csiphy_no_resource;
+ }
+
+ rc = msm_camera_register_irq(pdev, csiphy_dev->irq,
+ cam_csiphy_irq, IRQF_TRIGGER_RISING, "csiphy", csiphy_dev);
+ if (rc < 0) {
+ pr_err("%s: irq request fail\n", __func__);
+ rc = -EBUSY;
+ goto csiphy_no_resource;
+ }
+ msm_camera_enable_irq(csiphy_dev->irq, false);
+ return rc;
+
+csiphy_no_resource:
+ msm_camera_put_reg_base(pdev, csiphy_dev->base, "csiphy", true);
+clk_mem_ovf_err:
+ msm_camera_put_clk_info(csiphy_dev->v4l2_dev_str.pdev,
+ &csiphy_dev->csiphy_clk_info,
+ &csiphy_dev->csiphy_clk,
+ csiphy_dev->num_clk);
+ return rc;
+}
+
+int32_t cam_csiphy_soc_release(struct csiphy_device *csiphy_dev)
+{
+
+ if (!csiphy_dev || !csiphy_dev->ref_count) {
+ pr_err("%s csiphy dev NULL / ref_count ZERO\n", __func__);
+ return 0;
+ }
+
+ if (--csiphy_dev->ref_count) {
+ pr_err("%s:%d csiphy refcount = %d\n", __func__,
+ __LINE__, csiphy_dev->ref_count);
+ return 0;
+ }
+
+ cam_csiphy_reset(csiphy_dev);
+
+ msm_camera_enable_irq(csiphy_dev->irq, false);
+
+ msm_camera_clk_enable(&csiphy_dev->v4l2_dev_str.pdev->dev,
+ csiphy_dev->csiphy_clk_info, csiphy_dev->csiphy_clk,
+ csiphy_dev->num_clk, false);
+
+ msm_camera_enable_vreg(&csiphy_dev->v4l2_dev_str.pdev->dev,
+ csiphy_dev->csiphy_vreg, csiphy_dev->num_vreg,
+ NULL, 0, &csiphy_dev->csiphy_reg_ptr[0], 0);
+
+ msm_camera_config_vreg(&csiphy_dev->v4l2_dev_str.pdev->dev,
+ csiphy_dev->csiphy_vreg, csiphy_dev->num_vreg,
+ NULL, 0, &csiphy_dev->csiphy_reg_ptr[0], 0);
+
+
+ return 0;
+}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_soc.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_soc.h
new file mode 100644
index 0000000..27de3fc
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_soc.h
@@ -0,0 +1,63 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_CSIPHY_SOC_H_
+#define _CAM_CSIPHY_SOC_H_
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/irqreturn.h>
+#include <linux/ion.h>
+#include <linux/iommu.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
+#include "cam_csiphy_dev.h"
+#include "cam_csiphy_core.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+#define CSI_3PHASE_HW 1
+#define CSIPHY_VERSION_V35 0x35
+#define CSIPHY_VERSION_V10 0x10
+
+/**
+ * @csiphy_dev: CSIPhy device structure
+ *
+ * This API release SOC related parameters
+ */
+int cam_csiphy_soc_release(struct csiphy_device *csiphy_dev);
+
+/**
+ * @pdev: Platform device
+ * @csiphy_dev: CSIPhy device structure
+ *
+ * This API parses csiphy device tree information
+ */
+int cam_csiphy_parse_dt_info(struct platform_device *pdev,
+ struct csiphy_device *csiphy_dev);
+
+/**
+ * @csiphy_dev: CSIPhy device structure
+ *
+ * This API enables SOC related parameters
+ */
+int cam_csiphy_enable_hw(struct csiphy_device *csiphy_dev);
+
+#endif /* _CAM_CSIPHY_SOC_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/include/cam_csiphy_1_0_hwreg.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/include/cam_csiphy_1_0_hwreg.h
new file mode 100644
index 0000000..7a4aede
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/include/cam_csiphy_1_0_hwreg.h
@@ -0,0 +1,351 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_CSIPHY_1_0_HWREG_H_
+#define _CAM_CSIPHY_1_0_HWREG_H_
+
+#include "../cam_csiphy_dev.h"
+
+struct csiphy_reg_parms_t csiphy_v1_0 = {
+ .mipi_csiphy_interrupt_status0_addr = 0x8B0,
+ .mipi_csiphy_interrupt_clear0_addr = 0x858,
+ .mipi_csiphy_glbl_irq_cmd_addr = 0x828,
+ .csiphy_common_array_size = 3,
+ .csiphy_reset_array_size = 3,
+ .csiphy_2ph_config_array_size = 14,
+ .csiphy_3ph_config_array_size = 19,
+};
+
+struct csiphy_reg_t csiphy_common_reg_1_0[] = {
+ {0x0814, 0x00, 0x00, CSIPHY_LANE_ENABLE},
+ {0x0818, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x081C, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+};
+
+struct csiphy_reg_t csiphy_reset_reg_1_0[] = {
+ {0x0814, 0x00, 0x50, CSIPHY_LANE_ENABLE},
+ {0x0818, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x081C, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+};
+
+struct csiphy_reg_t csiphy_irq_reg_1_0[] = {
+ {0x082c, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0830, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0834, 0xFB, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0838, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x083c, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0840, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0844, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0848, 0xEF, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x084c, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0850, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0854, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS},
+};
+
+struct csiphy_reg_t csiphy_2ph_v1_0_reg[MAX_LANES][MAX_SETTINGS_PER_LANE] = {
+ {
+ {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0028, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0000, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0008, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x000c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0060, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0064, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+ {
+ {0x0704, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x072C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0734, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x071C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x073C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0708, 0x14, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x070C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0760, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0764, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+ {
+ {0x0204, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x022C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0234, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x021C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0228, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x023C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0200, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0208, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x020C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0260, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0264, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+ {
+ {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0428, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0400, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0408, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x040C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0460, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0464, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+ {
+ {0x0604, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x062C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0634, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x061C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0628, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x063C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0600, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0608, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x060C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0638, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0660, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0664, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+};
+
+struct csiphy_reg_t
+ csiphy_2ph_v1_0_combo_mode_reg[MAX_LANES][MAX_SETTINGS_PER_LANE] = {
+ {
+ {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0028, 0x0A, 0x00, CSIPHY_DNP_PARAMS},
+ {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0000, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0008, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0060, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0064, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+ {
+ {0x0704, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x072C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0734, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x071C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x073C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0708, 0x14, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x070C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0760, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0764, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+ {
+ {0x0204, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x022C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0234, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x021C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0228, 0x0A, 0x00, CSIPHY_DNP_PARAMS},
+ {0x023C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0200, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0208, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0260, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0264, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+ {
+ {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0428, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0400, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0408, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0460, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0464, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+ {
+ {0x0604, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x062C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0634, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x061C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0628, 0x0E, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x063C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0600, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0608, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x060C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0638, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0660, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0664, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+};
+
+struct csiphy_reg_t csiphy_3ph_v1_0_reg[MAX_LANES][MAX_SETTINGS_PER_LANE] = {
+ {
+ {0x015C, 0x63, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0168, 0xAC, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x016C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0104, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x010C, 0x12, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0108, 0x00, 0x00, CSIPHY_SETTLE_CNT_HIGHER_BYTE},
+ {0x0114, 0x20, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0150, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0118, 0x3e, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x011C, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0120, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0124, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0128, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x012C, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0144, 0x12, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0160, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x01CC, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0164, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x01DC, 0x51, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+ {
+ {0x035C, 0x63, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0368, 0xAC, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x036C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0304, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x030C, 0x12, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0308, 0x00, 0x00, CSIPHY_SETTLE_CNT_HIGHER_BYTE},
+ {0x0314, 0x20, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0350, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0318, 0x3e, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x031C, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0320, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0324, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0328, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x032C, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0344, 0x12, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0360, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x03CC, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0364, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x03DC, 0x51, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+ {
+ {0x055C, 0x63, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0568, 0xAC, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x056C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0504, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x050C, 0x12, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0508, 0x00, 0x00, CSIPHY_SETTLE_CNT_HIGHER_BYTE},
+ {0x0514, 0x20, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0550, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0518, 0x3e, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x051C, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0520, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0524, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0528, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x052C, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0544, 0x12, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0560, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x05CC, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0564, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x05DC, 0x51, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+};
+
+struct csiphy_reg_t
+ csiphy_3ph_v1_0_combo_mode_reg[MAX_LANES][MAX_SETTINGS_PER_LANE] = {
+ {
+ {0x015C, 0x63, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0168, 0xAC, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x016C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0104, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x010C, 0x12, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0108, 0x00, 0x00, CSIPHY_SETTLE_CNT_HIGHER_BYTE},
+ {0x0114, 0x20, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0150, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0118, 0x3e, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x011C, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0120, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0124, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0128, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x012C, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0144, 0x12, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0160, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x01CC, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0164, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x01DC, 0x51, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+ {
+ {0x035C, 0x63, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0368, 0xAC, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x036C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0304, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x030C, 0x12, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0308, 0x00, 0x00, CSIPHY_SETTLE_CNT_HIGHER_BYTE},
+ {0x0314, 0x20, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0350, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0318, 0x3e, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x031C, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0320, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0324, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0328, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x032C, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0344, 0x12, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0360, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x03CC, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0364, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x03DC, 0x51, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+ {
+ {0x055C, 0x63, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0568, 0xAC, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x056C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0504, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x050C, 0x12, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0508, 0x00, 0x00, CSIPHY_SETTLE_CNT_HIGHER_BYTE},
+ {0x0514, 0x20, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0550, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0518, 0x3e, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x051C, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0520, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0524, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0528, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x052C, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0544, 0x12, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0560, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x05CC, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0564, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x05DC, 0x51, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+};
+
+#endif /* _CAM_CSIPHY_1_0_HWREG_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/Makefile b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/Makefile
new file mode 100644
index 0000000..d8c75fb
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/Makefile
@@ -0,0 +1,8 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_cci
+
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_dev.o cam_sensor_core.o cam_sensor_soc.o
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
new file mode 100644
index 0000000..7a03bdc
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
@@ -0,0 +1,1002 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <cam_sensor_cmn_header.h>
+#include "cam_sensor_core.h"
+#include <cam_sensor_util.h>
+
+static int32_t cam_sensor_i2c_pkt_parse(struct cam_sensor_ctrl_t *s_ctrl,
+ void *arg)
+{
+ int32_t rc = 0;
+ uint64_t generic_ptr;
+ struct cam_control *ioctl_ctrl = NULL;
+ struct cam_packet *csl_packet = NULL;
+ struct cam_cmd_buf_desc *cmd_desc = NULL;
+ struct i2c_settings_array *i2c_reg_settings = NULL;
+ size_t len_of_buff = 0;
+ uint32_t *offset = NULL;
+ struct cam_config_dev_cmd config;
+ struct i2c_data_settings *i2c_data = NULL;
+ struct cam_req_mgr_add_request add_req;
+
+ ioctl_ctrl = (struct cam_control *)arg;
+
+ if (ioctl_ctrl->handle_type != CAM_HANDLE_USER_POINTER) {
+ pr_err("%s:%d :Error: Invalid Handle Type\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if (copy_from_user(&config, (void __user *) ioctl_ctrl->handle,
+ sizeof(config)))
+ return -EFAULT;
+
+ rc = cam_mem_get_cpu_buf(
+ config.packet_handle,
+ (uint64_t *)&generic_ptr,
+ &len_of_buff);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: Failed in getting the buffer: %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ csl_packet = (struct cam_packet *)(generic_ptr +
+ config.offset);
+ if (config.offset > len_of_buff) {
+ pr_err("%s: %d offset is out of bounds: off: %lld len: %zu\n",
+ __func__, __LINE__, config.offset, len_of_buff);
+ return -EINVAL;
+ }
+
+ i2c_data = &(s_ctrl->i2c_data);
+ CDBG("%s:%d Header OpCode: %d\n",
+ __func__, __LINE__, csl_packet->header.op_code);
+ if ((csl_packet->header.op_code & 0xFFFFFF) ==
+ CAM_SENSOR_PACKET_OPCODE_SENSOR_INITIAL_CONFIG) {
+ i2c_reg_settings = &i2c_data->init_settings;
+ i2c_reg_settings->request_id = 0;
+ i2c_reg_settings->is_settings_valid = 1;
+ } else if ((csl_packet->header.op_code & 0xFFFFFF) ==
+ CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE) {
+ i2c_reg_settings =
+ &i2c_data->
+ per_frame[csl_packet->header.request_id %
+ MAX_PER_FRAME_ARRAY];
+ CDBG("%s:%d Received Packet: %lld\n", __func__, __LINE__,
+ csl_packet->header.request_id % MAX_PER_FRAME_ARRAY);
+ if (i2c_reg_settings->is_settings_valid == 1) {
+ pr_err("%s:%d :Error: Already some pkt in offset req : %lld\n",
+ __func__, __LINE__,
+ csl_packet->header.request_id);
+ rc = delete_request(i2c_reg_settings);
+ if (rc < 0) {
+ pr_err("%s: %d :Error: Failed in Deleting the err: %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+ }
+
+ i2c_reg_settings->request_id =
+ csl_packet->header.request_id;
+ i2c_reg_settings->is_settings_valid = 1;
+ } else if ((csl_packet->header.op_code & 0xFFFFFF) ==
+ CAM_PKT_NOP_OPCODE) {
+ goto update_req_mgr;
+ } else {
+ pr_err("%s:%d Invalid Packet Header\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ offset = (uint32_t *)&csl_packet->payload;
+ offset += csl_packet->cmd_buf_offset / 4;
+ cmd_desc = (struct cam_cmd_buf_desc *)(offset);
+
+ rc = cam_sensor_i2c_pkt_parser(i2c_reg_settings, cmd_desc, 1);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: Fail parsing I2C Pkt: %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+update_req_mgr:
+ if (((csl_packet->header.op_code & 0xFFFFFF) ==
+ CAM_PKT_NOP_OPCODE) || (csl_packet->header.op_code ==
+ CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE)) {
+ add_req.link_hdl = s_ctrl->bridge_intf.link_hdl;
+ add_req.req_id = csl_packet->header.request_id;
+ CDBG("%s:%d Rxed Req Id: %lld\n",
+ __func__, __LINE__, csl_packet->header.request_id);
+ add_req.dev_hdl = s_ctrl->bridge_intf.device_hdl;
+ if (s_ctrl->bridge_intf.crm_cb &&
+ s_ctrl->bridge_intf.crm_cb->add_req)
+ s_ctrl->bridge_intf.crm_cb->add_req(&add_req);
+ CDBG("%s:%d add req to req mgr: %lld\n",
+ __func__, __LINE__, add_req.req_id);
+ }
+ return rc;
+}
+
+int32_t cam_sensor_update_i2c_info(struct cam_cmd_i2c_info *i2c_info,
+ struct cam_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+ struct cam_sensor_cci_client *cci_client = NULL;
+
+ if (s_ctrl->io_master_info.master_type == CCI_MASTER) {
+ cci_client = s_ctrl->io_master_info.cci_client;
+ if (!cci_client) {
+ pr_err("failed: cci_client %pK", cci_client);
+ return -EINVAL;
+ }
+ cci_client->cci_i2c_master = s_ctrl->cci_i2c_master;
+ cci_client->sid = i2c_info->slave_addr >> 1;
+ cci_client->retries = 3;
+ cci_client->id_map = 0;
+ cci_client->i2c_freq_mode = i2c_info->i2c_freq_mode;
+ CDBG("%s:%d Master: %d sid: %d freq_mode: %d\n",
+ __func__, __LINE__,
+ cci_client->cci_i2c_master, i2c_info->slave_addr,
+ i2c_info->i2c_freq_mode);
+ }
+
+ return rc;
+}
+
+int32_t cam_sensor_update_slave_info(struct cam_cmd_probe *probe_info,
+ struct cam_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+
+ s_ctrl->sensordata->slave_info.sensor_id_reg_addr =
+ probe_info->reg_addr;
+ s_ctrl->sensordata->slave_info.sensor_id =
+ probe_info->expected_data;
+ s_ctrl->sensordata->slave_info.sensor_id_mask =
+ probe_info->data_mask;
+
+ s_ctrl->sensor_probe_addr_type = probe_info->addr_type;
+ s_ctrl->sensor_probe_data_type = probe_info->data_type;
+ CDBG("%s:%d Sensor Addr: 0x%x sensor_id: 0x%x sensor_mask: 0x%x\n",
+ __func__, __LINE__,
+ s_ctrl->sensordata->slave_info.sensor_id_reg_addr,
+ s_ctrl->sensordata->slave_info.sensor_id,
+ s_ctrl->sensordata->slave_info.sensor_id_mask);
+ return rc;
+}
+
+int32_t cam_sensor_update_power_settings(void *cmd_buf,
+ int cmd_length, struct cam_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0, tot_size = 0, last_cmd_type = 0;
+ int32_t i = 0, pwr_up = 0, pwr_down = 0;
+ void *ptr = cmd_buf, *scr;
+ struct cam_cmd_power *pwr_cmd = (struct cam_cmd_power *)cmd_buf;
+ struct common_header *cmm_hdr = (struct common_header *)cmd_buf;
+ struct cam_sensor_power_ctrl_t *power_info =
+ &s_ctrl->sensordata->power_info;
+
+ if (!pwr_cmd || !cmd_length) {
+ pr_err("%s:%d Invalid Args: pwr_cmd %pK, cmd_length: %d\n",
+ __func__, __LINE__, pwr_cmd, cmd_length);
+ return -EINVAL;
+ }
+
+ power_info->power_setting_size = 0;
+ power_info->power_setting =
+ (struct cam_sensor_power_setting *)
+ kzalloc(sizeof(struct cam_sensor_power_setting) *
+ MAX_POWER_CONFIG, GFP_KERNEL);
+ if (!power_info->power_setting)
+ return -ENOMEM;
+
+ power_info->power_down_setting =
+ (struct cam_sensor_power_setting *)
+ kzalloc(sizeof(struct cam_sensor_power_setting) *
+ MAX_POWER_CONFIG, GFP_KERNEL);
+ if (!power_info->power_down_setting) {
+ rc = -ENOMEM;
+ goto free_power_settings;
+ }
+
+ while (tot_size < cmd_length) {
+ if (cmm_hdr->cmd_type ==
+ CAMERA_SENSOR_CMD_TYPE_PWR_UP) {
+ struct cam_cmd_power *pwr_cmd =
+ (struct cam_cmd_power *)ptr;
+
+ power_info->
+ power_setting_size +=
+ pwr_cmd->count;
+ scr = ptr + sizeof(struct cam_cmd_power);
+ tot_size = tot_size + sizeof(struct cam_cmd_power);
+
+ if (pwr_cmd->count == 0)
+ CDBG("%s:%d Un expected Command\n",
+ __func__, __LINE__);
+
+ for (i = 0; i < pwr_cmd->count; i++, pwr_up++) {
+ power_info->
+ power_setting[pwr_up].seq_type =
+ pwr_cmd->power_settings[i].
+ power_seq_type;
+ power_info->
+ power_setting[pwr_up].config_val =
+ pwr_cmd->power_settings[i].
+ config_val_low;
+ power_info->power_setting[pwr_up].delay = 0;
+ if (i) {
+ scr = scr +
+ sizeof(
+ struct cam_power_settings);
+ tot_size = tot_size +
+ sizeof(
+ struct cam_power_settings);
+ }
+ if (tot_size > cmd_length) {
+ pr_err("%s:%d :Error: Command Buffer is wrong\n",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ goto free_power_down_settings;
+ }
+ CDBG("Seq Type[%d]: %d Config_val: %ldn",
+ pwr_up,
+ power_info->
+ power_setting[pwr_up].seq_type,
+ power_info->
+ power_setting[pwr_up].
+ config_val);
+ }
+ last_cmd_type = CAMERA_SENSOR_CMD_TYPE_PWR_UP;
+ ptr = (void *) scr;
+ cmm_hdr = (struct common_header *)ptr;
+ } else if (cmm_hdr->cmd_type == CAMERA_SENSOR_CMD_TYPE_WAIT) {
+ struct cam_cmd_unconditional_wait *wait_cmd =
+ (struct cam_cmd_unconditional_wait *)ptr;
+ if (wait_cmd->op_code ==
+ CAMERA_SENSOR_WAIT_OP_SW_UCND) {
+ if (last_cmd_type ==
+ CAMERA_SENSOR_CMD_TYPE_PWR_UP) {
+ if (pwr_up > 0)
+ power_info->
+ power_setting
+ [pwr_up - 1].delay +=
+ wait_cmd->delay;
+ else
+ pr_err("%s:%d Delay is expected only after valid power up setting\n",
+ __func__, __LINE__);
+ } else if (last_cmd_type ==
+ CAMERA_SENSOR_CMD_TYPE_PWR_DOWN) {
+ if (pwr_down > 0)
+ power_info->
+ power_down_setting
+ [pwr_down - 1].delay +=
+ wait_cmd->delay;
+ else
+ pr_err("%s:%d Delay is expected only after valid power down setting\n",
+ __func__, __LINE__);
+ }
+ } else
+ CDBG("%s:%d Invalid op code: %d\n",
+ __func__, __LINE__, wait_cmd->op_code);
+ tot_size = tot_size +
+ sizeof(struct cam_cmd_unconditional_wait);
+ if (tot_size > cmd_length) {
+ pr_err("Command Buffer is wrong\n");
+ return -EINVAL;
+ }
+ scr = (void *) (wait_cmd);
+ ptr = (void *)
+ (scr +
+ sizeof(struct cam_cmd_unconditional_wait));
+ CDBG("%s:%d ptr: %pK sizeof: %d Next: %pK\n",
+ __func__, __LINE__, scr,
+ (int32_t)sizeof(
+ struct cam_cmd_unconditional_wait), ptr);
+
+ cmm_hdr = (struct common_header *)ptr;
+ } else if (cmm_hdr->cmd_type ==
+ CAMERA_SENSOR_CMD_TYPE_PWR_DOWN) {
+ struct cam_cmd_power *pwr_cmd =
+ (struct cam_cmd_power *)ptr;
+
+ scr = ptr + sizeof(struct cam_cmd_power);
+ tot_size = tot_size + sizeof(struct cam_cmd_power);
+ power_info->power_down_setting_size += pwr_cmd->count;
+
+ if (pwr_cmd->count == 0)
+ pr_err("%s:%d Invalid Command\n",
+ __func__, __LINE__);
+
+ for (i = 0; i < pwr_cmd->count; i++, pwr_down++) {
+ power_info->
+ power_down_setting[pwr_down].
+ seq_type =
+ pwr_cmd->power_settings[i].
+ power_seq_type;
+ power_info->
+ power_down_setting[pwr_down].
+ config_val =
+ pwr_cmd->power_settings[i].
+ config_val_low;
+ power_info->
+ power_down_setting[pwr_down].delay = 0;
+ if (i) {
+ scr = scr +
+ sizeof(
+ struct cam_power_settings);
+ tot_size =
+ tot_size +
+ sizeof(
+ struct cam_power_settings);
+ }
+ if (tot_size > cmd_length) {
+ pr_err("Command Buffer is wrong\n");
+ rc = -EINVAL;
+ goto free_power_down_settings;
+ }
+ CDBG("%s:%d Seq Type[%d]: %d Config_val: %ldn",
+ __func__, __LINE__,
+ pwr_down,
+ power_info->
+ power_down_setting[pwr_down].
+ seq_type,
+ power_info->
+ power_down_setting[pwr_down].
+ config_val);
+ }
+ last_cmd_type = CAMERA_SENSOR_CMD_TYPE_PWR_DOWN;
+ ptr = (void *) scr;
+ cmm_hdr = (struct common_header *)ptr;
+ } else {
+ pr_err("%s:%d: :Error: Un expected Header Type: %d\n",
+ __func__, __LINE__, cmm_hdr->cmd_type);
+ }
+ }
+
+ return rc;
+free_power_down_settings:
+ kfree(power_info->power_down_setting);
+free_power_settings:
+ kfree(power_info->power_setting);
+ return rc;
+}
+
+int32_t cam_handle_cmd_buffers_for_probe(void *cmd_buf,
+ struct cam_sensor_ctrl_t *s_ctrl,
+ int32_t cmd_buf_num, int cmd_buf_length)
+{
+ int32_t rc = 0;
+
+ switch (cmd_buf_num) {
+ case 0: {
+ struct cam_cmd_i2c_info *i2c_info = NULL;
+ struct cam_cmd_probe *probe_info;
+
+ i2c_info = (struct cam_cmd_i2c_info *)cmd_buf;
+ rc = cam_sensor_update_i2c_info(i2c_info, s_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d Failed in Updating the i2c Info\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ probe_info = (struct cam_cmd_probe *)
+ (cmd_buf + sizeof(struct cam_cmd_i2c_info));
+ rc = cam_sensor_update_slave_info(probe_info, s_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: Updating the slave Info\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ cmd_buf = probe_info;
+ }
+ break;
+ case 1: {
+ rc = cam_sensor_update_power_settings(cmd_buf,
+ cmd_buf_length, s_ctrl);
+ if (rc < 0) {
+ pr_err("Failed in updating power settings\n");
+ return rc;
+ }
+ }
+ break;
+ default:
+ pr_err("%s:%d Invalid command buffer\n",
+ __func__, __LINE__);
+ break;
+ }
+ return rc;
+}
+
+int32_t cam_handle_mem_ptr(uint64_t handle, struct cam_sensor_ctrl_t *s_ctrl)
+{
+ int rc = 0, i;
+ void *packet = NULL, *cmd_buf1 = NULL;
+ uint32_t *cmd_buf;
+ void *ptr;
+ size_t len;
+ struct cam_packet *pkt;
+ struct cam_cmd_buf_desc *cmd_desc;
+
+ rc = cam_mem_get_cpu_buf(handle,
+ (uint64_t *)&packet, &len);
+ if (rc < 0) {
+ pr_err("%s: %d Failed to get the command Buffer\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ pkt = (struct cam_packet *)packet;
+ cmd_desc = (struct cam_cmd_buf_desc *)
+ ((uint32_t *)&pkt->payload + pkt->cmd_buf_offset/4);
+ if (cmd_desc == NULL) {
+ pr_err("%s: %d command descriptor pos is invalid\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ if (pkt->num_cmd_buf != 2) {
+ pr_err("%s: %d Expected More Command Buffers : %d\n",
+ __func__, __LINE__, pkt->num_cmd_buf);
+ return -EINVAL;
+ }
+ for (i = 0; i < pkt->num_cmd_buf; i++) {
+ if (!(cmd_desc[i].length))
+ continue;
+ rc = cam_mem_get_cpu_buf(cmd_desc[i].mem_handle,
+ (uint64_t *)&cmd_buf1, &len);
+ if (rc < 0) {
+ pr_err("%s: %d Failed to parse the command Buffer Header\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ cmd_buf = (uint32_t *)cmd_buf1;
+ cmd_buf += cmd_desc[i].offset/4;
+ ptr = (void *) cmd_buf;
+
+ rc = cam_handle_cmd_buffers_for_probe(ptr, s_ctrl,
+ i, cmd_desc[i].length);
+ if (rc < 0) {
+ pr_err("%s: %d Failed to parse the command Buffer Header\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ }
+ return rc;
+}
+
+void cam_sensor_query_cap(struct cam_sensor_ctrl_t *s_ctrl,
+ struct cam_sensor_query_cap *query_cap)
+{
+ query_cap->pos_roll = s_ctrl->sensordata->pos_roll;
+ query_cap->pos_pitch = s_ctrl->sensordata->pos_pitch;
+ query_cap->pos_yaw = s_ctrl->sensordata->pos_yaw;
+ query_cap->secure_camera = 0;
+ query_cap->actuator_slot_id =
+ s_ctrl->sensordata->subdev_id[SUB_MODULE_ACTUATOR];
+ query_cap->csiphy_slot_id =
+ s_ctrl->sensordata->subdev_id[SUB_MODULE_CSIPHY];
+ query_cap->eeprom_slot_id =
+ s_ctrl->sensordata->subdev_id[SUB_MODULE_EEPROM];
+ query_cap->flash_slot_id =
+ s_ctrl->sensordata->subdev_id[SUB_MODULE_LED_FLASH];
+ query_cap->ois_slot_id =
+ s_ctrl->sensordata->subdev_id[SUB_MODULE_OIS];
+ query_cap->slot_info =
+ s_ctrl->id;
+}
+
+static uint16_t cam_sensor_id_by_mask(struct cam_sensor_ctrl_t *s_ctrl,
+ uint32_t chipid)
+{
+ uint16_t sensor_id = (uint16_t)(chipid & 0xFFFF);
+ int16_t sensor_id_mask = s_ctrl->sensordata->slave_info.sensor_id_mask;
+
+ if (!sensor_id_mask)
+ sensor_id_mask = ~sensor_id_mask;
+
+ sensor_id &= sensor_id_mask;
+ sensor_id_mask &= -sensor_id_mask;
+ sensor_id_mask -= 1;
+ while (sensor_id_mask) {
+ sensor_id_mask >>= 1;
+ sensor_id >>= 1;
+ }
+ return sensor_id;
+}
+
+int cam_sensor_match_id(struct cam_sensor_ctrl_t *s_ctrl)
+{
+ int rc = 0;
+ uint32_t chipid = 0;
+ struct cam_camera_slave_info *slave_info;
+
+ slave_info = &(s_ctrl->sensordata->slave_info);
+
+ if (!slave_info) {
+ pr_err("%s:%d failed: %pK\n",
+ __func__, __LINE__, slave_info);
+ return -EINVAL;
+ }
+
+ rc = camera_io_dev_read(
+ &(s_ctrl->io_master_info),
+ slave_info->sensor_id_reg_addr,
+ &chipid, CAMERA_SENSOR_I2C_TYPE_WORD);
+
+ CDBG("%s:%d read id: 0x%x expected id 0x%x:\n",
+ __func__, __LINE__, chipid, slave_info->sensor_id);
+ if (cam_sensor_id_by_mask(s_ctrl, chipid) != slave_info->sensor_id) {
+ pr_err("%s: chip id %x does not match %x\n",
+ __func__, chipid, slave_info->sensor_id);
+ return -ENODEV;
+ }
+ return rc;
+}
+
+int32_t cam_sensor_driver_cmd(struct cam_sensor_ctrl_t *s_ctrl,
+ void *arg)
+{
+ int rc = 0;
+ struct cam_control *cmd = (struct cam_control *)arg;
+ struct cam_sensor_power_setting *pu = NULL;
+ struct cam_sensor_power_setting *pd = NULL;
+ struct cam_sensor_power_ctrl_t *power_info =
+ &s_ctrl->sensordata->power_info;
+
+ if (!s_ctrl || !arg) {
+ pr_err("%s: %d s_ctrl is NULL\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&(s_ctrl->cam_sensor_mutex));
+ switch (cmd->op_code) {
+ case CAM_SENSOR_PROBE_CMD: {
+ if (s_ctrl->is_probe_succeed == 1) {
+ pr_err("Already Sensor Probed in the slot\n");
+ break;
+ }
+ /* Allocate memory for power up setting */
+ pu = kzalloc(sizeof(struct cam_sensor_power_setting) *
+ MAX_POWER_CONFIG, GFP_KERNEL);
+ if (!pu) {
+ rc = -ENOMEM;
+ goto release_mutex;
+ }
+
+ pd = kzalloc(sizeof(struct cam_sensor_power_setting) *
+ MAX_POWER_CONFIG, GFP_KERNEL);
+ if (!pd) {
+ kfree(pu);
+ rc = -ENOMEM;
+ goto release_mutex;
+ }
+
+ power_info->power_setting = pu;
+ power_info->power_down_setting = pd;
+
+ if (cmd->handle_type ==
+ CAM_HANDLE_MEM_HANDLE) {
+ rc = cam_handle_mem_ptr(cmd->handle, s_ctrl);
+ if (rc < 0) {
+ pr_err("%s: %d Get Buffer Handle Failed\n",
+ __func__, __LINE__);
+ kfree(pu);
+ kfree(pd);
+ goto release_mutex;
+ }
+ } else {
+ pr_err("%s:%d :Error: Invalid Command Type: %d",
+ __func__, __LINE__, cmd->handle_type);
+ }
+
+ /* Parse and fill vreg params for powerup settings */
+ rc = msm_camera_fill_vreg_params(
+ s_ctrl->sensordata->power_info.cam_vreg,
+ s_ctrl->sensordata->power_info.num_vreg,
+ s_ctrl->sensordata->power_info.power_setting,
+ s_ctrl->sensordata->power_info.power_setting_size);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: Fail in filling vreg params for PUP rc %d",
+ __func__, __LINE__, rc);
+ kfree(pu);
+ kfree(pd);
+ goto release_mutex;
+ }
+
+ /* Parse and fill vreg params for powerdown settings*/
+ rc = msm_camera_fill_vreg_params(
+ s_ctrl->sensordata->power_info.cam_vreg,
+ s_ctrl->sensordata->power_info.num_vreg,
+ s_ctrl->sensordata->power_info.power_down_setting,
+ s_ctrl->sensordata->power_info.power_down_setting_size);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: Fail in filling vreg params for PDOWN rc %d",
+ __func__, __LINE__, rc);
+ kfree(pu);
+ kfree(pd);
+ goto release_mutex;
+ }
+
+ /* Power up and probe sensor */
+ rc = cam_sensor_power_up(s_ctrl);
+ if (rc < 0) {
+ pr_err("power up failed");
+ cam_sensor_power_down(s_ctrl);
+ kfree(pu);
+ kfree(pd);
+ goto release_mutex;
+ }
+
+ /* Match sensor ID */
+ rc = cam_sensor_match_id(s_ctrl);
+ if (rc < 0) {
+ cam_sensor_power_down(s_ctrl);
+ msleep(20);
+ kfree(pu);
+ kfree(pd);
+ goto release_mutex;
+ }
+
+ CDBG("%s:%d Probe Succeeded on the slot: %d\n",
+ __func__, __LINE__, s_ctrl->id);
+ rc = cam_sensor_power_down(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: fail in Sensor Power Down\n",
+ __func__, __LINE__);
+ kfree(pu);
+ kfree(pd);
+ goto release_mutex;
+ }
+ /*
+ * Set probe succeeded flag to 1 so that no other camera shall
+ * probed on this slot
+ */
+ s_ctrl->is_probe_succeed = 1;
+ }
+ break;
+ case CAM_ACQUIRE_DEV: {
+ struct cam_sensor_acquire_dev sensor_acq_dev;
+ struct cam_create_dev_hdl bridge_params;
+
+ if (s_ctrl->bridge_intf.device_hdl != -1) {
+ pr_err("%s:%d Device is already acquired\n",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ goto release_mutex;
+ }
+ rc = copy_from_user(&sensor_acq_dev,
+ (void __user *) cmd->handle, sizeof(sensor_acq_dev));
+ if (rc < 0) {
+ pr_err("Failed Copying from user\n");
+ goto release_mutex;
+ }
+
+ bridge_params.session_hdl = sensor_acq_dev.session_handle;
+ bridge_params.ops = &s_ctrl->bridge_intf.ops;
+ bridge_params.v4l2_sub_dev_flag = 0;
+ bridge_params.media_entity_flag = 0;
+ bridge_params.priv = s_ctrl;
+
+ sensor_acq_dev.device_handle =
+ cam_create_device_hdl(&bridge_params);
+ s_ctrl->bridge_intf.device_hdl = sensor_acq_dev.device_handle;
+ s_ctrl->bridge_intf.session_hdl = sensor_acq_dev.session_handle;
+
+ CDBG("%s:%d Device Handle: %d\n", __func__, __LINE__,
+ sensor_acq_dev.device_handle);
+ if (copy_to_user((void __user *) cmd->handle, &sensor_acq_dev,
+ sizeof(struct cam_sensor_acquire_dev))) {
+ pr_err("Failed Copy to User\n");
+ rc = -EFAULT;
+ goto release_mutex;
+ }
+ }
+ break;
+ case CAM_RELEASE_DEV: {
+ if (s_ctrl->bridge_intf.device_hdl == -1) {
+ pr_err("%s:%d Invalid Handles: link hdl: %d device hdl: %d\n",
+ __func__, __LINE__,
+ s_ctrl->bridge_intf.device_hdl,
+ s_ctrl->bridge_intf.link_hdl);
+ rc = -EINVAL;
+ goto release_mutex;
+ }
+ rc = cam_destroy_device_hdl(s_ctrl->bridge_intf.device_hdl);
+ if (rc < 0)
+ pr_err("%s:%d Failed in destroying the device hdl\n",
+ __func__, __LINE__);
+ s_ctrl->bridge_intf.device_hdl = -1;
+ s_ctrl->bridge_intf.link_hdl = -1;
+ s_ctrl->bridge_intf.session_hdl = -1;
+ }
+ break;
+ case CAM_QUERY_CAP: {
+ struct cam_sensor_query_cap sensor_cap;
+
+ cam_sensor_query_cap(s_ctrl, &sensor_cap);
+ if (copy_to_user((void __user *) cmd->handle, &sensor_cap,
+ sizeof(struct cam_sensor_query_cap))) {
+ pr_err("Failed Copy to User\n");
+ rc = -EFAULT;
+ goto release_mutex;
+ }
+ break;
+ }
+ case CAM_START_DEV: {
+ rc = cam_sensor_power_up(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: Sensor Power up failed\n",
+ __func__, __LINE__);
+ goto release_mutex;
+ }
+ rc = cam_sensor_apply_settings(s_ctrl, 0);
+ if (rc < 0) {
+ pr_err("cannot apply settings\n");
+ goto release_mutex;
+ }
+ rc = delete_request(&s_ctrl->i2c_data.init_settings);
+ if (rc < 0) {
+ pr_err("%s:%d Fail in deleting the Init settings\n",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ goto release_mutex;
+ }
+ }
+ break;
+ case CAM_STOP_DEV: {
+ rc = cam_sensor_power_down(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d Sensor Power Down failed\n",
+ __func__, __LINE__);
+ goto release_mutex;
+ }
+ }
+ break;
+ case CAM_CONFIG_DEV: {
+ rc = cam_sensor_i2c_pkt_parse(s_ctrl, arg);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: Failed CCI Config: %d\n",
+ __func__, __LINE__, rc);
+ goto release_mutex;
+ }
+ }
+ break;
+ default:
+ pr_err("%s:%d :Error: Invalid Opcode: %d\n",
+ __func__, __LINE__, cmd->op_code);
+ rc = -EINVAL;
+ goto release_mutex;
+ }
+
+release_mutex:
+ mutex_unlock(&(s_ctrl->cam_sensor_mutex));
+ return rc;
+}
+
+int cam_sensor_publish_dev_info(struct cam_req_mgr_device_info *info)
+{
+ int rc = 0;
+
+ if (!info)
+ return -EINVAL;
+
+ info->dev_id = CAM_REQ_MGR_DEVICE_SENSOR;
+ strlcpy(info->name, CAM_SENSOR_NAME, sizeof(info->name));
+ info->p_delay = 2;
+
+ return rc;
+}
+
+int cam_sensor_establish_link(struct cam_req_mgr_core_dev_link_setup *link)
+{
+ struct cam_sensor_ctrl_t *s_ctrl = NULL;
+
+ if (!link)
+ return -EINVAL;
+
+ s_ctrl = (struct cam_sensor_ctrl_t *)
+ cam_get_device_priv(link->dev_hdl);
+ if (!s_ctrl) {
+ pr_err("%s: Device data is NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (link->link_enable) {
+ s_ctrl->bridge_intf.link_hdl = link->link_hdl;
+ s_ctrl->bridge_intf.crm_cb = link->crm_cb;
+ } else {
+ s_ctrl->bridge_intf.link_hdl = -1;
+ s_ctrl->bridge_intf.crm_cb = NULL;
+ }
+
+ return 0;
+}
+
+int cam_sensor_power(struct v4l2_subdev *sd, int on)
+{
+ struct cam_sensor_ctrl_t *s_ctrl = v4l2_get_subdevdata(sd);
+
+ mutex_lock(&(s_ctrl->cam_sensor_mutex));
+ if (!on && s_ctrl->sensor_state == CAM_SENSOR_POWER_UP) {
+ cam_sensor_power_down(s_ctrl);
+ s_ctrl->sensor_state = CAM_SENSOR_POWER_DOWN;
+ }
+ mutex_unlock(&(s_ctrl->cam_sensor_mutex));
+
+ return 0;
+}
+
+int cam_sensor_power_up(struct cam_sensor_ctrl_t *s_ctrl)
+{
+ int rc;
+ struct cam_sensor_power_ctrl_t *power_info;
+ struct cam_camera_slave_info *slave_info;
+
+ if (!s_ctrl) {
+ pr_err("%s:%d failed: %pK\n",
+ __func__, __LINE__, s_ctrl);
+ return -EINVAL;
+ }
+
+ power_info = &s_ctrl->sensordata->power_info;
+ slave_info = &(s_ctrl->sensordata->slave_info);
+
+ if (!power_info || !slave_info) {
+ pr_err("%s:%d failed: %pK %pK\n",
+ __func__, __LINE__, power_info,
+ slave_info);
+ return -EINVAL;
+ }
+
+ rc = cam_sensor_core_power_up(power_info);
+ if (rc < 0) {
+ pr_err("%s:%d power up the core is failed:%d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ if (s_ctrl->io_master_info.master_type == CCI_MASTER) {
+ rc = camera_io_init(&(s_ctrl->io_master_info));
+ if (rc < 0) {
+ pr_err("%s cci_init failed\n", __func__);
+ return -EINVAL;
+ }
+ }
+
+ s_ctrl->sensor_state = CAM_SENSOR_POWER_UP;
+
+ return rc;
+}
+
+int cam_sensor_power_down(struct cam_sensor_ctrl_t *s_ctrl)
+{
+ struct cam_sensor_power_ctrl_t *power_info;
+ int rc = 0;
+
+ if (!s_ctrl) {
+ pr_err("%s:%d failed: s_ctrl %pK\n",
+ __func__, __LINE__, s_ctrl);
+ return -EINVAL;
+ }
+
+ power_info = &s_ctrl->sensordata->power_info;
+
+ if (!power_info) {
+ pr_err("%s:%d failed: power_info %pK\n",
+ __func__, __LINE__, power_info);
+ return -EINVAL;
+ }
+ rc = msm_camera_power_down(power_info);
+ if (rc < 0) {
+ pr_err("%s:%d power down the core is failed:%d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ if (s_ctrl->io_master_info.master_type == CCI_MASTER)
+ camera_io_release(&(s_ctrl->io_master_info));
+
+ s_ctrl->sensor_state = CAM_SENSOR_POWER_DOWN;
+
+ return rc;
+}
+
+int cam_sensor_apply_settings(struct cam_sensor_ctrl_t *s_ctrl,
+ int64_t req_id)
+{
+ int rc = 0, offset, del_req_id;
+ struct i2c_settings_array *i2c_set = NULL;
+ struct i2c_settings_list *i2c_list;
+
+ if (req_id == 0) {
+ i2c_set = &s_ctrl->i2c_data.init_settings;
+ if (i2c_set->is_settings_valid == 1) {
+ list_for_each_entry(i2c_list,
+ &(i2c_set->list_head), list) {
+ rc = camera_io_dev_write(
+ &(s_ctrl->io_master_info),
+ &(i2c_list->i2c_settings));
+ if (rc < 0) {
+ pr_err("Failed to write the I2C settings\n");
+ return rc;
+ }
+ }
+ rc = delete_request(&(s_ctrl->i2c_data.init_settings));
+ i2c_set->is_settings_valid = 0;
+ if (rc < 0) {
+ pr_err("%s:%d :Error: Failed in deleting the Init request: %d\n",
+ __func__, __LINE__, rc);
+ }
+ }
+ } else {
+ offset = req_id % MAX_PER_FRAME_ARRAY;
+ i2c_set = &(s_ctrl->i2c_data.per_frame[offset]);
+ if (i2c_set->is_settings_valid == 1 &&
+ i2c_set->request_id == req_id) {
+ list_for_each_entry(i2c_list,
+ &(i2c_set->list_head), list) {
+ rc = camera_io_dev_write(
+ &(s_ctrl->io_master_info),
+ &(i2c_list->i2c_settings));
+ if (rc < 0) {
+ pr_err("%s:%d :Error: Fail to write the I2C settings: %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+ }
+ del_req_id = (req_id +
+ MAX_PER_FRAME_ARRAY -
+ MAX_SYSTEM_PIPELINE_DELAY) %
+ MAX_PER_FRAME_ARRAY;
+ CDBG("%s:%d Deleting the Request: %d\n",
+ __func__, __LINE__, del_req_id);
+ if (req_id >
+ s_ctrl->i2c_data.per_frame[del_req_id].
+ request_id) {
+ s_ctrl->i2c_data.per_frame[del_req_id].
+ request_id = 0;
+ rc = delete_request(
+ &(s_ctrl->i2c_data.
+ per_frame[del_req_id]));
+ if (rc < 0)
+ pr_err("%s:%d :Error: Failed in deleting the request: %d rc: %d\n",
+ __func__, __LINE__,
+ del_req_id, rc);
+ }
+ } else {
+ pr_err("%s:%d Invalid/NOP request to apply: %lld\n",
+ __func__, __LINE__, req_id);
+ }
+ }
+ return rc;
+}
+
+int32_t cam_sensor_apply_request(struct cam_req_mgr_apply_request *apply)
+{
+ int32_t rc = 0;
+ struct cam_sensor_ctrl_t *s_ctrl = NULL;
+
+ if (!apply)
+ return -EINVAL;
+
+ s_ctrl = (struct cam_sensor_ctrl_t *)
+ cam_get_device_priv(apply->dev_hdl);
+ if (!s_ctrl) {
+ pr_err("%s: Device data is NULL\n", __func__);
+ return -EINVAL;
+ }
+ CDBG("%s:%d Req Id: %lld\n", __func__, __LINE__,
+ apply->request_id);
+ rc = cam_sensor_apply_settings(s_ctrl, apply->request_id);
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.h
new file mode 100644
index 0000000..b23edce
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.h
@@ -0,0 +1,77 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_SENSOR_CORE_H_
+#define _CAM_SENSOR_CORE_H_
+
+#include "cam_sensor_dev.h"
+
+/**
+ * @s_ctrl: Sensor ctrl structure
+ *
+ * This API powers up the camera sensor module
+ */
+int cam_sensor_power_up(struct cam_sensor_ctrl_t *s_ctrl);
+
+/**
+ * @s_ctrl: Sensor ctrl structure
+ *
+ * This API powers down the camera sensor module
+ */
+int cam_sensor_power_down(struct cam_sensor_ctrl_t *s_ctrl);
+
+/**
+ * @sd: V4L2 subdevice
+ * @on: Turn off/on flag
+ *
+ * This API powers down the sensor module
+ */
+int cam_sensor_power(struct v4l2_subdev *sd, int on);
+
+/**
+ * @s_ctrl: Sensor ctrl structure
+ * @req_id: Request id
+ *
+ * This API applies the req_id settings to sensor
+ */
+int cam_sensor_apply_settings(struct cam_sensor_ctrl_t *s_ctrl, int64_t req_id);
+
+/**
+ * @apply: Req mgr structure for applying request
+ *
+ * This API applies the request that is mentioned
+ */
+int cam_sensor_apply_request(struct cam_req_mgr_apply_request *apply);
+
+/**
+ * @info: Sub device info to req mgr
+ *
+ * Publish the subdevice info
+ */
+int cam_sensor_publish_dev_info(struct cam_req_mgr_device_info *info);
+
+/**
+ * @link: Link setup info
+ *
+ * This API establishes link with sensor subdevice with req mgr
+ */
+int cam_sensor_establish_link(struct cam_req_mgr_core_dev_link_setup *link);
+
+/**
+ * @s_ctrl: Sensor ctrl structure
+ * @arg: Camera control command argument
+ *
+ * This API handles the camera control argument reached to sensor
+ */
+int32_t cam_sensor_driver_cmd(struct cam_sensor_ctrl_t *s_ctrl, void *arg);
+
+#endif /* _CAM_SENSOR_CORE_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.c
new file mode 100644
index 0000000..448ce51
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.c
@@ -0,0 +1,294 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "cam_sensor_dev.h"
+#include "cam_req_mgr_dev.h"
+#include "cam_sensor_soc.h"
+#include "cam_sensor_core.h"
+
+static long cam_sensor_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ int rc = 0;
+ struct cam_sensor_ctrl_t *s_ctrl =
+ v4l2_get_subdevdata(sd);
+
+ switch (cmd) {
+ case VIDIOC_CAM_CONTROL:
+ rc = cam_sensor_driver_cmd(s_ctrl, arg);
+ break;
+ default:
+ pr_err("%s:%d Invalid ioctl cmd: %d\n",
+ __func__, __LINE__, cmd);
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static int32_t cam_sensor_driver_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int32_t rc = 0;
+ struct cam_sensor_ctrl_t *s_ctrl;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("%s %s :Error: i2c_check_functionality failed\n",
+ __func__, client->name);
+ return -EFAULT;
+ }
+
+ /* Create sensor control structure */
+ s_ctrl = kzalloc(sizeof(*s_ctrl), GFP_KERNEL);
+ if (!s_ctrl)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, s_ctrl);
+
+ /* Initialize sensor device type */
+ s_ctrl->of_node = client->dev.of_node;
+ s_ctrl->io_master_info.master_type = I2C_MASTER;
+
+ rc = cam_sensor_parse_dt(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: cam_sensor_parse_dt rc %d",
+ __func__, __LINE__, rc);
+ goto free_s_ctrl;
+ }
+
+ return rc;
+free_s_ctrl:
+ kfree(s_ctrl);
+ return rc;
+}
+
+static int cam_sensor_platform_remove(struct platform_device *pdev)
+{
+ struct cam_sensor_ctrl_t *s_ctrl;
+
+ s_ctrl = platform_get_drvdata(pdev);
+ if (!s_ctrl) {
+ pr_err("%s: sensor device is NULL\n", __func__);
+ return 0;
+ }
+
+ kfree(s_ctrl->i2c_data.per_frame);
+ devm_kfree(&pdev->dev, s_ctrl);
+
+ return 0;
+}
+
+static int cam_sensor_driver_i2c_remove(struct i2c_client *client)
+{
+ struct cam_sensor_ctrl_t *s_ctrl = i2c_get_clientdata(client);
+
+ if (!s_ctrl) {
+ pr_err("%s: sensor device is NULL\n", __func__);
+ return 0;
+ }
+
+ kfree(s_ctrl->i2c_data.per_frame);
+ kfree(s_ctrl);
+
+ return 0;
+}
+
+#ifdef CONFIG_COMPAT
+static long cam_sensor_init_subdev_do_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, unsigned long arg)
+{
+ struct cam_control cmd_data;
+ int32_t rc = 0;
+
+ if (copy_from_user(&cmd_data, (void __user *)arg,
+ sizeof(cmd_data))) {
+ pr_err("Failed to copy from user_ptr=%pK size=%zu\n",
+ (void __user *)arg, sizeof(cmd_data));
+ return -EFAULT;
+ }
+
+ switch (cmd) {
+ case VIDIOC_CAM_CONTROL:
+ rc = cam_sensor_subdev_ioctl(sd, cmd, &cmd_data);
+ if (rc < 0)
+ pr_err("%s:%d cam_sensor_subdev_ioctl failed\n",
+ __func__, __LINE__);
+ break;
+ default:
+ pr_err("%s:%d Invalid compat ioctl cmd_type: %d\n",
+ __func__, __LINE__, cmd);
+ rc = -EINVAL;
+ }
+
+ if (!rc) {
+ if (copy_to_user((void __user *)arg, &cmd_data,
+ sizeof(cmd_data))) {
+ pr_err("Failed to copy to user_ptr=%pK size=%zu\n",
+ (void __user *)arg, sizeof(cmd_data));
+ rc = -EFAULT;
+ }
+ }
+ return rc;
+}
+
+#endif
+
+static struct v4l2_subdev_core_ops cam_sensor_subdev_core_ops = {
+ .ioctl = cam_sensor_subdev_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl32 = cam_sensor_init_subdev_do_ioctl,
+#endif
+ .s_power = cam_sensor_power,
+};
+
+static struct v4l2_subdev_ops cam_sensor_subdev_ops = {
+ .core = &cam_sensor_subdev_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops cam_sensor_internal_ops;
+
+static const struct of_device_id cam_sensor_driver_dt_match[] = {
+ {.compatible = "qcom,cam-sensor"},
+ {}
+};
+
+static int32_t cam_sensor_driver_platform_probe(
+ struct platform_device *pdev)
+{
+ int32_t rc = 0, i = 0;
+ struct cam_sensor_ctrl_t *s_ctrl = NULL;
+
+ /* Create sensor control structure */
+ s_ctrl = devm_kzalloc(&pdev->dev,
+ sizeof(struct cam_sensor_ctrl_t), GFP_KERNEL);
+ if (!s_ctrl)
+ return -ENOMEM;
+
+ /* Initialize sensor device type */
+ s_ctrl->of_node = pdev->dev.of_node;
+ s_ctrl->is_probe_succeed = 0;
+
+ /*fill in platform device*/
+ s_ctrl->pdev = pdev;
+
+ s_ctrl->io_master_info.master_type = CCI_MASTER;
+
+ rc = cam_sensor_parse_dt(s_ctrl);
+ if (rc < 0) {
+ pr_err("failed: cam_sensor_parse_dt rc %d", rc);
+ goto free_s_ctrl;
+ }
+
+ /* Fill platform device id*/
+ pdev->id = s_ctrl->id;
+
+ s_ctrl->v4l2_dev_str.internal_ops =
+ &cam_sensor_internal_ops;
+ s_ctrl->v4l2_dev_str.ops =
+ &cam_sensor_subdev_ops;
+ strlcpy(s_ctrl->device_name, CAMX_SENSOR_DEV_NAME,
+ sizeof(s_ctrl->device_name));
+ s_ctrl->v4l2_dev_str.name =
+ s_ctrl->device_name;
+ s_ctrl->v4l2_dev_str.sd_flags =
+ (V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS);
+ s_ctrl->v4l2_dev_str.ent_function =
+ CAM_SENSOR_DEVICE_TYPE;
+ s_ctrl->v4l2_dev_str.token = s_ctrl;
+
+ rc = cam_register_subdev(&(s_ctrl->v4l2_dev_str));
+ if (rc < 0) {
+ pr_err("%s:%d :ERROR: Fail with cam_register_subdev\n",
+ __func__, __LINE__);
+ goto free_s_ctrl;
+ }
+
+ s_ctrl->i2c_data.per_frame =
+ (struct i2c_settings_array *)
+ kzalloc(sizeof(struct i2c_settings_array) *
+ MAX_PER_FRAME_ARRAY, GFP_KERNEL);
+ if (s_ctrl->i2c_data.per_frame == NULL) {
+ rc = -ENOMEM;
+ goto free_s_ctrl;
+ }
+
+ INIT_LIST_HEAD(&(s_ctrl->i2c_data.init_settings.list_head));
+
+ for (i = 0; i < MAX_PER_FRAME_ARRAY; i++)
+ INIT_LIST_HEAD(&(s_ctrl->i2c_data.per_frame[i].list_head));
+
+ s_ctrl->bridge_intf.device_hdl = -1;
+ s_ctrl->bridge_intf.ops.get_dev_info = cam_sensor_publish_dev_info;
+ s_ctrl->bridge_intf.ops.link_setup = cam_sensor_establish_link;
+ s_ctrl->bridge_intf.ops.apply_req = cam_sensor_apply_request;
+
+ s_ctrl->sensordata->power_info.dev = &pdev->dev;
+ platform_set_drvdata(pdev, s_ctrl);
+ v4l2_set_subdevdata(&(s_ctrl->v4l2_dev_str.sd), s_ctrl);
+
+ return rc;
+free_s_ctrl:
+ devm_kfree(&pdev->dev, s_ctrl);
+ return rc;
+}
+
+MODULE_DEVICE_TABLE(of, cam_sensor_driver_dt_match);
+
+static struct platform_driver cam_sensor_platform_driver = {
+ .probe = cam_sensor_driver_platform_probe,
+ .driver = {
+ .name = "qcom,camera",
+ .owner = THIS_MODULE,
+ .of_match_table = cam_sensor_driver_dt_match,
+ },
+ .remove = cam_sensor_platform_remove,
+};
+
+static const struct i2c_device_id i2c_id[] = {
+ {SENSOR_DRIVER_I2C, (kernel_ulong_t)NULL},
+ { }
+};
+
+static struct i2c_driver cam_sensor_driver_i2c = {
+ .id_table = i2c_id,
+ .probe = cam_sensor_driver_i2c_probe,
+ .remove = cam_sensor_driver_i2c_remove,
+ .driver = {
+ .name = SENSOR_DRIVER_I2C,
+ },
+};
+
+static int __init cam_sensor_driver_init(void)
+{
+ int32_t rc = 0;
+
+ rc = platform_driver_register(&cam_sensor_platform_driver);
+ if (rc)
+ pr_err("%s platform_driver_register failed rc = %d",
+ __func__, rc);
+ rc = i2c_add_driver(&cam_sensor_driver_i2c);
+ if (rc)
+ pr_err("%s i2c_add_driver failed rc = %d", __func__, rc);
+
+ return rc;
+}
+
+static void __exit cam_sensor_driver_exit(void)
+{
+ platform_driver_unregister(&cam_sensor_platform_driver);
+ i2c_del_driver(&cam_sensor_driver_i2c);
+}
+
+module_init(cam_sensor_driver_init);
+module_exit(cam_sensor_driver_exit);
+MODULE_DESCRIPTION("cam_sensor_driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.h
new file mode 100644
index 0000000..f597c36
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.h
@@ -0,0 +1,111 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_SENSOR_DEV_H_
+#define _CAM_SENSOR_DEV_H_
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/irqreturn.h>
+#include <linux/ion.h>
+#include <linux/iommu.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
+#include <cam_cci_dev.h>
+#include <cam_sensor_cmn_header.h>
+#include <cam_subdev.h>
+#include <cam_sensor_soc_api.h>
+#include <cam_sensor_io.h>
+
+#define NUM_MASTERS 2
+#define NUM_QUEUES 2
+
+#define TRUE 1
+#define FALSE 0
+
+#undef CDBG
+#ifdef CAM_SENSOR_DEBUG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+#define SENSOR_DRIVER_I2C "i2c_camera"
+#define CAMX_SENSOR_DEV_NAME "cam-sensor-driver"
+
+enum cam_sensor_state_t {
+ CAM_SENSOR_POWER_DOWN,
+ CAM_SENSOR_POWER_UP,
+};
+
+/**
+ * struct intf_params
+ * @device_hdl: Device Handle
+ * @session_hdl: Session Handle
+ * @link_hdl: Link Handle
+ * @ops: KMD operations
+ * @crm_cb: Callback API pointers
+ */
+struct intf_params {
+ int32_t device_hdl;
+ int32_t session_hdl;
+ int32_t link_hdl;
+ struct cam_req_mgr_kmd_ops ops;
+ struct cam_req_mgr_crm_cb *crm_cb;
+};
+
+/**
+ * struct cam_sensor_ctrl_t: Camera control structure
+ * @pdev: Platform device
+ * @cam_sensor_mutex: Sensor mutex
+ * @sensordata: Sensor board Information
+ * @cci_i2c_master: I2C structure
+ * @io_master_info: Information about the communication master
+ * @sensor_state: Sensor states
+ * @is_probe_succeed: Probe succeeded or not
+ * @id: Cell Index
+ * @of_node: Of node ptr
+ * @v4l2_dev_str: V4L2 device structure
+ * @sensor_probe_addr_type: Sensor probe address type
+ * @sensor_probe_data_type: Sensor probe data type
+ * @i2c_data: Sensor I2C register settings
+ * @sensor_info: Sensor query cap structure
+ * @bridge_intf: Bridge interface structure
+ * @device_name: Sensor device structure
+ */
+struct cam_sensor_ctrl_t {
+ struct platform_device *pdev;
+ struct mutex cam_sensor_mutex;
+ struct cam_sensor_board_info *sensordata;
+ enum cci_i2c_master_t cci_i2c_master;
+ struct camera_io_master io_master_info;
+ enum cam_sensor_state_t sensor_state;
+ uint8_t is_probe_succeed;
+ uint32_t id;
+ struct device_node *of_node;
+ struct cam_subdev v4l2_dev_str;
+ uint8_t sensor_probe_addr_type;
+ uint8_t sensor_probe_data_type;
+ struct i2c_data_settings i2c_data;
+ struct cam_sensor_query_cap sensor_info;
+ struct intf_params bridge_intf;
+ char device_name[20];
+};
+
+#endif /* _CAM_SENSOR_DEV_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_soc.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_soc.c
new file mode 100644
index 0000000..8cb1078
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_soc.c
@@ -0,0 +1,267 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <cam_sensor_cmn_header.h>
+#include <cam_sensor_util.h>
+#include <cam_sensor_io.h>
+#include <cam_req_mgr_util.h>
+#include "cam_sensor_soc.h"
+
+int32_t cam_sensor_get_sub_module_index(struct device_node *of_node,
+ struct cam_sensor_board_info *s_info)
+{
+ int rc = 0, i = 0;
+ uint32_t val = 0;
+ struct device_node *src_node = NULL;
+ struct cam_sensor_board_info *sensor_info;
+
+ sensor_info = s_info;
+
+ for (i = 0; i < SUB_MODULE_MAX; i++)
+ sensor_info->subdev_id[i] = -1;
+
+ src_node = of_parse_phandle(of_node, "qcom,actuator-src", 0);
+ if (!src_node) {
+ CDBG("%s:%d src_node NULL\n", __func__, __LINE__);
+ } else {
+ rc = of_property_read_u32(src_node, "cell-index", &val);
+ CDBG("%s qcom,actuator cell index %d, rc %d\n", __func__,
+ val, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ of_node_put(src_node);
+ return rc;
+ }
+ sensor_info->subdev_id[SUB_MODULE_ACTUATOR] = val;
+ of_node_put(src_node);
+ }
+
+ src_node = of_parse_phandle(of_node, "qcom,ois-src", 0);
+ if (!src_node) {
+ CDBG("%s:%d src_node NULL\n", __func__, __LINE__);
+ } else {
+ rc = of_property_read_u32(src_node, "cell-index", &val);
+ CDBG("%s qcom,ois cell index %d, rc %d\n", __func__,
+ val, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ of_node_put(src_node);
+ return rc;
+ }
+ sensor_info->subdev_id[SUB_MODULE_OIS] = val;
+ of_node_put(src_node);
+ }
+
+ src_node = of_parse_phandle(of_node, "qcom,eeprom-src", 0);
+ if (!src_node) {
+ CDBG("%s:%d eeprom src_node NULL\n", __func__, __LINE__);
+ } else {
+ rc = of_property_read_u32(src_node, "cell-index", &val);
+ CDBG("%s qcom,eeprom cell index %d, rc %d\n", __func__,
+ val, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ of_node_put(src_node);
+ return rc;
+ }
+ sensor_info->subdev_id[SUB_MODULE_EEPROM] = val;
+ of_node_put(src_node);
+ }
+
+ src_node = of_parse_phandle(of_node, "qcom,led-flash-src", 0);
+ if (!src_node) {
+ CDBG("%s:%d src_node NULL\n", __func__, __LINE__);
+ } else {
+ rc = of_property_read_u32(src_node, "cell-index", &val);
+ CDBG("%s qcom,led flash cell index %d, rc %d\n", __func__,
+ val, rc);
+ if (rc < 0) {
+ pr_err("%s:%d failed %d\n", __func__, __LINE__, rc);
+ of_node_put(src_node);
+ return rc;
+ }
+ sensor_info->subdev_id[SUB_MODULE_LED_FLASH] = val;
+ of_node_put(src_node);
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,csiphy-sd-index", &val);
+ if (rc < 0)
+ pr_err("%s:%d :Error: paring the dt node for csiphy rc %d\n",
+ __func__, __LINE__, rc);
+ else
+ sensor_info->subdev_id[SUB_MODULE_CSIPHY] = val;
+
+ return rc;
+}
+
+static int32_t cam_sensor_driver_get_dt_data(struct cam_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+ struct cam_sensor_board_info *sensordata = NULL;
+ struct device_node *of_node = s_ctrl->of_node;
+ uint32_t cell_id;
+
+ s_ctrl->sensordata = kzalloc(sizeof(*sensordata), GFP_KERNEL);
+ if (!s_ctrl->sensordata)
+ return -ENOMEM;
+
+ sensordata = s_ctrl->sensordata;
+ /*
+ * Read cell index - this cell index will be the camera slot where
+ * this camera will be mounted
+ */
+ rc = of_property_read_u32(of_node, "cell-index", &cell_id);
+ if (rc < 0) {
+ pr_err("failed: cell-index rc %d", rc);
+ goto FREE_SENSOR_DATA;
+ }
+ s_ctrl->id = cell_id;
+
+ /* Validate cell_id */
+ if (cell_id >= MAX_CAMERAS) {
+ pr_err("failed: invalid cell_id %d", cell_id);
+ rc = -EINVAL;
+ goto FREE_SENSOR_DATA;
+ }
+
+ /* Read subdev info */
+ rc = cam_sensor_get_sub_module_index(of_node, sensordata);
+ if (rc < 0) {
+ pr_err("failed");
+ goto FREE_SENSOR_DATA;
+ }
+
+ /* Read vreg information */
+ rc = cam_sensor_get_dt_vreg_data(of_node,
+ &sensordata->power_info.cam_vreg,
+ &sensordata->power_info.num_vreg);
+ if (rc < 0) {
+ pr_err("failed: cam_sensor_get_dt_vreg_data rc %d", rc);
+ goto FREE_SENSOR_DATA;
+ }
+
+ /* Read gpio information */
+ rc = msm_sensor_driver_get_gpio_data
+ (&(sensordata->power_info.gpio_conf), of_node);
+ if (rc < 0) {
+ pr_err("failed: msm_sensor_driver_get_gpio_data rc %d", rc);
+ goto FREE_VREG_DATA;
+ }
+
+ /* Get CCI master */
+ rc = of_property_read_u32(of_node, "qcom,cci-master",
+ &s_ctrl->cci_i2c_master);
+ CDBG("qcom,cci-master %d, rc %d", s_ctrl->cci_i2c_master, rc);
+ if (rc < 0) {
+ /* Set default master 0 */
+ s_ctrl->cci_i2c_master = MASTER_0;
+ rc = 0;
+ }
+
+ if (of_property_read_u32(of_node, "qcom,sensor-position-pitch",
+ &sensordata->pos_pitch) < 0) {
+ CDBG("%s:%d Invalid sensor position\n", __func__, __LINE__);
+ sensordata->pos_pitch = 360;
+ }
+ if (of_property_read_u32(of_node, "qcom,sensor-position-roll",
+ &sensordata->pos_roll) < 0) {
+ CDBG("%s:%d Invalid sensor position\n", __func__, __LINE__);
+ sensordata->pos_roll = 360;
+ }
+ if (of_property_read_u32(of_node, "qcom,sensor-position-yaw",
+ &sensordata->pos_yaw) < 0) {
+ CDBG("%s:%d Invalid sensor position\n", __func__, __LINE__);
+ sensordata->pos_yaw = 360;
+ }
+
+ return rc;
+
+FREE_VREG_DATA:
+ kfree(sensordata->power_info.cam_vreg);
+FREE_SENSOR_DATA:
+ kfree(sensordata);
+ return rc;
+}
+
+int32_t msm_sensor_init_default_params(struct cam_sensor_ctrl_t *s_ctrl)
+{
+ /* Validate input parameters */
+ if (!s_ctrl) {
+ pr_err("%s:%d failed: invalid params s_ctrl %pK\n", __func__,
+ __LINE__, s_ctrl);
+ return -EINVAL;
+ }
+
+ CDBG("%s: %d master_type: %d\n", __func__, __LINE__,
+ s_ctrl->io_master_info.master_type);
+ /* Initialize cci_client */
+ if (s_ctrl->io_master_info.master_type == CCI_MASTER) {
+ s_ctrl->io_master_info.cci_client = kzalloc(sizeof(
+ struct cam_sensor_cci_client), GFP_KERNEL);
+ if (!(s_ctrl->io_master_info.cci_client))
+ return -ENOMEM;
+
+ } else {
+ pr_err("%s:%d Invalid master / Master type Not supported\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int32_t cam_sensor_parse_dt(struct cam_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+
+ /* Parse dt information and store in sensor control structure */
+ rc = cam_sensor_driver_get_dt_data(s_ctrl);
+ if (rc < 0) {
+ pr_err("failed: rc %d", rc);
+ return rc;
+ }
+
+ /* Initialize mutex */
+ mutex_init(&(s_ctrl->cam_sensor_mutex));
+
+ pr_err("%s: %d\n", __func__, __LINE__);
+ /* Initialize default parameters */
+ rc = msm_sensor_init_default_params(s_ctrl);
+ if (rc < 0) {
+ pr_err("failed: msm_sensor_init_default_params rc %d", rc);
+ goto FREE_DT_DATA;
+ }
+
+ /* Get clocks information */
+ rc = msm_camera_get_clk_info(s_ctrl->pdev,
+ &s_ctrl->sensordata->power_info.clk_info,
+ &s_ctrl->sensordata->power_info.clk_ptr,
+ &s_ctrl->sensordata->power_info.clk_info_size);
+ if (rc < 0) {
+ pr_err("failed: msm_camera_get_clk_info rc %d", rc);
+ goto FREE_DT_DATA;
+ }
+
+ return rc;
+
+FREE_DT_DATA:
+ kfree(s_ctrl->sensordata->power_info.gpio_conf->gpio_num_info);
+ kfree(s_ctrl->sensordata->power_info.gpio_conf->cam_gpio_req_tbl);
+ kfree(s_ctrl->sensordata->power_info.gpio_conf);
+ kfree(s_ctrl->sensordata->power_info.cam_vreg);
+ kfree(s_ctrl->sensordata);
+ s_ctrl->sensordata = NULL;
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_soc.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_soc.h
new file mode 100644
index 0000000..7f4a551
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_soc.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_SENSOR_SOC_H_
+#define _CAM_SENSOR_SOC_H_
+
+#include "cam_sensor_dev.h"
+
+/**
+ * @s_ctrl: Sensor ctrl structure
+ *
+ * Parses sensor dt
+ */
+int cam_sensor_parse_dt(struct cam_sensor_ctrl_t *s_ctrl);
+
+#endif /* _CAM_SENSOR_SOC_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/Makefile b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/Makefile
new file mode 100644
index 0000000..bdae1d1
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/Makefile
@@ -0,0 +1,8 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_cci
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_io.o cam_sensor_cci_i2c.o
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_cci_i2c.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_cci_i2c.c
new file mode 100644
index 0000000..1709fd3
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_cci_i2c.c
@@ -0,0 +1,184 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "cam_sensor_cmn_header.h"
+#include "cam_sensor_i2c.h"
+#include "cam_cci_dev.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+int32_t cam_cci_i2c_read(struct cam_sensor_cci_client *cci_client,
+ uint32_t addr, uint32_t *data,
+ enum camera_sensor_i2c_type addr_type,
+ enum camera_sensor_i2c_type data_type)
+{
+ int32_t rc = -EINVAL;
+ unsigned char buf[data_type];
+ struct cam_cci_ctrl cci_ctrl;
+
+ if (addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID
+ || addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX
+ || data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID
+ || data_type >= CAMERA_SENSOR_I2C_TYPE_MAX)
+ return rc;
+
+ cci_ctrl.cmd = MSM_CCI_I2C_READ;
+ cci_ctrl.cci_info = cci_client;
+ cci_ctrl.cfg.cci_i2c_read_cfg.addr = addr;
+ cci_ctrl.cfg.cci_i2c_read_cfg.addr_type = addr_type;
+ cci_ctrl.cfg.cci_i2c_read_cfg.data = buf;
+ cci_ctrl.cfg.cci_i2c_read_cfg.num_byte = data_type;
+ rc = v4l2_subdev_call(cci_client->cci_subdev,
+ core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl);
+ if (rc < 0) {
+ pr_err("%s: line %d rc = %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ rc = cci_ctrl.status;
+ if (data_type == CAMERA_SENSOR_I2C_TYPE_BYTE)
+ *data = buf[0];
+ else if (data_type == CAMERA_SENSOR_I2C_TYPE_WORD)
+ *data = buf[0] << 8 | buf[1];
+ else if (data_type == CAMERA_SENSOR_I2C_TYPE_3B)
+ *data = buf[0] << 16 | buf[1] << 8 | buf[2];
+ else
+ *data = buf[0] << 24 | buf[1] << 16 |
+ buf[2] << 8 | buf[3];
+
+ return rc;
+}
+
+static int32_t cam_cci_i2c_write_table_cmd(
+ struct camera_io_master *client,
+ struct cam_sensor_i2c_reg_setting *write_setting,
+ enum cam_cci_cmd_type cmd)
+{
+ int32_t rc = -EINVAL;
+ struct cam_cci_ctrl cci_ctrl;
+
+ if (!client || !write_setting)
+ return rc;
+
+ if (write_setting->addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID
+ || write_setting->addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX
+ || write_setting->data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID
+ || write_setting->data_type >= CAMERA_SENSOR_I2C_TYPE_MAX)
+ return rc;
+
+ cci_ctrl.cmd = cmd;
+ cci_ctrl.cci_info = client->cci_client;
+ cci_ctrl.cfg.cci_i2c_write_cfg.reg_setting =
+ write_setting->reg_setting;
+ cci_ctrl.cfg.cci_i2c_write_cfg.data_type = write_setting->data_type;
+ cci_ctrl.cfg.cci_i2c_write_cfg.addr_type = write_setting->addr_type;
+ cci_ctrl.cfg.cci_i2c_write_cfg.size = write_setting->size;
+ rc = v4l2_subdev_call(client->cci_client->cci_subdev,
+ core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl);
+ if (rc < 0) {
+ pr_err("%s: line %d rc = %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ rc = cci_ctrl.status;
+ if (write_setting->delay > 20)
+ msleep(write_setting->delay);
+ else if (write_setting->delay)
+ usleep_range(write_setting->delay * 1000, (write_setting->delay
+ * 1000) + 1000);
+
+ return rc;
+}
+
+
+int32_t cam_cci_i2c_write_table(
+ struct camera_io_master *client,
+ struct cam_sensor_i2c_reg_setting *write_setting)
+{
+ return cam_cci_i2c_write_table_cmd(client, write_setting,
+ MSM_CCI_I2C_WRITE);
+}
+
+static int32_t cam_cci_i2c_compare(struct cam_sensor_cci_client *client,
+ uint32_t addr, uint16_t data, uint16_t data_mask,
+ enum camera_sensor_i2c_type data_type,
+ enum camera_sensor_i2c_type addr_type)
+{
+ int32_t rc;
+ uint32_t reg_data = 0;
+
+ rc = cam_cci_i2c_read(client, addr, ®_data,
+ addr_type, data_type);
+ if (rc < 0)
+ return rc;
+
+ reg_data = reg_data & 0xFFFF;
+ if (data == (reg_data & ~data_mask))
+ return I2C_COMPARE_MATCH;
+ return I2C_COMPARE_MISMATCH;
+}
+
+int32_t cam_cci_i2c_poll(struct cam_sensor_cci_client *client,
+ uint32_t addr, uint16_t data, uint16_t data_mask,
+ enum camera_sensor_i2c_type data_type,
+ enum camera_sensor_i2c_type addr_type,
+ uint32_t delay_ms)
+{
+ int32_t rc = -EINVAL;
+ int32_t i = 0;
+
+ CDBG("%s: addr: 0x%x data: 0x%x dt: %d\n",
+ __func__, addr, data, data_type);
+
+ if (delay_ms > MAX_POLL_DELAY_MS) {
+ pr_err("%s:%d invalid delay = %d max_delay = %d\n",
+ __func__, __LINE__, delay_ms, MAX_POLL_DELAY_MS);
+ return -EINVAL;
+ }
+ for (i = 0; i < delay_ms; i++) {
+ rc = cam_cci_i2c_compare(client,
+ addr, data, data_mask, data_type, addr_type);
+ if (!rc) {
+ pr_err("%s:%d Fail in comparing the data in poll\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ usleep_range(1000, 1010);
+ }
+
+ /* If rc is 1 then read is successful but poll is failure */
+ if (rc == 1)
+ pr_err("%s:%d poll failed rc=%d(non-fatal)\n",
+ __func__, __LINE__, rc);
+
+ if (rc < 0)
+ pr_err("%s:%d poll failed rc=%d\n", __func__, __LINE__, rc);
+
+ return rc;
+}
+
+int32_t cam_sensor_cci_i2c_util(struct cam_sensor_cci_client *cci_client,
+ uint16_t cci_cmd)
+{
+ int32_t rc = 0;
+ struct cam_cci_ctrl cci_ctrl;
+
+ CDBG("%s line %d\n", __func__, __LINE__);
+ cci_ctrl.cmd = cci_cmd;
+ cci_ctrl.cci_info = cci_client;
+ rc = v4l2_subdev_call(cci_client->cci_subdev,
+ core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl);
+ if (rc < 0) {
+ pr_err("%s line %d rc = %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ return cci_ctrl.status;
+}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h
new file mode 100644
index 0000000..1261c4b
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h
@@ -0,0 +1,78 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_SENSOR_I2C_H_
+#define _CAM_SENSOR_I2C_H_
+
+#include <linux/delay.h>
+#include <media/v4l2-subdev.h>
+#include <media/cam_sensor.h>
+#include <media/cam_sensor.h>
+#include "cam_cci_dev.h"
+#include "cam_sensor_io.h"
+
+#define I2C_POLL_TIME_MS 5
+#define MAX_POLL_DELAY_MS 100
+
+#define I2C_COMPARE_MATCH 0
+#define I2C_COMPARE_MISMATCH 1
+
+/**
+ * @client: CCI client structure
+ * @data: I2C data
+ * @addr_type: I2c address type
+ * @data_type: I2C data type
+ *
+ * This API handles CCI read
+ */
+int32_t cam_cci_i2c_read(struct cam_sensor_cci_client *client,
+ uint32_t addr, uint32_t *data,
+ enum camera_sensor_i2c_type addr_type,
+ enum camera_sensor_i2c_type data_type);
+
+/**
+ * @client: CCI client structure
+ * @cci_cmd: CCI command type
+ *
+ * This API handles CCI random write
+ */
+int32_t cam_cci_i2c_write_table(
+ struct camera_io_master *client,
+ struct cam_sensor_i2c_reg_setting *write_setting);
+
+/**
+ * @cci_client: CCI client structure
+ * @cci_cmd: CCI command type
+ *
+ * Does I2C call to I2C functionalities
+ */
+int32_t cam_sensor_cci_i2c_util(struct cam_sensor_cci_client *cci_client,
+ uint16_t cci_cmd);
+
+/**
+ * @client: CCI client structure
+ * @addr: I2C address
+ * @data: I2C data
+ * @data_mask: I2C data mask
+ * @data_type: I2C data type
+ * @addr_type: I2C addr type
+ * @delay_ms: Delay in milli seconds
+ *
+ * This API implements CCI based I2C poll
+ */
+int32_t cam_cci_i2c_poll(struct cam_sensor_cci_client *client,
+ uint32_t addr, uint16_t data, uint16_t data_mask,
+ enum camera_sensor_i2c_type data_type,
+ enum camera_sensor_i2c_type addr_type,
+ uint32_t delay_ms);
+
+#endif /* _CAM_SENSOR_I2C_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
new file mode 100644
index 0000000..f889abc
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
@@ -0,0 +1,111 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "cam_sensor_io.h"
+#include "cam_sensor_i2c.h"
+
+int32_t camera_io_dev_poll(struct camera_io_master *io_master_info,
+ uint32_t addr, uint16_t data, uint32_t data_mask,
+ enum camera_sensor_i2c_type data_type,
+ enum camera_sensor_i2c_type addr_type,
+ uint32_t delay_ms)
+{
+ int16_t mask = data_mask & 0xFF;
+
+ if (!io_master_info) {
+ pr_err("%s:%d Invalid Args\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if (io_master_info->master_type == CCI_MASTER) {
+ return cam_cci_i2c_poll(io_master_info->cci_client,
+ addr, data, mask, data_type, addr_type, delay_ms);
+ } else {
+ pr_err("%s:%d Invalid Comm. Master:%d\n", __func__,
+ __LINE__, io_master_info->master_type);
+ return -EINVAL;
+ }
+}
+
+int32_t camera_io_dev_read(struct camera_io_master *io_master_info,
+ uint32_t addr, uint32_t *data,
+ enum camera_sensor_i2c_type data_type)
+{
+ if (!io_master_info) {
+ pr_err("%s:%d Invalid Args\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if (io_master_info->master_type == CCI_MASTER) {
+ return cam_cci_i2c_read(io_master_info->cci_client,
+ addr, data, data_type, data_type);
+ } else {
+ pr_err("%s:%d Invalid Comm. Master:%d\n", __func__,
+ __LINE__, io_master_info->master_type);
+ return -EINVAL;
+ }
+}
+
+int32_t camera_io_dev_write(struct camera_io_master *io_master_info,
+ struct cam_sensor_i2c_reg_setting *write_setting)
+{
+ if (!write_setting || !io_master_info) {
+ pr_err("Input parameters not valid ws: %pK ioinfo: %pK",
+ write_setting, io_master_info);
+ return -EINVAL;
+ }
+
+ if (io_master_info->master_type == CCI_MASTER) {
+ return cam_cci_i2c_write_table(io_master_info,
+ write_setting);
+ } else {
+ pr_err("%s:%d Invalid Comm. Master:%d\n", __func__,
+ __LINE__, io_master_info->master_type);
+ return -EINVAL;
+ }
+}
+
+int32_t camera_io_init(struct camera_io_master *io_master_info)
+{
+ if (!io_master_info) {
+ pr_err("%s:%d Invalid Args\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if (io_master_info->master_type == CCI_MASTER) {
+ io_master_info->cci_client->cci_subdev =
+ cam_cci_get_subdev();
+ return cam_sensor_cci_i2c_util(io_master_info->cci_client,
+ MSM_CCI_INIT);
+ } else {
+ pr_err("%s:%d Invalid Comm. Master:%d\n", __func__,
+ __LINE__, io_master_info->master_type);
+ return -EINVAL;
+ }
+}
+
+int32_t camera_io_release(struct camera_io_master *io_master_info)
+{
+ if (!io_master_info) {
+ pr_err("%s:%d Invalid Args\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if (io_master_info->master_type == CCI_MASTER) {
+ return cam_sensor_cci_i2c_util(io_master_info->cci_client,
+ MSM_CCI_RELEASE);
+ } else {
+ pr_err("%s:%d Invalid Comm. Master:%d\n", __func__,
+ __LINE__, io_master_info->master_type);
+ return -EINVAL;
+ }
+}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.h
new file mode 100644
index 0000000..757ac17
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.h
@@ -0,0 +1,89 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_SENSOR_IO_H_
+#define _CAM_SENSOR_IO_H_
+
+#include <media/cam_sensor.h>
+
+#include "cam_sensor_cmn_header.h"
+
+#define CCI_MASTER 1
+#define I2C_MASTER 2
+#define SPI_MASTER 3
+
+/**
+ * @master_type: CCI master type
+ * @client: I2C client information structure
+ * @cci_client: CCI client information structure
+ * @spi_client: SPI client information structure
+ */
+struct camera_io_master {
+ int master_type;
+ struct i2c_client *client;
+ struct cam_sensor_cci_client *cci_client;
+ struct cam_sensor_spi_client *spi_client;
+};
+
+/**
+ * @io_master_info: I2C/SPI master information
+ * @addr: I2C address
+ * @data: I2C data
+ * @data_type: I2C data type
+ *
+ * This API abstracts read functionality based on master type
+ */
+int32_t camera_io_dev_read(struct camera_io_master *io_master_info,
+ uint32_t addr, uint32_t *data,
+ enum camera_sensor_i2c_type data_type);
+
+/**
+ * @io_master_info: I2C/SPI master information
+ *
+ * This API initializes the I2C/SPI master based on master type
+ */
+int32_t camera_io_init(struct camera_io_master *io_master_info);
+
+/**
+ * @io_master_info: I2C/SPI master information
+ *
+ * This API releases the I2C/SPI master based on master type
+ */
+int32_t camera_io_release(struct camera_io_master *io_master_info);
+
+/**
+ * @io_master_info: I2C/SPI master information
+ * @write_setting: write settings information
+ *
+ * This API abstracts write functionality based on master type
+ */
+int32_t camera_io_dev_write(struct camera_io_master *io_master_info,
+ struct cam_sensor_i2c_reg_setting *write_setting);
+
+/**
+ * @io_master_info: I2C/SPI master information
+ * @addr: I2C address
+ * @data: I2C data
+ * @data_mask: I2C data mask
+ * @data_type: I2C data type
+ * @addr_type: I2C address type
+ * @delay_ms: delay in milli seconds
+ *
+ * This API abstracts poll functionality based on master type
+ */
+int32_t camera_io_dev_poll(struct camera_io_master *io_master_info,
+ uint32_t addr, uint16_t data, uint32_t data_mask,
+ enum camera_sensor_i2c_type data_type,
+ enum camera_sensor_i2c_type addr_type,
+ uint32_t delay_ms);
+
+#endif /* _CAM_SENSOR_IO_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/Makefile b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/Makefile
new file mode 100644
index 0000000..766828e
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/Makefile
@@ -0,0 +1,6 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_cci
+
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_util.o cam_sensor_soc_api.o
\ No newline at end of file
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h
new file mode 100644
index 0000000..e5e4872
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h
@@ -0,0 +1,375 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_SENSOR_CMN_HEADER_
+#define _CAM_SENSOR_CMN_HEADER_
+
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <media/cam_sensor.h>
+#include <media/cam_req_mgr.h>
+
+#define MAX_REGULATOR 5
+#define MAX_POWER_CONFIG 12
+
+#define MAX_PER_FRAME_ARRAY 8
+
+#define CAM_SENSOR_NAME "cam-sensor"
+#define CAM_ACTUATOR_NAME "cam-actuator"
+#define CAM_CSIPHY_NAME "cam-csiphy"
+
+#define MAX_SYSTEM_PIPELINE_DELAY 2
+
+#define CAM_PKT_NOP_OPCODE 127
+
+enum camera_sensor_cmd_type {
+ CAMERA_SENSOR_CMD_TYPE_INVALID,
+ CAMERA_SENSOR_CMD_TYPE_PROBE,
+ CAMERA_SENSOR_CMD_TYPE_PWR_UP,
+ CAMERA_SENSOR_CMD_TYPE_PWR_DOWN,
+ CAMERA_SENSOR_CMD_TYPE_I2C_INFO,
+ CAMERA_SENSOR_CMD_TYPE_I2C_RNDM_WR,
+ CAMERA_SENSOR_CMD_TYPE_I2C_RNDM_RD,
+ CAMERA_SENSOR_CMD_TYPE_I2C_CONT_WR,
+ CAMERA_SENSOR_CMD_TYPE_I2C_CONT_RD,
+ CAMERA_SENSOR_CMD_TYPE_WAIT,
+ CAMERA_SENSOR_CMD_TYPE_MAX,
+};
+
+enum camera_sensor_i2c_op_code {
+ CAMERA_SENSOR_I2C_OP_INVALID,
+ CAMERA_SENSOR_I2C_OP_RNDM_WR,
+ CAMERA_SENSOR_I2C_OP_RNDM_WR_VERF,
+ CAMERA_SENSOR_I2C_OP_CONT_WR_BRST,
+ CAMERA_SENSOR_I2C_OP_CONT_WR_BRST_VERF,
+ CAMERA_SENSOR_I2C_OP_CONT_WR_SEQN,
+ CAMERA_SENSOR_I2C_OP_CONT_WR_SEQN_VERF,
+ CAMERA_SENSOR_I2C_OP_MAX,
+};
+
+enum camera_sensor_wait_op_code {
+ CAMERA_SENSOR_WAIT_OP_INVALID,
+ CAMERA_SENSOR_WAIT_OP_COND,
+ CAMERA_SENSOR_WAIT_OP_HW_UCND,
+ CAMERA_SENSOR_WAIT_OP_SW_UCND,
+ CAMERA_SENSOR_WAIT_OP_MAX,
+};
+
+enum camera_sensor_i2c_type {
+ CAMERA_SENSOR_I2C_TYPE_INVALID,
+ CAMERA_SENSOR_I2C_TYPE_BYTE,
+ CAMERA_SENSOR_I2C_TYPE_WORD,
+ CAMERA_SENSOR_I2C_TYPE_3B,
+ CAMERA_SENSOR_I2C_TYPE_DWORD,
+ CAMERA_SENSOR_I2C_TYPE_MAX,
+};
+
+enum i2c_freq_mode {
+ I2C_STANDARD_MODE,
+ I2C_FAST_MODE,
+ I2C_CUSTOM_MODE,
+ I2C_FAST_PLUS_MODE,
+ I2C_MAX_MODES,
+};
+
+enum position_roll {
+ ROLL_0 = 0,
+ ROLL_90 = 90,
+ ROLL_180 = 180,
+ ROLL_270 = 270,
+ ROLL_INVALID = 360,
+};
+
+enum position_yaw {
+ FRONT_CAMERA_YAW = 0,
+ REAR_CAMERA_YAW = 180,
+ INVALID_YAW = 360,
+};
+
+enum position_pitch {
+ LEVEL_PITCH = 0,
+ INVALID_PITCH = 360,
+};
+
+enum sensor_sub_module {
+ SUB_MODULE_SENSOR,
+ SUB_MODULE_ACTUATOR,
+ SUB_MODULE_EEPROM,
+ SUB_MODULE_LED_FLASH,
+ SUB_MODULE_CSID,
+ SUB_MODULE_CSIPHY,
+ SUB_MODULE_OIS,
+ SUB_MODULE_EXT,
+ SUB_MODULE_MAX,
+};
+
+enum msm_camera_power_seq_type {
+ SENSOR_MCLK,
+ SENSOR_VANA,
+ SENSOR_VDIG,
+ SENSOR_VIO,
+ SENSOR_VAF,
+ SENSOR_VAF_PWDM,
+ SENSOR_CUSTOM_REG1,
+ SENSOR_CUSTOM_REG2,
+ SENSOR_RESET,
+ SENSOR_STANDBY,
+ SENSOR_CUSTOM_GPIO1,
+ SENSOR_CUSTOM_GPIO2,
+ SENSOR_SEQ_TYPE_MAX,
+};
+
+enum cam_sensor_packet_opcodes {
+ CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMON,
+ CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE,
+ CAM_SENSOR_PACKET_OPCODE_SENSOR_INITIAL_CONFIG,
+ CAM_SENSOR_PACKET_OPCODE_SENSOR_PROBE
+};
+
+enum cam_actuator_packet_opcodes {
+ CAM_ACTUATOR_PACKET_OPCODE_INIT,
+ CAM_ACTUATOR_PACKET_AUTO_MOVE_LENS,
+ CAM_ACTUATOR_PACKET_MANUAL_MOVE_LENS
+};
+
+enum msm_bus_perf_setting {
+ S_INIT,
+ S_PREVIEW,
+ S_VIDEO,
+ S_CAPTURE,
+ S_ZSL,
+ S_STEREO_VIDEO,
+ S_STEREO_CAPTURE,
+ S_DEFAULT,
+ S_LIVESHOT,
+ S_DUAL,
+ S_EXIT
+};
+
+enum msm_camera_device_type_t {
+ MSM_CAMERA_I2C_DEVICE,
+ MSM_CAMERA_PLATFORM_DEVICE,
+ MSM_CAMERA_SPI_DEVICE,
+};
+
+enum cci_i2c_master_t {
+ MASTER_0,
+ MASTER_1,
+ MASTER_MAX,
+};
+
+enum camera_vreg_type {
+ VREG_TYPE_DEFAULT,
+ VREG_TYPE_CUSTOM,
+};
+
+enum cam_sensor_i2c_cmd_type {
+ CAM_SENSOR_I2C_WRITE_RANDOM,
+ CAM_SENSOR_I2C_READ,
+ CAM_SENSOR_I2C_POLL
+};
+
+struct common_header {
+ uint16_t first_word;
+ uint8_t third_byte;
+ uint8_t cmd_type;
+};
+
+struct camera_vreg_t {
+ const char *reg_name;
+ int min_voltage;
+ int max_voltage;
+ int op_mode;
+ uint32_t delay;
+ const char *custom_vreg_name;
+ enum camera_vreg_type type;
+};
+
+struct cam_sensor_module_power_setting {
+ enum msm_camera_power_seq_type seq_type;
+ unsigned short seq_val;
+ uint32_t config_val_low;
+ uint32_t config_val_high;
+ unsigned short delay;
+};
+
+struct msm_camera_gpio_num_info {
+ uint16_t gpio_num[SENSOR_SEQ_TYPE_MAX];
+ uint8_t valid[SENSOR_SEQ_TYPE_MAX];
+};
+
+struct msm_cam_clk_info {
+ const char *clk_name;
+ long clk_rate;
+ uint32_t delay;
+};
+
+struct msm_pinctrl_info {
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *gpio_state_active;
+ struct pinctrl_state *gpio_state_suspend;
+ bool use_pinctrl;
+};
+
+struct cam_sensor_i2c_reg_array {
+ uint32_t reg_addr;
+ uint32_t reg_data;
+ uint32_t delay;
+ uint32_t data_mask;
+};
+
+struct cam_sensor_i2c_reg_setting {
+ struct cam_sensor_i2c_reg_array *reg_setting;
+ unsigned short size;
+ enum camera_sensor_i2c_type addr_type;
+ enum camera_sensor_i2c_type data_type;
+ unsigned short delay;
+};
+
+struct i2c_settings_list {
+ struct cam_sensor_i2c_reg_setting i2c_settings;
+ enum cam_sensor_i2c_cmd_type op_code;
+ struct list_head list;
+};
+
+struct i2c_settings_array {
+ struct list_head list_head;
+ int32_t is_settings_valid;
+ int64_t request_id;
+};
+
+struct i2c_data_settings {
+ struct i2c_settings_array init_settings;
+ struct i2c_settings_array *per_frame;
+};
+
+struct cam_sensor_power_ctrl_t {
+ struct device *dev;
+ struct cam_sensor_power_setting *power_setting;
+ uint16_t power_setting_size;
+ struct cam_sensor_power_setting *power_down_setting;
+ uint16_t power_down_setting_size;
+ struct msm_camera_gpio_conf *gpio_conf;
+ struct camera_vreg_t *cam_vreg;
+ int num_vreg;
+ struct clk **clk_ptr;
+ struct msm_cam_clk_info *clk_info;
+ struct msm_pinctrl_info pinctrl_info;
+ uint8_t cam_pinctrl_status;
+ size_t clk_info_size;
+};
+
+struct cam_camera_slave_info {
+ uint16_t sensor_slave_addr;
+ uint16_t sensor_id_reg_addr;
+ uint16_t sensor_id;
+ uint16_t sensor_id_mask;
+};
+
+struct msm_sensor_init_params {
+ int modes_supported;
+ unsigned int sensor_mount_angle;
+};
+
+enum msm_sensor_camera_id_t {
+ CAMERA_0,
+ CAMERA_1,
+ CAMERA_2,
+ CAMERA_3,
+ MAX_CAMERAS,
+};
+
+struct msm_sensor_id_info_t {
+ unsigned short sensor_id_reg_addr;
+ unsigned short sensor_id;
+ unsigned short sensor_id_mask;
+};
+
+enum msm_sensor_output_format_t {
+ MSM_SENSOR_BAYER,
+ MSM_SENSOR_YCBCR,
+ MSM_SENSOR_META,
+};
+
+struct cam_sensor_power_setting {
+ enum msm_camera_power_seq_type seq_type;
+ unsigned short seq_val;
+ long config_val;
+ unsigned short delay;
+ void *data[10];
+};
+
+struct cam_sensor_power_setting_array {
+ struct cam_sensor_power_setting power_setting_a[MAX_POWER_CONFIG];
+ struct cam_sensor_power_setting *power_setting;
+ unsigned short size;
+ struct cam_sensor_power_setting power_down_setting_a[MAX_POWER_CONFIG];
+ struct cam_sensor_power_setting *power_down_setting;
+ unsigned short size_down;
+};
+
+struct msm_camera_sensor_slave_info {
+ enum msm_sensor_camera_id_t camera_id;
+ unsigned short slave_addr;
+ enum i2c_freq_mode i2c_freq_mode;
+ enum camera_sensor_i2c_type addr_type;
+ struct msm_sensor_id_info_t sensor_id_info;
+ struct cam_sensor_power_setting_array power_setting_array;
+ unsigned char is_init_params_valid;
+ enum msm_sensor_output_format_t output_format;
+};
+
+struct cam_sensor_board_info {
+ struct cam_camera_slave_info slave_info;
+ int32_t sensor_mount_angle;
+ int32_t secure_mode;
+ int modes_supported;
+ int32_t pos_roll;
+ int32_t pos_yaw;
+ int32_t pos_pitch;
+ int32_t subdev_id[SUB_MODULE_MAX];
+ int32_t subdev_intf[SUB_MODULE_MAX];
+ const char *misc_regulator;
+ struct cam_sensor_power_ctrl_t power_info;
+};
+
+enum msm_camera_vreg_name_t {
+ CAM_VDIG,
+ CAM_VIO,
+ CAM_VANA,
+ CAM_VAF,
+ CAM_V_CUSTOM1,
+ CAM_V_CUSTOM2,
+ CAM_VREG_MAX,
+};
+
+struct msm_camera_gpio_conf {
+ void *cam_gpiomux_conf_tbl;
+ uint8_t cam_gpiomux_conf_tbl_size;
+ struct gpio *cam_gpio_common_tbl;
+ uint8_t cam_gpio_common_tbl_size;
+ struct gpio *cam_gpio_req_tbl;
+ uint8_t cam_gpio_req_tbl_size;
+ uint32_t gpio_no_mux;
+ uint32_t *camera_off_table;
+ uint8_t camera_off_table_size;
+ uint32_t *camera_on_table;
+ uint8_t camera_on_table_size;
+ struct msm_camera_gpio_num_info *gpio_num_info;
+};
+
+#endif /* _CAM_SENSOR_CMN_HEADER_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_soc_api.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_soc_api.c
new file mode 100644
index 0000000..2eed9ce
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_soc_api.c
@@ -0,0 +1,1331 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/msm-bus.h>
+#include "cam_sensor_soc_api.h"
+
+#define NO_SET_RATE -1
+#define INIT_RATE -2
+
+#ifdef CONFIG_CAM_SOC_API_DBG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+int msm_cam_clk_sel_src(struct device *dev, struct msm_cam_clk_info *clk_info,
+ struct msm_cam_clk_info *clk_src_info, int num_clk)
+{
+ int i;
+ int rc = 0;
+ struct clk *mux_clk = NULL;
+ struct clk *src_clk = NULL;
+
+ for (i = 0; i < num_clk; i++) {
+ if (clk_src_info[i].clk_name) {
+ mux_clk = clk_get(dev, clk_info[i].clk_name);
+ if (IS_ERR(mux_clk)) {
+ pr_err("%s get failed\n",
+ clk_info[i].clk_name);
+ continue;
+ }
+ src_clk = clk_get(dev, clk_src_info[i].clk_name);
+ if (IS_ERR(src_clk)) {
+ pr_err("%s get failed\n",
+ clk_src_info[i].clk_name);
+ continue;
+ }
+ clk_set_parent(mux_clk, src_clk);
+ }
+ }
+ return rc;
+}
+
+int msm_cam_clk_enable(struct device *dev, struct msm_cam_clk_info *clk_info,
+ struct clk **clk_ptr, int num_clk, int enable)
+{
+ int i;
+ int rc = 0;
+ long clk_rate;
+
+ if (enable) {
+ for (i = 0; i < num_clk; i++) {
+ CDBG("%s enable %s\n", __func__, clk_info[i].clk_name);
+ clk_ptr[i] = clk_get(dev, clk_info[i].clk_name);
+ if (IS_ERR(clk_ptr[i])) {
+ pr_err("%s get failed\n", clk_info[i].clk_name);
+ rc = PTR_ERR(clk_ptr[i]);
+ goto cam_clk_get_err;
+ }
+ if (clk_info[i].clk_rate > 0) {
+ clk_rate = clk_round_rate(clk_ptr[i],
+ clk_info[i].clk_rate);
+ if (clk_rate < 0) {
+ pr_err("%s round failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_set_err;
+ }
+ rc = clk_set_rate(clk_ptr[i],
+ clk_rate);
+ if (rc < 0) {
+ pr_err("%s set failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_set_err;
+ }
+
+ } else if (clk_info[i].clk_rate == INIT_RATE) {
+ clk_rate = clk_get_rate(clk_ptr[i]);
+ if (clk_rate == 0) {
+ clk_rate =
+ clk_round_rate(clk_ptr[i], 0);
+ if (clk_rate < 0) {
+ pr_err("%s round rate failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_set_err;
+ }
+ rc = clk_set_rate(clk_ptr[i],
+ clk_rate);
+ if (rc < 0) {
+ pr_err("%s set rate failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_set_err;
+ }
+ }
+ }
+ rc = clk_prepare(clk_ptr[i]);
+ if (rc < 0) {
+ pr_err("%s prepare failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_prepare_err;
+ }
+
+ rc = clk_enable(clk_ptr[i]);
+ if (rc < 0) {
+ pr_err("%s enable failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_enable_err;
+ }
+ if (clk_info[i].delay > 20)
+ msleep(clk_info[i].delay);
+ else if (clk_info[i].delay)
+ usleep_range(clk_info[i].delay * 1000,
+ (clk_info[i].delay * 1000) + 1000);
+ }
+ } else {
+ for (i = num_clk - 1; i >= 0; i--) {
+ if (clk_ptr[i] != NULL) {
+ CDBG("%s disable %s\n", __func__,
+ clk_info[i].clk_name);
+ clk_disable(clk_ptr[i]);
+ clk_unprepare(clk_ptr[i]);
+ clk_put(clk_ptr[i]);
+ }
+ }
+ }
+
+ return rc;
+
+cam_clk_enable_err:
+ clk_unprepare(clk_ptr[i]);
+cam_clk_prepare_err:
+cam_clk_set_err:
+ clk_put(clk_ptr[i]);
+cam_clk_get_err:
+ for (i--; i >= 0; i--) {
+ if (clk_ptr[i] != NULL) {
+ clk_disable(clk_ptr[i]);
+ clk_unprepare(clk_ptr[i]);
+ clk_put(clk_ptr[i]);
+ }
+ }
+
+ return rc;
+}
+
+int msm_camera_config_vreg(struct device *dev, struct camera_vreg_t *cam_vreg,
+ int num_vreg, enum msm_camera_vreg_name_t *vreg_seq,
+ int num_vreg_seq, struct regulator **reg_ptr, int config)
+{
+ int i = 0, j = 0;
+ int rc = 0;
+ struct camera_vreg_t *curr_vreg;
+
+ if (num_vreg_seq > num_vreg) {
+ pr_err("%s:%d vreg sequence invalid\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ if (!num_vreg_seq)
+ num_vreg_seq = num_vreg;
+
+ if (config) {
+ for (i = 0; i < num_vreg_seq; i++) {
+ if (vreg_seq) {
+ j = vreg_seq[i];
+ if (j >= num_vreg)
+ continue;
+ } else {
+ j = i;
+ }
+ curr_vreg = &cam_vreg[j];
+ reg_ptr[j] = regulator_get(dev,
+ curr_vreg->reg_name);
+ if (IS_ERR(reg_ptr[j])) {
+ pr_err("%s: %s get failed\n",
+ __func__,
+ curr_vreg->reg_name);
+ reg_ptr[j] = NULL;
+ goto vreg_get_fail;
+ }
+ if (regulator_count_voltages(reg_ptr[j]) > 0) {
+ rc = regulator_set_voltage(
+ reg_ptr[j],
+ curr_vreg->min_voltage,
+ curr_vreg->max_voltage);
+ if (rc < 0) {
+ pr_err("%s: %s set voltage failed\n",
+ __func__,
+ curr_vreg->reg_name);
+ goto vreg_set_voltage_fail;
+ }
+ if (curr_vreg->op_mode >= 0) {
+ rc = regulator_set_load(
+ reg_ptr[j],
+ curr_vreg->op_mode);
+ if (rc < 0) {
+ pr_err(
+ "%s:%s set optimum mode fail\n",
+ __func__,
+ curr_vreg->reg_name);
+ goto vreg_set_opt_mode_fail;
+ }
+ }
+ }
+ }
+ } else {
+ for (i = num_vreg_seq-1; i >= 0; i--) {
+ if (vreg_seq) {
+ j = vreg_seq[i];
+ if (j >= num_vreg)
+ continue;
+ } else {
+ j = i;
+ }
+ curr_vreg = &cam_vreg[j];
+ if (reg_ptr[j]) {
+ if (regulator_count_voltages(reg_ptr[j]) > 0) {
+ if (curr_vreg->op_mode >= 0) {
+ regulator_set_load(
+ reg_ptr[j], 0);
+ }
+ regulator_set_voltage(
+ reg_ptr[j], 0, curr_vreg->
+ max_voltage);
+ }
+ regulator_put(reg_ptr[j]);
+ reg_ptr[j] = NULL;
+ }
+ }
+ }
+
+ return 0;
+
+vreg_unconfig:
+ if (regulator_count_voltages(reg_ptr[j]) > 0)
+ regulator_set_load(reg_ptr[j], 0);
+
+vreg_set_opt_mode_fail:
+ if (regulator_count_voltages(reg_ptr[j]) > 0)
+ regulator_set_voltage(reg_ptr[j], 0,
+ curr_vreg->max_voltage);
+
+vreg_set_voltage_fail:
+ regulator_put(reg_ptr[j]);
+ reg_ptr[j] = NULL;
+
+vreg_get_fail:
+ for (i--; i >= 0; i--) {
+ if (vreg_seq) {
+ j = vreg_seq[i];
+ if (j >= num_vreg)
+ continue;
+ } else {
+ j = i;
+ }
+ curr_vreg = &cam_vreg[j];
+ goto vreg_unconfig;
+ }
+
+ return -ENODEV;
+}
+
+int msm_camera_enable_vreg(struct device *dev, struct camera_vreg_t *cam_vreg,
+ int num_vreg, enum msm_camera_vreg_name_t *vreg_seq,
+ int num_vreg_seq, struct regulator **reg_ptr, int enable)
+{
+ int i = 0, j = 0, rc = 0;
+
+ if (num_vreg_seq > num_vreg) {
+ pr_err("%s:%d vreg sequence invalid\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ if (!num_vreg_seq)
+ num_vreg_seq = num_vreg;
+
+ if (enable) {
+ for (i = 0; i < num_vreg_seq; i++) {
+ if (vreg_seq) {
+ j = vreg_seq[i];
+ if (j >= num_vreg)
+ continue;
+ } else
+ j = i;
+ if (IS_ERR(reg_ptr[j])) {
+ pr_err("%s: %s null regulator\n",
+ __func__, cam_vreg[j].reg_name);
+ goto disable_vreg;
+ }
+ rc = regulator_enable(reg_ptr[j]);
+ if (rc < 0) {
+ pr_err("%s: %s enable failed\n",
+ __func__, cam_vreg[j].reg_name);
+ goto disable_vreg;
+ }
+ if (cam_vreg[j].delay > 20)
+ msleep(cam_vreg[j].delay);
+ else if (cam_vreg[j].delay)
+ usleep_range(cam_vreg[j].delay * 1000,
+ (cam_vreg[j].delay * 1000) + 1000);
+ }
+ } else {
+ for (i = num_vreg_seq-1; i >= 0; i--) {
+ if (vreg_seq) {
+ j = vreg_seq[i];
+ if (j >= num_vreg)
+ continue;
+ } else
+ j = i;
+ regulator_disable(reg_ptr[j]);
+ if (cam_vreg[j].delay > 20)
+ msleep(cam_vreg[j].delay);
+ else if (cam_vreg[j].delay)
+ usleep_range(cam_vreg[j].delay * 1000,
+ (cam_vreg[j].delay * 1000) + 1000);
+ }
+ }
+
+ return rc;
+disable_vreg:
+ for (i--; i >= 0; i--) {
+ if (vreg_seq) {
+ j = vreg_seq[i];
+ if (j >= num_vreg)
+ continue;
+ } else
+ j = i;
+ regulator_disable(reg_ptr[j]);
+ if (cam_vreg[j].delay > 20)
+ msleep(cam_vreg[j].delay);
+ else if (cam_vreg[j].delay)
+ usleep_range(cam_vreg[j].delay * 1000,
+ (cam_vreg[j].delay * 1000) + 1000);
+ }
+
+ return rc;
+}
+
+int msm_camera_set_gpio_table(struct msm_gpio_set_tbl *gpio_tbl,
+ uint8_t gpio_tbl_size, int gpio_en)
+{
+ int rc = 0, i;
+
+ if (gpio_en) {
+ for (i = 0; i < gpio_tbl_size; i++) {
+ gpio_set_value_cansleep(gpio_tbl[i].gpio,
+ gpio_tbl[i].flags);
+ usleep_range(gpio_tbl[i].delay,
+ gpio_tbl[i].delay + 1000);
+ }
+ } else {
+ for (i = gpio_tbl_size - 1; i >= 0; i--) {
+ if (gpio_tbl[i].flags)
+ gpio_set_value_cansleep(gpio_tbl[i].gpio,
+ GPIOF_OUT_INIT_LOW);
+ }
+ }
+
+ return rc;
+}
+
+int msm_camera_config_single_vreg(struct device *dev,
+ struct camera_vreg_t *cam_vreg, struct regulator **reg_ptr, int config)
+{
+ int rc = 0;
+ const char *vreg_name = NULL;
+
+ if (!dev || !cam_vreg || !reg_ptr) {
+ pr_err("%s: get failed NULL parameter\n", __func__);
+ goto vreg_get_fail;
+ }
+ if (cam_vreg->type == VREG_TYPE_CUSTOM) {
+ if (cam_vreg->custom_vreg_name == NULL) {
+ pr_err("%s : can't find sub reg name",
+ __func__);
+ goto vreg_get_fail;
+ }
+ vreg_name = cam_vreg->custom_vreg_name;
+ } else {
+ if (cam_vreg->reg_name == NULL) {
+ pr_err("%s : can't find reg name", __func__);
+ goto vreg_get_fail;
+ }
+ vreg_name = cam_vreg->reg_name;
+ }
+
+ if (config) {
+ CDBG("%s enable %s\n", __func__, vreg_name);
+ *reg_ptr = regulator_get(dev, vreg_name);
+ if (IS_ERR(*reg_ptr)) {
+ pr_err("%s: %s get failed\n", __func__, vreg_name);
+ *reg_ptr = NULL;
+ goto vreg_get_fail;
+ }
+ if (regulator_count_voltages(*reg_ptr) > 0) {
+ CDBG("%s: voltage min=%d, max=%d\n",
+ __func__, cam_vreg->min_voltage,
+ cam_vreg->max_voltage);
+ rc = regulator_set_voltage(
+ *reg_ptr, cam_vreg->min_voltage,
+ cam_vreg->max_voltage);
+ if (rc < 0) {
+ pr_err("%s: %s set voltage failed\n",
+ __func__, vreg_name);
+ goto vreg_set_voltage_fail;
+ }
+ if (cam_vreg->op_mode >= 0) {
+ rc = regulator_set_load(*reg_ptr,
+ cam_vreg->op_mode);
+ if (rc < 0) {
+ pr_err(
+ "%s: %s set optimum mode failed\n",
+ __func__, vreg_name);
+ goto vreg_set_opt_mode_fail;
+ }
+ }
+ }
+ rc = regulator_enable(*reg_ptr);
+ if (rc < 0) {
+ pr_err("%s: %s regulator_enable failed\n", __func__,
+ vreg_name);
+ goto vreg_unconfig;
+ }
+ } else {
+ CDBG("%s disable %s\n", __func__, vreg_name);
+ if (*reg_ptr) {
+ CDBG("%s disable %s\n", __func__, vreg_name);
+ regulator_disable(*reg_ptr);
+ if (regulator_count_voltages(*reg_ptr) > 0) {
+ if (cam_vreg->op_mode >= 0)
+ regulator_set_load(*reg_ptr, 0);
+ regulator_set_voltage(
+ *reg_ptr, 0, cam_vreg->max_voltage);
+ }
+ regulator_put(*reg_ptr);
+ *reg_ptr = NULL;
+ } else {
+ pr_err("%s can't disable %s\n", __func__, vreg_name);
+ }
+ }
+
+ return 0;
+
+vreg_unconfig:
+ if (regulator_count_voltages(*reg_ptr) > 0)
+ regulator_set_load(*reg_ptr, 0);
+
+vreg_set_opt_mode_fail:
+ if (regulator_count_voltages(*reg_ptr) > 0)
+ regulator_set_voltage(*reg_ptr, 0,
+ cam_vreg->max_voltage);
+
+vreg_set_voltage_fail:
+ regulator_put(*reg_ptr);
+ *reg_ptr = NULL;
+
+vreg_get_fail:
+ return -EINVAL;
+}
+
+int msm_camera_request_gpio_table(struct gpio *gpio_tbl, uint8_t size,
+ int gpio_en)
+{
+ int rc = 0, i = 0, err = 0;
+
+ if (!gpio_tbl || !size) {
+ pr_err("%s:%d invalid gpio_tbl %pK / size %d\n", __func__,
+ __LINE__, gpio_tbl, size);
+ return -EINVAL;
+ }
+ for (i = 0; i < size; i++) {
+ CDBG("%s:%d i %d, gpio %d dir %ld\n", __func__, __LINE__, i,
+ gpio_tbl[i].gpio, gpio_tbl[i].flags);
+ }
+ if (gpio_en) {
+ for (i = 0; i < size; i++) {
+ err = gpio_request_one(gpio_tbl[i].gpio,
+ gpio_tbl[i].flags, gpio_tbl[i].label);
+ if (err) {
+ /*
+ * After GPIO request fails, contine to
+ * apply new gpios, outout a error message
+ * for driver bringup debug
+ */
+ pr_err("%s:%d gpio %d:%s request fails\n",
+ __func__, __LINE__,
+ gpio_tbl[i].gpio, gpio_tbl[i].label);
+ }
+ }
+ } else {
+ gpio_free_array(gpio_tbl, size);
+ }
+
+ return rc;
+}
+
+/* Get all clocks from DT */
+static int msm_camera_get_clk_info_internal(struct device *dev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr,
+ size_t *num_clk)
+{
+ int rc = 0;
+ size_t cnt, tmp;
+ uint32_t *rates, i = 0;
+ const char *clk_ctl = NULL;
+ bool clock_cntl_support = false;
+ struct device_node *of_node;
+
+ of_node = dev->of_node;
+
+ cnt = of_property_count_strings(of_node, "clock-names");
+ if (cnt <= 0) {
+ pr_err("err: No clocks found in DT=%zu\n", cnt);
+ return -EINVAL;
+ }
+
+ tmp = of_property_count_u32_elems(of_node, "qcom,clock-rates");
+ if (tmp <= 0) {
+ pr_err("err: No clk rates device tree, count=%zu", tmp);
+ return -EINVAL;
+ }
+
+ if (cnt != tmp) {
+ pr_err("err: clk name/rates mismatch, strings=%zu, rates=%zu\n",
+ cnt, tmp);
+ return -EINVAL;
+ }
+
+ if (of_property_read_bool(of_node, "qcom,clock-cntl-support")) {
+ tmp = of_property_count_strings(of_node,
+ "qcom,clock-control");
+ if (tmp <= 0) {
+ pr_err("err: control strings not found in DT count=%zu",
+ tmp);
+ return -EINVAL;
+ }
+ if (cnt != tmp) {
+ pr_err("err: controls mismatch, strings=%zu, ctl=%zu\n",
+ cnt, tmp);
+ return -EINVAL;
+ }
+ clock_cntl_support = true;
+ }
+
+ *num_clk = cnt;
+
+ *clk_info = devm_kcalloc(dev, cnt,
+ sizeof(struct msm_cam_clk_info), GFP_KERNEL);
+ if (!*clk_info)
+ return -ENOMEM;
+
+ *clk_ptr = devm_kcalloc(dev, cnt, sizeof(struct clk *),
+ GFP_KERNEL);
+ if (!*clk_ptr) {
+ rc = -ENOMEM;
+ goto free_clk_info;
+ }
+
+ rates = devm_kcalloc(dev, cnt, sizeof(long), GFP_KERNEL);
+ if (!rates) {
+ rc = -ENOMEM;
+ goto free_clk_ptr;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,clock-rates",
+ rates, cnt);
+ if (rc < 0) {
+ pr_err("err: failed reading clock rates\n");
+ rc = -EINVAL;
+ goto free_rates;
+ }
+
+ for (i = 0; i < cnt; i++) {
+ rc = of_property_read_string_index(of_node, "clock-names",
+ i, &((*clk_info)[i].clk_name));
+ if (rc < 0) {
+ pr_err("%s reading clock-name failed index %d\n",
+ __func__, i);
+ rc = -EINVAL;
+ goto free_rates;
+ }
+
+ CDBG("dbg: clk-name[%d] = %s\n", i, (*clk_info)[i].clk_name);
+ if (clock_cntl_support) {
+ rc = of_property_read_string_index(of_node,
+ "qcom,clock-control", i, &clk_ctl);
+ if (rc < 0) {
+ pr_err("%s reading clock-control failed index %d\n",
+ __func__, i);
+ rc = -EINVAL;
+ goto free_rates;
+ }
+
+ if (!strcmp(clk_ctl, "NO_SET_RATE")) {
+ (*clk_info)[i].clk_rate = NO_SET_RATE;
+ } else if (!strcmp(clk_ctl, "INIT_RATE")) {
+ (*clk_info)[i].clk_rate = INIT_RATE;
+ } else if (!strcmp(clk_ctl, "SET_RATE")) {
+ (*clk_info)[i].clk_rate = rates[i];
+ } else {
+ pr_err("%s: error: clock control has invalid value\n",
+ __func__);
+ rc = -EINVAL;
+ goto free_rates;
+ }
+ } else {
+ (*clk_info)[i].clk_rate =
+ (rates[i] == 0) ? (long)-1 : rates[i];
+ }
+
+ CDBG("dbg: clk-rate[%d] = rate: %ld\n",
+ i, (*clk_info)[i].clk_rate);
+
+ (*clk_ptr)[i] =
+ devm_clk_get(dev, (*clk_info)[i].clk_name);
+ if (IS_ERR((*clk_ptr)[i])) {
+ rc = PTR_ERR((*clk_ptr)[i]);
+ goto release_clk;
+ }
+ CDBG("clk ptr[%d] :%pK\n", i, (*clk_ptr)[i]);
+ }
+
+ devm_kfree(dev, rates);
+
+ return rc;
+
+release_clk:
+ for (--i; i >= 0; i--)
+ devm_clk_put(dev, (*clk_ptr)[i]);
+free_rates:
+ devm_kfree(dev, rates);
+free_clk_ptr:
+ devm_kfree(dev, *clk_ptr);
+free_clk_info:
+ devm_kfree(dev, *clk_info);
+ return rc;
+}
+
+/* Get all clocks from DT for I2C devices */
+int msm_camera_i2c_dev_get_clk_info(struct device *dev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr,
+ size_t *num_clk)
+{
+ int rc = 0;
+
+ if (!dev || !clk_info || !clk_ptr || !num_clk)
+ return -EINVAL;
+
+ rc = msm_camera_get_clk_info_internal(dev, clk_info, clk_ptr, num_clk);
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_i2c_dev_get_clk_info);
+
+/* Get all clocks from DT for platform devices */
+int msm_camera_get_clk_info(struct platform_device *pdev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr,
+ size_t *num_clk)
+{
+ int rc = 0;
+
+ if (!pdev || !&pdev->dev || !clk_info || !clk_ptr || !num_clk)
+ return -EINVAL;
+
+ rc = msm_camera_get_clk_info_internal(&pdev->dev,
+ clk_info, clk_ptr, num_clk);
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_get_clk_info);
+
+/* Get all clocks and multiple rates from DT */
+int msm_camera_get_clk_info_and_rates(
+ struct platform_device *pdev,
+ struct msm_cam_clk_info **pclk_info,
+ struct clk ***pclks,
+ uint32_t ***pclk_rates,
+ size_t *num_set,
+ size_t *num_clk)
+{
+ int rc = 0, tmp_var, cnt, tmp;
+ uint32_t i = 0, j = 0;
+ struct device_node *of_node;
+ uint32_t **rates;
+ struct clk **clks;
+ struct msm_cam_clk_info *clk_info;
+
+ if (!pdev || !pclk_info || !num_clk
+ || !pclk_rates || !pclks || !num_set)
+ return -EINVAL;
+
+ of_node = pdev->dev.of_node;
+
+ cnt = of_property_count_strings(of_node, "clock-names");
+ if (cnt <= 0) {
+ pr_err("err: No clocks found in DT=%d\n", cnt);
+ return -EINVAL;
+ }
+
+ tmp = of_property_count_u32_elems(of_node, "qcom,clock-rates");
+ if (tmp <= 0) {
+ pr_err("err: No clk rates device tree, count=%d\n", tmp);
+ return -EINVAL;
+ }
+
+ if ((tmp % cnt) != 0) {
+ pr_err("err: clk name/rates mismatch, strings=%d, rates=%d\n",
+ cnt, tmp);
+ return -EINVAL;
+ }
+
+ *num_clk = cnt;
+ *num_set = (tmp / cnt);
+
+ clk_info = devm_kcalloc(&pdev->dev, cnt,
+ sizeof(struct msm_cam_clk_info), GFP_KERNEL);
+ if (!clk_info)
+ return -ENOMEM;
+
+ clks = devm_kcalloc(&pdev->dev, cnt, sizeof(struct clk *),
+ GFP_KERNEL);
+ if (!clks) {
+ rc = -ENOMEM;
+ goto free_clk_info;
+ }
+
+ rates = devm_kcalloc(&pdev->dev, *num_set,
+ sizeof(uint32_t *), GFP_KERNEL);
+ if (!rates) {
+ rc = -ENOMEM;
+ goto free_clk;
+ }
+
+ for (i = 0; i < *num_set; i++) {
+ rates[i] = devm_kcalloc(&pdev->dev, *num_clk,
+ sizeof(uint32_t), GFP_KERNEL);
+ if (!rates[i]) {
+ rc = -ENOMEM;
+ for (--i; i >= 0; i--)
+ devm_kfree(&pdev->dev, rates[i]);
+ goto free_rate;
+ }
+ }
+
+ tmp_var = 0;
+ for (i = 0; i < *num_set; i++) {
+ for (j = 0; j < *num_clk; j++) {
+ rc = of_property_read_u32_index(of_node,
+ "qcom,clock-rates", tmp_var++, &rates[i][j]);
+ if (rc < 0) {
+ pr_err("err: failed reading clock rates\n");
+ rc = -EINVAL;
+ goto free_rate_array;
+ }
+ CDBG("Clock rate idx %d idx %d value %d\n",
+ i, j, rates[i][j]);
+ }
+ }
+ for (i = 0; i < *num_clk; i++) {
+ rc = of_property_read_string_index(of_node, "clock-names",
+ i, &clk_info[i].clk_name);
+ if (rc < 0) {
+ pr_err("%s reading clock-name failed index %d\n",
+ __func__, i);
+ rc = -EINVAL;
+ goto free_rate_array;
+ }
+
+ CDBG("dbg: clk-name[%d] = %s\n", i, clk_info[i].clk_name);
+
+ clks[i] =
+ devm_clk_get(&pdev->dev, clk_info[i].clk_name);
+ if (IS_ERR(clks[i])) {
+ rc = PTR_ERR(clks[i]);
+ goto release_clk;
+ }
+ CDBG("clk ptr[%d] :%pK\n", i, clks[i]);
+ }
+ *pclk_info = clk_info;
+ *pclks = clks;
+ *pclk_rates = rates;
+
+ return rc;
+
+release_clk:
+ for (--i; i >= 0; i--)
+ devm_clk_put(&pdev->dev, clks[i]);
+free_rate_array:
+ for (i = 0; i < *num_set; i++)
+ devm_kfree(&pdev->dev, rates[i]);
+free_rate:
+ devm_kfree(&pdev->dev, rates);
+free_clk:
+ devm_kfree(&pdev->dev, clks);
+free_clk_info:
+ devm_kfree(&pdev->dev, clk_info);
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_get_clk_info_and_rates);
+
+/* Enable/Disable all clocks */
+int msm_camera_clk_enable(struct device *dev,
+ struct msm_cam_clk_info *clk_info,
+ struct clk **clk_ptr, int num_clk, int enable)
+{
+ int i;
+ int rc = 0;
+ long clk_rate;
+
+ if (enable) {
+ for (i = 0; i < num_clk; i++) {
+ pr_err("enable %s\n", clk_info[i].clk_name);
+ if (clk_info[i].clk_rate > 0) {
+ clk_rate = clk_round_rate(clk_ptr[i],
+ clk_info[i].clk_rate);
+ if (clk_rate < 0) {
+ pr_err("%s round failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_set_err;
+ }
+ rc = clk_set_rate(clk_ptr[i],
+ clk_rate);
+ if (rc < 0) {
+ pr_err("%s set failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_set_err;
+ }
+
+ } else if (clk_info[i].clk_rate == INIT_RATE) {
+ clk_rate = clk_get_rate(clk_ptr[i]);
+ if (clk_rate == 0) {
+ clk_rate =
+ clk_round_rate(clk_ptr[i], 0);
+ if (clk_rate < 0) {
+ pr_err("%s round rate failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_set_err;
+ }
+ rc = clk_set_rate(clk_ptr[i],
+ clk_rate);
+ if (rc < 0) {
+ pr_err("%s set rate failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_set_err;
+ }
+ }
+ }
+ rc = clk_prepare_enable(clk_ptr[i]);
+ if (rc < 0) {
+ pr_err("%s enable failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_enable_err;
+ }
+ if (clk_info[i].delay > 20) {
+ msleep(clk_info[i].delay);
+ } else if (clk_info[i].delay) {
+ usleep_range(clk_info[i].delay * 1000,
+ (clk_info[i].delay * 1000) + 1000);
+ }
+ }
+ } else {
+ for (i = num_clk - 1; i >= 0; i--) {
+ if (clk_ptr[i] != NULL) {
+ pr_err("%s disable %s\n", __func__,
+ clk_info[i].clk_name);
+ clk_disable_unprepare(clk_ptr[i]);
+ }
+ }
+ }
+ return rc;
+
+cam_clk_enable_err:
+cam_clk_set_err:
+ for (i--; i >= 0; i--) {
+ if (clk_ptr[i] != NULL)
+ clk_disable_unprepare(clk_ptr[i]);
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_clk_enable);
+
+/* Set rate on a specific clock */
+long msm_camera_clk_set_rate(struct device *dev,
+ struct clk *clk,
+ long clk_rate)
+{
+ int rc = 0;
+ long rate = 0;
+
+ if (!dev || !clk || (clk_rate < 0))
+ return -EINVAL;
+
+ CDBG("clk : %pK, enable : %ld\n", clk, clk_rate);
+
+ if (clk_rate > 0) {
+ rate = clk_round_rate(clk, clk_rate);
+ if (rate < 0) {
+ pr_err("round rate failed\n");
+ return -EINVAL;
+ }
+
+ rc = clk_set_rate(clk, rate);
+ if (rc < 0) {
+ pr_err("set rate failed\n");
+ return -EINVAL;
+ }
+ }
+
+ return rate;
+}
+EXPORT_SYMBOL(msm_camera_clk_set_rate);
+
+/* release memory allocated for clocks */
+static int msm_camera_put_clk_info_internal(struct device *dev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr, int cnt)
+{
+ int i;
+
+ for (i = cnt - 1; i >= 0; i--) {
+ if (clk_ptr[i] != NULL)
+ devm_clk_put(dev, (*clk_ptr)[i]);
+
+ CDBG("clk ptr[%d] :%pK\n", i, (*clk_ptr)[i]);
+ }
+ devm_kfree(dev, *clk_info);
+ devm_kfree(dev, *clk_ptr);
+ *clk_info = NULL;
+ *clk_ptr = NULL;
+ return 0;
+}
+
+/* release memory allocated for clocks for i2c devices */
+int msm_camera_i2c_dev_put_clk_info(struct device *dev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr, int cnt)
+{
+ int rc = 0;
+
+ if (!dev || !clk_info || !clk_ptr)
+ return -EINVAL;
+
+ rc = msm_camera_put_clk_info_internal(dev, clk_info, clk_ptr, cnt);
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_i2c_dev_put_clk_info);
+
+/* release memory allocated for clocks for platform devices */
+int msm_camera_put_clk_info(struct platform_device *pdev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr, int cnt)
+{
+ int rc = 0;
+
+ if (!pdev || !&pdev->dev || !clk_info || !clk_ptr)
+ return -EINVAL;
+
+ rc = msm_camera_put_clk_info_internal(&pdev->dev,
+ clk_info, clk_ptr, cnt);
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_put_clk_info);
+
+int msm_camera_put_clk_info_and_rates(struct platform_device *pdev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr, uint32_t ***clk_rates,
+ size_t set, size_t cnt)
+{
+ int i;
+
+ for (i = set - 1; i >= 0; i--)
+ devm_kfree(&pdev->dev, (*clk_rates)[i]);
+
+ devm_kfree(&pdev->dev, *clk_rates);
+ for (i = cnt - 1; i >= 0; i--) {
+ if (clk_ptr[i] != NULL)
+ devm_clk_put(&pdev->dev, (*clk_ptr)[i]);
+ CDBG("clk ptr[%d] :%pK\n", i, (*clk_ptr)[i]);
+ }
+ devm_kfree(&pdev->dev, *clk_info);
+ devm_kfree(&pdev->dev, *clk_ptr);
+ *clk_info = NULL;
+ *clk_ptr = NULL;
+ *clk_rates = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_camera_put_clk_info_and_rates);
+
+/* Get regulators from DT */
+int msm_camera_get_regulator_info(struct platform_device *pdev,
+ struct msm_cam_regulator **vdd_info,
+ int *num_reg)
+{
+ uint32_t cnt;
+ int i, rc;
+ struct device_node *of_node;
+ char prop_name[32];
+ struct msm_cam_regulator *tmp_reg;
+
+ if (!pdev || !vdd_info || !num_reg)
+ return -EINVAL;
+
+ of_node = pdev->dev.of_node;
+
+ if (!of_get_property(of_node, "qcom,vdd-names", NULL)) {
+ pr_err("err: Regulators property not found\n");
+ return -EINVAL;
+ }
+
+ cnt = of_property_count_strings(of_node, "qcom,vdd-names");
+ if (cnt <= 0) {
+ pr_err("err: no regulators found in device tree, count=%d",
+ cnt);
+ return -EINVAL;
+ }
+
+ tmp_reg = devm_kcalloc(&pdev->dev, cnt,
+ sizeof(struct msm_cam_regulator), GFP_KERNEL);
+ if (!tmp_reg)
+ return -ENOMEM;
+
+ for (i = 0; i < cnt; i++) {
+ rc = of_property_read_string_index(of_node,
+ "qcom,vdd-names", i, &tmp_reg[i].name);
+ if (rc < 0) {
+ pr_err("Fail to fetch regulators: %d\n", i);
+ rc = -EINVAL;
+ goto err1;
+ }
+
+ CDBG("regulator-names[%d] = %s\n", i, tmp_reg[i].name);
+
+ snprintf(prop_name, 32, "%s-supply", tmp_reg[i].name);
+
+ if (of_get_property(of_node, prop_name, NULL)) {
+ tmp_reg[i].vdd =
+ devm_regulator_get(&pdev->dev, tmp_reg[i].name);
+ if (IS_ERR(tmp_reg[i].vdd)) {
+ rc = -EINVAL;
+ pr_err("Fail to get regulator :%d\n", i);
+ goto err1;
+ }
+ } else {
+ pr_err("Regulator phandle not found :%s\n",
+ tmp_reg[i].name);
+ rc = -EINVAL;
+ goto err1;
+ }
+ CDBG("vdd ptr[%d] :%pK\n", i, tmp_reg[i].vdd);
+ }
+
+ *num_reg = cnt;
+ *vdd_info = tmp_reg;
+
+ return 0;
+
+err1:
+ for (--i; i >= 0; i--)
+ devm_regulator_put(tmp_reg[i].vdd);
+ devm_kfree(&pdev->dev, tmp_reg);
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_get_regulator_info);
+
+
+/* Enable/Disable regulators */
+int msm_camera_regulator_enable(struct msm_cam_regulator *vdd_info,
+ int cnt, int enable)
+{
+ int i;
+ int rc;
+ struct msm_cam_regulator *tmp = vdd_info;
+
+ if (!tmp) {
+ pr_err("Invalid params");
+ return -EINVAL;
+ }
+ CDBG("cnt : %d\n", cnt);
+
+ for (i = 0; i < cnt; i++) {
+ if (tmp && !IS_ERR_OR_NULL(tmp->vdd)) {
+ CDBG("name : %s, enable : %d\n", tmp->name, enable);
+ if (enable) {
+ rc = regulator_enable(tmp->vdd);
+ if (rc < 0) {
+ pr_err("regulator enable failed %d\n",
+ i);
+ goto disable_reg;
+ }
+ } else {
+ rc = regulator_disable(tmp->vdd);
+ if (rc < 0)
+ pr_err("regulator disable failed %d\n",
+ i);
+ }
+ }
+ tmp++;
+ }
+
+ return 0;
+disable_reg:
+ for (--i; i > 0; i--) {
+ --tmp;
+ if (!IS_ERR_OR_NULL(tmp->vdd))
+ regulator_disable(tmp->vdd);
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_regulator_enable);
+
+/* Put regulators regulators */
+void msm_camera_put_regulators(struct platform_device *pdev,
+ struct msm_cam_regulator **vdd_info, int cnt)
+{
+ int i;
+
+ if (!vdd_info || !*vdd_info) {
+ pr_err("Invalid params\n");
+ return;
+ }
+
+ for (i = cnt - 1; i >= 0; i--) {
+ if (vdd_info[i] && !IS_ERR_OR_NULL(vdd_info[i]->vdd))
+ devm_regulator_put(vdd_info[i]->vdd);
+ CDBG("vdd ptr[%d] :%pK\n", i, vdd_info[i]->vdd);
+ }
+
+ devm_kfree(&pdev->dev, *vdd_info);
+ *vdd_info = NULL;
+}
+EXPORT_SYMBOL(msm_camera_put_regulators);
+
+struct resource *msm_camera_get_irq(struct platform_device *pdev,
+ char *irq_name)
+{
+ if (!pdev || !irq_name) {
+ pr_err("Invalid params\n");
+ return NULL;
+ }
+
+ CDBG("Get irq for %s\n", irq_name);
+ return platform_get_resource_byname(pdev, IORESOURCE_IRQ, irq_name);
+}
+EXPORT_SYMBOL(msm_camera_get_irq);
+
+int msm_camera_register_irq(struct platform_device *pdev,
+ struct resource *irq, irq_handler_t handler,
+ unsigned long irqflags, char *irq_name, void *dev_id)
+{
+ int rc = 0;
+
+ if (!pdev || !irq || !handler || !irq_name || !dev_id) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ rc = devm_request_irq(&pdev->dev, irq->start, handler,
+ irqflags, irq_name, dev_id);
+ if (rc < 0) {
+ pr_err("irq request fail\n");
+ rc = -EINVAL;
+ }
+
+ CDBG("Registered irq for %s[resource - %pK]\n", irq_name, irq);
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_register_irq);
+
+int msm_camera_register_threaded_irq(struct platform_device *pdev,
+ struct resource *irq, irq_handler_t handler_fn,
+ irq_handler_t thread_fn, unsigned long irqflags,
+ const char *irq_name, void *dev_id)
+{
+ int rc = 0;
+
+ if (!pdev || !irq || !irq_name || !dev_id) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ rc = devm_request_threaded_irq(&pdev->dev, irq->start, handler_fn,
+ thread_fn, irqflags, irq_name, dev_id);
+ if (rc < 0) {
+ pr_err("irq request fail\n");
+ rc = -EINVAL;
+ }
+
+ CDBG("Registered irq for %s[resource - %pK]\n", irq_name, irq);
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_register_threaded_irq);
+
+int msm_camera_enable_irq(struct resource *irq, int enable)
+{
+ if (!irq) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ CDBG("irq Enable %d\n", enable);
+ if (enable)
+ enable_irq(irq->start);
+ else
+ disable_irq(irq->start);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_camera_enable_irq);
+
+int msm_camera_unregister_irq(struct platform_device *pdev,
+ struct resource *irq, void *dev_id)
+{
+
+ if (!pdev || !irq || !dev_id) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ CDBG("Un Registering irq for [resource - %pK]\n", irq);
+ devm_free_irq(&pdev->dev, irq->start, dev_id);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_camera_unregister_irq);
+
+void __iomem *msm_camera_get_reg_base(struct platform_device *pdev,
+ char *device_name, int reserve_mem)
+{
+ struct resource *mem;
+ void *base;
+
+ if (!pdev || !device_name) {
+ pr_err("Invalid params\n");
+ return NULL;
+ }
+
+ CDBG("device name :%s\n", device_name);
+ mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, device_name);
+ if (!mem) {
+ pr_err("err: mem resource %s not found\n", device_name);
+ return NULL;
+ }
+
+ if (reserve_mem) {
+ CDBG("device:%pK, mem : %pK, size : %d\n",
+ &pdev->dev, mem, (int)resource_size(mem));
+ if (!devm_request_mem_region(&pdev->dev, mem->start,
+ resource_size(mem),
+ device_name)) {
+ pr_err("err: no valid mem region for device:%s\n",
+ device_name);
+ return NULL;
+ }
+ }
+
+ base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+ if (!base) {
+ devm_release_mem_region(&pdev->dev, mem->start,
+ resource_size(mem));
+ pr_err("err: ioremap failed: %s\n", device_name);
+ return NULL;
+ }
+
+ CDBG("base : %pK\n", base);
+ return base;
+}
+EXPORT_SYMBOL(msm_camera_get_reg_base);
+
+uint32_t msm_camera_get_res_size(struct platform_device *pdev,
+ char *device_name)
+{
+ struct resource *mem;
+
+ if (!pdev || !device_name) {
+ pr_err("Invalid params\n");
+ return 0;
+ }
+
+ CDBG("device name :%s\n", device_name);
+ mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, device_name);
+ if (!mem) {
+ pr_err("err: mem resource %s not found\n", device_name);
+ return 0;
+ }
+ return resource_size(mem);
+}
+EXPORT_SYMBOL(msm_camera_get_res_size);
+
+
+int msm_camera_put_reg_base(struct platform_device *pdev,
+ void __iomem *base, char *device_name, int reserve_mem)
+{
+ struct resource *mem;
+
+ if (!pdev || !base || !device_name) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ CDBG("device name :%s\n", device_name);
+ mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, device_name);
+ if (!mem) {
+ pr_err("err: mem resource %s not found\n", device_name);
+ return -EINVAL;
+ }
+ CDBG("mem : %pK, size : %d\n", mem, (int)resource_size(mem));
+
+ devm_iounmap(&pdev->dev, base);
+ if (reserve_mem)
+ devm_release_mem_region(&pdev->dev,
+ mem->start, resource_size(mem));
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_camera_put_reg_base);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_soc_api.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_soc_api.h
new file mode 100644
index 0000000..c316090
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_soc_api.h
@@ -0,0 +1,473 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_SENSOR_SOC_API_H_
+#define _CAM_SENSOR_SOC_API_H_
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/spinlock_types.h>
+#include <linux/mutex.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include "cam_sensor_cmn_header.h"
+
+struct msm_cam_regulator {
+ const char *name;
+ struct regulator *vdd;
+};
+
+struct msm_gpio_set_tbl {
+ unsigned int gpio;
+ unsigned long flags;
+ uint32_t delay;
+};
+
+/**
+ * @brief : Gets clock information from dtsi
+ *
+ * This function extracts the clocks information for a specific
+ * platform device
+ *
+ * @param pdev : Platform device to get clocks information
+ * @param clk_info : Pointer to populate clock information array
+ * @param clk_ptr : Pointer to populate clock resource pointers
+ * @param num_clk: Pointer to populate the number of clocks
+ * extracted from dtsi
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int msm_camera_get_clk_info(struct platform_device *pdev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr,
+ size_t *num_clk);
+
+/**
+ * @brief : Gets clock information from dtsi
+ *
+ * This function extracts the clocks information for a specific
+ * i2c device
+ *
+ * @param dev : i2c device to get clocks information
+ * @param clk_info : Pointer to populate clock information array
+ * @param clk_ptr : Pointer to populate clock resource pointers
+ * @param num_clk: Pointer to populate the number of clocks
+ * extracted from dtsi
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int msm_camera_i2c_dev_get_clk_info(struct device *dev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr,
+ size_t *num_clk);
+
+/**
+ * @brief : Gets clock information and rates from dtsi
+ *
+ * This function extracts the clocks information for a specific
+ * platform device
+ *
+ * @param pdev : Platform device to get clocks information
+ * @param clk_info : Pointer to populate clock information array
+ * @param clk_ptr : Pointer to populate clock resource pointers
+ * @param clk_rates : Pointer to populate clock rates
+ * @param num_set: Pointer to populate the number of sets of rates
+ * @param num_clk: Pointer to populate the number of clocks
+ * extracted from dtsi
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int msm_camera_get_clk_info_and_rates(
+ struct platform_device *pdev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr,
+ uint32_t ***clk_rates,
+ size_t *num_set,
+ size_t *num_clk);
+
+/**
+ * @brief : Puts clock information
+ *
+ * This function releases the memory allocated for the clocks
+ *
+ * @param pdev : Pointer to platform device
+ * @param clk_info : Pointer to release the allocated memory
+ * @param clk_ptr : Pointer to release the clock resources
+ * @param cnt : Number of clk resources
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int msm_camera_put_clk_info(struct platform_device *pdev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr, int cnt);
+
+/**
+ * @brief : Puts clock information
+ *
+ * This function releases the memory allocated for the clocks
+ *
+ * @param dev : Pointer to i2c device
+ * @param clk_info : Pointer to release the allocated memory
+ * @param clk_ptr : Pointer to release the clock resources
+ * @param cnt : Number of clk resources
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int msm_camera_i2c_dev_put_clk_info(struct device *dev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr, int cnt);
+
+/**
+ * @brief : Puts clock information
+ *
+ * This function releases the memory allocated for the clocks
+ *
+ * @param pdev : Pointer to platform device
+ * @param clk_info : Pointer to release the allocated memory
+ * @param clk_ptr : Pointer to release the clock resources
+ * @param clk_ptr : Pointer to release the clock rates
+ * @param set : Number of sets of clock rates
+ * @param cnt : Number of clk resources
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int msm_camera_put_clk_info_and_rates(struct platform_device *pdev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr, uint32_t ***clk_rates,
+ size_t set, size_t cnt);
+/**
+ * @brief : Enable clocks
+ *
+ * This function enables the clocks for a specified device
+ *
+ * @param dev : Device to get clocks information
+ * @param clk_info : Pointer to populate clock information
+ * @param clk_ptr : Pointer to populate clock information
+ * @param num_clk: Pointer to populate the number of clocks
+ * extracted from dtsi
+ * @param enable : Flag to specify enable/disable
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int msm_camera_clk_enable(struct device *dev,
+ struct msm_cam_clk_info *clk_info,
+ struct clk **clk_ptr,
+ int num_clk,
+ int enable);
+/**
+ * @brief : Set clock rate
+ *
+ * This function sets the rate for a specified clock and
+ * returns the rounded value
+ *
+ * @param dev : Device to get clocks information
+ * @param clk : Pointer to clock to set rate
+ * @param clk_rate : Rate to be set
+ *
+ * @return Status of operation. Negative in case of error. clk rate otherwise.
+ */
+
+long msm_camera_clk_set_rate(struct device *dev,
+ struct clk *clk,
+ long clk_rate);
+/**
+ * @brief : Gets regulator info
+ *
+ * This function extracts the regulator information for a specific
+ * platform device
+ *
+ * @param pdev : platform device to get regulator information
+ * @param vdd_info: Pointer to populate the regulator names
+ * @param num_reg: Pointer to populate the number of regulators
+ * extracted from dtsi
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int msm_camera_get_regulator_info(struct platform_device *pdev,
+ struct msm_cam_regulator **vdd_info, int *num_reg);
+/**
+ * @brief : Enable/Disable the regultors
+ *
+ * This function enables/disables the regulators for a specific
+ * platform device
+ *
+ * @param vdd_info: Pointer to list of regulators
+ * @param cnt: Number of regulators to enable/disable
+ * @param enable: Flags specifies either enable/disable
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int msm_camera_regulator_enable(struct msm_cam_regulator *vdd_info,
+ int cnt, int enable);
+
+/**
+ * @brief : Release the regulators
+ *
+ * This function releases the regulator resources.
+ *
+ * @param pdev: Pointer to platform device
+ * @param vdd_info: Pointer to list of regulators
+ * @param cnt: Number of regulators to release
+ */
+
+void msm_camera_put_regulators(struct platform_device *pdev,
+ struct msm_cam_regulator **vdd_info, int cnt);
+/**
+ * @brief : Get the IRQ resource
+ *
+ * This function gets the irq resource from dtsi for a specific
+ * platform device
+ *
+ * @param pdev : Platform device to get IRQ
+ * @param irq_name: Name of the IRQ resource to get from DTSI
+ *
+ * @return Pointer to resource if success else null
+ */
+
+struct resource *msm_camera_get_irq(struct platform_device *pdev,
+ char *irq_name);
+/**
+ * @brief : Register the IRQ
+ *
+ * This function registers the irq resource for specified hardware
+ *
+ * @param pdev : Platform device to register IRQ resource
+ * @param irq : IRQ resource
+ * @param handler : IRQ handler
+ * @param irqflags : IRQ flags
+ * @param irq_name: Name of the IRQ
+ * @param dev : Token of the device
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int msm_camera_register_irq(struct platform_device *pdev,
+ struct resource *irq,
+ irq_handler_t handler,
+ unsigned long irqflags,
+ char *irq_name,
+ void *dev);
+
+/**
+ * @brief : Register the threaded IRQ
+ *
+ * This function registers the irq resource for specified hardware
+ *
+ * @param pdev : Platform device to register IRQ resource
+ * @param irq : IRQ resource
+ * @param handler_fn : IRQ handler function
+ * @param thread_fn : thread handler function
+ * @param irqflags : IRQ flags
+ * @param irq_name: Name of the IRQ
+ * @param dev : Token of the device
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int msm_camera_register_threaded_irq(struct platform_device *pdev,
+ struct resource *irq,
+ irq_handler_t handler_fn,
+ irq_handler_t thread_fn,
+ unsigned long irqflags,
+ const char *irq_name,
+ void *dev);
+
+/**
+ * @brief : Enable/Disable the IRQ
+ *
+ * This function enables or disables a specific IRQ
+ *
+ * @param irq : IRQ resource
+ * @param flag : flag to enable/disable
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int msm_camera_enable_irq(struct resource *irq, int flag);
+
+/**
+ * @brief : UnRegister the IRQ
+ *
+ * This function Unregisters/Frees the irq resource
+ *
+ * @param pdev : Pointer to platform device
+ * @param irq : IRQ resource
+ * @param dev : Token of the device
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int msm_camera_unregister_irq(struct platform_device *pdev,
+ struct resource *irq, void *dev_id);
+
+/**
+ * @brief : Gets device register base
+ *
+ * This function extracts the device's register base from the dtsi
+ * for the specified platform device
+ *
+ * @param pdev : Platform device to get regulator infor
+ * @param device_name : Name of the device to fetch the register base
+ * @param reserve_mem : Flag to decide whether to reserve memory
+ * region or not.
+ *
+ * @return Pointer to resource if success else null
+ */
+
+void __iomem *msm_camera_get_reg_base(struct platform_device *pdev,
+ char *device_name, int reserve_mem);
+
+/**
+ * @brief : Puts device register base
+ *
+ * This function releases the memory region for the specified
+ * resource
+ *
+ * @param pdev : Pointer to platform device
+ * @param base : Pointer to base to unmap
+ * @param device_name : Device name
+ * @param reserve_mem : Flag to decide whether to release memory
+ * region or not.
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int msm_camera_put_reg_base(struct platform_device *pdev, void __iomem *base,
+ char *device_name, int reserve_mem);
+
+/**
+ * @brief : Gets resource size
+ *
+ * This function returns the size of the resource for the
+ * specified platform device
+ *
+ * @param pdev : Platform device to get regulator infor
+ * @param device_name : Name of the device to fetch the register base
+ *
+ * @return size of the resource
+ */
+
+uint32_t msm_camera_get_res_size(struct platform_device *pdev,
+ char *device_name);
+
+/**
+ * @brief : Selects clock source
+ *
+ *
+ * @param dev : Token of the device
+ * @param clk_info : Clock Info structure
+ * @param clk_src_info : Clock Info structure
+ * @param num_clk : Number of clocks
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int msm_cam_clk_sel_src(struct device *dev, struct msm_cam_clk_info *clk_info,
+ struct msm_cam_clk_info *clk_src_info, int num_clk);
+
+/**
+ * @brief : Enables the clock
+ *
+ *
+ * @param dev : Token of the device
+ * @param clk_info : Clock Info structure
+ * @param clk_tr : Pointer to lock strucure
+ * @param num_clk : Number of clocks
+ * @param enable : Enable/disable the clock
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int msm_cam_clk_enable(struct device *dev, struct msm_cam_clk_info *clk_info,
+ struct clk **clk_ptr, int num_clk, int enable);
+
+/**
+ * @brief : Configures voltage regulator
+ *
+ *
+ * @param dev : Token of the device
+ * @param cam_vreg : Regulator dt structure
+ * @param num_vreg : Number of regulators
+ * @param vreg_seq : Regulator sequence type
+ * @param num_clk : Number of clocks
+ * @param reg_ptr : Regulator pointer
+ * @param config : Enable/disable configuring the regulator
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int msm_camera_config_vreg(struct device *dev, struct camera_vreg_t *cam_vreg,
+ int num_vreg, enum msm_camera_vreg_name_t *vreg_seq,
+ int num_vreg_seq, struct regulator **reg_ptr, int config);
+
+/**
+ * @brief : Enables voltage regulator
+ *
+ *
+ * @param dev : Token of the device
+ * @param cam_vreg : Regulator dt structure
+ * @param num_vreg : Number of regulators
+ * @param vreg_seq : Regulator sequence type
+ * @param num_clk : Number of clocks
+ * @param reg_ptr : Regulator pointer
+ * @param config : Enable/disable configuring the regulator
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int msm_camera_enable_vreg(struct device *dev, struct camera_vreg_t *cam_vreg,
+ int num_vreg, enum msm_camera_vreg_name_t *vreg_seq,
+ int num_vreg_seq, struct regulator **reg_ptr, int enable);
+
+/**
+ * @brief : Sets table of GPIOs
+ *
+ * @param gpio_tbl : GPIO table parsed from dt
+ * @param gpio_tbl_size : Size of GPIO table
+ * @param gpio_en : Enable/disable the GPIO
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int msm_camera_set_gpio_table(struct msm_gpio_set_tbl *gpio_tbl,
+ uint8_t gpio_tbl_size, int gpio_en);
+
+/**
+ * @brief : Configures single voltage regulator
+ *
+ *
+ * @param dev : Token of the device
+ * @param cam_vreg : Regulator dt structure
+ * @param num_vreg : Number of regulators
+ * @param reg_ptr : Regulator pointer
+ * @param config : Enable/disable configuring the regulator
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int msm_camera_config_single_vreg(struct device *dev,
+ struct camera_vreg_t *cam_vreg, struct regulator **reg_ptr, int config);
+
+/**
+ * @brief : Request table of gpios
+ *
+ *
+ * @param gpio_tbl : Table of GPIOs
+ * @param size : Size of table
+ * @param gpio_en : Enable/disable the gpio
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int msm_camera_request_gpio_table(struct gpio *gpio_tbl, uint8_t size,
+ int gpio_en);
+
+#endif /* _CAM_SENSOR_SOC_API_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c
new file mode 100644
index 0000000..44294e8
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c
@@ -0,0 +1,1399 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include "cam_sensor_util.h"
+#include "cam_sensor_soc_api.h"
+
+#define CAM_SENSOR_PINCTRL_STATE_SLEEP "cam_suspend"
+#define CAM_SENSOR_PINCTRL_STATE_DEFAULT "cam_default"
+
+#define VALIDATE_VOLTAGE(min, max, config_val) ((config_val) && \
+ (config_val >= min) && (config_val <= max))
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+static struct i2c_settings_list*
+ cam_sensor_get_i2c_ptr(struct i2c_settings_array *i2c_reg_settings,
+ uint32_t size)
+{
+ struct i2c_settings_list *tmp;
+
+ tmp = (struct i2c_settings_list *)
+ kzalloc(sizeof(struct i2c_settings_list), GFP_KERNEL);
+
+ if (tmp != NULL)
+ list_add_tail(&(tmp->list),
+ &(i2c_reg_settings->list_head));
+ else
+ return NULL;
+
+ tmp->i2c_settings.reg_setting = (struct cam_sensor_i2c_reg_array *)
+ kzalloc(sizeof(struct cam_sensor_i2c_reg_array) *
+ size, GFP_KERNEL);
+ if (tmp->i2c_settings.reg_setting == NULL) {
+ list_del(&(tmp->list));
+ kfree(tmp);
+ return NULL;
+ }
+ tmp->i2c_settings.size = size;
+
+ return tmp;
+}
+
+int32_t delete_request(struct i2c_settings_array *i2c_array)
+{
+ struct i2c_settings_list *i2c_list = NULL, *i2c_next = NULL;
+ int32_t rc = 0;
+
+ if (i2c_array == NULL) {
+ pr_err("%s:%d ::FATAL:: Invalid argument\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ list_for_each_entry_safe(i2c_list, i2c_next,
+ &(i2c_array->list_head), list) {
+ kfree(i2c_list->i2c_settings.reg_setting);
+ list_del(&(i2c_list->list));
+ kfree(i2c_list);
+ }
+ INIT_LIST_HEAD(&(i2c_array->list_head));
+ i2c_array->is_settings_valid = 0;
+
+ return rc;
+}
+
+int32_t cam_sensor_handle_delay(
+ uint32_t **cmd_buf,
+ uint16_t generic_op_code,
+ struct i2c_settings_array *i2c_reg_settings,
+ uint32_t offset, uint32_t *byte_cnt,
+ struct list_head *list_ptr)
+{
+ int32_t rc = 0;
+ struct cam_cmd_unconditional_wait *cmd_uncond_wait =
+ (struct cam_cmd_unconditional_wait *) *cmd_buf;
+ struct i2c_settings_list *i2c_list = NULL;
+
+ if (i2c_list == NULL) {
+ pr_err("%s:%d Invalid list ptr\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if (offset > 0) {
+ i2c_list =
+ list_entry(list_ptr, struct i2c_settings_list, list);
+ if (generic_op_code ==
+ CAMERA_SENSOR_WAIT_OP_HW_UCND)
+ i2c_list->i2c_settings.
+ reg_setting[offset - 1].delay =
+ cmd_uncond_wait->delay;
+ else
+ i2c_list->i2c_settings.delay =
+ cmd_uncond_wait->delay;
+ (*cmd_buf) +=
+ sizeof(
+ struct cam_cmd_unconditional_wait) / sizeof(uint32_t);
+ (*byte_cnt) +=
+ sizeof(
+ struct cam_cmd_unconditional_wait);
+ } else {
+ pr_err("%s: %d Error: Delay Rxed Before any buffer: %d\n",
+ __func__, __LINE__, offset);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+int32_t cam_sensor_handle_poll(
+ uint32_t **cmd_buf,
+ struct i2c_settings_array *i2c_reg_settings,
+ uint32_t *byte_cnt, int32_t *offset,
+ struct list_head **list_ptr)
+{
+ struct i2c_settings_list *i2c_list;
+ int32_t rc = 0;
+ struct cam_cmd_conditional_wait *cond_wait
+ = (struct cam_cmd_conditional_wait *) *cmd_buf;
+
+ i2c_list =
+ cam_sensor_get_i2c_ptr(i2c_reg_settings, 1);
+ if (!i2c_list || !i2c_list->i2c_settings.reg_setting) {
+ pr_err("%s: %d Failed in allocating mem for list\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ i2c_list->op_code = CAM_SENSOR_I2C_POLL;
+ i2c_list->i2c_settings.data_type =
+ cond_wait->data_type;
+ i2c_list->i2c_settings.addr_type =
+ cond_wait->addr_type;
+ i2c_list->i2c_settings.reg_setting->reg_addr =
+ cond_wait->reg_addr;
+ i2c_list->i2c_settings.reg_setting->reg_data =
+ cond_wait->reg_data;
+ i2c_list->i2c_settings.reg_setting->delay =
+ cond_wait->timeout;
+
+ (*cmd_buf) += sizeof(struct cam_cmd_conditional_wait) /
+ sizeof(uint32_t);
+ (*byte_cnt) += sizeof(struct cam_cmd_conditional_wait);
+
+ (*offset) += 1;
+ *list_ptr = &(i2c_list->list);
+
+ return rc;
+}
+
+int32_t cam_sensor_handle_random_write(
+ struct cam_cmd_i2c_random_wr *cam_cmd_i2c_random_wr,
+ struct i2c_settings_array *i2c_reg_settings,
+ uint16_t *cmd_length_in_bytes, int32_t *offset,
+ struct list_head **list)
+{
+ struct i2c_settings_list *i2c_list;
+ int32_t rc = 0, cnt;
+
+ i2c_list = cam_sensor_get_i2c_ptr(i2c_reg_settings,
+ cam_cmd_i2c_random_wr->header.count);
+ if (i2c_list == NULL ||
+ i2c_list->i2c_settings.reg_setting == NULL) {
+ pr_err("%s: %d Failed in allocating i2c_list\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ *cmd_length_in_bytes = (sizeof(struct i2c_rdwr_header) +
+ sizeof(struct i2c_random_wr_payload) *
+ (cam_cmd_i2c_random_wr->header.count));
+ i2c_list->op_code = CAM_SENSOR_I2C_WRITE_RANDOM;
+ i2c_list->i2c_settings.addr_type =
+ cam_cmd_i2c_random_wr->header.addr_type;
+ i2c_list->i2c_settings.data_type =
+ cam_cmd_i2c_random_wr->header.data_type;
+
+ for (cnt = 0; cnt < (cam_cmd_i2c_random_wr->header.count);
+ cnt++) {
+ i2c_list->i2c_settings.reg_setting[cnt].reg_addr =
+ cam_cmd_i2c_random_wr->
+ random_wr_payload[cnt].reg_addr;
+ i2c_list->i2c_settings.
+ reg_setting[cnt].reg_data =
+ cam_cmd_i2c_random_wr->
+ random_wr_payload[cnt].reg_data;
+ i2c_list->i2c_settings.
+ reg_setting[cnt].data_mask = 0;
+ }
+ (*offset) += cnt;
+ *list = &(i2c_list->list);
+
+ return rc;
+}
+
+/**
+ * Name : cam_sensor_i2c_pkt_parser
+ * Description : Parse CSL CCI packet and apply register settings
+ * Parameters : s_ctrl input/output sub_device
+ * arg input cam_control
+ * Description :
+ * Handle multiple I2C RD/WR and WAIT cmd formats in one command
+ * buffer, for example, a command buffer of m x RND_WR + 1 x HW_
+ * WAIT + n x RND_WR with num_cmd_buf = 1. Do not exepect RD/WR
+ * with different cmd_type and op_code in one command buffer.
+ */
+int cam_sensor_i2c_pkt_parser(struct i2c_settings_array *i2c_reg_settings,
+ struct cam_cmd_buf_desc *cmd_desc, int32_t num_cmd_buffers)
+{
+ int16_t rc = 0, i = 0;
+ size_t len_of_buff = 0;
+ uint64_t generic_ptr;
+
+ for (i = 0; i < num_cmd_buffers; i++) {
+ uint32_t *cmd_buf = NULL;
+ struct common_header *cmm_hdr;
+ uint16_t generic_op_code;
+ uint32_t byte_cnt = 0;
+ uint32_t j = 0;
+ struct list_head *list = NULL;
+
+ /*
+ * It is not expected the same settings to
+ * be spread across multiple cmd buffers
+ */
+
+ CDBG("%s:%d Total cmd Buf in Bytes: %d\n", __func__,
+ __LINE__, cmd_desc[i].length);
+
+ if (!cmd_desc[i].length)
+ continue;
+
+ rc = cam_mem_get_cpu_buf(cmd_desc[i].mem_handle,
+ (uint64_t *)&generic_ptr, &len_of_buff);
+ cmd_buf = (uint32_t *)generic_ptr;
+ if (rc < 0) {
+ pr_err("%s:%d Failed in getting cmd hdl: %d Err: %d Buffer Len: %ld\n",
+ __func__, __LINE__,
+ cmd_desc[i].mem_handle, rc,
+ len_of_buff);
+ return rc;
+ }
+ cmd_buf += cmd_desc[i].offset / sizeof(uint32_t);
+
+ while (byte_cnt < cmd_desc[i].length) {
+ cmm_hdr = (struct common_header *)cmd_buf;
+ generic_op_code = cmm_hdr->third_byte;
+ switch (cmm_hdr->cmd_type) {
+ case CAMERA_SENSOR_CMD_TYPE_I2C_RNDM_WR: {
+ uint16_t cmd_length_in_bytes = 0;
+ struct cam_cmd_i2c_random_wr
+ *cam_cmd_i2c_random_wr =
+ (struct cam_cmd_i2c_random_wr *)cmd_buf;
+
+ rc = cam_sensor_handle_random_write(
+ cam_cmd_i2c_random_wr,
+ i2c_reg_settings,
+ &cmd_length_in_bytes, &j, &list);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: Failed in random read %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ cmd_buf += cmd_length_in_bytes /
+ sizeof(uint32_t);
+ byte_cnt += cmd_length_in_bytes;
+ break;
+ }
+ case CAMERA_SENSOR_CMD_TYPE_WAIT: {
+ if (generic_op_code ==
+ CAMERA_SENSOR_WAIT_OP_HW_UCND ||
+ generic_op_code ==
+ CAMERA_SENSOR_WAIT_OP_SW_UCND) {
+
+ rc = cam_sensor_handle_delay(
+ &cmd_buf, generic_op_code,
+ i2c_reg_settings, j, &byte_cnt,
+ list);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: Failed in handling delay %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ } else if (generic_op_code ==
+ CAMERA_SENSOR_WAIT_OP_COND) {
+ rc = cam_sensor_handle_poll(
+ &cmd_buf, i2c_reg_settings,
+ &byte_cnt, &j, &list);
+ if (rc < 0) {
+ pr_err("%s:%d :Error: Failed in random read %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+ } else {
+ pr_err("%s: %d Wrong Wait Command: %d\n",
+ __func__, __LINE__,
+ generic_op_code);
+ return -EINVAL;
+ }
+ break;
+ }
+ default:
+ pr_err("%s:%d Invalid Command Type:%d\n",
+ __func__, __LINE__, cmm_hdr->cmd_type);
+ return -EINVAL;
+ }
+ }
+ i2c_reg_settings->is_settings_valid = 1;
+ }
+
+ return rc;
+}
+
+int32_t msm_camera_fill_vreg_params(struct camera_vreg_t *cam_vreg,
+ int num_vreg, struct cam_sensor_power_setting *power_setting,
+ uint16_t power_setting_size)
+{
+ int32_t rc = 0, j = 0, i = 0;
+
+ /* Validate input parameters */
+ if (!cam_vreg || !power_setting) {
+ pr_err("%s:%d failed: cam_vreg %pK power_setting %pK", __func__,
+ __LINE__, cam_vreg, power_setting);
+ return -EINVAL;
+ }
+
+ /* Validate size of num_vreg */
+ if (num_vreg <= 0) {
+ pr_err("failed: num_vreg %d", num_vreg);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < power_setting_size; i++) {
+ switch (power_setting[i].seq_type) {
+ case SENSOR_VDIG:
+ for (j = 0; j < num_vreg; j++) {
+ if (!strcmp(cam_vreg[j].reg_name, "cam_vdig")) {
+ CDBG("%s:%d i %d j %d cam_vdig\n",
+ __func__, __LINE__, i, j);
+ power_setting[i].seq_val = j;
+ if (VALIDATE_VOLTAGE(
+ cam_vreg[j].min_voltage,
+ cam_vreg[j].max_voltage,
+ power_setting[i].config_val)) {
+ cam_vreg[j].min_voltage =
+ cam_vreg[j].max_voltage =
+ power_setting[i].config_val;
+ }
+ break;
+ }
+ }
+ if (j == num_vreg)
+ power_setting[i].seq_val = INVALID_VREG;
+ break;
+
+ case SENSOR_VIO:
+ for (j = 0; j < num_vreg; j++) {
+ if (!strcmp(cam_vreg[j].reg_name, "cam_vio")) {
+ CDBG("%s:%d i %d j %d cam_vio\n",
+ __func__, __LINE__, i, j);
+ power_setting[i].seq_val = j;
+ if (VALIDATE_VOLTAGE(
+ cam_vreg[j].min_voltage,
+ cam_vreg[j].max_voltage,
+ power_setting[i].config_val)) {
+ cam_vreg[j].min_voltage =
+ cam_vreg[j].max_voltage =
+ power_setting[i].config_val;
+ }
+ break;
+ }
+ }
+ if (j == num_vreg)
+ power_setting[i].seq_val = INVALID_VREG;
+ break;
+
+ case SENSOR_VANA:
+ for (j = 0; j < num_vreg; j++) {
+ if (!strcmp(cam_vreg[j].reg_name, "cam_vana")) {
+ CDBG("%s:%d i %d j %d cam_vana\n",
+ __func__, __LINE__, i, j);
+ power_setting[i].seq_val = j;
+ if (VALIDATE_VOLTAGE(
+ cam_vreg[j].min_voltage,
+ cam_vreg[j].max_voltage,
+ power_setting[i].config_val)) {
+ cam_vreg[j].min_voltage =
+ cam_vreg[j].max_voltage =
+ power_setting[i].config_val;
+ }
+ break;
+ }
+ }
+ if (j == num_vreg)
+ power_setting[i].seq_val = INVALID_VREG;
+ break;
+
+ case SENSOR_VAF:
+ for (j = 0; j < num_vreg; j++) {
+ if (!strcmp(cam_vreg[j].reg_name, "cam_vaf")) {
+ CDBG("%s:%d i %d j %d cam_vaf\n",
+ __func__, __LINE__, i, j);
+ power_setting[i].seq_val = j;
+ if (VALIDATE_VOLTAGE(
+ cam_vreg[j].min_voltage,
+ cam_vreg[j].max_voltage,
+ power_setting[i].config_val)) {
+ cam_vreg[j].min_voltage =
+ cam_vreg[j].max_voltage =
+ power_setting[i].config_val;
+ }
+ break;
+ }
+ }
+ if (j == num_vreg)
+ power_setting[i].seq_val = INVALID_VREG;
+ break;
+
+ case SENSOR_CUSTOM_REG1:
+ for (j = 0; j < num_vreg; j++) {
+ if (!strcmp(cam_vreg[j].reg_name,
+ "cam_v_custom1")) {
+ CDBG("%s:%d i %d j %d cam_vcustom1\n",
+ __func__, __LINE__, i, j);
+ power_setting[i].seq_val = j;
+ if (VALIDATE_VOLTAGE(
+ cam_vreg[j].min_voltage,
+ cam_vreg[j].max_voltage,
+ power_setting[i].config_val)) {
+ cam_vreg[j].min_voltage =
+ cam_vreg[j].max_voltage =
+ power_setting[i].config_val;
+ }
+ break;
+ }
+ }
+ if (j == num_vreg)
+ power_setting[i].seq_val = INVALID_VREG;
+ break;
+ case SENSOR_CUSTOM_REG2:
+ for (j = 0; j < num_vreg; j++) {
+ if (!strcmp(cam_vreg[j].reg_name,
+ "cam_v_custom2")) {
+ CDBG("%s:%d i %d j %d cam_vcustom2\n",
+ __func__, __LINE__, i, j);
+ power_setting[i].seq_val = j;
+ if (VALIDATE_VOLTAGE(
+ cam_vreg[j].min_voltage,
+ cam_vreg[j].max_voltage,
+ power_setting[i].config_val)) {
+ cam_vreg[j].min_voltage =
+ cam_vreg[j].max_voltage =
+ power_setting[i].config_val;
+ }
+ break;
+ }
+ }
+ if (j == num_vreg)
+ power_setting[i].seq_val = INVALID_VREG;
+ break;
+
+ default: {
+ pr_err("%s:%d invalid seq_val %d\n", __func__,
+ __LINE__, power_setting[i].seq_val);
+ break;
+ }
+ }
+ }
+
+ return rc;
+}
+
+int32_t msm_camera_get_dt_gpio_req_tbl(struct device_node *of_node,
+ struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array,
+ uint16_t gpio_array_size)
+{
+ int32_t rc = 0, i = 0;
+ uint32_t count = 0, *val_array = NULL;
+
+ if (!of_get_property(of_node, "qcom,gpio-req-tbl-num", &count))
+ return 0;
+
+ count /= sizeof(uint32_t);
+ if (!count) {
+ pr_err("%s qcom,gpio-req-tbl-num 0\n", __func__);
+ return 0;
+ }
+
+ val_array = kcalloc(count, sizeof(uint32_t), GFP_KERNEL);
+ if (!val_array)
+ return -ENOMEM;
+
+ gconf->cam_gpio_req_tbl = kcalloc(count, sizeof(struct gpio),
+ GFP_KERNEL);
+ if (!gconf->cam_gpio_req_tbl) {
+ rc = -ENOMEM;
+ goto free_val_array;
+ }
+ gconf->cam_gpio_req_tbl_size = count;
+
+ rc = of_property_read_u32_array(of_node, "qcom,gpio-req-tbl-num",
+ val_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto free_gpio_req_tbl;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (val_array[i] >= gpio_array_size) {
+ pr_err("%s gpio req tbl index %d invalid\n",
+ __func__, val_array[i]);
+ return -EINVAL;
+ }
+ gconf->cam_gpio_req_tbl[i].gpio = gpio_array[val_array[i]];
+ CDBG("%s cam_gpio_req_tbl[%d].gpio = %d\n", __func__, i,
+ gconf->cam_gpio_req_tbl[i].gpio);
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,gpio-req-tbl-flags",
+ val_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto free_gpio_req_tbl;
+ }
+
+ for (i = 0; i < count; i++) {
+ gconf->cam_gpio_req_tbl[i].flags = val_array[i];
+ CDBG("%s cam_gpio_req_tbl[%d].flags = %ld\n", __func__, i,
+ gconf->cam_gpio_req_tbl[i].flags);
+ }
+
+ for (i = 0; i < count; i++) {
+ rc = of_property_read_string_index(of_node,
+ "qcom,gpio-req-tbl-label", i,
+ &gconf->cam_gpio_req_tbl[i].label);
+ CDBG("%s cam_gpio_req_tbl[%d].label = %s\n", __func__, i,
+ gconf->cam_gpio_req_tbl[i].label);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto free_gpio_req_tbl;
+ }
+ }
+
+ kfree(val_array);
+
+ return rc;
+
+free_gpio_req_tbl:
+ kfree(gconf->cam_gpio_req_tbl);
+free_val_array:
+ kfree(val_array);
+ gconf->cam_gpio_req_tbl_size = 0;
+
+ return rc;
+}
+
+int msm_camera_init_gpio_pin_tbl(struct device_node *of_node,
+ struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array,
+ uint16_t gpio_array_size)
+{
+ int rc = 0, val = 0;
+
+ gconf->gpio_num_info = kzalloc(sizeof(struct msm_camera_gpio_num_info),
+ GFP_KERNEL);
+ if (!gconf->gpio_num_info)
+ return -ENOMEM;
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-vana", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-vana failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto free_gpio_info;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-vana invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto free_gpio_info;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_VANA] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_VANA] = 1;
+ CDBG("%s qcom,gpio-vana %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_VANA]);
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-vio", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-vio failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto free_gpio_info;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-vio invalid %d\n",
+ __func__, __LINE__, val);
+ goto free_gpio_info;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_VIO] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_VIO] = 1;
+ CDBG("%s qcom,gpio-vio %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_VIO]);
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-vaf", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-vaf failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto free_gpio_info;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-vaf invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto free_gpio_info;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_VAF] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_VAF] = 1;
+ CDBG("%s qcom,gpio-vaf %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_VAF]);
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-vdig", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-vdig failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto free_gpio_info;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-vdig invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto free_gpio_info;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_VDIG] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_VDIG] = 1;
+ CDBG("%s qcom,gpio-vdig %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_VDIG]);
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-reset", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-reset failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto free_gpio_info;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-reset invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto free_gpio_info;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_RESET] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_RESET] = 1;
+ CDBG("%s qcom,gpio-reset %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_RESET]);
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-standby", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-standby failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto free_gpio_info;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-standby invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto free_gpio_info;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_STANDBY] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_STANDBY] = 1;
+ CDBG("%s qcom,gpio-standby %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_STANDBY]);
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-af-pwdm", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-af-pwdm failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto free_gpio_info;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-af-pwdm invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto free_gpio_info;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_VAF_PWDM] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_VAF_PWDM] = 1;
+ CDBG("%s qcom,gpio-af-pwdm %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_VAF_PWDM]);
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-custom1", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-custom1 failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto free_gpio_info;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-custom1 invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto free_gpio_info;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_CUSTOM_GPIO1] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_CUSTOM_GPIO1] = 1;
+ CDBG("%s qcom,gpio-custom1 %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_CUSTOM_GPIO1]);
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-custom2", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-custom2 failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto free_gpio_info;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-custom2 invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto free_gpio_info;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_CUSTOM_GPIO2] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_CUSTOM_GPIO2] = 1;
+ CDBG("%s qcom,gpio-custom2 %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_CUSTOM_GPIO2]);
+ } else {
+ rc = 0;
+ }
+
+ return rc;
+
+free_gpio_info:
+ kfree(gconf->gpio_num_info);
+ gconf->gpio_num_info = NULL;
+ return rc;
+}
+
+int cam_sensor_get_dt_vreg_data(struct device_node *of_node,
+ struct camera_vreg_t **cam_vreg, int *num_vreg)
+{
+ int rc = 0, i = 0;
+ int32_t count = 0;
+ uint32_t *vreg_array = NULL;
+ struct camera_vreg_t *vreg = NULL;
+
+ count = of_property_count_strings(of_node, "qcom,cam-vreg-name");
+ CDBG("%s qcom,cam-vreg-name count %d\n", __func__, count);
+
+ if (!count || (count == -EINVAL)) {
+ pr_err("%s:%d number of entries is 0 or not present in dts\n",
+ __func__, __LINE__);
+ *num_vreg = 0;
+ return 0;
+ }
+
+ vreg = kcalloc(count, sizeof(*vreg), GFP_KERNEL);
+ if (!vreg)
+ return -ENOMEM;
+
+ *cam_vreg = vreg;
+ *num_vreg = count;
+ for (i = 0; i < count; i++) {
+ rc = of_property_read_string_index(of_node,
+ "qcom,cam-vreg-name", i,
+ &vreg[i].reg_name);
+ CDBG("%s reg_name[%d] = %s\n", __func__, i,
+ vreg[i].reg_name);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto free_vreg;
+ }
+ }
+
+ vreg_array = kcalloc(count, sizeof(uint32_t), GFP_KERNEL);
+ if (!vreg_array) {
+ rc = -ENOMEM;
+ goto free_vreg;
+ }
+
+ for (i = 0; i < count; i++)
+ vreg[i].type = VREG_TYPE_DEFAULT;
+
+ rc = of_property_read_u32_array(of_node, "qcom,cam-vreg-type",
+ vreg_array, count);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto free_vreg_array;
+ } else {
+ for (i = 0; i < count; i++) {
+ vreg[i].type = vreg_array[i];
+ CDBG("%s cam_vreg[%d].type = %d\n",
+ __func__, i, vreg[i].type);
+ }
+ }
+ } else {
+ CDBG("%s:%d no qcom,cam-vreg-type entries in dts\n",
+ __func__, __LINE__);
+ rc = 0;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,cam-vreg-min-voltage",
+ vreg_array, count);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto free_vreg_array;
+ } else {
+ for (i = 0; i < count; i++) {
+ vreg[i].min_voltage = vreg_array[i];
+ CDBG("%s cam_vreg[%d].min_voltage = %d\n",
+ __func__, i, vreg[i].min_voltage);
+ }
+ }
+ } else {
+ CDBG("%s:%d no qcom,cam-vreg-min-voltage entries in dts\n",
+ __func__, __LINE__);
+ rc = 0;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,cam-vreg-max-voltage",
+ vreg_array, count);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto free_vreg_array;
+ } else {
+ for (i = 0; i < count; i++) {
+ vreg[i].max_voltage = vreg_array[i];
+ CDBG("%s cam_vreg[%d].max_voltage = %d\n",
+ __func__, i, vreg[i].max_voltage);
+ }
+ }
+ } else {
+ CDBG("%s:%d no qcom,cam-vreg-max-voltage entries in dts\n",
+ __func__, __LINE__);
+ rc = 0;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,cam-vreg-op-mode",
+ vreg_array, count);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto free_vreg_array;
+ } else {
+ for (i = 0; i < count; i++) {
+ vreg[i].op_mode = vreg_array[i];
+ CDBG("%s cam_vreg[%d].op_mode = %d\n",
+ __func__, i, vreg[i].op_mode);
+ }
+ }
+ } else {
+ CDBG("%s:%d no qcom,cam-vreg-op-mode entries in dts\n",
+ __func__, __LINE__);
+ rc = 0;
+ }
+
+ kfree(vreg_array);
+
+ return rc;
+
+free_vreg_array:
+ kfree(vreg_array);
+free_vreg:
+ kfree(vreg);
+ *num_vreg = 0;
+
+ return rc;
+}
+
+int msm_camera_pinctrl_init(
+ struct msm_pinctrl_info *sensor_pctrl, struct device *dev) {
+
+ sensor_pctrl->pinctrl = devm_pinctrl_get(dev);
+ if (IS_ERR_OR_NULL(sensor_pctrl->pinctrl)) {
+ pr_err("%s:%d Getting pinctrl handle failed\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ sensor_pctrl->gpio_state_active =
+ pinctrl_lookup_state(sensor_pctrl->pinctrl,
+ CAM_SENSOR_PINCTRL_STATE_DEFAULT);
+ if (IS_ERR_OR_NULL(sensor_pctrl->gpio_state_active)) {
+ pr_err("%s:%d Failed to get the active state pinctrl handle\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ sensor_pctrl->gpio_state_suspend
+ = pinctrl_lookup_state(sensor_pctrl->pinctrl,
+ CAM_SENSOR_PINCTRL_STATE_SLEEP);
+ if (IS_ERR_OR_NULL(sensor_pctrl->gpio_state_suspend)) {
+ pr_err("%s:%d Failed to get the suspend state pinctrl handle\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int msm_cam_sensor_handle_reg_gpio(int seq_type,
+ struct msm_camera_gpio_conf *gconf, int val)
+{
+
+ int gpio_offset = -1;
+
+ if (!gconf) {
+ pr_err("ERR:%s: Input Parameters are not proper\n", __func__);
+ return -EINVAL;
+ }
+ CDBG("%s: %d Seq type: %d, config: %d", __func__, __LINE__,
+ seq_type, val);
+
+ gpio_offset = seq_type;
+
+ if ((gconf->gpio_num_info->valid[gpio_offset] == 1)) {
+ CDBG("%s: %d VALID GPIO offset: %d, seqtype: %d\n",
+ __func__, __LINE__, gpio_offset, seq_type);
+ gpio_set_value_cansleep(
+ gconf->gpio_num_info->gpio_num
+ [gpio_offset], val);
+ }
+
+ return 0;
+}
+
+int32_t msm_sensor_driver_get_gpio_data(
+ struct msm_camera_gpio_conf **gpio_conf,
+ struct device_node *of_node)
+{
+ int32_t rc = 0, i = 0;
+ uint16_t *gpio_array = NULL;
+ int16_t gpio_array_size = 0;
+ struct msm_camera_gpio_conf *gconf = NULL;
+
+ /* Validate input parameters */
+ if (!of_node) {
+ pr_err("failed: invalid param of_node %pK", of_node);
+ return -EINVAL;
+ }
+
+ gpio_array_size = of_gpio_count(of_node);
+ CDBG("gpio count %d\n", gpio_array_size);
+ if (gpio_array_size <= 0)
+ return 0;
+
+ gconf = kzalloc(sizeof(*gconf), GFP_KERNEL);
+ if (!gconf)
+ return -ENOMEM;
+
+ *gpio_conf = gconf;
+
+ gpio_array = kcalloc(gpio_array_size, sizeof(uint16_t), GFP_KERNEL);
+ if (!gpio_array)
+ goto free_gpio_conf;
+
+ for (i = 0; i < gpio_array_size; i++) {
+ gpio_array[i] = of_get_gpio(of_node, i);
+ CDBG("gpio_array[%d] = %d", i, gpio_array[i]);
+ }
+ rc = msm_camera_get_dt_gpio_req_tbl(of_node, gconf, gpio_array,
+ gpio_array_size);
+ if (rc < 0) {
+ pr_err("failed in msm_camera_get_dt_gpio_req_tbl\n");
+ goto free_gpio_array;
+ }
+
+ rc = msm_camera_init_gpio_pin_tbl(of_node, gconf, gpio_array,
+ gpio_array_size);
+ if (rc < 0) {
+ pr_err("failed in msm_camera_init_gpio_pin_tbl\n");
+ goto free_gpio_req_tbl;
+ }
+ kfree(gpio_array);
+
+ return rc;
+
+free_gpio_req_tbl:
+ kfree(gconf->cam_gpio_req_tbl);
+free_gpio_array:
+ kfree(gpio_array);
+free_gpio_conf:
+ kfree(gconf);
+ *gpio_conf = NULL;
+
+ return rc;
+}
+
+int cam_sensor_core_power_up(struct cam_sensor_power_ctrl_t *ctrl)
+{
+ int rc = 0, index = 0, no_gpio = 0, ret = 0, num_vreg, j = 0;
+ struct cam_sensor_power_setting *power_setting = NULL;
+ struct camera_vreg_t *cam_vreg;
+
+ CDBG("%s:%d\n", __func__, __LINE__);
+ if (!ctrl) {
+ pr_err("failed ctrl %pK\n", ctrl);
+ return -EINVAL;
+ }
+
+ cam_vreg = ctrl->cam_vreg;
+ num_vreg = ctrl->num_vreg;
+
+ if (ctrl->gpio_conf->cam_gpiomux_conf_tbl != NULL)
+ CDBG("%s:%d mux install\n", __func__, __LINE__);
+
+ ret = msm_camera_pinctrl_init(&(ctrl->pinctrl_info), ctrl->dev);
+ if (ret < 0) {
+ pr_err("%s:%d Initialization of pinctrl failed\n",
+ __func__, __LINE__);
+ ctrl->cam_pinctrl_status = 0;
+ } else {
+ ctrl->cam_pinctrl_status = 1;
+ }
+ rc = msm_camera_request_gpio_table(
+ ctrl->gpio_conf->cam_gpio_req_tbl,
+ ctrl->gpio_conf->cam_gpio_req_tbl_size, 1);
+ if (rc < 0)
+ no_gpio = rc;
+ if (ctrl->cam_pinctrl_status) {
+ ret = pinctrl_select_state(ctrl->pinctrl_info.pinctrl,
+ ctrl->pinctrl_info.gpio_state_active);
+ if (ret)
+ pr_err("%s:%d cannot set pin to active state",
+ __func__, __LINE__);
+ }
+
+ for (index = 0; index < ctrl->power_setting_size; index++) {
+ CDBG("%s index %d\n", __func__, index);
+ power_setting = &ctrl->power_setting[index];
+
+ switch (power_setting->seq_type) {
+ case SENSOR_MCLK:
+ if (power_setting->seq_val >= ctrl->clk_info_size) {
+ pr_err("%s:%d :Error: clk index %d >= max %zu\n",
+ __func__, __LINE__,
+ power_setting->seq_val,
+ ctrl->clk_info_size);
+ goto power_up_failed;
+ }
+ for (j = 0; j < num_vreg; j++) {
+ if (!strcmp(cam_vreg[j].reg_name,
+ "cam_clk")) {
+ CDBG("%s:%d Enable cam_clk: %d\n",
+ __func__, __LINE__, j);
+ msm_camera_config_single_vreg(ctrl->dev,
+ &cam_vreg[j],
+ (struct regulator **)
+ &power_setting->data[0],
+ 1);
+ }
+ }
+ if (power_setting->config_val)
+ ctrl->clk_info[power_setting->seq_val].
+ clk_rate = power_setting->config_val;
+ rc = msm_camera_clk_enable(ctrl->dev,
+ ctrl->clk_info, ctrl->clk_ptr,
+ ctrl->clk_info_size, true);
+ if (rc < 0) {
+ pr_err("%s: clk enable failed\n", __func__);
+ goto power_up_failed;
+ }
+ break;
+ case SENSOR_RESET:
+ case SENSOR_STANDBY:
+ case SENSOR_CUSTOM_GPIO1:
+ case SENSOR_CUSTOM_GPIO2:
+ if (no_gpio) {
+ pr_err("%s: request gpio failed\n", __func__);
+ return no_gpio;
+ }
+ if (power_setting->seq_val >= CAM_VREG_MAX ||
+ !ctrl->gpio_conf->gpio_num_info) {
+ pr_err("%s gpio index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ CAM_VREG_MAX);
+ goto power_up_failed;
+ }
+ CDBG("%s:%d gpio set val %d\n",
+ __func__, __LINE__,
+ ctrl->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val]);
+
+ rc = msm_cam_sensor_handle_reg_gpio(
+ power_setting->seq_type,
+ ctrl->gpio_conf, 1);
+ if (rc < 0) {
+ pr_err("ERR:%s Error in handling VREG GPIO\n",
+ __func__);
+ goto power_up_failed;
+ }
+ break;
+ case SENSOR_VANA:
+ case SENSOR_VDIG:
+ case SENSOR_VIO:
+ case SENSOR_VAF:
+ case SENSOR_VAF_PWDM:
+ case SENSOR_CUSTOM_REG1:
+ case SENSOR_CUSTOM_REG2:
+ if (power_setting->seq_val == INVALID_VREG)
+ break;
+
+ if (power_setting->seq_val >= CAM_VREG_MAX) {
+ pr_err("%s vreg index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ CAM_VREG_MAX);
+ goto power_up_failed;
+ }
+ if (power_setting->seq_val < ctrl->num_vreg)
+ msm_camera_config_single_vreg(ctrl->dev,
+ &ctrl->cam_vreg
+ [power_setting->seq_val],
+ (struct regulator **)
+ &power_setting->data[0],
+ 1);
+ else
+ pr_err("%s: %d usr_idx:%d dts_idx:%d\n",
+ __func__, __LINE__,
+ power_setting->seq_val, ctrl->num_vreg);
+
+ rc = msm_cam_sensor_handle_reg_gpio(
+ power_setting->seq_type,
+ ctrl->gpio_conf, 1);
+ if (rc < 0) {
+ pr_err("ERR:%s Error in handling VREG GPIO\n",
+ __func__);
+ goto power_up_failed;
+ }
+ break;
+ default:
+ pr_err("%s error power seq type %d\n", __func__,
+ power_setting->seq_type);
+ break;
+ }
+ if (power_setting->delay > 20)
+ msleep(power_setting->delay);
+ else if (power_setting->delay)
+ usleep_range(power_setting->delay * 1000,
+ (power_setting->delay * 1000) + 1000);
+ }
+
+ return 0;
+power_up_failed:
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ for (index--; index >= 0; index--) {
+ CDBG("%s index %d\n", __func__, index);
+ power_setting = &ctrl->power_setting[index];
+ CDBG("%s type %d\n", __func__, power_setting->seq_type);
+ switch (power_setting->seq_type) {
+ case SENSOR_RESET:
+ case SENSOR_STANDBY:
+ case SENSOR_CUSTOM_GPIO1:
+ case SENSOR_CUSTOM_GPIO2:
+ if (!ctrl->gpio_conf->gpio_num_info)
+ continue;
+ if (!ctrl->gpio_conf->gpio_num_info->valid
+ [power_setting->seq_val])
+ continue;
+ gpio_set_value_cansleep(
+ ctrl->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val], GPIOF_OUT_INIT_LOW);
+ break;
+ case SENSOR_VANA:
+ case SENSOR_VDIG:
+ case SENSOR_VIO:
+ case SENSOR_VAF:
+ case SENSOR_VAF_PWDM:
+ case SENSOR_CUSTOM_REG1:
+ case SENSOR_CUSTOM_REG2:
+ if (power_setting->seq_val < ctrl->num_vreg)
+ msm_camera_config_single_vreg(ctrl->dev,
+ &ctrl->cam_vreg
+ [power_setting->seq_val],
+ (struct regulator **)
+ &power_setting->data[0],
+ 0);
+ else
+ pr_err("%s:%d:seq_val: %d > num_vreg: %d\n",
+ __func__, __LINE__,
+ power_setting->seq_val, ctrl->num_vreg);
+
+ msm_cam_sensor_handle_reg_gpio(power_setting->seq_type,
+ ctrl->gpio_conf, GPIOF_OUT_INIT_LOW);
+ break;
+ default:
+ pr_err("%s error power seq type %d\n", __func__,
+ power_setting->seq_type);
+ break;
+ }
+ if (power_setting->delay > 20) {
+ msleep(power_setting->delay);
+ } else if (power_setting->delay) {
+ usleep_range(power_setting->delay * 1000,
+ (power_setting->delay * 1000) + 1000);
+ }
+ }
+ if (ctrl->cam_pinctrl_status) {
+ ret = pinctrl_select_state(ctrl->pinctrl_info.pinctrl,
+ ctrl->pinctrl_info.gpio_state_suspend);
+ if (ret)
+ pr_err("%s:%d cannot set pin to suspend state\n",
+ __func__, __LINE__);
+ devm_pinctrl_put(ctrl->pinctrl_info.pinctrl);
+ }
+ ctrl->cam_pinctrl_status = 0;
+ msm_camera_request_gpio_table(
+ ctrl->gpio_conf->cam_gpio_req_tbl,
+ ctrl->gpio_conf->cam_gpio_req_tbl_size, 0);
+
+ return rc;
+}
+
+static struct cam_sensor_power_setting*
+msm_camera_get_power_settings(struct cam_sensor_power_ctrl_t *ctrl,
+ enum msm_camera_power_seq_type seq_type,
+ uint16_t seq_val)
+{
+ struct cam_sensor_power_setting *power_setting, *ps = NULL;
+ int idx;
+
+ for (idx = 0; idx < ctrl->power_setting_size; idx++) {
+ power_setting = &ctrl->power_setting[idx];
+ if (power_setting->seq_type == seq_type &&
+ power_setting->seq_val == seq_val) {
+ ps = power_setting;
+ return ps;
+ }
+
+ }
+
+ return ps;
+}
+
+static int cam_config_mclk_reg(struct cam_sensor_power_ctrl_t *ctrl,
+ int32_t index)
+{
+ struct camera_vreg_t *cam_vreg;
+ int32_t num_vreg = 0, j = 0, rc = 0, idx = 0;
+ struct cam_sensor_power_setting *ps = NULL;
+ struct cam_sensor_power_setting *pd = NULL;
+
+ cam_vreg = ctrl->cam_vreg;
+ num_vreg = ctrl->num_vreg;
+ pd = &ctrl->power_down_setting[index];
+
+ for (j = 0; j < num_vreg; j++) {
+ if (!strcmp(cam_vreg[j].reg_name, "cam_clk")) {
+
+ ps = NULL;
+ for (idx = 0; idx <
+ ctrl->power_setting_size; idx++) {
+ if (ctrl->power_setting[idx].
+ seq_type == pd->seq_type) {
+ ps = &ctrl->power_setting[idx];
+ break;
+ }
+ }
+
+ if (ps != NULL)
+ msm_camera_config_single_vreg(
+ ctrl->dev,
+ &cam_vreg[j],
+ (struct regulator **)
+ &ps->data[0], 0);
+ }
+ }
+
+ return rc;
+}
+
+int msm_camera_power_down(struct cam_sensor_power_ctrl_t *ctrl)
+{
+ int index = 0, ret = 0, num_vreg = 0;
+ struct cam_sensor_power_setting *pd = NULL;
+ struct cam_sensor_power_setting *ps;
+ struct camera_vreg_t *cam_vreg;
+
+ CDBG("%s:%d\n", __func__, __LINE__);
+ if (!ctrl) {
+ pr_err("failed ctrl %pK\n", ctrl);
+ return -EINVAL;
+ }
+
+ cam_vreg = ctrl->cam_vreg;
+ num_vreg = ctrl->num_vreg;
+
+ for (index = 0; index < ctrl->power_down_setting_size; index++) {
+ CDBG("%s index %d\n", __func__, index);
+ pd = &ctrl->power_down_setting[index];
+ ps = NULL;
+ CDBG("%s type %d\n", __func__, pd->seq_type);
+ switch (pd->seq_type) {
+ case SENSOR_MCLK:
+ ret = cam_config_mclk_reg(ctrl, index);
+ if (ret < 0) {
+ pr_err("%s:%d :Error: in config clk reg\n",
+ __func__, __LINE__);
+ return ret;
+ }
+ msm_camera_clk_enable(ctrl->dev,
+ ctrl->clk_info, ctrl->clk_ptr,
+ ctrl->clk_info_size, false);
+ break;
+ case SENSOR_RESET:
+ case SENSOR_STANDBY:
+ case SENSOR_CUSTOM_GPIO1:
+ case SENSOR_CUSTOM_GPIO2:
+ if (!ctrl->gpio_conf->gpio_num_info->valid
+ [pd->seq_val])
+ continue;
+ gpio_set_value_cansleep(
+ ctrl->gpio_conf->gpio_num_info->gpio_num
+ [pd->seq_val],
+ (int) pd->config_val);
+ break;
+ case SENSOR_VANA:
+ case SENSOR_VDIG:
+ case SENSOR_VIO:
+ case SENSOR_VAF:
+ case SENSOR_VAF_PWDM:
+ case SENSOR_CUSTOM_REG1:
+ case SENSOR_CUSTOM_REG2:
+ if (pd->seq_val == INVALID_VREG)
+ break;
+ ps = msm_camera_get_power_settings(
+ ctrl, pd->seq_type,
+ pd->seq_val);
+ if (ps) {
+ if (pd->seq_val < ctrl->num_vreg)
+ msm_camera_config_single_vreg(ctrl->dev,
+ &ctrl->cam_vreg
+ [pd->seq_val],
+ (struct regulator **)
+ &ps->data[0],
+ 0);
+ else
+ pr_err("%s:%d:seq_val:%d > num_vreg: %d\n",
+ __func__, __LINE__, pd->seq_val,
+ ctrl->num_vreg);
+ } else
+ pr_err("%s error in power up/down seq data\n",
+ __func__);
+ ret = msm_cam_sensor_handle_reg_gpio(pd->seq_type,
+ ctrl->gpio_conf, GPIOF_OUT_INIT_LOW);
+ if (ret < 0)
+ pr_err("ERR:%s Error while disabling VREG GPIO\n",
+ __func__);
+ break;
+ default:
+ pr_err("%s error power seq type %d\n", __func__,
+ pd->seq_type);
+ break;
+ }
+ if (pd->delay > 20)
+ msleep(pd->delay);
+ else if (pd->delay)
+ usleep_range(pd->delay * 1000,
+ (pd->delay * 1000) + 1000);
+ }
+
+ if (ctrl->cam_pinctrl_status) {
+ ret = pinctrl_select_state(ctrl->pinctrl_info.pinctrl,
+ ctrl->pinctrl_info.gpio_state_suspend);
+ if (ret)
+ pr_err("%s:%d cannot set pin to suspend state",
+ __func__, __LINE__);
+ devm_pinctrl_put(ctrl->pinctrl_info.pinctrl);
+ }
+
+ ctrl->cam_pinctrl_status = 0;
+ msm_camera_request_gpio_table(
+ ctrl->gpio_conf->cam_gpio_req_tbl,
+ ctrl->gpio_conf->cam_gpio_req_tbl_size, 0);
+
+ return 0;
+}
+
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.h
new file mode 100644
index 0000000..7e7fc35
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.h
@@ -0,0 +1,62 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_SENSOR_UTIL_H_
+#define _CAM_SENSOR_UTIL_H_
+
+#include <linux/kernel.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <cam_sensor_cmn_header.h>
+#include <cam_req_mgr_util.h>
+#include <cam_req_mgr_interface.h>
+#include <cam_mem_mgr.h>
+
+#define INVALID_VREG 100
+
+int msm_camera_get_dt_power_setting_data(struct device_node *of_node,
+ struct camera_vreg_t *cam_vreg, int num_vreg,
+ struct cam_sensor_power_ctrl_t *power_info);
+
+int msm_camera_get_dt_gpio_req_tbl(struct device_node *of_node,
+ struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array,
+ uint16_t gpio_array_size);
+
+int msm_camera_init_gpio_pin_tbl(struct device_node *of_node,
+ struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array,
+ uint16_t gpio_array_size);
+
+int cam_sensor_get_dt_vreg_data(struct device_node *of_node,
+ struct camera_vreg_t **cam_vreg, int *num_vreg);
+
+int cam_sensor_core_power_up(struct cam_sensor_power_ctrl_t *ctrl);
+
+int msm_camera_power_down(struct cam_sensor_power_ctrl_t *ctrl);
+
+int msm_camera_fill_vreg_params(struct camera_vreg_t *cam_vreg,
+ int num_vreg, struct cam_sensor_power_setting *power_setting,
+ uint16_t power_setting_size);
+
+int msm_camera_pinctrl_init
+ (struct msm_pinctrl_info *sensor_pctrl, struct device *dev);
+
+int32_t msm_sensor_driver_get_gpio_data(
+ struct msm_camera_gpio_conf **gpio_conf,
+ struct device_node *of_node);
+
+int cam_sensor_i2c_pkt_parser(struct i2c_settings_array *i2c_reg_settings,
+ struct cam_cmd_buf_desc *cmd_desc, int32_t num_cmd_buffers);
+
+int32_t delete_request(struct i2c_settings_array *i2c_array);
+#endif /* _CAM_SENSOR_UTIL_H_ */
diff --git a/drivers/media/platform/msm/camera/icp/Makefile b/drivers/media/platform/msm/camera/icp/Makefile
new file mode 100644
index 0000000..c42b162
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/Makefile
@@ -0,0 +1,14 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_core
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sync
+ccflags-y += -Idrivers/media/platform/msm/camera/icp
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/icp_hw/include
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/fw_inc
+ccflags-y += -Idrivers/media/platform/msm/camera
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
+
+
+obj-$(CONFIG_SPECTRA_CAMERA) += icp_hw/
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_icp_subdev.o cam_icp_context.o hfi.o
diff --git a/drivers/media/platform/msm/camera/icp/cam_icp_context.c b/drivers/media/platform/msm/camera/icp/cam_icp_context.c
new file mode 100644
index 0000000..41290f4
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/cam_icp_context.c
@@ -0,0 +1,195 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "CAM-ICP-CTXT %s:%d " fmt, __func__, __LINE__
+
+#include <linux/debugfs.h>
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <media/cam_sync.h>
+#include <media/cam_defs.h>
+#include "cam_sync_api.h"
+#include "cam_node.h"
+#include "cam_context.h"
+#include "cam_context_utils.h"
+#include "cam_icp_context.h"
+#include "cam_req_mgr_util.h"
+#include "cam_mem_mgr.h"
+
+static int __cam_icp_acquire_dev_in_available(struct cam_context *ctx,
+ struct cam_acquire_dev_cmd *cmd)
+{
+ int rc;
+
+ rc = cam_context_acquire_dev_to_hw(ctx, cmd);
+ if (!rc)
+ ctx->state = CAM_CTX_ACQUIRED;
+
+ return rc;
+}
+
+static int __cam_icp_release_dev_in_acquired(struct cam_context *ctx,
+ struct cam_release_dev_cmd *cmd)
+{
+ int rc;
+
+ rc = cam_context_release_dev_to_hw(ctx, cmd);
+ if (rc)
+ pr_err("Unable to release device\n");
+
+ ctx->state = CAM_CTX_AVAILABLE;
+ return rc;
+}
+
+static int __cam_icp_start_dev_in_acquired(struct cam_context *ctx,
+ struct cam_start_stop_dev_cmd *cmd)
+{
+ int rc;
+
+ rc = cam_context_start_dev_to_hw(ctx, cmd);
+ if (!rc)
+ ctx->state = CAM_CTX_READY;
+
+ return rc;
+}
+
+static int __cam_icp_config_dev_in_ready(struct cam_context *ctx,
+ struct cam_config_dev_cmd *cmd)
+{
+ int rc;
+
+ rc = cam_context_prepare_dev_to_hw(ctx, cmd);
+ if (rc)
+ pr_err("Unable to prepare device\n");
+
+ return rc;
+}
+
+static int __cam_icp_stop_dev_in_ready(struct cam_context *ctx,
+ struct cam_start_stop_dev_cmd *cmd)
+{
+ int rc;
+
+ rc = cam_context_stop_dev_to_hw(ctx);
+ if (rc)
+ pr_err("Unable to stop device\n");
+
+ ctx->state = CAM_CTX_ACQUIRED;
+ return rc;
+}
+
+static int __cam_icp_release_dev_in_ready(struct cam_context *ctx,
+ struct cam_release_dev_cmd *cmd)
+{
+ int rc;
+
+ rc = __cam_icp_stop_dev_in_ready(ctx, NULL);
+ if (rc)
+ pr_err("Unable to stop device\n");
+
+ rc = __cam_icp_release_dev_in_acquired(ctx, cmd);
+ if (rc)
+ pr_err("Unable to stop device\n");
+
+ return rc;
+}
+
+static int __cam_icp_handle_buf_done_in_ready(void *ctx,
+ uint32_t evt_id, void *done)
+{
+ return cam_context_buf_done_from_hw(ctx, done, 0);
+}
+
+static struct cam_ctx_ops
+ cam_icp_ctx_state_machine[CAM_CTX_STATE_MAX] = {
+ /* Uninit */
+ {
+ .ioctl_ops = {},
+ .crm_ops = {},
+ .irq_ops = NULL,
+ },
+ /* Available */
+ {
+ .ioctl_ops = {
+ .acquire_dev = __cam_icp_acquire_dev_in_available,
+ },
+ .crm_ops = {},
+ .irq_ops = NULL,
+ },
+ /* Acquired */
+ {
+ .ioctl_ops = {
+ .release_dev = __cam_icp_release_dev_in_acquired,
+ .start_dev = __cam_icp_start_dev_in_acquired,
+ .config_dev = __cam_icp_config_dev_in_ready,
+ },
+ .crm_ops = {},
+ .irq_ops = __cam_icp_handle_buf_done_in_ready,
+ },
+ /* Ready */
+ {
+ .ioctl_ops = {
+ .stop_dev = __cam_icp_stop_dev_in_ready,
+ .release_dev = __cam_icp_release_dev_in_ready,
+ .config_dev = __cam_icp_config_dev_in_ready,
+ },
+ .crm_ops = {},
+ .irq_ops = __cam_icp_handle_buf_done_in_ready,
+ },
+ /* Activated */
+ {
+ .ioctl_ops = {},
+ .crm_ops = {},
+ .irq_ops = NULL,
+ },
+};
+
+int cam_icp_context_init(struct cam_icp_context *ctx,
+ struct cam_hw_mgr_intf *hw_intf)
+{
+ int rc;
+
+ if ((!ctx) || (!ctx->base) || (!hw_intf)) {
+ pr_err("Invalid params: %pK %pK\n", ctx, hw_intf);
+ rc = -EINVAL;
+ goto err;
+ }
+
+ rc = cam_context_init(ctx->base, NULL, hw_intf, ctx->req_base,
+ CAM_CTX_REQ_MAX);
+ if (rc) {
+ pr_err("Camera Context Base init failed!\n");
+ goto err;
+ }
+
+ ctx->base->state_machine = cam_icp_ctx_state_machine;
+ ctx->base->ctx_priv = ctx;
+ ctx->ctxt_to_hw_map = NULL;
+
+err:
+ return rc;
+}
+
+int cam_icp_context_deinit(struct cam_icp_context *ctx)
+{
+ if ((!ctx) || (!ctx->base)) {
+ pr_err("Invalid params: %pK\n", ctx);
+ return -EINVAL;
+ }
+
+ cam_context_deinit(ctx->base);
+ memset(ctx, 0, sizeof(*ctx));
+
+ return 0;
+}
+
diff --git a/drivers/media/platform/msm/camera/icp/cam_icp_context.h b/drivers/media/platform/msm/camera/icp/cam_icp_context.h
new file mode 100644
index 0000000..709fc56
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/cam_icp_context.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_ICP_CONTEXT_H_
+#define _CAM_ICP_CONTEXT_H_
+
+#include "cam_context.h"
+
+/**
+ * struct cam_icp_context - icp context
+ * @base: icp context object
+ * @state_machine: state machine for ICP context
+ * @req_base: common request structure
+ * @state: icp context state
+ * @ctxt_to_hw_map: context to FW handle mapping
+ */
+struct cam_icp_context {
+ struct cam_context *base;
+ struct cam_ctx_ops *state_machine;
+ struct cam_ctx_request req_base[CAM_CTX_REQ_MAX];
+ uint32_t state;
+ void *ctxt_to_hw_map;
+};
+
+/**
+ * cam_icp_context_init() - ICP context init
+ * @ctx: Pointer to context
+ * @hw_intf: Pointer to ICP hardware interface
+ */
+int cam_icp_context_init(struct cam_icp_context *ctx,
+ struct cam_hw_mgr_intf *hw_intf);
+
+/**
+ * cam_icp_context_deinit() - ICP context deinit
+ * @ctx: Pointer to context
+ */
+int cam_icp_context_deinit(struct cam_icp_context *ctx);
+
+#endif /* _CAM_ICP_CONTEXT_H_ */
diff --git a/drivers/media/platform/msm/camera/icp/cam_icp_subdev.c b/drivers/media/platform/msm/camera/icp/cam_icp_subdev.c
new file mode 100644
index 0000000..703561d
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/cam_icp_subdev.c
@@ -0,0 +1,259 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "CAM-ICP %s:%d " fmt, __func__, __LINE__
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/iommu.h>
+#include <linux/timer.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
+#include <media/cam_req_mgr.h>
+#include <media/cam_defs.h>
+#include <media/cam_icp.h>
+#include "cam_req_mgr_dev.h"
+#include "cam_subdev.h"
+#include "cam_node.h"
+#include "cam_context.h"
+#include "cam_icp_context.h"
+#include "cam_hw_mgr_intf.h"
+#include "cam_icp_hw_mgr_intf.h"
+
+#define CAM_ICP_DEV_NAME "cam-icp"
+
+struct cam_icp_subdev {
+ struct cam_subdev sd;
+ struct cam_node *node;
+ struct cam_context ctx[CAM_CTX_MAX];
+ struct cam_icp_context ctx_icp[CAM_CTX_MAX];
+ struct mutex icp_lock;
+ int32_t open_cnt;
+ int32_t reserved;
+};
+
+static struct cam_icp_subdev g_icp_dev;
+
+static const struct of_device_id cam_icp_dt_match[] = {
+ {.compatible = "qcom,cam-icp"},
+ {}
+};
+
+static int cam_icp_subdev_open(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct cam_hw_mgr_intf *hw_mgr_intf = NULL;
+ struct cam_node *node = v4l2_get_subdevdata(sd);
+ int rc = 0;
+
+ mutex_lock(&g_icp_dev.icp_lock);
+ if (g_icp_dev.open_cnt >= 1) {
+ pr_err("ICP subdev is already opened\n");
+ rc = -EALREADY;
+ goto end;
+ }
+
+ if (!node) {
+ pr_err("Invalid args\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ hw_mgr_intf = &node->hw_mgr_intf;
+ rc = hw_mgr_intf->download_fw(hw_mgr_intf->hw_mgr_priv, NULL);
+ if (rc < 0) {
+ pr_err("FW download failed\n");
+ goto end;
+ }
+ g_icp_dev.open_cnt++;
+end:
+ mutex_unlock(&g_icp_dev.icp_lock);
+ return rc;
+}
+
+static int cam_icp_subdev_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ int rc = 0;
+ struct cam_hw_mgr_intf *hw_mgr_intf = NULL;
+ struct cam_node *node = v4l2_get_subdevdata(sd);
+
+ mutex_lock(&g_icp_dev.icp_lock);
+ if (g_icp_dev.open_cnt <= 0) {
+ pr_err("ICP subdev is already closed\n");
+ rc = -EINVAL;
+ goto end;
+ }
+ g_icp_dev.open_cnt--;
+ if (!node) {
+ pr_err("Invalid args\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ hw_mgr_intf = &node->hw_mgr_intf;
+ if (!hw_mgr_intf) {
+ pr_err("hw_mgr_intf is not initialized\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ rc = hw_mgr_intf->hw_close(hw_mgr_intf->hw_mgr_priv, NULL);
+ if (rc < 0) {
+ pr_err("HW close failed\n");
+ goto end;
+ }
+
+end:
+ mutex_unlock(&g_icp_dev.icp_lock);
+ return 0;
+}
+
+const struct v4l2_subdev_internal_ops cam_icp_subdev_internal_ops = {
+ .open = cam_icp_subdev_open,
+ .close = cam_icp_subdev_close,
+};
+
+static int cam_icp_probe(struct platform_device *pdev)
+{
+ int rc = 0, i = 0;
+ struct cam_node *node;
+ struct cam_hw_mgr_intf *hw_mgr_intf;
+
+ if (!pdev) {
+ pr_err("pdev is NULL\n");
+ return -EINVAL;
+ }
+
+ memset(&g_icp_dev, 0, sizeof(g_icp_dev));
+
+ g_icp_dev.sd.pdev = pdev;
+ g_icp_dev.sd.internal_ops = &cam_icp_subdev_internal_ops;
+ rc = cam_subdev_probe(&g_icp_dev.sd, pdev, CAM_ICP_DEV_NAME,
+ CAM_ICP_DEVICE_TYPE);
+ if (rc) {
+ pr_err("ICP cam_subdev_probe failed!\n");
+ goto probe_fail;
+ }
+
+ node = (struct cam_node *) g_icp_dev.sd.token;
+
+ hw_mgr_intf = kzalloc(sizeof(*hw_mgr_intf), GFP_KERNEL);
+ if (!hw_mgr_intf) {
+ rc = -EINVAL;
+ goto hw_alloc_fail;
+ }
+
+ rc = cam_icp_hw_mgr_init(pdev->dev.of_node, (uint64_t *)hw_mgr_intf);
+ if (rc) {
+ pr_err("ICP HW manager init failed: %d\n", rc);
+ goto hw_init_fail;
+ }
+
+ pr_debug("Initializing the ICP contexts\n");
+ for (i = 0; i < CAM_CTX_MAX; i++) {
+ g_icp_dev.ctx_icp[i].base = &g_icp_dev.ctx[i];
+ rc = cam_icp_context_init(&g_icp_dev.ctx_icp[i],
+ hw_mgr_intf);
+ if (rc) {
+ pr_err("ICP context init failed!\n");
+ goto ctx_fail;
+ }
+ }
+
+ pr_debug("Initializing the ICP Node\n");
+ rc = cam_node_init(node, hw_mgr_intf, g_icp_dev.ctx,
+ CAM_CTX_MAX, CAM_ICP_DEV_NAME);
+ if (rc) {
+ pr_err("ICP node init failed!\n");
+ goto ctx_fail;
+ }
+
+ g_icp_dev.open_cnt = 0;
+ mutex_init(&g_icp_dev.icp_lock);
+
+ return rc;
+
+ctx_fail:
+ for (--i; i >= 0; i--)
+ cam_icp_context_deinit(&g_icp_dev.ctx_icp[i]);
+hw_init_fail:
+ kfree(hw_mgr_intf);
+hw_alloc_fail:
+ cam_subdev_remove(&g_icp_dev.sd);
+probe_fail:
+ return rc;
+}
+
+static int cam_icp_remove(struct platform_device *pdev)
+{
+ int i;
+ struct v4l2_subdev *sd;
+ struct cam_subdev *subdev;
+
+ if (!pdev) {
+ pr_err("pdev is NULL\n");
+ return -EINVAL;
+ }
+
+ sd = platform_get_drvdata(pdev);
+ if (!sd) {
+ pr_err("V4l2 subdev is NULL\n");
+ return -EINVAL;
+ }
+
+ subdev = v4l2_get_subdevdata(sd);
+ if (!subdev) {
+ pr_err("cam subdev is NULL\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < CAM_CTX_MAX; i++)
+ cam_icp_context_deinit(&g_icp_dev.ctx_icp[i]);
+ cam_node_deinit(g_icp_dev.node);
+ cam_subdev_remove(&g_icp_dev.sd);
+ mutex_destroy(&g_icp_dev.icp_lock);
+
+ return 0;
+}
+
+static struct platform_driver cam_icp_driver = {
+ .probe = cam_icp_probe,
+ .remove = cam_icp_remove,
+ .driver = {
+ .name = "cam_icp",
+ .owner = THIS_MODULE,
+ .of_match_table = cam_icp_dt_match,
+ },
+};
+
+static int __init cam_icp_init_module(void)
+{
+ return platform_driver_register(&cam_icp_driver);
+}
+
+static void __exit cam_icp_exit_module(void)
+{
+ platform_driver_unregister(&cam_icp_driver);
+}
+module_init(cam_icp_init_module);
+module_exit(cam_icp_exit_module);
+MODULE_DESCRIPTION("MSM ICP driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera/icp/fw_inc/hfi_intf.h b/drivers/media/platform/msm/camera/icp/fw_inc/hfi_intf.h
new file mode 100644
index 0000000..1e42f75
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/fw_inc/hfi_intf.h
@@ -0,0 +1,105 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _HFI_INTF_H_
+#define _HFI_INTF_H_
+
+#include <linux/types.h>
+
+/**
+ * struct hfi_mem
+ * @len: length of memory
+ * @kva: kernel virtual address
+ * @iova: IO virtual address
+ * @reserved: reserved field
+ */
+struct hfi_mem {
+ uint64_t len;
+ uint64_t kva;
+ uint32_t iova;
+ uint32_t reserved;
+};
+
+/**
+ * struct hfi_mem_info
+ * @qtbl: qtable hfi memory
+ * @cmd_q: command queue hfi memory for host to firmware communication
+ * @msg_q: message queue hfi memory for firmware to host communication
+ * @dbg_q: debug queue hfi memory for firmware debug information
+ * @sec_heap: secondary heap hfi memory for firmware
+ * @icp_base: icp base address
+ */
+struct hfi_mem_info {
+ struct hfi_mem qtbl;
+ struct hfi_mem cmd_q;
+ struct hfi_mem msg_q;
+ struct hfi_mem dbg_q;
+ struct hfi_mem sec_heap;
+ void __iomem *icp_base;
+};
+
+/**
+ * hfi_write_cmd() - function for hfi write
+ * @cmd_ptr: pointer to command data for hfi write
+ *
+ * Returns success(zero)/failure(non zero)
+ */
+int hfi_write_cmd(void *cmd_ptr);
+
+/**
+ * hfi_read_message() - function for hfi read
+ * @pmsg: buffer to place read message for hfi queue
+ * @q_id: queue id
+ *
+ * Returns success(zero)/failure(non zero)
+ */
+int hfi_read_message(uint32_t *pmsg, uint8_t q_id);
+
+/**
+ * hfi_init() - function initialize hfi after firmware download
+ * @event_driven_mode: event mode
+ * @hfi_mem: hfi memory info
+ * @icp_base: icp base address
+ * @debug: debug flag
+ *
+ * Returns success(zero)/failure(non zero)
+ */
+int cam_hfi_init(uint8_t event_driven_mode, struct hfi_mem_info *hfi_mem,
+ void *__iomem icp_base, bool debug);
+
+/**
+ * hfi_get_hw_caps() - hardware capabilities from firmware
+ * @query_caps: holds query information from hfi
+ *
+ * Returns success(zero)/failure(non zero)
+ */
+int hfi_get_hw_caps(void *query_caps);
+
+/**
+ * hfi_send_system_cmd() - send hfi system command to firmware
+ * @type: type of system command
+ * @data: command data
+ * @size: size of command data
+ */
+void hfi_send_system_cmd(uint32_t type, uint64_t data, uint32_t size);
+
+/**
+ * cam_hfi_enable_cpu() - enable A5 CPU
+ * @icp_base: icp base address
+ */
+void cam_hfi_enable_cpu(void __iomem *icp_base);
+/**
+ * cam_hfi_deinit() - cleanup HFI
+ */
+void cam_hfi_deinit(void);
+
+#endif /* _HFI_INTF_H_ */
diff --git a/drivers/media/platform/msm/camera/icp/fw_inc/hfi_reg.h b/drivers/media/platform/msm/camera/icp/fw_inc/hfi_reg.h
new file mode 100644
index 0000000..d1bbe01
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/fw_inc/hfi_reg.h
@@ -0,0 +1,308 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_HFI_REG_H_
+#define _CAM_HFI_REG_H_
+
+#include <linux/types.h>
+#include "hfi_intf.h"
+
+
+/* start of ICP CSR registers */
+#define HFI_REG_A5_HW_VERSION 0x0
+#define HFI_REG_A5_CSR_NSEC_RESET 0x4
+#define HFI_REG_A5_CSR_A5_CONTROL 0x8
+#define HFI_REG_A5_CSR_ETM 0xC
+#define HFI_REG_A5_CSR_A2HOSTINTEN 0x10
+#define HFI_REG_A5_CSR_A2HOSTINT 0x14
+#define HFI_REG_A5_CSR_A2HOSTINTCLR 0x18
+#define HFI_REG_A5_CSR_A2HOSTINTSTATUS 0x1C
+#define HFI_REG_A5_CSR_A2HOSTINTSET 0x20
+#define HFI_REG_A5_CSR_HOST2ICPINT 0x30
+#define HFI_REG_A5_CSR_A5_STATUS 0x200
+#define HFI_REG_A5_QGIC2_LM_ID 0x204
+#define HFI_REG_A5_SPARE 0x400
+
+/* general purpose registers from */
+#define HFI_REG_FW_VERSION 0x44
+#define HFI_REG_HOST_ICP_INIT_REQUEST 0x48
+#define HFI_REG_ICP_HOST_INIT_RESPONSE 0x4C
+#define HFI_REG_SHARED_MEM_PTR 0x50
+#define HFI_REG_SHARED_MEM_SIZE 0x54
+#define HFI_REG_QTBL_PTR 0x58
+#define HFI_REG_UNCACHED_HEAP_PTR 0x5C
+#define HFI_REG_UNCACHED_HEAP_SIZE 0x60
+/* end of ICP CSR registers */
+
+/* flags for ICP CSR registers */
+#define ICP_FLAG_CSR_WAKE_UP_EN (1 << 4)
+#define ICP_FLAG_CSR_A5_EN (1 << 9)
+#define ICP_CSR_EN_CLKGATE_WFI (1 << 12)
+#define ICP_CSR_EDBGRQ (1 << 14)
+#define ICP_CSR_DBGSWENABLE (1 << 22)
+
+/* start of Queue table and queues */
+#define MAX_ICP_HFI_QUEUES 4
+#define ICP_QHDR_TX_TYPE_MASK 0xFF000000
+#define ICP_QHDR_RX_TYPE_MASK 0x00FF0000
+#define ICP_QHDR_PRI_TYPE_MASK 0x0000FF00
+#define ICP_QHDR_Q_ID_MASK 0x000000FF
+
+#define ICP_CMD_Q_SIZE_IN_BYTES 4096
+#define ICP_MSG_Q_SIZE_IN_BYTES 4096
+#define ICP_DBG_Q_SIZE_IN_BYTES 8192
+
+#define ICP_SHARED_MEM_IN_BYTES (1024 * 1024)
+#define ICP_UNCACHED_HEAP_SIZE_IN_BYTES (2 * 1024 * 1024)
+#define ICP_HFI_MAX_MSG_SIZE_IN_WORDS 128
+
+#define ICP_HFI_QTBL_HOSTID1 0x01000000
+#define ICP_HFI_QTBL_STATUS_ENABLED 0x00000001
+#define ICP_HFI_NUMBER_OF_QS 3
+#define ICP_HFI_NUMBER_OF_ACTIVE_QS 3
+#define ICP_HFI_QTBL_OFFSET 0
+#define ICP_HFI_VAR_SIZE_PKT 0
+#define ICP_HFI_MAX_MSG_SIZE_IN_WORDS 128
+
+
+/* Queue Header type masks. Use these to access bitfields in qhdr_type */
+#define HFI_MASK_QHDR_TX_TYPE 0xFF000000
+#define HFI_MASK_QHDR_RX_TYPE 0x00FF0000
+#define HFI_MASK_QHDR_PRI_TYPE 0x0000FF00
+#define HFI_MASK_QHDR_Q_ID_TYPE 0x000000FF
+
+
+#define TX_EVENT_DRIVEN_MODE_1 0
+#define RX_EVENT_DRIVEN_MODE_1 0
+#define TX_EVENT_DRIVEN_MODE_2 0x01000000
+#define RX_EVENT_DRIVEN_MODE_2 0x00010000
+#define TX_EVENT_POLL_MODE_2 0x02000000
+#define RX_EVENT_POLL_MODE_2 0x00020000
+#define U32_OFFSET 0x1
+#define BYTE_WORD_SHIFT 2
+
+/**
+ * @INVALID: Invalid state
+ * @FW_LOAD_DONE: Firmware load is completed
+ * @FW_RESP_DONE: Firmware response is received
+ * @FW_START_SENT: firmware start is send
+ * @FW_READY: firmware is ready to accept commands
+ */
+enum hfi_state {
+ INVALID,
+ FW_LOAD_DONE,
+ FW_RESP_DONE,
+ FW_START_SENT,
+ FW_READY
+};
+
+/**
+ * @RESET: init success
+ * @SET: init failed
+ */
+enum reg_settings {
+ RESET,
+ SET
+};
+
+/**
+ * @INTR_DISABLE: Disable interrupt
+ * @INTR_ENABLE: Enable interrupt
+ */
+enum intr_status {
+ INTR_DISABLE,
+ INTR_ENABLE
+};
+
+/**
+ * @ICP_INIT_RESP_RESET: reset init state
+ * @ICP_INIT_RESP_SUCCESS: init success
+ * @ICP_INIT_RESP_FAILED: init failed
+ */
+enum host_init_resp {
+ ICP_INIT_RESP_RESET,
+ ICP_INIT_RESP_SUCCESS,
+ ICP_INIT_RESP_FAILED
+};
+
+/**
+ * @ICP_INIT_REQUEST_RESET: reset init request
+ * @ICP_INIT_REQUEST_SET: set init request
+ */
+enum host_init_request {
+ ICP_INIT_REQUEST_RESET,
+ ICP_INIT_REQUEST_SET
+};
+
+/**
+ * @QHDR_INACTIVE: Queue is inactive
+ * @QHDR_ACTIVE: Queue is active
+ */
+enum qhdr_status {
+ QHDR_INACTIVE,
+ QHDR_ACTIVE
+};
+
+/**
+ * @INTR_MODE: event driven mode 1, each send and receive generates interrupt
+ * @WM_MODE: event driven mode 2, interrupts based on watermark mechanism
+ * @POLL_MODE: poll method
+ */
+enum qhdr_event_drv_type {
+ INTR_MODE,
+ WM_MODE,
+ POLL_MODE
+};
+
+/**
+ * @TX_INT: event driven mode 1, each send and receive generates interrupt
+ * @TX_INT_WM: event driven mode 2, interrupts based on watermark mechanism
+ * @TX_POLL: poll method
+ * @ICP_QHDR_TX_TYPE_MASK defines position in qhdr_type
+ */
+enum qhdr_tx_type {
+ TX_INT,
+ TX_INT_WM,
+ TX_POLL
+};
+
+/**
+ * @RX_INT: event driven mode 1, each send and receive generates interrupt
+ * @RX_INT_WM: event driven mode 2, interrupts based on watermark mechanism
+ * @RX_POLL: poll method
+ * @ICP_QHDR_RX_TYPE_MASK defines position in qhdr_type
+ */
+enum qhdr_rx_type {
+ RX_INT,
+ RX_INT_WM,
+ RX_POLL
+};
+
+/**
+ * @Q_CMD: Host to FW command queue
+ * @Q_MSG: FW to Host message queue
+ * @Q_DEBUG: FW to Host debug queue
+ * @ICP_QHDR_Q_ID_MASK defines position in qhdr_type
+ */
+enum qhdr_q_id {
+ Q_CMD,
+ Q_MSG,
+ Q_DBG
+};
+
+/**
+ * struct hfi_qtbl_hdr
+ * @qtbl_version: Queue table version number
+ * Higher 16 bits: Major version
+ * Lower 16 bits: Minor version
+ * @qtbl_size: Queue table size from version to last parametr in qhdr entry
+ * @qtbl_qhdr0_offset: Offset to the start of first qhdr
+ * @qtbl_qhdr_size: Queue header size in bytes
+ * @qtbl_num_q: Total number of queues in Queue table
+ * @qtbl_num_active_q: Total number of active queues
+ */
+struct hfi_qtbl_hdr {
+ uint32_t qtbl_version;
+ uint32_t qtbl_size;
+ uint32_t qtbl_qhdr0_offset;
+ uint32_t qtbl_qhdr_size;
+ uint32_t qtbl_num_q;
+ uint32_t qtbl_num_active_q;
+} __packed;
+
+/**
+ * struct hfi_q_hdr
+ * @qhdr_status: Queue status, qhdr_state define possible status
+ * @qhdr_start_addr: Queue start address in non cached memory
+ * @qhdr_type: qhdr_tx, qhdr_rx, qhdr_q_id and priority defines qhdr type
+ * @qhdr_q_size: Queue size
+ * Number of queue packets if qhdr_pkt_size is non-zero
+ * Queue size in bytes if qhdr_pkt_size is zero
+ * @qhdr_pkt_size: Size of queue packet entries
+ * 0x0: variable queue packet size
+ * non zero: size of queue packet entry, fixed
+ * @qhdr_pkt_drop_cnt: Number of packets dropped by sender
+ * @qhdr_rx_wm: Receiver watermark, applicable in event driven mode
+ * @qhdr_tx_wm: Sender watermark, applicable in event driven mode
+ * @qhdr_rx_req: Receiver sets this bit if queue is empty
+ * @qhdr_tx_req: Sender sets this bit if queue is full
+ * @qhdr_rx_irq_status: Receiver sets this bit and triggers an interrupt to
+ * the sender after packets are dequeued. Sender clears this bit
+ * @qhdr_tx_irq_status: Sender sets this bit and triggers an interrupt to
+ * the receiver after packets are queued. Receiver clears this bit
+ * @qhdr_read_idx: Read index
+ * @qhdr_write_idx: Write index
+ */
+struct hfi_q_hdr {
+ uint32_t dummy[15];
+ uint32_t qhdr_status;
+ uint32_t dummy1[15];
+ uint32_t qhdr_start_addr;
+ uint32_t dummy2[15];
+ uint32_t qhdr_type;
+ uint32_t dummy3[15];
+ uint32_t qhdr_q_size;
+ uint32_t dummy4[15];
+ uint32_t qhdr_pkt_size;
+ uint32_t dummy5[15];
+ uint32_t qhdr_pkt_drop_cnt;
+ uint32_t dummy6[15];
+ uint32_t qhdr_rx_wm;
+ uint32_t dummy7[15];
+ uint32_t qhdr_tx_wm;
+ uint32_t dummy8[15];
+ uint32_t qhdr_rx_req;
+ uint32_t dummy9[15];
+ uint32_t qhdr_tx_req;
+ uint32_t dummy10[15];
+ uint32_t qhdr_rx_irq_status;
+ uint32_t dummy11[15];
+ uint32_t qhdr_tx_irq_status;
+ uint32_t dummy12[15];
+ uint32_t qhdr_read_idx;
+ uint32_t dummy13[15];
+ uint32_t qhdr_write_idx;
+ uint32_t dummy14[15];
+};
+
+/**
+ * struct hfi_q_tbl
+ * @q_tbl_hdr: Queue table header
+ * @q_hdr: Queue header info, it holds info of cmd, msg and debug queues
+ */
+struct hfi_qtbl {
+ struct hfi_qtbl_hdr q_tbl_hdr;
+ struct hfi_q_hdr q_hdr[MAX_ICP_HFI_QUEUES];
+};
+
+/**
+ * struct hfi_info
+ * @map: Hfi shared memory info
+ * @smem_size: Shared memory size
+ * @uncachedheap_size: uncached heap size
+ * @msgpacket_buf: message buffer
+ * @hfi_state: State machine for hfi
+ * @cmd_q_lock: Lock for command queue
+ * @csr_base: CSR base address
+ */
+struct hfi_info {
+ struct hfi_mem_info map;
+ uint32_t smem_size;
+ uint32_t uncachedheap_size;
+ uint32_t msgpacket_buf[ICP_HFI_MAX_MSG_SIZE_IN_WORDS];
+ uint8_t hfi_state;
+ struct mutex cmd_q_lock;
+ struct mutex msg_q_lock;
+ void __iomem *csr_base;
+};
+
+#endif /* _CAM_HFI_REG_H_ */
diff --git a/drivers/media/platform/msm/camera/icp/fw_inc/hfi_session_defs.h b/drivers/media/platform/msm/camera/icp/fw_inc/hfi_session_defs.h
new file mode 100644
index 0000000..837efec
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/fw_inc/hfi_session_defs.h
@@ -0,0 +1,428 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_HFI_SESSION_DEFS_H
+#define _CAM_HFI_SESSION_DEFS_H
+
+#include <linux/types.h>
+
+#define HFI_IPEBPS_CMD_OPCODE_BPS_CONFIG_IO 0x1
+#define HFI_IPEBPS_CMD_OPCODE_BPS_FRAME_PROCESS 0x2
+#define HFI_IPEBPS_CMD_OPCODE_BPS_ABORT 0x3
+#define HFI_IPEBPS_CMD_OPCODE_BPS_DESTROY 0x4
+
+#define HFI_IPEBPS_CMD_OPCODE_IPE_CONFIG_IO 0x5
+#define HFI_IPEBPS_CMD_OPCODE_IPE_FRAME_PROCESS 0x6
+#define HFI_IPEBPS_CMD_OPCODE_IPE_ABORT 0x7
+#define HFI_IPEBPS_CMD_OPCODE_IPE_DESTROY 0x8
+
+#define HFI_IPEBPS_CMD_OPCODE_BPS_WAIT_FOR_IPE 0x9
+#define HFI_IPEBPS_CMD_OPCODE_BPS_WAIT_FOR_BPS 0xa
+#define HFI_IPEBPS_CMD_OPCODE_IPE_WAIT_FOR_BPS 0xb
+#define HFI_IPEBPS_CMD_OPCODE_IPE_WAIT_FOR_IPE 0xc
+
+#define HFI_IPEBPS_HANDLE_TYPE_BPS 0x1
+#define HFI_IPEBPS_HANDLE_TYPE_IPE_RT 0x2
+#define HFI_IPEBPS_HANDLE_TYPE_IPE_NON_RT 0x3
+
+/**
+ * struct hfi_cmd_abort_destroy
+ * @user_data: user supplied data
+ *
+ * IPE/BPS destroy/abort command
+ * @HFI_IPEBPS_CMD_OPCODE_IPE_ABORT
+ * @HFI_IPEBPS_CMD_OPCODE_BPS_ABORT
+ * @HFI_IPEBPS_CMD_OPCODE_IPE_DESTROY
+ * @HFI_IPEBPS_CMD_OPCODE_BPS_DESTROY
+ */
+struct hfi_cmd_abort_destroy {
+ uint64_t user_data;
+} __packed;
+
+/**
+ * struct hfi_cmd_chaining_ops
+ * @wait_hdl: current session handle waits on wait_hdl to complete operation
+ * @user_data: user supplied argument
+ *
+ * this structure for chaining opcodes
+ * BPS_WAITS_FOR_IPE
+ * BPS_WAITS_FOR_BPS
+ * IPE_WAITS_FOR_BPS
+ * IPE_WAITS_FOR_IPE
+ */
+struct hfi_cmd_chaining_ops {
+ uint32_t wait_hdl;
+ uint64_t user_data;
+} __packed;
+
+/**
+ * struct hfi_cmd_create_handle
+ * @size: packet size in bytes
+ * @pkt_type: opcode of a packet
+ * @handle_type: IPE/BPS firmware session handle type
+ * @user_data1: caller provided data1
+ * @user_data2: caller provided data2
+ *
+ * create firmware session handle
+ */
+struct hfi_cmd_create_handle {
+ uint32_t size;
+ uint32_t pkt_type;
+ uint32_t handle_type;
+ uint64_t user_data1;
+ uint64_t user_data2;
+} __packed;
+
+/**
+ * struct hfi_cmd_ipebps_async
+ * @size: packet size in bytes
+ * @pkt_type: opcode of a packet
+ * @opcode: opcode for IPE/BPS async operation
+ * CONFIG_IO: configures I/O for IPE/BPS handle
+ * FRAME_PROCESS: image frame to be processed by IPE/BPS
+ * ABORT: abort all processing frames of IPE/BPS handle
+ * DESTROY: destroy earlier created IPE/BPS handle
+ * BPS_WAITS_FOR_IPE: sync for BPS to wait for IPE
+ * BPS_WAITS_FOR_BPS: sync for BPS to wait for BPS
+ * IPE_WAITS_FOR_IPE: sync for IPE to wait for IPE
+ * IPE_WAITS_FOR_BPS: sync for IPE to wait for BPS
+ * @num_fw_handles: number of IPE/BPS firmware handles in fw_handles array
+ * @fw_handles: IPE/BPS handles array
+ * @payload: command payload for IPE/BPS opcodes
+ * @direct: points to actual payload
+ * @indirect: points to address of payload
+ *
+ * sends async command to the earlier created IPE or BPS handle
+ * for asynchronous operation.
+ */
+struct hfi_cmd_ipebps_async {
+ uint32_t size;
+ uint32_t pkt_type;
+ uint32_t opcode;
+ uint64_t user_data1;
+ uint64_t user_data2;
+ uint32_t num_fw_handles;
+ uint32_t fw_handles[1];
+ union {
+ uint32_t direct[1];
+ uint32_t indirect;
+ } payload;
+} __packed;
+
+/**
+ * struct hfi_msg_create_handle_ack
+ * @size: packet size in bytes
+ * @pkt_type: opcode of a packet
+ * @err_type: error code
+ * @fw_handle: output param for IPE/BPS handle
+ * @user_data1: user provided data1
+ * @user_data2: user provided data2
+ *
+ * ack for create handle command of IPE/BPS
+ * @HFI_MSG_IPEBPS_CREATE_HANDLE_ACK
+ */
+struct hfi_msg_create_handle_ack {
+ uint32_t size;
+ uint32_t pkt_type;
+ uint32_t err_type;
+ uint32_t fw_handle;
+ uint64_t user_data1;
+ uint64_t user_data2;
+} __packed;
+
+/**
+ * struct hfi_msg_ipebps_async
+ * @size: packet size in bytes
+ * @pkt_type: opcode of a packet
+ * @opcode: opcode of IPE/BPS async operation
+ * @user_data1: user provided data1
+ * @user_data2: user provided data2
+ * @err_type: error code
+ * @msg_data: IPE/BPS async done message data
+ *
+ * result of IPE/BPS async command
+ * @HFI_MSG_IPEBPS_ASYNC_COMMAND_ACK
+ */
+struct hfi_msg_ipebps_async_ack {
+ uint32_t size;
+ uint32_t pkt_type;
+ uint32_t opcode;
+ uint64_t user_data1;
+ uint64_t user_data2;
+ uint32_t err_type;
+ uint32_t msg_data[1];
+} __packed;
+
+/**
+ * struct hfi_msg_frame_process_done
+ * @result: result of frame process command
+ * @scratch_buffer_address: address of scratch buffer
+ */
+struct hfi_msg_frame_process_done {
+ uint32_t result;
+ uint32_t scratch_buffer_address;
+};
+
+/**
+ * struct hfi_msg_chaining_op
+ * @status: return status
+ * @user_data: user data provided as part of chaining ops
+ *
+ * IPE/BPS wait response
+ */
+struct hfi_msg_chaining_op {
+ uint32_t status;
+ uint64_t user_data;
+} __packed;
+
+/**
+ * struct hfi_msg_abort_destroy
+ * @status: return status
+ * @user_data: user data provided as part of abort/destroy ops
+ *
+ * IPE/BPS abort/destroy response
+ */
+struct hfi_msg_abort_destroy {
+ uint32_t status;
+ uint64_t user_data;
+} __packed;
+
+#define MAX_NUM_OF_IMAGE_PLANES 2
+
+enum hfi_ipe_io_images {
+ IPE_INPUT_IMAGE_FULL,
+ IPE_INPUT_IMAGE_DS4,
+ IPE_INPUT_IMAGE_DS16,
+ IPE_INPUT_IMAGE_DS64,
+ IPE_INPUT_IMAGE_FULL_REF,
+ IPE_INPUT_IMAGE_DS4_REF,
+ IPE_INPUT_IMAGE_DS16_REF,
+ IPE_INPUT_IMAGE_DS64_REF,
+ IPE_OUTPUT_IMAGE_DISPLAY,
+ IPE_OUTPUT_IMAGE_VIDEO,
+ IPE_OUTPUT_IMAGE_FULL_REF,
+ IPE_OUTPUT_IMAGE_DS4_REF,
+ IPE_OUTPUT_IMAGE_DS16_REF,
+ IPE_OUTPUT_IMAGE_DS64_REF,
+ IPE_INPUT_IMAGE_FIRST = IPE_INPUT_IMAGE_FULL,
+ IPE_INPUT_IMAGE_LAST = IPE_INPUT_IMAGE_DS64_REF,
+ IPE_OUTPUT_IMAGE_FIRST = IPE_OUTPUT_IMAGE_DISPLAY,
+ IPE_OUTPUT_IMAGE_LAST = IPE_OUTPUT_IMAGE_DS64_REF,
+ IPE_IO_IMAGES_MAX
+};
+
+enum hfi_ipe_image_format {
+ IMAGE_FORMAT_INVALID,
+ IMAGE_FORMAT_MIPI_8,
+ IMAGE_FORMAT_MIPI_10,
+ IMAGE_FORMAT_MIPI_12,
+ IMAGE_FORMAT_MIPI_14,
+ IMAGE_FORMAT_BAYER_8,
+ IMAGE_FORMAT_BAYER_10,
+ IMAGE_FORMAT_BAYER_12,
+ IMAGE_FORMAT_BAYER_14,
+ IMAGE_FORMAT_PDI_10,
+ IMAGE_FORMAT_PD_10,
+ IMAGE_FORMAT_PD_8,
+ IMAGE_FORMAT_INDICATIONS,
+ IMAGE_FORMAT_REFINEMENT,
+ IMAGE_FORMAT_UBWC_TP_10,
+ IMAGE_FORMAT_UBWC_NV_12,
+ IMAGE_FORMAT_UBWC_NV12_4R,
+ IMAGE_FORMAT_UBWC_P010,
+ IMAGE_FORMAT_LINEAR_TP_10,
+ IMAGE_FORMAT_LINEAR_P010,
+ IMAGE_FORMAT_LINEAR_NV12,
+ IMAGE_FORMAT_LINEAR_PLAIN_16,
+ IMAGE_FORMAT_YUV422_8,
+ IMAGE_FORMAT_YUV422_10,
+ IMAGE_FORMAT_STATISTICS_BAYER_GRID,
+ IMAGE_FORMAT_STATISTICS_BAYER_HISTOGRAM,
+ IMAGE_FORMAT_MAX
+};
+
+enum hfi_ipe_plane_format {
+ PLANE_FORMAT_INVALID = 0,
+ PLANE_FORMAT_MIPI_8,
+ PLANE_FORMAT_MIPI_10,
+ PLANE_FORMAT_MIPI_12,
+ PLANE_FORMAT_MIPI_14,
+ PLANE_FORMAT_BAYER_8,
+ PLANE_FORMAT_BAYER_10,
+ PLANE_FORMAT_BAYER_12,
+ PLANE_FORMAT_BAYER_14,
+ PLANE_FORMAT_PDI_10,
+ PLANE_FORMAT_PD_10,
+ PLANE_FORMAT_PD_8,
+ PLANE_FORMAT_INDICATIONS,
+ PLANE_FORMAT_REFINEMENT,
+ PLANE_FORMAT_UBWC_TP_10_Y,
+ PLANE_FORMAT_UBWC_TP_10_C,
+ PLANE_FORMAT_UBWC_NV_12_Y,
+ PLANE_FORMAT_UBWC_NV_12_C,
+ PLANE_FORMAT_UBWC_NV_12_4R_Y,
+ PLANE_FORMAT_UBWC_NV_12_4R_C,
+ PLANE_FORMAT_UBWC_P010_Y,
+ PLANE_FORMAT_UBWC_P010_C,
+ PLANE_FORMAT_LINEAR_TP_10_Y,
+ PLANE_FORMAT_LINEAR_TP_10_C,
+ PLANE_FORMAT_LINEAR_P010_Y,
+ PLANE_FORMAT_LINEAR_P010_C,
+ PLANE_FORMAT_LINEAR_NV12_Y,
+ PLANE_FORMAT_LINEAR_NV12_C,
+ PLANE_FORMAT_LINEAR_PLAIN_16_Y,
+ PLANE_FORMAT_LINEAR_PLAIN_16_C,
+ PLANE_FORMAT_YUV422_8,
+ PLANE_FORMAT_YUV422_10,
+ PLANE_FORMAT_STATISTICS_BAYER_GRID,
+ PLANE_FORMAT_STATISTICS_BAYER_HISTOGRAM,
+ PLANE_FORMAT_MAX
+};
+
+enum hfi_ipe_bayer_pixel_order {
+ FIRST_PIXEL_R,
+ FIRST_PIXEL_GR,
+ FIRST_PIXEL_B,
+ FIRST_PIXEL_GB,
+ FIRST_PIXEL_MAX
+};
+
+enum hfi_ipe_pixel_pack_alignment {
+ PIXEL_LSB_ALIGNED,
+ PIXEL_MSB_ALIGNED,
+};
+
+enum hfi_ipe_yuv_422_order {
+ PIXEL_ORDER_Y_U_Y_V,
+ PIXEL_ORDER_Y_V_Y_U,
+ PIXEL_ORDER_U_Y_V_Y,
+ PIXEL_ORDER_V_Y_U_Y,
+ PIXEL_ORDER_YUV422_MAX
+};
+
+enum ubwc_write_client {
+ IPE_WR_CLIENT_0 = 0,
+ IPE_WR_CLIENT_1,
+ IPE_WR_CLIENT_5,
+ IPE_WR_CLIENT_6,
+ IPE_WR_CLIENT_7,
+ IPE_WR_CLIENT_8,
+ IPE_WR_CLIENT_MAX
+};
+
+/**
+ * struct image_info
+ * @format: image format
+ * @img_width: image width
+ * @img_height: image height
+ * @bayer_order: pixel order
+ * @pix_align: alignment
+ * @yuv422_order: YUV order
+ * @byte_swap: byte swap
+ */
+struct image_info {
+ enum hfi_ipe_image_format format;
+ uint32_t img_width;
+ uint32_t img_height;
+ enum hfi_ipe_bayer_pixel_order bayer_order;
+ enum hfi_ipe_pixel_pack_alignment pix_align;
+ enum hfi_ipe_yuv_422_order yuv422_order;
+ uint32_t byte_swap;
+} __packed;
+
+/**
+ * struct buffer_layout
+ * @buf_stride: buffer stride
+ * @buf_height: buffer height
+ */
+struct buffer_layout {
+ uint32_t buf_stride;
+ uint32_t buf_height;
+} __packed;
+
+/**
+ * struct image_desc
+ * @info: image info
+ * @buf_layout: buffer layout
+ * @meta_buf_layout: meta buffer layout
+ */
+struct image_desc {
+ struct image_info info;
+ struct buffer_layout buf_layout[MAX_NUM_OF_IMAGE_PLANES];
+ struct buffer_layout meta_buf_layout[MAX_NUM_OF_IMAGE_PLANES];
+} __packed;
+
+/**
+ * struct hfi_cmd_ipe_config
+ * @images: images descreptions
+ * @user_data: user supplied data
+ *
+ * payload for IPE async command
+ */
+struct hfi_cmd_ipe_config {
+ struct image_desc images[IPE_IO_IMAGES_MAX];
+ uint64_t user_data;
+} __packed;
+
+/**
+ * struct frame_buffers
+ * @buf_ptr: buffer pointers for all planes
+ * @meta_buf_ptr: meta buffer pointers for all planes
+ */
+struct frame_buffers {
+ uint32_t buf_ptr[MAX_NUM_OF_IMAGE_PLANES];
+ uint32_t meta_buf_ptr[MAX_NUM_OF_IMAGE_PLANES];
+} __packed;
+
+/**
+ * struct hfi_msg_ipe_config
+ * @rc: result of ipe config command
+ * @scratch_mem_size: scratch mem size for a config
+ * @user_data: user data
+ */
+struct hfi_msg_ipe_config {
+ uint32_t rc;
+ uint32_t scratch_mem_size;
+ uint64_t user_data;
+} __packed;
+
+/**
+ * struct hfi_msg_bps_common
+ * @rc: result of ipe config command
+ * @user_data: user data
+ */
+struct hfi_msg_bps_common {
+ uint32_t rc;
+ uint64_t user_data;
+} __packed;
+
+/**
+ * struct ipe_bps_destroy
+ * @user_data: user data
+ */
+struct ipe_bps_destroy {
+ uint64_t userdata;
+};
+
+/**
+ * struct hfi_msg_ipe_frame_process
+ * @status: result of ipe frame process command
+ * @scratch_buf_addr: address of scratch buffer
+ * @user_data: user data
+ */
+struct hfi_msg_ipe_frame_process {
+ uint32_t status;
+ uint32_t scratch_buf_addr;
+ uint64_t user_data;
+} __packed;
+
+#endif /* _CAM_HFI_SESSION_DEFS_H */
diff --git a/drivers/media/platform/msm/camera/icp/fw_inc/hfi_sys_defs.h b/drivers/media/platform/msm/camera/icp/fw_inc/hfi_sys_defs.h
new file mode 100644
index 0000000..e7163ac
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/fw_inc/hfi_sys_defs.h
@@ -0,0 +1,483 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _HFI_DEFS_H_
+#define _HFI_DEFS_H_
+
+#include <linux/types.h>
+
+/*
+ * Following base acts as common starting points
+ * for all enumerations.
+ */
+#define HFI_COMMON_BASE 0x0
+
+/* HFI Domain base offset for commands and messages */
+#define HFI_DOMAIN_SHFT (24)
+#define HFI_DOMAIN_BMSK (0x7 << HFI_DOMAIN_SHFT)
+#define HFI_DOMAIN_BASE_ICP (0x0 << HFI_DOMAIN_SHFT)
+#define HFI_DOMAIN_BASE_IPE_BPS (0x1 << HFI_DOMAIN_SHFT)
+#define HFI_DOMAIN_BASE_CDM (0x2 << HFI_DOMAIN_SHFT)
+#define HFI_DOMAIN_BASE_DBG (0x3 << HFI_DOMAIN_SHFT)
+
+/* Command base offset for commands */
+#define HFI_CMD_START_OFFSET 0x10000
+
+/* Command base offset for messages */
+#define HFI_MSG_START_OFFSET 0x20000
+
+/* System Level Error types */
+#define HFI_ERR_SYS_NONE (HFI_COMMON_BASE)
+#define HFI_ERR_SYS_FATAL (HFI_COMMON_BASE + 0x1)
+#define HFI_ERR_SYS_VERSION_MISMATCH (HFI_COMMON_BASE + 0x2)
+#define HFI_ERR_SYS_UNSUPPORTED_DOMAIN (HFI_COMMON_BASE + 0x3)
+#define HFI_ERR_SYS_UNSUPPORT_CMD (HFI_COMMON_BASE + 0x4)
+#define HFI_ERR_SYS_CMDFAILED (HFI_COMMON_BASE + 0x5)
+#define HFI_ERR_SYS_CMDSIZE (HFI_COMMON_BASE + 0x6)
+
+/* System Level Event types */
+#define HFI_EVENT_SYS_ERROR (HFI_COMMON_BASE + 0x1)
+#define HFI_EVENT_ICP_ERROR (HFI_COMMON_BASE + 0x2)
+#define HFI_EVENT_IPE_BPS_ERROR (HFI_COMMON_BASE + 0x3)
+#define HFI_EVENT_CDM_ERROR (HFI_COMMON_BASE + 0x4)
+#define HFI_EVENT_DBG_ERROR (HFI_COMMON_BASE + 0x5)
+
+/* Core level start Ranges for errors */
+#define HFI_ERR_ICP_START (HFI_COMMON_BASE + 0x64)
+#define HFI_ERR_IPE_BPS_START (HFI_ERR_ICP_START + 0x64)
+#define HFI_ERR_CDM_START (HFI_ERR_IPE_BPS_START + 0x64)
+#define HFI_ERR_DBG_START (HFI_ERR_CDM_START + 0x64)
+
+/*ICP Core level error messages */
+#define HFI_ERR_NO_RES (HFI_ERR_ICP_START + 0x1)
+#define HFI_ERR_UNSUPPORTED_RES (HFI_ERR_ICP_START + 0x2)
+#define HFI_ERR_UNSUPPORTED_PROP (HFI_ERR_ICP_START + 0x3)
+#define HFI_ERR_INIT_EXPECTED (HFI_ERR_ICP_START + 0x4)
+#define HFI_ERR_INIT_IGNORED (HFI_ERR_ICP_START + 0x5)
+
+/* System level commands */
+#define HFI_CMD_COMMON_START \
+ (HFI_DOMAIN_BASE_ICP + HFI_CMD_START_OFFSET + 0x0)
+#define HFI_CMD_SYS_INIT (HFI_CMD_COMMON_START + 0x1)
+#define HFI_CMD_SYS_PC_PREP (HFI_CMD_COMMON_START + 0x2)
+#define HFI_CMD_SYS_SET_PROPERTY (HFI_CMD_COMMON_START + 0x3)
+#define HFI_CMD_SYS_GET_PROPERTY (HFI_CMD_COMMON_START + 0x4)
+#define HFI_CMD_SYS_PING (HFI_CMD_COMMON_START + 0x5)
+#define HFI_CMD_SYS_RESET (HFI_CMD_COMMON_START + 0x6)
+
+/* Core level commands */
+/* IPE/BPS core Commands */
+#define HFI_CMD_IPE_BPS_COMMON_START \
+ (HFI_DOMAIN_BASE_IPE_BPS + HFI_CMD_START_OFFSET + 0x0)
+#define HFI_CMD_IPEBPS_CREATE_HANDLE \
+ (HFI_CMD_IPE_BPS_COMMON_START + 0x8)
+#define HFI_CMD_IPEBPS_ASYNC_COMMAND_DIRECT \
+ (HFI_CMD_IPE_BPS_COMMON_START + 0xa)
+#define HFI_CMD_IPEBPS_ASYNC_COMMAND_INDIRECT \
+ (HFI_CMD_IPE_BPS_COMMON_START + 0xe)
+
+/* CDM core Commands */
+#define HFI_CMD_CDM_COMMON_START \
+ (HFI_DOMAIN_BASE_CDM + HFI_CMD_START_OFFSET + 0x0)
+#define HFI_CMD_CDM_TEST_START (HFI_CMD_CDM_COMMON_START + 0x800)
+#define HFI_CMD_CDM_END (HFI_CMD_CDM_COMMON_START + 0xFFF)
+
+/* Debug/Test Commands */
+#define HFI_CMD_DBG_COMMON_START \
+ (HFI_DOMAIN_BASE_DBG + HFI_CMD_START_OFFSET + 0x0)
+#define HFI_CMD_DBG_TEST_START (HFI_CMD_DBG_COMMON_START + 0x800)
+#define HFI_CMD_DBG_END (HFI_CMD_DBG_COMMON_START + 0xFFF)
+
+/* System level messages */
+#define HFI_MSG_ICP_COMMON_START \
+ (HFI_DOMAIN_BASE_ICP + HFI_MSG_START_OFFSET + 0x0)
+#define HFI_MSG_SYS_INIT_DONE (HFI_MSG_ICP_COMMON_START + 0x1)
+#define HFI_MSG_SYS_PC_PREP_DONE (HFI_MSG_ICP_COMMON_START + 0x2)
+#define HFI_MSG_SYS_DEBUG (HFI_MSG_ICP_COMMON_START + 0x3)
+#define HFI_MSG_SYS_IDLE (HFI_MSG_ICP_COMMON_START + 0x4)
+#define HFI_MSG_SYS_PROPERTY_INFO (HFI_MSG_ICP_COMMON_START + 0x5)
+#define HFI_MSG_SYS_PING_ACK (HFI_MSG_ICP_COMMON_START + 0x6)
+#define HFI_MSG_SYS_RESET_ACK (HFI_MSG_ICP_COMMON_START + 0x7)
+#define HFI_MSG_EVENT_NOTIFY (HFI_MSG_ICP_COMMON_START + 0x8)
+
+/* Core level Messages */
+/* IPE/BPS core Messages */
+#define HFI_MSG_IPE_BPS_COMMON_START \
+ (HFI_DOMAIN_BASE_IPE_BPS + HFI_MSG_START_OFFSET + 0x0)
+#define HFI_MSG_IPEBPS_CREATE_HANDLE_ACK \
+ (HFI_MSG_IPE_BPS_COMMON_START + 0x08)
+#define HFI_MSG_IPEBPS_ASYNC_COMMAND_DIRECT_ACK \
+ (HFI_MSG_IPE_BPS_COMMON_START + 0x0a)
+#define HFI_MSG_IPEBPS_ASYNC_COMMAND_INDIRECT_ACK \
+ (HFI_MSG_IPE_BPS_COMMON_START + 0x0e)
+#define HFI_MSG_IPE_BPS_TEST_START \
+ (HFI_MSG_IPE_BPS_COMMON_START + 0x800)
+#define HFI_MSG_IPE_BPS_END \
+ (HFI_MSG_IPE_BPS_COMMON_START + 0xFFF)
+
+/* CDM core Messages */
+#define HFI_MSG_CDM_COMMON_START \
+ (HFI_DOMAIN_BASE_CDM + HFI_MSG_START_OFFSET + 0x0)
+#define HFI_MSG_PRI_CDM_PAYLOAD_ACK (HFI_MSG_CDM_COMMON_START + 0xa)
+#define HFI_MSG_PRI_LLD_PAYLOAD_ACK (HFI_MSG_CDM_COMMON_START + 0xb)
+#define HFI_MSG_CDM_TEST_START (HFI_MSG_CDM_COMMON_START + 0x800)
+#define HFI_MSG_CDM_END (HFI_MSG_CDM_COMMON_START + 0xFFF)
+
+/* core level test command ranges */
+/* ICP core level test command range */
+#define HFI_CMD_ICP_TEST_START (HFI_CMD_ICP_COMMON_START + 0x800)
+#define HFI_CMD_ICP_END (HFI_CMD_ICP_COMMON_START + 0xFFF)
+
+/* IPE/BPS core level test command range */
+#define HFI_CMD_IPE_BPS_TEST_START \
+ (HFI_CMD_IPE_BPS_COMMON_START + 0x800)
+#define HFI_CMD_IPE_BPS_END (HFI_CMD_IPE_BPS_COMMON_START + 0xFFF)
+
+/* ICP core level test message range */
+#define HFI_MSG_ICP_TEST_START (HFI_MSG_ICP_COMMON_START + 0x800)
+#define HFI_MSG_ICP_END (HFI_MSG_ICP_COMMON_START + 0xFFF)
+
+/* ICP core level Debug test message range */
+#define HFI_MSG_DBG_COMMON_START \
+ (HFI_DOMAIN_BASE_DBG + 0x0)
+#define HFI_MSG_DBG_TEST_START (HFI_MSG_DBG_COMMON_START + 0x800)
+#define HFI_MSG_DBG_END (HFI_MSG_DBG_COMMON_START + 0xFFF)
+
+/* System level property base offset */
+#define HFI_PROPERTY_ICP_COMMON_START (HFI_DOMAIN_BASE_ICP + 0x0)
+
+#define HFI_PROP_SYS_DEBUG_CFG (HFI_PROPERTY_ICP_COMMON_START + 0x1)
+#define HFI_PROP_SYS_IMAGE_VER (HFI_PROPERTY_ICP_COMMON_START + 0x3)
+#define HFI_PROP_SYS_SUPPORTED (HFI_PROPERTY_ICP_COMMON_START + 0x4)
+
+/* Capabilities reported at sys init */
+#define HFI_CAPS_PLACEHOLDER_1 (HFI_COMMON_BASE + 0x1)
+#define HFI_CAPS_PLACEHOLDER_2 (HFI_COMMON_BASE + 0x2)
+
+/* Section describes different debug levels (HFI_DEBUG_MSG_X)
+ * available for debug messages from FW
+ */
+#define HFI_DEBUG_MSG_LOW 0x00000001
+#define HFI_DEBUG_MSG_MEDIUM 0x00000002
+#define HFI_DEBUG_MSG_HIGH 0x00000004
+#define HFI_DEBUG_MSG_ERROR 0x00000008
+#define HFI_DEBUG_MSG_FATAL 0x00000010
+/* Messages containing performance data */
+#define HFI_DEBUG_MSG_PERF 0x00000020
+/* Disable ARM9 WFI in low power mode. */
+#define HFI_DEBUG_CFG_WFI 0x01000000
+/* Disable ARM9 watchdog. */
+#define HFI_DEBUG_CFG_ARM9WD 0x10000000
+
+/* Debug Msg Communication types:
+ * Section describes different modes (HFI_DEBUG_MODE_X)
+ * available to communicate the debug messages
+ */
+ /* Debug message output through the interface debug queue. */
+#define HFI_DEBUG_MODE_QUEUE 0x00000001
+ /* Debug message output through QDSS. */
+#define HFI_DEBUG_MODE_QDSS 0x00000002
+
+
+#define HFI_DEBUG_MSG_LOW 0x00000001
+#define HFI_DEBUG_MSG_MEDIUM 0x00000002
+#define HFI_DEBUG_MSG_HIGH 0x00000004
+#define HFI_DEBUG_MSG_ERROR 0x00000008
+#define HFI_DEBUG_MSG_FATAL 0x00000010
+#define HFI_DEBUG_MSG_PERF 0x00000020
+#define HFI_DEBUG_CFG_WFI 0x01000000
+#define HFI_DEBUG_CFG_ARM9WD 0x10000000
+
+#define HFI_DEBUG_MODE_QUEUE 0x00000001
+#define HFI_DEBUG_MODE_QDSS 0x00000002
+
+/**
+ * start of sys command packet types
+ * These commands are used to get system level information
+ * from firmware
+ */
+
+/**
+ * struct hfi_caps_support
+ * payload to report caps through HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED
+ * @type: capability type
+ * @min: minimum supported value for the capability
+ * @max: maximum supported value for the capability
+ * @step_size: supported steps between min-max
+ */
+struct hfi_caps_support {
+ uint32_t type;
+ uint32_t min;
+ uint32_t max;
+ uint32_t step_size;
+} __packed;
+
+/**
+ * struct hfi_caps_support_info
+ * capability report through HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED
+ * @num_caps: number of capabilities listed
+ * @caps_data: capabilities info array
+ */
+struct hfi_caps_support_info {
+ uint32_t num_caps;
+ struct hfi_caps_support caps_data[1];
+} __packed;
+
+/**
+ * struct hfi_debug
+ * payload structure to configure HFI_PROPERTY_SYS_DEBUG_CONFIG
+ * @debug_config: it is a result of HFI_DEBUG_MSG_X values that
+ * are OR-ed together to specify the debug message types
+ * to otput
+ * @debug_mode: debug message output through debug queue/qdss
+ * @HFI_PROPERTY_SYS_DEBUG_CONFIG
+ */
+struct hfi_debug {
+ uint32_t debug_config;
+ uint32_t debug_mode;
+} __packed;
+
+
+/**
+ * struct hfi_cmd_sys_init
+ * command to initialization of system session
+ * @size: packet size in bytes
+ * @pkt_type: opcode of a packet
+ * @HFI_CMD_SYS_INIT
+ */
+struct hfi_cmd_sys_init {
+ uint32_t size;
+ uint32_t pkt_type;
+} __packed;
+
+/**
+ * struct hfi_cmd_pc_prep
+ * command to firmware to prepare for power collapse
+ * @eize: packet size in bytes
+ * @pkt_type: opcode of a packet
+ * @HFI_CMD_SYS_PC_PREP
+ */
+struct hfi_cmd_pc_prep {
+ uint32_t size;
+ uint32_t pkt_type;
+} __packed;
+
+/**
+ * struct hfi_cmd_prop
+ * command to get/set properties of firmware
+ * @size: packet size in bytes
+ * @pkt_type: opcode of a packet
+ * @num_prop: number of properties queried/set
+ * @prop_data: array of property IDs being queried. size depends on num_prop
+ * array of property IDs and associated structure pairs in set
+ * @HFI_CMD_SYS_GET_PROPERTY
+ * @HFI_CMD_SYS_SET_PROPERTY
+ */
+struct hfi_cmd_prop {
+ uint32_t size;
+ uint32_t pkt_type;
+ uint32_t num_prop;
+ uint32_t prop_data[1];
+} __packed;
+
+/**
+ * struct hfi_cmd_ping_pkt
+ * ping command pings the firmware to confirm whether
+ * it is alive.
+ * @size: packet size in bytes
+ * @pkt_type: opcode of a packet
+ * @user_data: client data, firmware returns this data
+ * as part of HFI_MSG_SYS_PING_ACK
+ * @HFI_CMD_SYS_PING
+ */
+struct hfi_cmd_ping_pkt {
+ uint32_t size;
+ uint32_t pkt_type;
+ uint64_t user_data;
+} __packed;
+
+/**
+ * struct hfi_cmd_sys_reset_pkt
+ * sends the reset command to FW. FW responds in the same type
+ * of packet. so can be used for reset_ack_pkt type also
+ * @size: packet size in bytes
+ * @pkt_type: opcode of a packet
+ * @user_data: client data, firmware returns this data
+ * as part of HFI_MSG_SYS_RESET_ACK
+ * @HFI_CMD_SYS_RESET
+ */
+
+struct hfi_cmd_sys_reset_pkt {
+ uint32_t size;
+ uint32_t pkt_type;
+ uint64_t user_data;
+} __packed;
+
+/* end of sys command packet types */
+
+/* start of sys message packet types */
+
+/**
+ * struct hfi_prop
+ * structure to report maximum supported features of firmware.
+ */
+struct hfi_sys_support {
+ uint32_t place_holder;
+} __packed;
+
+/**
+ * struct hfi_supported_prop
+ * structure to report HFI_PROPERTY_PARAM_PROPERTIES_SUPPORTED
+ * for a session
+ * @num_prop: number of properties supported
+ * @prop_data: array of supported property IDs
+ */
+struct hfi_supported_prop {
+ uint32_t num_prop;
+ uint32_t prop_data[1];
+} __packed;
+
+/**
+ * struct hfi_image_version
+ * system image version
+ * @major: major version number
+ * @minor: minor version number
+ * @ver_name_size: size of version name
+ * @ver_name: image version name
+ */
+struct hfi_image_version {
+ uint32_t major;
+ uint32_t minor;
+ uint32_t ver_name_size;
+ uint8_t ver_name[1];
+} __packed;
+
+/**
+ * struct hfi_msg_init_done
+ * system init done message from firmware. Many system level properties
+ * are returned with the packet
+ * @size: packet size in bytes
+ * @pkt_type: opcode of a packet
+ * @err_type: error code associated with response
+ * @num_prop: number of default capability info
+ * @prop_data: array of property ids and corresponding structure pairs
+ */
+struct hfi_msg_init_done {
+ uint32_t size;
+ uint32_t pkt_type;
+ uint32_t err_type;
+ uint32_t num_prop;
+ uint32_t prop_data[1];
+} __packed;
+
+/**
+ * struct hfi_msg_pc_prep_done
+ * system power collapse preperation done message
+ * @size: packet size in bytes
+ * @pkt_type: opcode of a packet
+ * @err_type: error code associated with the response
+ */
+struct hfi_msg_pc_prep_done {
+ uint32_t size;
+ uint32_t pkt_type;
+ uint32_t err_type;
+} __packed;
+
+/**
+ * struct hfi_msg_prop
+ * system property info from firmware
+ * @size: packet size in bytes
+ * @pkt_type: opcode of a packet
+ * @num_prop: number of property info structures
+ * @prop_data: array of property IDs and associated structure pairs
+ */
+struct hfi_msg_prop {
+ uint32_t size;
+ uint32_t pkt_type;
+ uint32_t num_prop;
+ uint32_t prop_data[1];
+} __packed;
+
+/**
+ * struct hfi_msg_idle
+ * system idle message from firmware
+ * @size: packet size in bytes
+ * @pkt_type: opcode of a packet
+ */
+struct hfi_msg_idle {
+ uint32_t size;
+ uint32_t pkt_type;
+} __packed;
+
+/**
+ * struct hfi_msg_ping_ack
+ * system ping ack message
+ * @size: packet size in bytes
+ * @pkt_type: opcode of a packet
+ * @user_data: this data is sent as part of ping command from host
+ */
+struct hfi_msg_ping_ack {
+ uint32_t size;
+ uint32_t pkt_type;
+ uint64_t user_data;
+} __packed;
+
+/**
+ * struct hfi_msg_debug
+ * system debug message defination
+ * @size: packet size in bytes
+ * @pkt_type: opcode of a packet
+ * @msg_type: debug message type
+ * @msg_size: size of debug message in bytes
+ * @timestamp_hi: most significant 32 bits of the 64 bit timestamp field.
+ * timestamp shall be interpreted as a signed 64-bit value
+ * representing microseconds.
+ * @timestamp_lo: least significant 32 bits of the 64 bit timestamp field.
+ * timestamp shall be interpreted as a signed 64-bit value
+ * representing microseconds.
+ * @msg_data: message data in string form
+ */
+struct hfi_msg_debug {
+ uint32_t size;
+ uint32_t pkt_type;
+ uint32_t msg_type;
+ uint32_t msg_size;
+ uint32_t timestamp_hi;
+ uint32_t timestamp_lo;
+ uint8_t msg_data[1];
+} __packed;
+/**
+ * struct hfi_msg_event_notify
+ * event notify message
+ * @size: packet size in bytes
+ * @pkt_type: opcode of a packet
+ * @fw_handle: firmware session handle
+ * @event_id: session event id
+ * @event_data1: event data corresponding to event ID
+ * @event_data2: event data corresponding to event ID
+ * @ext_event_data: info array, interpreted based on event_data1
+ * and event_data2
+ */
+struct hfi_msg_event_notify {
+ uint32_t size;
+ uint32_t pkt_type;
+ uint32_t fw_handle;
+ uint32_t event_id;
+ uint32_t event_data1;
+ uint32_t event_data2;
+ uint32_t ext_event_data[1];
+} __packed;
+/**
+ * end of sys message packet types
+ */
+
+#endif /* _HFI_DEFS_H_ */
diff --git a/drivers/media/platform/msm/camera/icp/hfi.c b/drivers/media/platform/msm/camera/icp/hfi.c
new file mode 100644
index 0000000..4315865
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/hfi.c
@@ -0,0 +1,522 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "HFI-FW %s:%d " fmt, __func__, __LINE__
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <asm/errno.h>
+#include <linux/timer.h>
+#include <media/cam_icp.h>
+#include "cam_io_util.h"
+#include "hfi_reg.h"
+#include "hfi_sys_defs.h"
+#include "hfi_session_defs.h"
+#include "hfi_intf.h"
+#include "cam_icp_hw_mgr_intf.h"
+
+#define HFI_VERSION_INFO_MAJOR_VAL 1
+#define HFI_VERSION_INFO_MINOR_VAL 1
+#define HFI_VERSION_INFO_STEP_VAL 0
+#define HFI_VERSION_INFO_STEP_VAL 0
+#define HFI_VERSION_INFO_MAJOR_BMSK 0xFF000000
+#define HFI_VERSION_INFO_MAJOR_SHFT 24
+#define HFI_VERSION_INFO_MINOR_BMSK 0xFFFF00
+#define HFI_VERSION_INFO_MINOR_SHFT 8
+#define HFI_VERSION_INFO_STEP_BMSK 0xFF
+#define HFI_VERSION_INFO_STEP_SHFT 0
+
+#undef HFI_DBG
+#define HFI_DBG(fmt, args...) pr_debug(fmt, ##args)
+
+struct hfi_info *g_hfi;
+unsigned int g_icp_mmu_hdl;
+
+int hfi_write_cmd(void *cmd_ptr)
+{
+ uint32_t size_in_words, empty_space, new_write_idx, read_idx, temp;
+ uint32_t *write_q, *write_ptr;
+ struct hfi_qtbl *q_tbl;
+ struct hfi_q_hdr *q;
+ int rc = 0;
+ int i = 0;
+
+ if (!cmd_ptr) {
+ pr_err("Invalid args\n");
+ return -EINVAL;
+ }
+
+ if (!g_hfi || g_hfi->hfi_state < FW_START_SENT) {
+ pr_err("FW not ready yet\n");
+ return -EIO;
+ }
+
+ mutex_lock(&g_hfi->cmd_q_lock);
+
+ q_tbl = (struct hfi_qtbl *)g_hfi->map.qtbl.kva;
+ q = &q_tbl->q_hdr[Q_CMD];
+
+ write_q = (uint32_t *)g_hfi->map.cmd_q.kva;
+
+ size_in_words = (*(uint32_t *)cmd_ptr) >> BYTE_WORD_SHIFT;
+ if (!size_in_words) {
+ pr_debug("failed");
+ rc = -EINVAL;
+ goto err;
+ }
+
+ HFI_DBG("size_in_words : %u\n", size_in_words);
+ HFI_DBG("q->qhdr_write_idx %x\n", q->qhdr_write_idx);
+
+ read_idx = q->qhdr_read_idx;
+
+ empty_space = (q->qhdr_write_idx >= read_idx) ?
+ (q->qhdr_q_size - (q->qhdr_write_idx - read_idx)) :
+ (read_idx - q->qhdr_write_idx);
+ if (empty_space <= size_in_words) {
+ pr_err("failed");
+ rc = -EIO;
+ goto err;
+ }
+ HFI_DBG("empty_space : %u\n", empty_space);
+
+ new_write_idx = q->qhdr_write_idx + size_in_words;
+ write_ptr = (uint32_t *)(write_q + q->qhdr_write_idx);
+
+ if (new_write_idx < q->qhdr_q_size) {
+ memcpy(write_ptr, (uint8_t *)cmd_ptr,
+ size_in_words << BYTE_WORD_SHIFT);
+ } else {
+ new_write_idx -= q->qhdr_q_size;
+ temp = (size_in_words - new_write_idx) << BYTE_WORD_SHIFT;
+ memcpy(write_ptr, (uint8_t *)cmd_ptr, temp);
+ memcpy(write_q, (uint8_t *)cmd_ptr + temp,
+ new_write_idx << BYTE_WORD_SHIFT);
+ }
+ for (i = 0; i < size_in_words; i++)
+ pr_debug("%x\n", write_ptr[i]);
+
+ q->qhdr_write_idx = new_write_idx;
+ HFI_DBG("q->qhdr_write_idx %x\n", q->qhdr_write_idx);
+ cam_io_w((uint32_t)INTR_ENABLE,
+ g_hfi->csr_base + HFI_REG_A5_CSR_HOST2ICPINT);
+err:
+ mutex_unlock(&g_hfi->cmd_q_lock);
+ return 0;
+}
+
+int hfi_read_message(uint32_t *pmsg, uint8_t q_id)
+{
+ struct hfi_qtbl *q_tbl_ptr;
+ struct hfi_q_hdr *q;
+ uint32_t new_read_idx, size_in_words, temp;
+ uint32_t *read_q, *read_ptr;
+ int rc = 0;
+ int i = 0;
+
+ if (!pmsg || q_id > Q_DBG) {
+ pr_err("Inavlid args\n");
+ return -EINVAL;
+ }
+
+ q_tbl_ptr = (struct hfi_qtbl *)g_hfi->map.qtbl.kva;
+ q = &q_tbl_ptr->q_hdr[q_id];
+
+ if ((g_hfi->hfi_state < FW_START_SENT) ||
+ (q->qhdr_read_idx == q->qhdr_write_idx)) {
+ pr_debug("FW or Q not ready, hfi state : %u, r idx : %u, w idx : %u\n",
+ g_hfi->hfi_state, q->qhdr_read_idx, q->qhdr_write_idx);
+ return -EIO;
+ }
+
+ mutex_lock(&g_hfi->msg_q_lock);
+
+ if (q_id == Q_CMD)
+ read_q = (uint32_t *)g_hfi->map.cmd_q.kva;
+ else if (q_id == Q_MSG)
+ read_q = (uint32_t *)g_hfi->map.msg_q.kva;
+ else
+ read_q = (uint32_t *)g_hfi->map.dbg_q.kva;
+
+ read_ptr = (uint32_t *)(read_q + q->qhdr_read_idx);
+ size_in_words = (*read_ptr) >> BYTE_WORD_SHIFT;
+
+ HFI_DBG("size_in_words : %u\n", size_in_words);
+ HFI_DBG("read_ptr : %pK\n", (void *)read_ptr);
+
+ if ((size_in_words == 0) ||
+ (size_in_words > ICP_HFI_MAX_MSG_SIZE_IN_WORDS)) {
+ pr_err("invalid HFI message packet size - 0x%08x\n",
+ size_in_words << BYTE_WORD_SHIFT);
+ q->qhdr_read_idx = q->qhdr_write_idx;
+ rc = -EIO;
+ goto err;
+ }
+
+ new_read_idx = q->qhdr_read_idx + size_in_words;
+ HFI_DBG("new_read_idx : %u\n", new_read_idx);
+
+ if (new_read_idx < q->qhdr_q_size) {
+ memcpy(pmsg, read_ptr, size_in_words << BYTE_WORD_SHIFT);
+ } else {
+ new_read_idx -= q->qhdr_q_size;
+ temp = (size_in_words - new_read_idx) << BYTE_WORD_SHIFT;
+ memcpy(pmsg, read_ptr, temp);
+ memcpy((uint8_t *)pmsg + temp, read_q,
+ new_read_idx << BYTE_WORD_SHIFT);
+ }
+
+ for (i = 0; i < size_in_words; i++)
+ pr_debug("%x\n", read_ptr[i]);
+
+ q->qhdr_read_idx = new_read_idx;
+err:
+ mutex_unlock(&g_hfi->msg_q_lock);
+ HFI_DBG("Exit\n");
+ return 0;
+}
+
+void hfi_send_system_cmd(uint32_t type, uint64_t data, uint32_t size)
+{
+ switch (type) {
+ case HFI_CMD_SYS_INIT: {
+ struct hfi_cmd_sys_init init;
+
+ memset(&init, 0, sizeof(init));
+
+ init.size = sizeof(struct hfi_cmd_sys_init);
+ init.pkt_type = type;
+ hfi_write_cmd(&init);
+ }
+ break;
+ case HFI_CMD_SYS_PC_PREP: {
+ struct hfi_cmd_pc_prep prep;
+
+ prep.size = sizeof(struct hfi_cmd_pc_prep);
+ prep.pkt_type = type;
+ hfi_write_cmd(&prep);
+ }
+ break;
+ case HFI_CMD_SYS_SET_PROPERTY: {
+ struct hfi_cmd_prop prop;
+
+ if ((uint32_t)data == (uint32_t)HFI_PROP_SYS_DEBUG_CFG) {
+ prop.size = sizeof(struct hfi_cmd_prop);
+ prop.pkt_type = type;
+ prop.num_prop = 1;
+ prop.prop_data[0] = HFI_PROP_SYS_DEBUG_CFG;
+ hfi_write_cmd(&prop);
+ }
+ }
+ break;
+ case HFI_CMD_SYS_GET_PROPERTY:
+ break;
+ case HFI_CMD_SYS_PING: {
+ struct hfi_cmd_ping_pkt ping;
+
+ ping.size = sizeof(struct hfi_cmd_ping_pkt);
+ ping.pkt_type = type;
+ ping.user_data = (uint64_t)data;
+ hfi_write_cmd(&ping);
+ }
+ break;
+ case HFI_CMD_SYS_RESET: {
+ struct hfi_cmd_sys_reset_pkt reset;
+
+ reset.size = sizeof(struct hfi_cmd_sys_reset_pkt);
+ reset.pkt_type = type;
+ reset.user_data = (uint64_t)data;
+ hfi_write_cmd(&reset);
+ }
+ break;
+ case HFI_CMD_IPEBPS_CREATE_HANDLE: {
+ struct hfi_cmd_create_handle handle;
+
+ handle.size = sizeof(struct hfi_cmd_create_handle);
+ handle.pkt_type = type;
+ handle.handle_type = (uint32_t)data;
+ handle.user_data1 = 0;
+ hfi_write_cmd(&handle);
+ }
+ break;
+ case HFI_CMD_IPEBPS_ASYNC_COMMAND_INDIRECT:
+ break;
+ default:
+ pr_err("command not supported :%d\n", type);
+ break;
+ }
+}
+
+
+int hfi_get_hw_caps(void *query_buf)
+{
+ int i = 0;
+ struct cam_icp_query_cap_cmd *query_cmd = NULL;
+
+ if (!query_buf) {
+ pr_err("%s: query buf is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ query_cmd = (struct cam_icp_query_cap_cmd *)query_buf;
+ query_cmd->fw_version.major = 0x12;
+ query_cmd->fw_version.minor = 0x12;
+ query_cmd->fw_version.revision = 0x12;
+
+ query_cmd->api_version.major = 0x13;
+ query_cmd->api_version.minor = 0x13;
+ query_cmd->api_version.revision = 0x13;
+
+ query_cmd->num_ipe = 2;
+ query_cmd->num_bps = 1;
+
+ for (i = 0; i < CAM_ICP_DEV_TYPE_MAX; i++) {
+ query_cmd->dev_ver[i].dev_type = i;
+ query_cmd->dev_ver[i].hw_ver.major = 0x34 + i;
+ query_cmd->dev_ver[i].hw_ver.minor = 0x34 + i;
+ query_cmd->dev_ver[i].hw_ver.incr = 0x34 + i;
+ }
+ return 0;
+}
+
+
+void cam_hfi_enable_cpu(void __iomem *icp_base)
+{
+ cam_io_w((uint32_t)ICP_FLAG_CSR_A5_EN,
+ icp_base + HFI_REG_A5_CSR_A5_CONTROL);
+ cam_io_w((uint32_t)0x10, icp_base + HFI_REG_A5_CSR_NSEC_RESET);
+}
+
+int cam_hfi_init(uint8_t event_driven_mode, struct hfi_mem_info *hfi_mem,
+ void __iomem *icp_base, bool debug)
+{
+ int rc = 0;
+ struct hfi_qtbl *qtbl;
+ struct hfi_qtbl_hdr *qtbl_hdr;
+ struct hfi_q_hdr *cmd_q_hdr, *msg_q_hdr, *dbg_q_hdr;
+ uint32_t hw_version, fw_version;
+ uint32_t status;
+
+ if (!g_hfi) {
+ g_hfi = kzalloc(sizeof(struct hfi_info), GFP_KERNEL);
+ if (!g_hfi) {
+ rc = -ENOMEM;
+ goto alloc_fail;
+ }
+ }
+
+ pr_debug("g_hfi: %pK\n", (void *)g_hfi);
+ if (g_hfi->hfi_state != INVALID) {
+ pr_err("hfi_init: invalid state\n");
+ return -EINVAL;
+ }
+
+ g_hfi->hfi_state = FW_LOAD_DONE;
+ memcpy(&g_hfi->map, hfi_mem, sizeof(g_hfi->map));
+
+ if (debug) {
+ cam_io_w_mb(
+ (uint32_t)(ICP_FLAG_CSR_A5_EN | ICP_FLAG_CSR_WAKE_UP_EN |
+ ICP_CSR_EDBGRQ | ICP_CSR_DBGSWENABLE),
+ icp_base + HFI_REG_A5_CSR_A5_CONTROL);
+ msleep(100);
+ cam_io_w_mb((uint32_t)(ICP_FLAG_CSR_A5_EN |
+ ICP_FLAG_CSR_WAKE_UP_EN | ICP_CSR_EN_CLKGATE_WFI),
+ icp_base + HFI_REG_A5_CSR_A5_CONTROL);
+ } else {
+ cam_io_w((uint32_t)ICP_FLAG_CSR_A5_EN |
+ ICP_FLAG_CSR_WAKE_UP_EN,
+ icp_base + HFI_REG_A5_CSR_A5_CONTROL);
+ }
+
+ mutex_init(&g_hfi->cmd_q_lock);
+ mutex_init(&g_hfi->msg_q_lock);
+
+ g_hfi->csr_base = icp_base;
+
+ qtbl = (struct hfi_qtbl *)hfi_mem->qtbl.kva;
+ qtbl_hdr = &qtbl->q_tbl_hdr;
+ qtbl_hdr->qtbl_version = 0xFFFFFFFF;
+ qtbl_hdr->qtbl_size = sizeof(struct hfi_qtbl);
+ qtbl_hdr->qtbl_qhdr0_offset = sizeof(struct hfi_qtbl_hdr);
+ qtbl_hdr->qtbl_qhdr_size = sizeof(struct hfi_q_hdr);
+ qtbl_hdr->qtbl_num_q = ICP_HFI_NUMBER_OF_QS;
+ qtbl_hdr->qtbl_num_active_q = ICP_HFI_NUMBER_OF_QS;
+
+ /* setup host-to-firmware command queue */
+ pr_debug("updating the command queue info\n");
+ cmd_q_hdr = &qtbl->q_hdr[Q_CMD];
+ cmd_q_hdr->qhdr_status = QHDR_ACTIVE;
+ cmd_q_hdr->qhdr_start_addr = hfi_mem->cmd_q.iova;
+ cmd_q_hdr->qhdr_q_size = ICP_CMD_Q_SIZE_IN_BYTES >> BYTE_WORD_SHIFT;
+ cmd_q_hdr->qhdr_pkt_size = ICP_HFI_VAR_SIZE_PKT;
+ cmd_q_hdr->qhdr_pkt_drop_cnt = RESET;
+ cmd_q_hdr->qhdr_read_idx = RESET;
+ cmd_q_hdr->qhdr_write_idx = RESET;
+
+ /* setup firmware-to-Host message queue */
+ pr_debug("updating the message queue info\n");
+ msg_q_hdr = &qtbl->q_hdr[Q_MSG];
+ msg_q_hdr->qhdr_status = QHDR_ACTIVE;
+ msg_q_hdr->qhdr_start_addr = hfi_mem->msg_q.iova;
+ msg_q_hdr->qhdr_q_size = ICP_MSG_Q_SIZE_IN_BYTES >> BYTE_WORD_SHIFT;
+ msg_q_hdr->qhdr_pkt_size = ICP_HFI_VAR_SIZE_PKT;
+ msg_q_hdr->qhdr_pkt_drop_cnt = RESET;
+ msg_q_hdr->qhdr_read_idx = RESET;
+ msg_q_hdr->qhdr_write_idx = RESET;
+
+ /* setup firmware-to-Host message queue */
+ pr_debug("updating the debug queue info\n");
+ dbg_q_hdr = &qtbl->q_hdr[Q_DBG];
+ dbg_q_hdr->qhdr_status = QHDR_ACTIVE;
+ dbg_q_hdr->qhdr_start_addr = hfi_mem->dbg_q.iova;
+ dbg_q_hdr->qhdr_q_size = ICP_DBG_Q_SIZE_IN_BYTES >> BYTE_WORD_SHIFT;
+ dbg_q_hdr->qhdr_pkt_size = ICP_HFI_VAR_SIZE_PKT;
+ dbg_q_hdr->qhdr_pkt_drop_cnt = RESET;
+ dbg_q_hdr->qhdr_read_idx = RESET;
+ dbg_q_hdr->qhdr_write_idx = RESET;
+ pr_debug("Done updating the debug queue info\n");
+
+ switch (event_driven_mode) {
+ case INTR_MODE:
+ cmd_q_hdr->qhdr_type = Q_CMD;
+ cmd_q_hdr->qhdr_rx_wm = SET;
+ cmd_q_hdr->qhdr_tx_wm = SET;
+ cmd_q_hdr->qhdr_rx_req = SET;
+ cmd_q_hdr->qhdr_tx_req = RESET;
+ cmd_q_hdr->qhdr_rx_irq_status = RESET;
+ cmd_q_hdr->qhdr_tx_irq_status = RESET;
+
+ msg_q_hdr->qhdr_type = Q_MSG;
+ msg_q_hdr->qhdr_rx_wm = SET;
+ msg_q_hdr->qhdr_tx_wm = SET;
+ msg_q_hdr->qhdr_rx_req = SET;
+ msg_q_hdr->qhdr_tx_req = RESET;
+ msg_q_hdr->qhdr_rx_irq_status = RESET;
+ msg_q_hdr->qhdr_tx_irq_status = RESET;
+
+ dbg_q_hdr->qhdr_type = Q_DBG;
+ dbg_q_hdr->qhdr_rx_wm = SET;
+ dbg_q_hdr->qhdr_tx_wm = SET;
+ dbg_q_hdr->qhdr_rx_req = SET;
+ dbg_q_hdr->qhdr_tx_req = RESET;
+ dbg_q_hdr->qhdr_rx_irq_status = RESET;
+ dbg_q_hdr->qhdr_tx_irq_status = RESET;
+
+ break;
+
+ case POLL_MODE:
+ cmd_q_hdr->qhdr_type = Q_CMD | TX_EVENT_POLL_MODE_2 |
+ RX_EVENT_POLL_MODE_2;
+ msg_q_hdr->qhdr_type = Q_MSG | TX_EVENT_POLL_MODE_2 |
+ RX_EVENT_POLL_MODE_2;
+ dbg_q_hdr->qhdr_type = Q_DBG | TX_EVENT_POLL_MODE_2 |
+ RX_EVENT_POLL_MODE_2;
+ break;
+
+ case WM_MODE:
+ cmd_q_hdr->qhdr_type = Q_CMD | TX_EVENT_DRIVEN_MODE_2 |
+ RX_EVENT_DRIVEN_MODE_2;
+ cmd_q_hdr->qhdr_rx_wm = SET;
+ cmd_q_hdr->qhdr_tx_wm = SET;
+ cmd_q_hdr->qhdr_rx_req = RESET;
+ cmd_q_hdr->qhdr_tx_req = SET;
+ cmd_q_hdr->qhdr_rx_irq_status = RESET;
+ cmd_q_hdr->qhdr_tx_irq_status = RESET;
+
+ msg_q_hdr->qhdr_type = Q_MSG | TX_EVENT_DRIVEN_MODE_2 |
+ RX_EVENT_DRIVEN_MODE_2;
+ msg_q_hdr->qhdr_rx_wm = SET;
+ msg_q_hdr->qhdr_tx_wm = SET;
+ msg_q_hdr->qhdr_rx_req = SET;
+ msg_q_hdr->qhdr_tx_req = RESET;
+ msg_q_hdr->qhdr_rx_irq_status = RESET;
+ msg_q_hdr->qhdr_tx_irq_status = RESET;
+
+ dbg_q_hdr->qhdr_type = Q_DBG | TX_EVENT_DRIVEN_MODE_2 |
+ RX_EVENT_DRIVEN_MODE_2;
+ dbg_q_hdr->qhdr_rx_wm = SET;
+ dbg_q_hdr->qhdr_tx_wm = SET;
+ dbg_q_hdr->qhdr_rx_req = SET;
+ dbg_q_hdr->qhdr_tx_req = RESET;
+ dbg_q_hdr->qhdr_rx_irq_status = RESET;
+ dbg_q_hdr->qhdr_tx_irq_status = RESET;
+ break;
+
+ default:
+ pr_err("Invalid event driven mode :%u", event_driven_mode);
+ break;
+ }
+
+ cam_io_w((uint32_t)hfi_mem->qtbl.iova, icp_base + HFI_REG_QTBL_PTR);
+ cam_io_w((uint32_t)0x7400000, icp_base + HFI_REG_SHARED_MEM_PTR);
+ cam_io_w((uint32_t)0x6400000, icp_base + HFI_REG_SHARED_MEM_SIZE);
+ cam_io_w((uint32_t)hfi_mem->sec_heap.iova,
+ icp_base + HFI_REG_UNCACHED_HEAP_PTR);
+ cam_io_w((uint32_t)hfi_mem->sec_heap.len,
+ icp_base + HFI_REG_UNCACHED_HEAP_SIZE);
+ cam_io_w((uint32_t)ICP_INIT_REQUEST_SET,
+ icp_base + HFI_REG_HOST_ICP_INIT_REQUEST);
+
+ hw_version = cam_io_r(icp_base + HFI_REG_A5_HW_VERSION);
+ pr_debug("hw version : %u[%x]\n", hw_version, hw_version);
+
+ do {
+ msleep(500);
+ status = cam_io_r(icp_base + HFI_REG_ICP_HOST_INIT_RESPONSE);
+ } while (status != ICP_INIT_RESP_SUCCESS);
+
+ if (status == ICP_INIT_RESP_SUCCESS) {
+ g_hfi->hfi_state = FW_RESP_DONE;
+ rc = 0;
+ } else {
+ rc = -ENODEV;
+ pr_err("FW initialization failed");
+ goto regions_fail;
+ }
+
+ fw_version = cam_io_r(icp_base + HFI_REG_FW_VERSION);
+ g_hfi->hfi_state = FW_START_SENT;
+
+ pr_debug("fw version : %u[%x]\n", fw_version, fw_version);
+ pr_debug("hfi init is successful\n");
+ cam_io_w((uint32_t)INTR_ENABLE, icp_base + HFI_REG_A5_CSR_A2HOSTINTEN);
+
+ return rc;
+regions_fail:
+ kzfree(g_hfi);
+alloc_fail:
+ return rc;
+}
+
+
+void cam_hfi_deinit(void)
+{
+ kfree(g_hfi);
+ g_hfi = NULL;
+}
+
+void icp_enable_fw_debug(void)
+{
+ hfi_send_system_cmd(HFI_CMD_SYS_SET_PROPERTY,
+ (uint64_t)HFI_PROP_SYS_DEBUG_CFG, 0);
+}
+
+int icp_ping_fw(void)
+{
+ hfi_send_system_cmd(HFI_CMD_SYS_PING,
+ (uint64_t)0x12123434, 0);
+
+ return 0;
+}
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/Makefile b/drivers/media/platform/msm/camera/icp/icp_hw/Makefile
new file mode 100644
index 0000000..8e95286
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/Makefile
@@ -0,0 +1,9 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_core
+ccflags-y += -Idrivers/media/platform/msm/camera/icp
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/icp_hw/include
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
+
+obj-$(CONFIG_SPECTRA_CAMERA) += icp_hw_mgr/ a5_hw/ ipe_hw/ bps_hw/
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/Makefile b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/Makefile
new file mode 100644
index 0000000..a4df0b8
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/Makefile
@@ -0,0 +1,11 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_core
+ccflags-y += -Idrivers/media/platform/msm/camera/icp
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/icp_hw/include
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/include
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/icp_hw/a5_hw
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/fw_inc
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
+
+obj-$(CONFIG_SPECTRA_CAMERA) += a5_dev.o a5_core.o a5_soc.o
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.c b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.c
new file mode 100644
index 0000000..f562bb9
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.c
@@ -0,0 +1,459 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "A5-CORE %s:%d " fmt, __func__, __LINE__
+
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/debugfs.h>
+#include <linux/videodev2.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/elf.h>
+#include <media/cam_icp.h>
+#include "cam_io_util.h"
+#include "cam_a5_hw_intf.h"
+#include "cam_hw.h"
+#include "cam_hw_intf.h"
+#include "a5_core.h"
+#include "a5_soc.h"
+#include "cam_soc_util.h"
+#include "cam_io_util.h"
+#include "hfi_intf.h"
+#include "hfi_sys_defs.h"
+#include "cam_icp_hw_mgr_intf.h"
+#include "cam_cpas_api.h"
+
+static int cam_a5_cpas_vote(struct cam_a5_device_core_info *core_info,
+ struct cam_icp_cpas_vote *cpas_vote)
+{
+ int rc = 0;
+
+ if (cpas_vote->ahb_vote_valid)
+ rc = cam_cpas_update_ahb_vote(core_info->cpas_handle,
+ &cpas_vote->ahb_vote);
+
+ if (cpas_vote->axi_vote_valid)
+ rc = cam_cpas_update_axi_vote(core_info->cpas_handle,
+ &cpas_vote->axi_vote);
+
+ if (rc)
+ pr_err("cpas vote is failed: %d\n", rc);
+
+ return rc;
+}
+
+static int32_t cam_icp_validate_fw(const uint8_t *elf)
+{
+ struct elf32_hdr *elf_hdr;
+
+ if (!elf) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ elf_hdr = (struct elf32_hdr *)elf;
+
+ if (memcmp(elf_hdr->e_ident, ELFMAG, SELFMAG)) {
+ pr_err("ICP elf identifier is failed\n");
+ return -EINVAL;
+ }
+
+ /* check architecture */
+ if (elf_hdr->e_machine != EM_ARM) {
+ pr_err("unsupported arch\n");
+ return -EINVAL;
+ }
+
+ /* check elf bit format */
+ if (elf_hdr->e_ident[EI_CLASS] != ELFCLASS32) {
+ pr_err("elf doesn't support 32 bit format\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int32_t cam_icp_get_fw_size(const uint8_t *elf, uint32_t *fw_size)
+{
+ int32_t rc = 0;
+ int32_t i = 0;
+ uint32_t num_prg_hdrs;
+ unsigned char *icp_prg_hdr_tbl;
+ uint32_t seg_mem_size = 0;
+ struct elf32_hdr *elf_hdr;
+ struct elf32_phdr *prg_hdr;
+
+ if (!elf || !fw_size) {
+ pr_err("invalid args\n");
+ return -EINVAL;
+ }
+
+ *fw_size = 0;
+
+ elf_hdr = (struct elf32_hdr *)elf;
+ num_prg_hdrs = elf_hdr->e_phnum;
+ icp_prg_hdr_tbl = (unsigned char *)elf + elf_hdr->e_phoff;
+ prg_hdr = (struct elf32_phdr *)&icp_prg_hdr_tbl[0];
+
+ if (!prg_hdr) {
+ pr_err("failed to get elf program header attr\n");
+ return -EINVAL;
+ }
+
+ pr_debug("num_prg_hdrs = %d\n", num_prg_hdrs);
+ for (i = 0; i < num_prg_hdrs; i++, prg_hdr++) {
+ if (prg_hdr->p_flags == 0)
+ continue;
+
+ seg_mem_size = (prg_hdr->p_memsz + prg_hdr->p_align - 1) &
+ ~(prg_hdr->p_align - 1);
+ seg_mem_size += prg_hdr->p_vaddr;
+ pr_debug("p_memsz = %x p_align = %x p_vaddr = %x seg_mem_size = %x\n",
+ (int)prg_hdr->p_memsz, (int)prg_hdr->p_align,
+ (int)prg_hdr->p_vaddr, (int)seg_mem_size);
+ if (*fw_size < seg_mem_size)
+ *fw_size = seg_mem_size;
+
+ }
+
+ if (*fw_size == 0) {
+ pr_err("invalid elf fw file\n");
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int32_t cam_icp_program_fw(const uint8_t *elf,
+ struct cam_a5_device_core_info *core_info)
+{
+ int32_t rc = 0;
+ uint32_t num_prg_hdrs;
+ unsigned char *icp_prg_hdr_tbl;
+ int32_t i = 0;
+ u8 *dest;
+ u8 *src;
+ struct elf32_hdr *elf_hdr;
+ struct elf32_phdr *prg_hdr;
+
+ elf_hdr = (struct elf32_hdr *)elf;
+ num_prg_hdrs = elf_hdr->e_phnum;
+ icp_prg_hdr_tbl = (unsigned char *)elf + elf_hdr->e_phoff;
+ prg_hdr = (struct elf32_phdr *)&icp_prg_hdr_tbl[0];
+
+ if (!prg_hdr) {
+ pr_err("failed to get elf program header attr\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num_prg_hdrs; i++, prg_hdr++) {
+ if (prg_hdr->p_flags == 0)
+ continue;
+
+ pr_debug("Loading FW header size: %u\n", prg_hdr->p_filesz);
+ if (prg_hdr->p_filesz != 0) {
+ src = (u8 *)((u8 *)elf + prg_hdr->p_offset);
+ dest = (u8 *)(((u8 *)core_info->fw_kva_addr) +
+ prg_hdr->p_vaddr);
+
+ memcpy_toio(dest, src, prg_hdr->p_filesz);
+ pr_debug("fw kva: %pK, p_vaddr: 0x%x\n",
+ dest, prg_hdr->p_vaddr);
+ }
+ }
+
+ return rc;
+}
+
+static int32_t cam_a5_download_fw(void *device_priv)
+{
+ int32_t rc = 0;
+ uint32_t fw_size;
+ const uint8_t *fw_start = NULL;
+ struct cam_hw_info *a5_dev = device_priv;
+ struct cam_hw_soc_info *soc_info = NULL;
+ struct cam_a5_device_core_info *core_info = NULL;
+ struct cam_a5_device_hw_info *hw_info = NULL;
+ struct platform_device *pdev = NULL;
+ struct a5_soc_info *cam_a5_soc_info = NULL;
+
+ if (!device_priv) {
+ pr_err("Invalid cam_dev_info\n");
+ return -EINVAL;
+ }
+
+ soc_info = &a5_dev->soc_info;
+ core_info = (struct cam_a5_device_core_info *)a5_dev->core_info;
+ hw_info = core_info->a5_hw_info;
+ pdev = soc_info->pdev;
+ cam_a5_soc_info = soc_info->soc_private;
+
+ rc = request_firmware(&core_info->fw_elf, "CAMERA_ICP.elf", &pdev->dev);
+ pr_debug("request_firmware: %d\n", rc);
+ if (rc < 0) {
+ pr_err("Failed to locate fw\n");
+ return rc;
+ }
+
+ if (!core_info->fw_elf) {
+ pr_err("request_firmware is failed\n");
+ return -EINVAL;
+ }
+
+ fw_start = core_info->fw_elf->data;
+ rc = cam_icp_validate_fw(fw_start);
+ if (rc < 0) {
+ pr_err("fw elf validation failed\n");
+ return -EINVAL;
+ }
+
+ rc = cam_icp_get_fw_size(fw_start, &fw_size);
+ if (rc < 0) {
+ pr_err("unable to get fw file size\n");
+ return rc;
+ }
+ pr_debug("cam_icp_get_fw_size: %u\n", fw_size);
+
+ /* Check FW firmware memory allocation is OK or not */
+ pr_debug("cam_icp_get_fw_size: %u %llu\n",
+ fw_size, core_info->fw_buf_len);
+
+ if (core_info->fw_buf_len < fw_size) {
+ pr_err("fw allocation failed\n");
+ goto fw_alloc_failed;
+ }
+
+ /* download fw */
+ rc = cam_icp_program_fw(fw_start, core_info);
+ if (rc < 0) {
+ pr_err("fw program is failed\n");
+ goto fw_program_failed;
+ }
+
+ return 0;
+fw_program_failed:
+fw_alloc_failed:
+ return rc;
+}
+
+int cam_a5_init_hw(void *device_priv,
+ void *init_hw_args, uint32_t arg_size)
+{
+ struct cam_hw_info *a5_dev = device_priv;
+ struct cam_hw_soc_info *soc_info = NULL;
+ struct cam_a5_device_core_info *core_info = NULL;
+ struct cam_icp_cpas_vote cpas_vote;
+ int rc = 0;
+
+ if (!device_priv) {
+ pr_err("Invalid cam_dev_info\n");
+ return -EINVAL;
+ }
+
+ soc_info = &a5_dev->soc_info;
+ core_info = (struct cam_a5_device_core_info *)a5_dev->core_info;
+
+ if ((!soc_info) || (!core_info)) {
+ pr_err("soc_info = %pK core_info = %pK\n", soc_info, core_info);
+ return -EINVAL;
+ }
+
+ cpas_vote.ahb_vote.type = CAM_VOTE_ABSOLUTE;
+ cpas_vote.ahb_vote.vote.level = CAM_TURBO_VOTE;
+ cpas_vote.axi_vote.compressed_bw = ICP_TURBO_VOTE;
+ cpas_vote.axi_vote.uncompressed_bw = ICP_TURBO_VOTE;
+
+ rc = cam_cpas_start(core_info->cpas_handle,
+ &cpas_vote.ahb_vote, &cpas_vote.axi_vote);
+ if (rc < 0) {
+ pr_err("cpass start failed: %d\n", rc);
+ return rc;
+ }
+
+ rc = cam_a5_enable_soc_resources(soc_info);
+ if (rc < 0) {
+ pr_err("soc enable is failed\n");
+ rc = cam_cpas_stop(core_info->cpas_handle);
+ return rc;
+ }
+
+ return 0;
+}
+
+int cam_a5_deinit_hw(void *device_priv,
+ void *init_hw_args, uint32_t arg_size)
+{
+ struct cam_hw_info *a5_dev = device_priv;
+ struct cam_hw_soc_info *soc_info = NULL;
+ struct cam_a5_device_core_info *core_info = NULL;
+ int rc = 0;
+
+ if (!device_priv) {
+ pr_err("Invalid cam_dev_info\n");
+ return -EINVAL;
+ }
+
+ soc_info = &a5_dev->soc_info;
+ core_info = (struct cam_a5_device_core_info *)a5_dev->core_info;
+ if ((!soc_info) || (!core_info)) {
+ pr_err("soc_info = %pK core_info = %pK\n", soc_info, core_info);
+ return -EINVAL;
+ }
+
+ rc = cam_a5_disable_soc_resources(soc_info);
+ if (rc < 0)
+ pr_err("soc enable is failed\n");
+
+ rc = cam_cpas_stop(core_info->cpas_handle);
+ if (rc < 0)
+ pr_err("cpas stop is failed: %d\n", rc);
+
+ return 0;
+}
+
+irqreturn_t cam_a5_irq(int irq_num, void *data)
+{
+ struct cam_hw_info *a5_dev = data;
+ struct cam_hw_soc_info *soc_info = NULL;
+ struct cam_a5_device_core_info *core_info = NULL;
+ struct cam_a5_device_hw_info *hw_info = NULL;
+ uint32_t irq_status = 0;
+
+ if (!data) {
+ pr_err("Invalid cam_dev_info or query_cap args\n");
+ return IRQ_HANDLED;
+ }
+
+ soc_info = &a5_dev->soc_info;
+ core_info = (struct cam_a5_device_core_info *)a5_dev->core_info;
+ hw_info = core_info->a5_hw_info;
+
+ irq_status = cam_io_r_mb(soc_info->reg_map[A5_SIERRA_BASE].mem_base +
+ core_info->a5_hw_info->a5_host_int_status);
+
+ cam_io_w_mb(irq_status,
+ soc_info->reg_map[A5_SIERRA_BASE].mem_base +
+ core_info->a5_hw_info->a5_host_int_clr);
+
+ pr_debug("irq_status = %x\n", irq_status);
+ if (irq_status & A5_HOST_INT)
+ pr_debug("A5 to Host interrupt, read msg Q\n");
+
+ if ((irq_status & A5_WDT_0) ||
+ (irq_status & A5_WDT_1)) {
+ pr_err_ratelimited("watch dog interrupt from A5\n");
+ }
+
+ if (core_info->irq_cb.icp_hw_mgr_cb)
+ core_info->irq_cb.icp_hw_mgr_cb(irq_status,
+ core_info->irq_cb.data);
+ return IRQ_HANDLED;
+}
+
+int cam_a5_process_cmd(void *device_priv, uint32_t cmd_type,
+ void *cmd_args, uint32_t arg_size)
+{
+ struct cam_hw_info *a5_dev = device_priv;
+ struct cam_hw_soc_info *soc_info = NULL;
+ struct cam_a5_device_core_info *core_info = NULL;
+ struct cam_a5_device_hw_info *hw_info = NULL;
+ int rc = 0;
+
+ if (!device_priv) {
+ pr_err("Invalid arguments\n");
+ return -EINVAL;
+ }
+
+ if (cmd_type >= CAM_ICP_A5_CMD_MAX) {
+ pr_err("Invalid command : %x\n", cmd_type);
+ return -EINVAL;
+ }
+
+ soc_info = &a5_dev->soc_info;
+ core_info = (struct cam_a5_device_core_info *)a5_dev->core_info;
+ hw_info = core_info->a5_hw_info;
+
+ switch (cmd_type) {
+ case CAM_ICP_A5_CMD_FW_DOWNLOAD:
+ rc = cam_a5_download_fw(device_priv);
+
+ break;
+ case CAM_ICP_A5_CMD_SET_FW_BUF: {
+ struct cam_icp_a5_set_fw_buf_info *fw_buf_info = cmd_args;
+
+ if (!cmd_args) {
+ pr_err("cmd args NULL\n");
+ return -EINVAL;
+ }
+
+ core_info->fw_buf = fw_buf_info->iova;
+ core_info->fw_kva_addr = fw_buf_info->kva;
+ core_info->fw_buf_len = fw_buf_info->len;
+
+ pr_debug("fw buf info = %x %llx %lld\n", core_info->fw_buf,
+ core_info->fw_kva_addr, core_info->fw_buf_len);
+ break;
+ }
+ case CAM_ICP_A5_SET_IRQ_CB: {
+ struct cam_icp_a5_set_irq_cb *irq_cb = cmd_args;
+
+ if (!cmd_args) {
+ pr_err("cmd args NULL\n");
+ return -EINVAL;
+ }
+
+ core_info->irq_cb.icp_hw_mgr_cb = irq_cb->icp_hw_mgr_cb;
+ core_info->irq_cb.data = irq_cb->data;
+ break;
+ }
+
+ case CAM_ICP_A5_SEND_INIT:
+ hfi_send_system_cmd(HFI_CMD_SYS_INIT, 0, 0);
+ break;
+ case CAM_ICP_A5_CMD_VOTE_CPAS: {
+ struct cam_icp_cpas_vote *cpas_vote = cmd_args;
+
+ if (!cmd_args) {
+ pr_err("cmd args NULL\n");
+ return -EINVAL;
+ }
+
+ cam_a5_cpas_vote(core_info, cpas_vote);
+ break;
+ }
+
+ case CAM_ICP_A5_CMD_CPAS_START: {
+ struct cam_icp_cpas_vote *cpas_vote = cmd_args;
+
+ if (!cmd_args) {
+ pr_err("cmd args NULL\n");
+ return -EINVAL;
+ }
+
+ rc = cam_cpas_start(core_info->cpas_handle,
+ &cpas_vote->ahb_vote, &cpas_vote->axi_vote);
+ break;
+ }
+
+ case CAM_ICP_A5_CMD_CPAS_STOP:
+ cam_cpas_stop(core_info->cpas_handle);
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.h b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.h
new file mode 100644
index 0000000..8b84270
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.h
@@ -0,0 +1,87 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CAM_A5_CORE_H
+#define CAM_A5_CORE_H
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/dma-buf.h>
+#include "cam_a5_hw_intf.h"
+
+#define A5_QGIC_BASE 0
+#define A5_SIERRA_BASE 1
+#define A5_CSR_BASE 2
+
+#define A5_HOST_INT 0x1
+#define A5_WDT_0 0x10
+#define A5_WDT_1 0x100
+
+#define ELF_GUARD_PAGE (2 * 1024 * 1024)
+
+struct cam_a5_device_hw_info {
+ uint32_t hw_ver;
+ uint32_t nsec_reset;
+ uint32_t a5_control;
+ uint32_t a5_host_int_en;
+ uint32_t a5_host_int;
+ uint32_t a5_host_int_clr;
+ uint32_t a5_host_int_status;
+ uint32_t a5_host_int_set;
+ uint32_t host_a5_int;
+ uint32_t fw_version;
+ uint32_t init_req;
+ uint32_t init_response;
+ uint32_t shared_mem_ptr;
+ uint32_t shared_mem_size;
+ uint32_t qtbl_ptr;
+ uint32_t uncached_heap_ptr;
+ uint32_t uncached_heap_size;
+ uint32_t a5_status;
+};
+
+/**
+ * struct cam_a5_device_hw_info
+ * @a5_hw_info: A5 hardware info
+ * @fw_elf: start address of fw start with elf header
+ * @fw: start address of fw blob
+ * @fw_buf: smmu alloc/mapped fw buffer
+ * @fw_buf_len: fw buffer length
+ * @query_cap: A5 query info from firmware
+ * @a5_acquire: Acquire information of A5
+ * @irq_cb: IRQ callback
+ * @cpas_handle: CPAS handle for A5
+ */
+struct cam_a5_device_core_info {
+ struct cam_a5_device_hw_info *a5_hw_info;
+ const struct firmware *fw_elf;
+ void *fw;
+ uint32_t fw_buf;
+ uint64_t fw_kva_addr;
+ uint64_t fw_buf_len;
+ struct cam_icp_a5_query_cap query_cap;
+ struct cam_icp_a5_acquire_dev a5_acquire[8];
+ struct cam_icp_a5_set_irq_cb irq_cb;
+ uint32_t cpas_handle;
+};
+
+int cam_a5_init_hw(void *device_priv,
+ void *init_hw_args, uint32_t arg_size);
+int cam_a5_deinit_hw(void *device_priv,
+ void *init_hw_args, uint32_t arg_size);
+int cam_a5_process_cmd(void *device_priv, uint32_t cmd_type,
+ void *cmd_args, uint32_t arg_size);
+
+irqreturn_t cam_a5_irq(int irq_num, void *data);
+#endif /* CAM_A5_CORE_H */
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_dev.c b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_dev.c
new file mode 100644
index 0000000..f649c3b
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_dev.c
@@ -0,0 +1,197 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of_device.h>
+#include <linux/timer.h>
+#include "a5_core.h"
+#include "a5_soc.h"
+#include "cam_io_util.h"
+#include "cam_hw.h"
+#include "cam_hw_intf.h"
+#include "cam_a5_hw_intf.h"
+#include "cam_icp_hw_mgr_intf.h"
+#include "cam_cpas_api.h"
+
+struct a5_soc_info cam_a5_soc_info;
+EXPORT_SYMBOL(cam_a5_soc_info);
+
+struct cam_a5_device_hw_info cam_a5_hw_info = {
+ .hw_ver = 0x0,
+ .nsec_reset = 0x4,
+ .a5_control = 0x8,
+ .a5_host_int_en = 0x10,
+ .a5_host_int = 0x14,
+ .a5_host_int_clr = 0x18,
+ .a5_host_int_status = 0x1c,
+ .a5_host_int_set = 0x20,
+ .host_a5_int = 0x30,
+ .fw_version = 0x44,
+ .init_req = 0x48,
+ .init_response = 0x4c,
+ .shared_mem_ptr = 0x50,
+ .shared_mem_size = 0x54,
+ .qtbl_ptr = 0x58,
+ .uncached_heap_ptr = 0x5c,
+ .uncached_heap_size = 0x60,
+ .a5_status = 0x200,
+};
+EXPORT_SYMBOL(cam_a5_hw_info);
+
+int cam_a5_register_cpas(struct cam_hw_soc_info *soc_info,
+ struct cam_a5_device_core_info *core_info,
+ uint32_t hw_idx)
+{
+ struct cam_cpas_register_params cpas_register_params;
+ int rc;
+
+ cpas_register_params.dev = &soc_info->pdev->dev;
+ memcpy(cpas_register_params.identifier, "icp", sizeof("icp"));
+ cpas_register_params.cam_cpas_client_cb = NULL;
+ cpas_register_params.cell_index = hw_idx;
+ cpas_register_params.userdata = NULL;
+
+ rc = cam_cpas_register_client(&cpas_register_params);
+ if (rc < 0) {
+ pr_err("cam_cpas_register_client is failed: %d\n", rc);
+ return rc;
+ }
+
+ core_info->cpas_handle = cpas_register_params.client_handle;
+ return rc;
+}
+
+int cam_a5_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct cam_hw_info *a5_dev = NULL;
+ struct cam_hw_intf *a5_dev_intf = NULL;
+ const struct of_device_id *match_dev = NULL;
+ struct cam_a5_device_core_info *core_info = NULL;
+ struct cam_a5_device_hw_info *hw_info = NULL;
+
+ a5_dev_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL);
+ if (!a5_dev_intf)
+ return -ENOMEM;
+
+ of_property_read_u32(pdev->dev.of_node,
+ "cell-index", &a5_dev_intf->hw_idx);
+
+ a5_dev = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL);
+ if (!a5_dev) {
+ rc = -ENOMEM;
+ goto a5_dev_alloc_failure;
+ }
+
+ a5_dev->soc_info.pdev = pdev;
+ a5_dev_intf->hw_priv = a5_dev;
+ a5_dev_intf->hw_ops.init = cam_a5_init_hw;
+ a5_dev_intf->hw_ops.deinit = cam_a5_deinit_hw;
+ a5_dev_intf->hw_ops.process_cmd = cam_a5_process_cmd;
+ a5_dev_intf->hw_type = CAM_ICP_DEV_A5;
+
+ pr_debug("%s: type %d index %d\n", __func__,
+ a5_dev_intf->hw_type,
+ a5_dev_intf->hw_idx);
+
+ platform_set_drvdata(pdev, a5_dev_intf);
+
+ a5_dev->core_info = kzalloc(sizeof(struct cam_a5_device_core_info),
+ GFP_KERNEL);
+ if (!a5_dev->core_info) {
+ rc = -ENOMEM;
+ goto core_info_alloc_failure;
+ }
+ core_info = (struct cam_a5_device_core_info *)a5_dev->core_info;
+
+ match_dev = of_match_device(pdev->dev.driver->of_match_table,
+ &pdev->dev);
+ if (!match_dev) {
+ pr_err("%s: No a5 hardware info\n", __func__);
+ rc = -EINVAL;
+ goto pr_err;
+ }
+ hw_info = (struct cam_a5_device_hw_info *)match_dev->data;
+ core_info->a5_hw_info = hw_info;
+
+ a5_dev->soc_info.soc_private = &cam_a5_soc_info;
+
+ rc = cam_a5_init_soc_resources(&a5_dev->soc_info, cam_a5_irq,
+ a5_dev);
+ if (rc < 0) {
+ pr_err("%s: failed to init_soc\n", __func__);
+ goto init_soc_failure;
+ }
+
+ pr_debug("cam_a5_init_soc_resources : %pK\n",
+ (void *)&a5_dev->soc_info);
+ rc = cam_a5_register_cpas(&a5_dev->soc_info,
+ core_info, a5_dev_intf->hw_idx);
+ if (rc < 0) {
+ pr_err("a5 cpas registration failed\n");
+ goto cpas_reg_failed;
+ }
+ a5_dev->hw_state = CAM_HW_STATE_POWER_DOWN;
+ mutex_init(&a5_dev->hw_mutex);
+ spin_lock_init(&a5_dev->hw_lock);
+ init_completion(&a5_dev->hw_complete);
+
+ pr_debug("%s: A5%d probe successful\n", __func__,
+ a5_dev_intf->hw_idx);
+ return 0;
+
+cpas_reg_failed:
+init_soc_failure:
+pr_err:
+ kfree(a5_dev->core_info);
+core_info_alloc_failure:
+ kfree(a5_dev);
+a5_dev_alloc_failure:
+ kfree(a5_dev_intf);
+
+ return rc;
+}
+
+static const struct of_device_id cam_a5_dt_match[] = {
+ {
+ .compatible = "qcom,cam_a5",
+ .data = &cam_a5_hw_info,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cam_a5_dt_match);
+
+static struct platform_driver cam_a5_driver = {
+ .probe = cam_a5_probe,
+ .driver = {
+ .name = "cam_a5",
+ .owner = THIS_MODULE,
+ .of_match_table = cam_a5_dt_match,
+ },
+};
+
+static int __init cam_a5_init_module(void)
+{
+ return platform_driver_register(&cam_a5_driver);
+}
+
+static void __exit cam_a5_exit_module(void)
+{
+ platform_driver_unregister(&cam_a5_driver);
+}
+
+module_init(cam_a5_init_module);
+module_exit(cam_a5_exit_module);
+MODULE_DESCRIPTION("CAM A5 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_soc.c b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_soc.c
new file mode 100644
index 0000000..641c154
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_soc.c
@@ -0,0 +1,101 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/dma-buf.h>
+#include <media/cam_defs.h>
+#include <media/cam_icp.h>
+#include "a5_soc.h"
+#include "cam_soc_util.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+static int cam_a5_get_dt_properties(struct cam_hw_soc_info *soc_info)
+{
+ int rc = 0;
+ const char *fw_name;
+ struct a5_soc_info *camp_a5_soc_info;
+ struct device_node *of_node = NULL;
+ struct platform_device *pdev = NULL;
+
+ pdev = soc_info->pdev;
+ of_node = pdev->dev.of_node;
+
+ rc = cam_soc_util_get_dt_properties(soc_info);
+ if (rc < 0) {
+ pr_err("%s: get a5 dt prop is failed\n", __func__);
+ return rc;
+ }
+
+ camp_a5_soc_info = soc_info->soc_private;
+ fw_name = camp_a5_soc_info->fw_name;
+
+ rc = of_property_read_string(of_node, "fw_name", &fw_name);
+ if (rc < 0)
+ pr_err("%s: fw_name read failed\n", __func__);
+
+ return rc;
+}
+
+static int cam_a5_request_platform_resource(
+ struct cam_hw_soc_info *soc_info,
+ irq_handler_t a5_irq_handler, void *irq_data)
+{
+ int rc = 0;
+
+ rc = cam_soc_util_request_platform_resource(soc_info, a5_irq_handler,
+ irq_data);
+
+ return rc;
+}
+
+int cam_a5_init_soc_resources(struct cam_hw_soc_info *soc_info,
+ irq_handler_t a5_irq_handler, void *irq_data)
+{
+ int rc = 0;
+
+ rc = cam_a5_get_dt_properties(soc_info);
+ if (rc < 0)
+ return rc;
+
+ rc = cam_a5_request_platform_resource(soc_info, a5_irq_handler,
+ irq_data);
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+int cam_a5_enable_soc_resources(struct cam_hw_soc_info *soc_info)
+{
+ int rc = 0;
+
+ rc = cam_soc_util_enable_platform_resource(soc_info, true, true);
+ if (rc)
+ pr_err("%s: enable platform failed\n", __func__);
+
+ return rc;
+}
+
+int cam_a5_disable_soc_resources(struct cam_hw_soc_info *soc_info)
+{
+ int rc = 0;
+
+ rc = cam_soc_util_disable_platform_resource(soc_info, true, true);
+ if (rc)
+ pr_err("%s: enable platform failed\n", __func__);
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_soc.h b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_soc.h
new file mode 100644
index 0000000..916143d
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_soc.h
@@ -0,0 +1,29 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CAM_A5_SOC_H
+#define CAM_A5_SOC_H
+
+#include "cam_soc_util.h"
+
+struct a5_soc_info {
+ char *fw_name;
+};
+
+int cam_a5_init_soc_resources(struct cam_hw_soc_info *soc_info,
+ irq_handler_t a5_irq_handler, void *irq_data);
+
+int cam_a5_enable_soc_resources(struct cam_hw_soc_info *soc_info);
+
+int cam_a5_disable_soc_resources(struct cam_hw_soc_info *soc_info);
+
+#endif
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/Makefile b/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/Makefile
new file mode 100644
index 0000000..6aeb5f1
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/Makefile
@@ -0,0 +1,11 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_core
+ccflags-y += -Idrivers/media/platform/msm/camera/icp
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/icp_hw/include
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/include
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/icp_hw/bps_hw
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/fw_inc
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
+
+obj-$(CONFIG_SPECTRA_CAMERA) += bps_dev.o bps_core.o bps_soc.o
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.c b/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.c
new file mode 100644
index 0000000..50863a5
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.c
@@ -0,0 +1,189 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "BPS-CORE %s:%d " fmt, __func__, __LINE__
+
+#include <linux/of.h>
+#include <linux/debugfs.h>
+#include <linux/videodev2.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include "cam_io_util.h"
+#include "cam_hw.h"
+#include "cam_hw_intf.h"
+#include "bps_core.h"
+#include "bps_soc.h"
+#include "cam_soc_util.h"
+#include "cam_io_util.h"
+#include "cam_bps_hw_intf.h"
+#include "cam_icp_hw_intf.h"
+#include "cam_icp_hw_mgr_intf.h"
+#include "cam_cpas_api.h"
+
+static int cam_bps_cpas_vote(struct cam_bps_device_core_info *core_info,
+ struct cam_icp_cpas_vote *cpas_vote)
+{
+ int rc = 0;
+
+ if (cpas_vote->ahb_vote_valid)
+ rc = cam_cpas_update_ahb_vote(core_info->cpas_handle,
+ &cpas_vote->ahb_vote);
+ if (cpas_vote->axi_vote_valid)
+ rc = cam_cpas_update_axi_vote(core_info->cpas_handle,
+ &cpas_vote->axi_vote);
+
+ if (rc < 0)
+ pr_err("cpas vote is failed: %d\n", rc);
+
+ return rc;
+}
+
+
+int cam_bps_init_hw(void *device_priv,
+ void *init_hw_args, uint32_t arg_size)
+{
+ struct cam_hw_info *bps_dev = device_priv;
+ struct cam_hw_soc_info *soc_info = NULL;
+ struct cam_bps_device_core_info *core_info = NULL;
+ struct cam_icp_cpas_vote cpas_vote;
+ int rc = 0;
+
+ if (!device_priv) {
+ pr_err("Invalid cam_dev_info\n");
+ return -EINVAL;
+ }
+
+ soc_info = &bps_dev->soc_info;
+ core_info = (struct cam_bps_device_core_info *)bps_dev->core_info;
+
+ if ((!soc_info) || (!core_info)) {
+ pr_err("soc_info = %pK core_info = %pK\n", soc_info, core_info);
+ return -EINVAL;
+ }
+
+ cpas_vote.ahb_vote.type = CAM_VOTE_ABSOLUTE;
+ cpas_vote.ahb_vote.vote.level = CAM_TURBO_VOTE;
+ cpas_vote.axi_vote.compressed_bw = ICP_TURBO_VOTE;
+ cpas_vote.axi_vote.uncompressed_bw = ICP_TURBO_VOTE;
+
+ rc = cam_cpas_start(core_info->cpas_handle,
+ &cpas_vote.ahb_vote, &cpas_vote.axi_vote);
+ if (rc < 0) {
+ pr_err("cpass start failed: %d\n", rc);
+ return rc;
+ }
+
+ rc = cam_bps_enable_soc_resources(soc_info);
+ if (rc < 0) {
+ pr_err("soc enable is failed\n");
+ rc = cam_cpas_stop(core_info->cpas_handle);
+ }
+
+ return rc;
+}
+
+int cam_bps_deinit_hw(void *device_priv,
+ void *init_hw_args, uint32_t arg_size)
+{
+ struct cam_hw_info *bps_dev = device_priv;
+ struct cam_hw_soc_info *soc_info = NULL;
+ struct cam_bps_device_core_info *core_info = NULL;
+ int rc = 0;
+
+ if (!device_priv) {
+ pr_err("Invalid cam_dev_info\n");
+ return -EINVAL;
+ }
+
+ soc_info = &bps_dev->soc_info;
+ core_info = (struct cam_bps_device_core_info *)bps_dev->core_info;
+ if ((!soc_info) || (!core_info)) {
+ pr_err("soc_info = %pK core_info = %pK\n", soc_info, core_info);
+ return -EINVAL;
+ }
+
+ rc = cam_bps_disable_soc_resources(soc_info);
+ if (rc < 0)
+ pr_err("soc enable is failed\n");
+
+ rc = cam_cpas_stop(core_info->cpas_handle);
+ if (rc < 0)
+ pr_err("cpas stop is failed: %d\n", rc);
+
+ return rc;
+}
+
+int cam_bps_process_cmd(void *device_priv, uint32_t cmd_type,
+ void *cmd_args, uint32_t arg_size)
+{
+ struct cam_hw_info *bps_dev = device_priv;
+ struct cam_hw_soc_info *soc_info = NULL;
+ struct cam_bps_device_core_info *core_info = NULL;
+ struct cam_bps_device_hw_info *hw_info = NULL;
+ int rc = 0;
+
+ if (!device_priv) {
+ pr_err("Invalid arguments\n");
+ return -EINVAL;
+ }
+
+ if (cmd_type >= CAM_ICP_BPS_CMD_MAX) {
+ pr_err("Invalid command : %x\n", cmd_type);
+ return -EINVAL;
+ }
+
+ soc_info = &bps_dev->soc_info;
+ core_info = (struct cam_bps_device_core_info *)bps_dev->core_info;
+ hw_info = core_info->bps_hw_info;
+
+ switch (cmd_type) {
+ case CAM_ICP_BPS_CMD_VOTE_CPAS: {
+ struct cam_icp_cpas_vote *cpas_vote = cmd_args;
+
+ if (!cmd_args) {
+ pr_err("cmd args NULL\n");
+ return -EINVAL;
+ }
+
+ cam_bps_cpas_vote(core_info, cpas_vote);
+ break;
+ }
+
+ case CAM_ICP_BPS_CMD_CPAS_START: {
+ struct cam_icp_cpas_vote *cpas_vote = cmd_args;
+
+ if (!cmd_args) {
+ pr_err("cmd args NULL\n");
+ return -EINVAL;
+ }
+
+ rc = cam_cpas_start(core_info->cpas_handle,
+ &cpas_vote->ahb_vote, &cpas_vote->axi_vote);
+ break;
+ }
+
+ case CAM_ICP_BPS_CMD_CPAS_STOP:
+ cam_cpas_stop(core_info->cpas_handle);
+ break;
+ default:
+ break;
+ }
+ return rc;
+}
+
+irqreturn_t cam_bps_irq(int irq_num, void *data)
+{
+ return IRQ_HANDLED;
+}
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.h b/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.h
new file mode 100644
index 0000000..67e1c03
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CAM_BPS_CORE_H
+#define CAM_BPS_CORE_H
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/dma-buf.h>
+
+struct cam_bps_device_hw_info {
+ uint32_t reserved;
+};
+
+struct cam_bps_device_core_info {
+ struct cam_bps_device_hw_info *bps_hw_info;
+ uint32_t cpas_handle;
+};
+
+int cam_bps_init_hw(void *device_priv,
+ void *init_hw_args, uint32_t arg_size);
+int cam_bps_deinit_hw(void *device_priv,
+ void *init_hw_args, uint32_t arg_size);
+int cam_bps_process_cmd(void *device_priv, uint32_t cmd_type,
+ void *cmd_args, uint32_t arg_size);
+
+irqreturn_t cam_bps_irq(int irq_num, void *data);
+#endif /* CAM_BPS_CORE_H */
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_dev.c b/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_dev.c
new file mode 100644
index 0000000..c3477ee
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_dev.c
@@ -0,0 +1,169 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of_device.h>
+#include <linux/timer.h>
+#include "bps_core.h"
+#include "bps_soc.h"
+#include "cam_hw.h"
+#include "cam_hw_intf.h"
+#include "cam_io_util.h"
+#include "cam_icp_hw_intf.h"
+#include "cam_icp_hw_mgr_intf.h"
+#include "cam_cpas_api.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+struct cam_bps_device_hw_info cam_bps_hw_info = {
+ .reserved = 0,
+};
+EXPORT_SYMBOL(cam_bps_hw_info);
+
+int cam_bps_register_cpas(struct cam_hw_soc_info *soc_info,
+ struct cam_bps_device_core_info *core_info,
+ uint32_t hw_idx)
+{
+ struct cam_cpas_register_params cpas_register_params;
+ int rc;
+
+ cpas_register_params.dev = &soc_info->pdev->dev;
+ memcpy(cpas_register_params.identifier, "bps", sizeof("bps"));
+ cpas_register_params.cam_cpas_client_cb = NULL;
+ cpas_register_params.cell_index = hw_idx;
+ cpas_register_params.userdata = NULL;
+
+ rc = cam_cpas_register_client(&cpas_register_params);
+ if (rc < 0) {
+ pr_err("cam_cpas_register_client is failed: %d\n", rc);
+ return rc;
+ }
+ core_info->cpas_handle = cpas_register_params.client_handle;
+
+ return rc;
+}
+
+int cam_bps_probe(struct platform_device *pdev)
+{
+ struct cam_hw_info *bps_dev = NULL;
+ struct cam_hw_intf *bps_dev_intf = NULL;
+ const struct of_device_id *match_dev = NULL;
+ struct cam_bps_device_core_info *core_info = NULL;
+ struct cam_bps_device_hw_info *hw_info = NULL;
+ int rc = 0;
+
+ bps_dev_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL);
+ if (!bps_dev_intf)
+ return -ENOMEM;
+
+ of_property_read_u32(pdev->dev.of_node,
+ "cell-index", &bps_dev_intf->hw_idx);
+
+ bps_dev = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL);
+ if (!bps_dev) {
+ kfree(bps_dev_intf);
+ return -ENOMEM;
+ }
+ bps_dev->soc_info.pdev = pdev;
+ bps_dev_intf->hw_priv = bps_dev;
+ bps_dev_intf->hw_ops.init = cam_bps_init_hw;
+ bps_dev_intf->hw_ops.deinit = cam_bps_deinit_hw;
+ bps_dev_intf->hw_ops.process_cmd = cam_bps_process_cmd;
+ bps_dev_intf->hw_type = CAM_ICP_DEV_BPS;
+ platform_set_drvdata(pdev, bps_dev_intf);
+ bps_dev->core_info = kzalloc(sizeof(struct cam_bps_device_core_info),
+ GFP_KERNEL);
+ if (!bps_dev->core_info) {
+ kfree(bps_dev);
+ kfree(bps_dev_intf);
+ return -ENOMEM;
+ }
+ core_info = (struct cam_bps_device_core_info *)bps_dev->core_info;
+
+ match_dev = of_match_device(pdev->dev.driver->of_match_table,
+ &pdev->dev);
+ if (!match_dev) {
+ pr_err("%s: No bps hardware info\n", __func__);
+ kfree(bps_dev->core_info);
+ kfree(bps_dev);
+ kfree(bps_dev_intf);
+ rc = -EINVAL;
+ return rc;
+ }
+ hw_info = (struct cam_bps_device_hw_info *)match_dev->data;
+ core_info->bps_hw_info = hw_info;
+
+ rc = cam_bps_init_soc_resources(&bps_dev->soc_info, cam_bps_irq,
+ bps_dev);
+ if (rc < 0) {
+ pr_err("%s: failed to init_soc\n", __func__);
+ kfree(bps_dev->core_info);
+ kfree(bps_dev);
+ kfree(bps_dev_intf);
+ return rc;
+ }
+ pr_debug("cam_bps_init_soc_resources : %pK\n",
+ (void *)&bps_dev->soc_info);
+
+ rc = cam_bps_register_cpas(&bps_dev->soc_info,
+ core_info, bps_dev_intf->hw_idx);
+ if (rc < 0) {
+ kfree(bps_dev->core_info);
+ kfree(bps_dev);
+ kfree(bps_dev_intf);
+ return rc;
+ }
+ bps_dev->hw_state = CAM_HW_STATE_POWER_DOWN;
+ mutex_init(&bps_dev->hw_mutex);
+ spin_lock_init(&bps_dev->hw_lock);
+ init_completion(&bps_dev->hw_complete);
+ pr_debug("%s: BPS%d probe successful\n", __func__,
+ bps_dev_intf->hw_idx);
+
+ return rc;
+}
+
+static const struct of_device_id cam_bps_dt_match[] = {
+ {
+ .compatible = "qcom,cam_bps",
+ .data = &cam_bps_hw_info,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cam_bps_dt_match);
+
+static struct platform_driver cam_bps_driver = {
+ .probe = cam_bps_probe,
+ .driver = {
+ .name = "cam_bps",
+ .owner = THIS_MODULE,
+ .of_match_table = cam_bps_dt_match,
+ },
+};
+
+static int __init cam_bps_init_module(void)
+{
+ return platform_driver_register(&cam_bps_driver);
+}
+
+static void __exit cam_bps_exit_module(void)
+{
+ platform_driver_unregister(&cam_bps_driver);
+}
+
+module_init(cam_bps_init_module);
+module_exit(cam_bps_exit_module);
+MODULE_DESCRIPTION("CAM BPS driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_soc.c b/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_soc.c
new file mode 100644
index 0000000..76884bf
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_soc.c
@@ -0,0 +1,85 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/dma-buf.h>
+#include <media/cam_defs.h>
+#include <media/cam_icp.h>
+#include "bps_soc.h"
+#include "cam_soc_util.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+static int cam_bps_get_dt_properties(struct cam_hw_soc_info *soc_info)
+{
+ int rc = 0;
+
+ rc = cam_soc_util_get_dt_properties(soc_info);
+ if (rc < 0)
+ pr_err("get bps dt prop is failed\n");
+
+ return rc;
+}
+
+static int cam_bps_request_platform_resource(
+ struct cam_hw_soc_info *soc_info,
+ irq_handler_t bps_irq_handler, void *irq_data)
+{
+ int rc = 0;
+
+ rc = cam_soc_util_request_platform_resource(soc_info, bps_irq_handler,
+ irq_data);
+
+ return rc;
+}
+
+int cam_bps_init_soc_resources(struct cam_hw_soc_info *soc_info,
+ irq_handler_t bps_irq_handler, void *irq_data)
+{
+ int rc = 0;
+
+ rc = cam_bps_get_dt_properties(soc_info);
+ if (rc < 0)
+ return rc;
+
+ rc = cam_bps_request_platform_resource(soc_info, bps_irq_handler,
+ irq_data);
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+int cam_bps_enable_soc_resources(struct cam_hw_soc_info *soc_info)
+{
+ int rc = 0;
+
+ rc = cam_soc_util_enable_platform_resource(soc_info, true, false);
+ if (rc)
+ pr_err("%s: enable platform failed\n", __func__);
+
+ return rc;
+}
+
+int cam_bps_disable_soc_resources(struct cam_hw_soc_info *soc_info)
+{
+ int rc = 0;
+
+ rc = cam_soc_util_disable_platform_resource(soc_info, true, false);
+ if (rc)
+ pr_err("%s: disable platform failed\n", __func__);
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_soc.h b/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_soc.h
new file mode 100644
index 0000000..b16db01
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_soc.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_BPS_SOC_H_
+#define _CAM_BPS_SOC_H_
+
+#include "cam_soc_util.h"
+
+int cam_bps_init_soc_resources(struct cam_hw_soc_info *soc_info,
+ irq_handler_t bps_irq_handler, void *irq_data);
+
+int cam_bps_enable_soc_resources(struct cam_hw_soc_info *soc_info);
+
+int cam_bps_disable_soc_resources(struct cam_hw_soc_info *soc_info);
+
+#endif /* _CAM_BPS_SOC_H_*/
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/Makefile b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/Makefile
new file mode 100644
index 0000000..4a6c3c0
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/Makefile
@@ -0,0 +1,16 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/isp/isp_hw/include
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/icp_hw/include
+ccflags-y += -Idrivers/media/platform/msm/camera/isp/isp_hw/hw_utils/include
+ccflags-y += -Idrivers/media/platform/msm/camera/isp/isp_hw/isp_hw_mgr/
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/fw_inc/
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_smmu/
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_core
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sync
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr/
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/include/
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/icp_hw/a5_hw/
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
+
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_icp_hw_mgr.o
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
new file mode 100644
index 0000000..489ded1
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
@@ -0,0 +1,2007 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "ICP-HW-MGR %s:%d " fmt, __func__, __LINE__
+
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/timer.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <media/cam_defs.h>
+#include <media/cam_icp.h>
+#include "cam_sync_api.h"
+#include "cam_hw.h"
+#include "cam_hw_mgr_intf.h"
+#include "cam_icp_hw_mgr_intf.h"
+#include "cam_icp_hw_mgr.h"
+#include "cam_a5_hw_intf.h"
+#include "cam_bps_hw_intf.h"
+#include "cam_ipe_hw_intf.h"
+#include "cam_smmu_api.h"
+#include "cam_mem_mgr.h"
+#include "hfi_intf.h"
+#include "hfi_reg.h"
+#include "hfi_session_defs.h"
+#include "hfi_sys_defs.h"
+#include "cam_req_mgr_workq.h"
+#include "cam_mem_mgr.h"
+#include "a5_core.h"
+#include "hfi_sys_defs.h"
+
+#undef ICP_DBG
+#define ICP_DBG(fmt, args...) pr_debug(fmt, ##args)
+
+#define ICP_WORKQ_NUM_TASK 30
+#define ICP_WORKQ_TASK_CMD_TYPE 1
+#define ICP_WORKQ_TASK_MSG_TYPE 2
+
+static struct cam_icp_hw_mgr icp_hw_mgr;
+
+static int cam_icp_stop_cpas(struct cam_icp_hw_mgr *hw_mgr_priv)
+{
+ struct cam_hw_intf *a5_dev_intf = NULL;
+ struct cam_hw_intf *ipe0_dev_intf = NULL;
+ struct cam_hw_intf *ipe1_dev_intf = NULL;
+ struct cam_hw_intf *bps_dev_intf = NULL;
+ struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv;
+ struct cam_icp_cpas_vote cpas_vote;
+ int rc = 0;
+
+ if (!hw_mgr) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ a5_dev_intf = hw_mgr->devices[CAM_ICP_DEV_A5][0];
+ bps_dev_intf = hw_mgr->devices[CAM_ICP_DEV_BPS][0];
+ ipe0_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][0];
+
+ if ((!a5_dev_intf) || (!bps_dev_intf) || (!ipe0_dev_intf)) {
+ pr_err("dev intfs are NULL\n");
+ return -EINVAL;
+ }
+
+ rc = a5_dev_intf->hw_ops.process_cmd(
+ a5_dev_intf->hw_priv,
+ CAM_ICP_A5_CMD_CPAS_STOP,
+ &cpas_vote,
+ sizeof(struct cam_icp_cpas_vote));
+ if (rc < 0)
+ pr_err("CAM_ICP_A5_CMD_CPAS_STOP is failed: %d\n", rc);
+
+ rc = bps_dev_intf->hw_ops.process_cmd(
+ bps_dev_intf->hw_priv,
+ CAM_ICP_BPS_CMD_CPAS_STOP,
+ &cpas_vote,
+ sizeof(struct cam_icp_cpas_vote));
+ if (rc < 0)
+ pr_err("CAM_ICP_BPS_CMD_CPAS_STOP is failed: %d\n", rc);
+
+ rc = ipe0_dev_intf->hw_ops.process_cmd(
+ ipe0_dev_intf->hw_priv,
+ CAM_ICP_IPE_CMD_CPAS_STOP,
+ &cpas_vote,
+ sizeof(struct cam_icp_cpas_vote));
+ if (rc < 0)
+ pr_err("CAM_ICP_IPE_CMD_CPAS_STOP is failed: %d\n", rc);
+
+ ipe1_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][1];
+ if (!ipe1_dev_intf)
+ return rc;
+
+ rc = ipe1_dev_intf->hw_ops.process_cmd(
+ ipe1_dev_intf->hw_priv,
+ CAM_ICP_IPE_CMD_CPAS_STOP,
+ &cpas_vote,
+ sizeof(struct cam_icp_cpas_vote));
+ if (rc < 0)
+ pr_err("CAM_ICP_IPE_CMD_CPAS_STOP is failed: %d\n", rc);
+
+ return rc;
+}
+
+static int cam_icp_start_cpas(struct cam_icp_hw_mgr *hw_mgr_priv)
+{
+ struct cam_hw_intf *a5_dev_intf = NULL;
+ struct cam_hw_intf *ipe0_dev_intf = NULL;
+ struct cam_hw_intf *ipe1_dev_intf = NULL;
+ struct cam_hw_intf *bps_dev_intf = NULL;
+ struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv;
+ struct cam_icp_cpas_vote cpas_vote;
+ int rc = 0;
+
+ if (!hw_mgr) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ a5_dev_intf = hw_mgr->devices[CAM_ICP_DEV_A5][0];
+ bps_dev_intf = hw_mgr->devices[CAM_ICP_DEV_BPS][0];
+ ipe0_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][0];
+
+ if ((!a5_dev_intf) || (!bps_dev_intf) || (!ipe0_dev_intf)) {
+ pr_err("dev intfs are null\n");
+ return -EINVAL;
+ }
+
+ cpas_vote.ahb_vote.type = CAM_VOTE_ABSOLUTE;
+ cpas_vote.ahb_vote.vote.level = CAM_TURBO_VOTE;
+ cpas_vote.axi_vote.compressed_bw = 640000000;
+ cpas_vote.axi_vote.uncompressed_bw = 640000000;
+
+ rc = a5_dev_intf->hw_ops.process_cmd(
+ a5_dev_intf->hw_priv,
+ CAM_ICP_A5_CMD_CPAS_START,
+ &cpas_vote,
+ sizeof(struct cam_icp_cpas_vote));
+ if (rc) {
+ pr_err("CAM_ICP_A5_CMD_CPAS_START is failed: %d\n", rc);
+ goto a5_cpas_start_failed;
+ }
+
+ rc = bps_dev_intf->hw_ops.process_cmd(
+ bps_dev_intf->hw_priv,
+ CAM_ICP_BPS_CMD_CPAS_START,
+ &cpas_vote,
+ sizeof(struct cam_icp_cpas_vote));
+ if (rc < 0) {
+ pr_err("CAM_ICP_BPS_CMD_CPAS_START is failed: %d\n", rc);
+ goto bps_cpas_start_failed;
+ }
+
+ rc = ipe0_dev_intf->hw_ops.process_cmd(
+ ipe0_dev_intf->hw_priv,
+ CAM_ICP_IPE_CMD_CPAS_START,
+ &cpas_vote,
+ sizeof(struct cam_icp_cpas_vote));
+ if (rc < 0) {
+ pr_err("CAM_ICP_IPE_CMD_CPAS_START is failed: %d\n", rc);
+ goto ipe0_cpas_start_failed;
+ }
+
+ ipe1_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][1];
+ if (!ipe1_dev_intf)
+ return rc;
+
+ rc = ipe1_dev_intf->hw_ops.process_cmd(
+ ipe1_dev_intf->hw_priv,
+ CAM_ICP_IPE_CMD_CPAS_START,
+ &cpas_vote,
+ sizeof(struct cam_icp_cpas_vote));
+ if (rc < 0) {
+ pr_err("CAM_ICP_IPE_CMD_CPAS_START is failed: %d\n", rc);
+ goto ipe1_cpas_start_failed;
+ }
+
+ return rc;
+
+ipe1_cpas_start_failed:
+ rc = ipe0_dev_intf->hw_ops.process_cmd(
+ ipe0_dev_intf->hw_priv,
+ CAM_ICP_IPE_CMD_CPAS_STOP,
+ &cpas_vote,
+ sizeof(struct cam_icp_cpas_vote));
+ipe0_cpas_start_failed:
+ rc = bps_dev_intf->hw_ops.process_cmd(
+ bps_dev_intf->hw_priv,
+ CAM_ICP_BPS_CMD_CPAS_STOP,
+ &cpas_vote,
+ sizeof(struct cam_icp_cpas_vote));
+bps_cpas_start_failed:
+ rc = a5_dev_intf->hw_ops.process_cmd(
+ a5_dev_intf->hw_priv,
+ CAM_ICP_A5_CMD_CPAS_STOP,
+ &cpas_vote,
+ sizeof(struct cam_icp_cpas_vote));
+a5_cpas_start_failed:
+ return rc;
+}
+
+static int cam_icp_mgr_process_cmd(void *priv, void *data)
+{
+ int rc;
+ struct hfi_cmd_work_data *task_data = NULL;
+ struct cam_icp_hw_mgr *hw_mgr;
+
+ if (!data || !priv) {
+ pr_err("Invalid params%pK %pK\n", data, priv);
+ return -EINVAL;
+ }
+
+ hw_mgr = priv;
+ task_data = (struct hfi_cmd_work_data *)data;
+
+ rc = hfi_write_cmd(task_data->data);
+ if (rc < 0)
+ pr_err("unable to write\n");
+
+ ICP_DBG("task type : %u, rc : %d\n", task_data->type, rc);
+ return rc;
+}
+
+static int cam_icp_mgr_process_msg_frame_process(uint32_t *msg_ptr)
+{
+ int i;
+ uint32_t idx;
+ uint32_t request_id;
+ struct cam_icp_hw_ctx_data *ctx_data = NULL;
+ struct hfi_msg_ipebps_async_ack *ioconfig_ack = NULL;
+ struct hfi_msg_frame_process_done *frame_done;
+ struct hfi_frame_process_info *hfi_frame_process;
+ struct cam_hw_done_event_data buf_data;
+
+ ioconfig_ack = (struct hfi_msg_ipebps_async_ack *)msg_ptr;
+ if (ioconfig_ack->err_type != HFI_ERR_SYS_NONE) {
+ pr_err("failed with error : %u\n", ioconfig_ack->err_type);
+ return -EIO;
+ }
+
+ frame_done =
+ (struct hfi_msg_frame_process_done *)ioconfig_ack->msg_data;
+ if (frame_done->result) {
+ pr_err("result : %u\n", frame_done->result);
+ return -EIO;
+ }
+ ICP_DBG("result : %u\n", frame_done->result);
+
+ ctx_data = (struct cam_icp_hw_ctx_data *)ioconfig_ack->user_data1;
+ request_id = ioconfig_ack->user_data2;
+ ICP_DBG("ctx : %pK, request_id :%d\n",
+ (void *)ctx_data->context_priv, request_id);
+
+ hfi_frame_process = &ctx_data->hfi_frame_process;
+ for (i = 0; i < CAM_FRAME_CMD_MAX; i++)
+ if (hfi_frame_process->request_id[i] == request_id)
+ break;
+
+ if (i >= CAM_FRAME_CMD_MAX) {
+ pr_err("unable to find pkt in ctx data for req_id =%d\n",
+ request_id);
+ return -EINVAL;
+ }
+ idx = i;
+
+ /* send event to ctx this needs to be done in msg handler */
+ buf_data.num_handles = hfi_frame_process->num_out_resources[idx];
+ for (i = 0; i < buf_data.num_handles; i++)
+ buf_data.resource_handle[i] =
+ hfi_frame_process->out_resource[idx][i];
+
+ ctx_data->ctxt_event_cb(ctx_data->context_priv, 0, &buf_data);
+
+ /* now release memory for hfi frame process command */
+ ICP_DBG("matching request id: %d\n",
+ hfi_frame_process->request_id[idx]);
+ mutex_lock(&ctx_data->hfi_frame_process.lock);
+ hfi_frame_process->request_id[idx] = 0;
+ clear_bit(idx, ctx_data->hfi_frame_process.bitmap);
+ mutex_unlock(&ctx_data->hfi_frame_process.lock);
+ return 0;
+}
+
+static int cam_icp_mgr_process_msg_config_io(uint32_t *msg_ptr)
+{
+ struct cam_icp_hw_ctx_data *ctx_data = NULL;
+ struct hfi_msg_ipebps_async_ack *ioconfig_ack = NULL;
+ struct hfi_msg_ipe_config *ipe_config_ack = NULL;
+ struct hfi_msg_bps_common *bps_config_ack = NULL;
+
+ ioconfig_ack = (struct hfi_msg_ipebps_async_ack *)msg_ptr;
+ ICP_DBG("opcode : %u\n", ioconfig_ack->opcode);
+
+ if (ioconfig_ack->opcode == HFI_IPEBPS_CMD_OPCODE_IPE_CONFIG_IO) {
+ ipe_config_ack =
+ (struct hfi_msg_ipe_config *)(ioconfig_ack->msg_data);
+ if (ipe_config_ack->rc) {
+ pr_err("rc = %d err = %u\n",
+ ipe_config_ack->rc, ioconfig_ack->err_type);
+ return -EIO;
+ }
+ ctx_data =
+ (struct cam_icp_hw_ctx_data *)ioconfig_ack->user_data1;
+ mutex_lock(&ctx_data->ctx_mutex);
+ ctx_data->scratch_mem_size = ipe_config_ack->scratch_mem_size;
+ mutex_unlock(&ctx_data->ctx_mutex);
+ ICP_DBG("scratch_mem_size = %u\n",
+ ipe_config_ack->scratch_mem_size);
+ } else {
+ bps_config_ack =
+ (struct hfi_msg_bps_common *)(ioconfig_ack->msg_data);
+ if (bps_config_ack->rc) {
+ pr_err("rc : %u, opcode :%u\n",
+ bps_config_ack->rc, ioconfig_ack->opcode);
+ return -EIO;
+ }
+ ctx_data =
+ (struct cam_icp_hw_ctx_data *)ioconfig_ack->user_data1;
+ }
+ complete(&ctx_data->wait_complete);
+
+ return 0;
+}
+
+static int cam_icp_mgr_process_msg_create_handle(uint32_t *msg_ptr)
+{
+ struct hfi_msg_create_handle_ack *create_handle_ack = NULL;
+ struct cam_icp_hw_ctx_data *ctx_data = NULL;
+
+ create_handle_ack = (struct hfi_msg_create_handle_ack *)msg_ptr;
+ if (!create_handle_ack) {
+ pr_err("Invalid create_handle_ack\n");
+ return -EINVAL;
+ }
+
+ ICP_DBG("err type : %u\n", create_handle_ack->err_type);
+
+ ctx_data = (struct cam_icp_hw_ctx_data *)create_handle_ack->user_data1;
+ if (!ctx_data) {
+ pr_err("Invalid ctx_data\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&ctx_data->ctx_mutex);
+ ctx_data->fw_handle = create_handle_ack->fw_handle;
+ mutex_unlock(&ctx_data->ctx_mutex);
+ ICP_DBG("fw_handle = %x\n", ctx_data->fw_handle);
+ complete(&ctx_data->wait_complete);
+
+ return 0;
+}
+
+static int cam_icp_mgr_process_msg_ping_ack(uint32_t *msg_ptr)
+{
+ struct hfi_msg_ping_ack *ping_ack = NULL;
+ struct cam_icp_hw_ctx_data *ctx_data = NULL;
+
+ ping_ack = (struct hfi_msg_ping_ack *)msg_ptr;
+ if (!ping_ack) {
+ pr_err("Empty ping ack message\n");
+ return -EINVAL;
+ }
+
+ ctx_data = (struct cam_icp_hw_ctx_data *)ping_ack->user_data;
+ if (!ctx_data) {
+ pr_err("Invalid ctx_data\n");
+ return -EINVAL;
+ }
+
+ ICP_DBG("%x %x %pK\n", ping_ack->size, ping_ack->pkt_type,
+ (void *)ping_ack->user_data);
+ complete(&ctx_data->wait_complete);
+
+ return 0;
+}
+
+static int cam_icp_mgr_process_indirect_ack_msg(uint32_t *msg_ptr)
+{
+ int rc;
+
+ switch (msg_ptr[ICP_PACKET_IPCODE]) {
+ case HFI_IPEBPS_CMD_OPCODE_IPE_CONFIG_IO:
+ case HFI_IPEBPS_CMD_OPCODE_BPS_CONFIG_IO:
+ ICP_DBG("received HFI_IPEBPS_CMD_OPCODE_IPE/BPS_CONFIG_IO:\n");
+ rc = cam_icp_mgr_process_msg_config_io(msg_ptr);
+ if (rc < 0) {
+ pr_err("error in process_msg_config_io\n");
+ return rc;
+ }
+ break;
+
+ case HFI_IPEBPS_CMD_OPCODE_IPE_FRAME_PROCESS:
+ case HFI_IPEBPS_CMD_OPCODE_BPS_FRAME_PROCESS:
+ ICP_DBG("received OPCODE_IPE/BPS_FRAME_PROCESS:\n");
+ rc = cam_icp_mgr_process_msg_frame_process(msg_ptr);
+ if (rc < 0) {
+ pr_err("error in msg_frame_process\n");
+ return rc;
+ }
+ break;
+ default:
+ pr_err("Invalid opcode : %u\n",
+ msg_ptr[ICP_PACKET_IPCODE]);
+ break;
+ }
+
+ return 0;
+}
+
+static int cam_icp_mgr_process_direct_ack_msg(uint32_t *msg_ptr)
+{
+ struct cam_icp_hw_ctx_data *ctx_data = NULL;
+ struct hfi_msg_ipebps_async_ack *ioconfig_ack = NULL;
+
+ if (msg_ptr[ICP_PACKET_IPCODE] ==
+ HFI_IPEBPS_CMD_OPCODE_IPE_DESTROY ||
+ msg_ptr[ICP_PACKET_IPCODE] ==
+ HFI_IPEBPS_CMD_OPCODE_BPS_DESTROY) {
+ ICP_DBG("received HFI_IPEBPS_CMD_OPCODE_IPE/BPS_DESTROY:\n");
+ ioconfig_ack = (struct hfi_msg_ipebps_async_ack *)msg_ptr;
+ ctx_data =
+ (struct cam_icp_hw_ctx_data *)ioconfig_ack->user_data1;
+ complete(&ctx_data->wait_complete);
+
+ } else {
+ pr_err("Invalid opcode : %u\n", msg_ptr[ICP_PACKET_IPCODE]);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int32_t cam_icp_mgr_process_msg(void *priv, void *data)
+{
+ int rc = 0;
+ uint32_t *msg_ptr = NULL;
+ struct hfi_msg_work_data *task_data;
+ struct cam_icp_hw_mgr *hw_mgr;
+ int read_len;
+
+ if (!data || !priv) {
+ pr_err("Invalid data\n");
+ return -EINVAL;
+ }
+
+ task_data = data;
+ hw_mgr = priv;
+ ICP_DBG("irq status : %u\n", task_data->irq_status);
+
+ read_len = hfi_read_message(icp_hw_mgr.msg_buf, Q_MSG);
+ if (read_len < 0) {
+ ICP_DBG("Unable to read msg q\n");
+ return read_len;
+ }
+
+ msg_ptr = (uint32_t *)icp_hw_mgr.msg_buf;
+ ICP_DBG("packet type: %x\n", msg_ptr[ICP_PACKET_TYPE]);
+
+ mutex_lock(&hw_mgr->hw_mgr_mutex);
+ switch (msg_ptr[ICP_PACKET_TYPE]) {
+ case HFI_MSG_SYS_INIT_DONE:
+ ICP_DBG("received HFI_MSG_SYS_INIT_DONE\n");
+ complete(&hw_mgr->a5_complete);
+ break;
+
+ case HFI_MSG_SYS_PING_ACK:
+ ICP_DBG("received HFI_MSG_SYS_PING_ACK\n");
+ rc = cam_icp_mgr_process_msg_ping_ack(msg_ptr);
+ if (rc)
+ pr_err("fail process PING_ACK\n");
+ break;
+
+ case HFI_MSG_IPEBPS_CREATE_HANDLE_ACK:
+ ICP_DBG("received HFI_MSG_IPEBPS_CREATE_HANDLE_ACK\n");
+ rc = cam_icp_mgr_process_msg_create_handle(msg_ptr);
+ if (rc)
+ pr_err("fail process CREATE_HANDLE_ACK\n");
+ break;
+
+ case HFI_MSG_IPEBPS_ASYNC_COMMAND_INDIRECT_ACK:
+ rc = cam_icp_mgr_process_indirect_ack_msg(msg_ptr);
+ if (rc)
+ pr_err("fail process INDIRECT_ACK\n");
+ break;
+
+ case HFI_MSG_IPEBPS_ASYNC_COMMAND_DIRECT_ACK:
+ rc = cam_icp_mgr_process_direct_ack_msg(msg_ptr);
+ if (rc)
+ pr_err("fail process DIRECT_ACK\n");
+ break;
+
+ case HFI_MSG_EVENT_NOTIFY:
+ ICP_DBG("received HFI_MSG_EVENT_NOTIFY\n");
+ break;
+
+ default:
+ pr_err("invalid msg : %u\n", msg_ptr[ICP_PACKET_TYPE]);
+ break;
+ }
+
+ mutex_unlock(&icp_hw_mgr.hw_mgr_mutex);
+
+ return rc;
+}
+
+int32_t cam_icp_hw_mgr_cb(uint32_t irq_status, void *data)
+{
+ int32_t rc = 0;
+ unsigned long flags;
+ struct cam_icp_hw_mgr *hw_mgr = data;
+ struct crm_workq_task *task;
+ struct hfi_msg_work_data *task_data;
+
+ spin_lock_irqsave(&hw_mgr->hw_mgr_lock, flags);
+ task = cam_req_mgr_workq_get_task(icp_hw_mgr.msg_work);
+ if (!task) {
+ pr_err("no empty task\n");
+ spin_unlock_irqrestore(&hw_mgr->hw_mgr_lock, flags);
+ return -ENOMEM;
+ }
+
+ task_data = (struct hfi_msg_work_data *)task->payload;
+ task_data->data = hw_mgr;
+ task_data->irq_status = irq_status;
+ task_data->type = ICP_WORKQ_TASK_MSG_TYPE;
+ task->process_cb = cam_icp_mgr_process_msg;
+ rc = cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr,
+ CRM_TASK_PRIORITY_0);
+ spin_unlock_irqrestore(&hw_mgr->hw_mgr_lock, flags);
+
+ return rc;
+}
+
+static int cam_icp_free_hfi_mem(void)
+{
+ cam_smmu_dealloc_firmware(icp_hw_mgr.iommu_hdl);
+ cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.qtbl);
+ cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.cmd_q);
+ cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.msg_q);
+ cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.dbg_q);
+ cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.sec_heap);
+
+ return 0;
+}
+
+static int cam_icp_allocate_hfi_mem(void)
+{
+ int rc;
+ struct cam_mem_mgr_request_desc alloc;
+ struct cam_mem_mgr_memory_desc out;
+ dma_addr_t iova;
+ uint64_t kvaddr;
+ size_t len;
+
+ pr_err("Allocating FW for iommu handle: %x\n", icp_hw_mgr.iommu_hdl);
+ rc = cam_smmu_alloc_firmware(icp_hw_mgr.iommu_hdl,
+ &iova, &kvaddr, &len);
+ if (rc < 0) {
+ pr_err("Unable to allocate FW memory\n");
+ return -ENOMEM;
+ }
+
+ icp_hw_mgr.hfi_mem.fw_buf.len = len;
+ icp_hw_mgr.hfi_mem.fw_buf.kva = kvaddr;
+ icp_hw_mgr.hfi_mem.fw_buf.iova = iova;
+ icp_hw_mgr.hfi_mem.fw_buf.smmu_hdl = icp_hw_mgr.iommu_hdl;
+
+ ICP_DBG("kva = %llX\n", kvaddr);
+ ICP_DBG("IOVA = %llX\n", iova);
+ ICP_DBG("length = %zu\n", len);
+
+ memset(&alloc, 0, sizeof(alloc));
+ memset(&out, 0, sizeof(out));
+ alloc.size = SZ_1M;
+ alloc.align = 0;
+ alloc.region = CAM_MEM_MGR_REGION_SHARED;
+ alloc.smmu_hdl = icp_hw_mgr.iommu_hdl;
+ rc = cam_mem_mgr_request_mem(&alloc, &out);
+ if (rc < 0) {
+ pr_err("Unable to allocate qtbl memory\n");
+ goto qtbl_alloc_failed;
+ }
+ icp_hw_mgr.hfi_mem.qtbl = out;
+
+ ICP_DBG("kva = %llX\n", out.kva);
+ ICP_DBG("qtbl IOVA = %X\n", out.iova);
+ ICP_DBG("SMMU HDL = %X\n", out.smmu_hdl);
+ ICP_DBG("MEM HDL = %X\n", out.mem_handle);
+ ICP_DBG("length = %lld\n", out.len);
+ ICP_DBG("region = %d\n", out.region);
+
+ /* Allocate memory for cmd queue */
+ memset(&alloc, 0, sizeof(alloc));
+ memset(&out, 0, sizeof(out));
+ alloc.size = SZ_1M;
+ alloc.align = 0;
+ alloc.region = CAM_MEM_MGR_REGION_SHARED;
+ alloc.smmu_hdl = icp_hw_mgr.iommu_hdl;
+ rc = cam_mem_mgr_request_mem(&alloc, &out);
+ if (rc < 0) {
+ pr_err("Unable to allocate cmd q memory\n");
+ goto cmd_q_alloc_failed;
+ }
+ icp_hw_mgr.hfi_mem.cmd_q = out;
+
+ ICP_DBG("kva = %llX\n", out.kva);
+ ICP_DBG("cmd_q IOVA = %X\n", out.iova);
+ ICP_DBG("SMMU HDL = %X\n", out.smmu_hdl);
+ ICP_DBG("MEM HDL = %X\n", out.mem_handle);
+ ICP_DBG("length = %lld\n", out.len);
+ ICP_DBG("region = %d\n", out.region);
+
+ /* Allocate memory for msg queue */
+ memset(&alloc, 0, sizeof(alloc));
+ memset(&out, 0, sizeof(out));
+ alloc.size = SZ_1M;
+ alloc.align = 0;
+ alloc.region = CAM_MEM_MGR_REGION_SHARED;
+ alloc.smmu_hdl = icp_hw_mgr.iommu_hdl;
+ rc = cam_mem_mgr_request_mem(&alloc, &out);
+ if (rc < 0) {
+ pr_err("Unable to allocate msg q memory\n");
+ goto msg_q_alloc_failed;
+ }
+ icp_hw_mgr.hfi_mem.msg_q = out;
+
+ ICP_DBG("kva = %llX\n", out.kva);
+ ICP_DBG("msg_q IOVA = %X\n", out.iova);
+ ICP_DBG("SMMU HDL = %X\n", out.smmu_hdl);
+ ICP_DBG("MEM HDL = %X\n", out.mem_handle);
+ ICP_DBG("length = %lld\n", out.len);
+ ICP_DBG("region = %d\n", out.region);
+
+ /* Allocate memory for dbg queue */
+ memset(&alloc, 0, sizeof(alloc));
+ memset(&out, 0, sizeof(out));
+ alloc.size = SZ_1M;
+ alloc.align = 0;
+ alloc.region = CAM_MEM_MGR_REGION_SHARED;
+ alloc.smmu_hdl = icp_hw_mgr.iommu_hdl;
+ rc = cam_mem_mgr_request_mem(&alloc, &out);
+ if (rc < 0) {
+ pr_err("Unable to allocate dbg q memory\n");
+ goto dbg_q_alloc_failed;
+ }
+ icp_hw_mgr.hfi_mem.dbg_q = out;
+
+ ICP_DBG("kva = %llX\n", out.kva);
+ ICP_DBG("dbg_q IOVA = %X\n", out.iova);
+ ICP_DBG("SMMU HDL = %X\n", out.smmu_hdl);
+ ICP_DBG("MEM HDL = %X\n", out.mem_handle);
+ ICP_DBG("length = %lld\n", out.len);
+ ICP_DBG("region = %d\n", out.region);
+
+ /* Allocate memory for sec heap queue */
+ memset(&alloc, 0, sizeof(alloc));
+ memset(&out, 0, sizeof(out));
+ alloc.size = SZ_1M;
+ alloc.align = 0;
+ alloc.region = CAM_MEM_MGR_REGION_SHARED;
+ alloc.smmu_hdl = icp_hw_mgr.iommu_hdl;
+ rc = cam_mem_mgr_request_mem(&alloc, &out);
+ if (rc < 0) {
+ pr_err("Unable to allocate sec heap q memory\n");
+ goto sec_heap_alloc_failed;
+ }
+ icp_hw_mgr.hfi_mem.sec_heap = out;
+
+ ICP_DBG("kva = %llX\n", out.kva);
+ ICP_DBG("sec_heap IOVA = %X\n", out.iova);
+ ICP_DBG("SMMU HDL = %X\n", out.smmu_hdl);
+ ICP_DBG("MEM HDL = %X\n", out.mem_handle);
+ ICP_DBG("length = %lld\n", out.len);
+ ICP_DBG("region = %d\n", out.region);
+
+ return rc;
+
+sec_heap_alloc_failed:
+ cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.dbg_q);
+dbg_q_alloc_failed:
+ cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.msg_q);
+msg_q_alloc_failed:
+ cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.cmd_q);
+cmd_q_alloc_failed:
+ cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.qtbl);
+qtbl_alloc_failed:
+ cam_smmu_dealloc_firmware(icp_hw_mgr.iommu_hdl);
+ pr_err("returned with error : %d\n", rc);
+
+ return rc;
+}
+
+static int cam_icp_mgr_get_free_ctx(struct cam_icp_hw_mgr *hw_mgr)
+{
+ int i = 0;
+ int num_ctx = CAM_ICP_CTX_MAX;
+
+ for (i = 0; i < num_ctx; i++) {
+ mutex_lock(&hw_mgr->ctx_data[i].ctx_mutex);
+ if (hw_mgr->ctx_data[i].in_use == 0) {
+ hw_mgr->ctx_data[i].in_use = 1;
+ mutex_unlock(&hw_mgr->ctx_data[i].ctx_mutex);
+ break;
+ }
+ mutex_unlock(&hw_mgr->ctx_data[i].ctx_mutex);
+ }
+
+ return i;
+}
+
+static int cam_icp_mgr_destroy_handle(
+ struct cam_icp_hw_ctx_data *ctx_data,
+ struct crm_workq_task *task)
+{
+ int rc = 0;
+ int timeout = 5000;
+ struct hfi_cmd_work_data *task_data;
+ struct hfi_cmd_ipebps_async destroy_cmd;
+ unsigned long rem_jiffies;
+
+ destroy_cmd.size =
+ sizeof(struct hfi_cmd_ipebps_async) +
+ sizeof(struct ipe_bps_destroy) -
+ sizeof(destroy_cmd.payload.direct);
+ destroy_cmd.pkt_type = HFI_CMD_IPEBPS_ASYNC_COMMAND_DIRECT;
+ if (ctx_data->icp_dev_acquire_info.dev_type == CAM_ICP_RES_TYPE_BPS)
+ destroy_cmd.opcode = HFI_IPEBPS_CMD_OPCODE_BPS_DESTROY;
+ else
+ destroy_cmd.opcode = HFI_IPEBPS_CMD_OPCODE_IPE_DESTROY;
+
+ reinit_completion(&ctx_data->wait_complete);
+ destroy_cmd.num_fw_handles = 1;
+ destroy_cmd.fw_handles[0] = ctx_data->fw_handle;
+ destroy_cmd.user_data1 = (uint64_t)ctx_data;
+ destroy_cmd.user_data2 = (uint64_t)0x0;
+ memcpy(destroy_cmd.payload.direct, &ctx_data->temp_payload,
+ sizeof(uint32_t));
+
+ task_data = (struct hfi_cmd_work_data *)task->payload;
+ task_data->data = (void *)&destroy_cmd;
+ task_data->request_id = 0;
+ task_data->type = ICP_WORKQ_TASK_CMD_TYPE;
+ task->process_cb = cam_icp_mgr_process_cmd;
+ cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr, CRM_TASK_PRIORITY_0);
+ ICP_DBG("fw_handle = %x ctx_data = %pK\n",
+ ctx_data->fw_handle, ctx_data);
+ rem_jiffies = wait_for_completion_timeout(&ctx_data->wait_complete,
+ msecs_to_jiffies((timeout)));
+ if (!rem_jiffies) {
+ rc = -ETIMEDOUT;
+ pr_err("timeout/err in iconfig command: %d\n", rc);
+ }
+
+ return rc;
+}
+
+static int cam_icp_mgr_release_ctx(struct cam_icp_hw_mgr *hw_mgr, int ctx_id)
+{
+ struct crm_workq_task *task;
+ int i = 0;
+
+ if (ctx_id >= CAM_ICP_CTX_MAX) {
+ pr_err("ctx_id is wrong: %d\n", ctx_id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
+ if (!hw_mgr->ctx_data[ctx_id].in_use) {
+ pr_err("ctx is already in use: %d\n", ctx_id);
+ mutex_unlock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
+ return -EINVAL;
+ }
+ mutex_unlock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
+
+ mutex_lock(&hw_mgr->hw_mgr_mutex);
+ task = cam_req_mgr_workq_get_task(icp_hw_mgr.cmd_work);
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ if (task)
+ cam_icp_mgr_destroy_handle(&hw_mgr->ctx_data[ctx_id], task);
+
+ mutex_lock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
+ hw_mgr->ctx_data[ctx_id].in_use = 0;
+ hw_mgr->ctx_data[ctx_id].fw_handle = 0;
+ hw_mgr->ctx_data[ctx_id].scratch_mem_size = 0;
+ mutex_lock(&hw_mgr->ctx_data[ctx_id].hfi_frame_process.lock);
+ for (i = 0; i < CAM_FRAME_CMD_MAX; i++)
+ clear_bit(i, hw_mgr->ctx_data[ctx_id].hfi_frame_process.bitmap);
+ mutex_unlock(&hw_mgr->ctx_data[ctx_id].hfi_frame_process.lock);
+ mutex_destroy(&hw_mgr->ctx_data[ctx_id].hfi_frame_process.lock);
+ mutex_unlock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
+ kfree(hw_mgr->ctx_data[ctx_id].hfi_frame_process.bitmap);
+
+ return 0;
+}
+
+static int cam_icp_mgr_get_ctx_from_fw_handle(struct cam_icp_hw_mgr *hw_mgr,
+ uint32_t fw_handle)
+{
+ int ctx_id;
+
+ for (ctx_id = 0; ctx_id < CAM_ICP_CTX_MAX; ctx_id++) {
+ mutex_lock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
+ if (hw_mgr->ctx_data[ctx_id].in_use) {
+ if (hw_mgr->ctx_data[ctx_id].fw_handle == fw_handle) {
+ mutex_unlock(
+ &hw_mgr->ctx_data[ctx_id].ctx_mutex);
+ return ctx_id;
+ }
+ }
+ mutex_unlock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
+ }
+ ICP_DBG("Invalid fw handle to get ctx\n");
+
+ return -EINVAL;
+}
+
+static int cam_icp_mgr_hw_close(void *hw_priv, void *hw_close_args)
+{
+ struct cam_icp_hw_mgr *hw_mgr = hw_priv;
+ struct cam_hw_intf *a5_dev_intf = NULL;
+ struct cam_hw_intf *ipe0_dev_intf = NULL;
+ struct cam_hw_intf *ipe1_dev_intf = NULL;
+ struct cam_hw_intf *bps_dev_intf = NULL;
+ int rc = 0;
+
+ a5_dev_intf = hw_mgr->devices[CAM_ICP_DEV_A5][0];
+ ipe0_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][0];
+ bps_dev_intf = hw_mgr->devices[CAM_ICP_DEV_BPS][0];
+
+ if ((!a5_dev_intf) || (!ipe0_dev_intf) || (!bps_dev_intf)) {
+ pr_err("dev intfs are wrong\n");
+ return rc;
+ }
+ mutex_lock(&hw_mgr->hw_mgr_mutex);
+ rc = a5_dev_intf->hw_ops.deinit(a5_dev_intf->hw_priv, NULL, 0);
+ if (rc < 0)
+ pr_err("a5 dev de-init failed\n");
+
+ rc = bps_dev_intf->hw_ops.deinit(bps_dev_intf->hw_priv, NULL, 0);
+ if (rc < 0)
+ pr_err("bps dev de-init failed\n");
+
+ rc = ipe0_dev_intf->hw_ops.deinit(ipe0_dev_intf->hw_priv, NULL, 0);
+ if (rc < 0)
+ pr_err("ipe0 dev de-init failed\n");
+
+ ipe1_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][1];
+ if (ipe1_dev_intf) {
+ rc = ipe1_dev_intf->hw_ops.deinit(ipe1_dev_intf->hw_priv,
+ NULL, 0);
+ if (rc < 0)
+ pr_err("ipe1 dev de-init failed\n");
+ }
+
+ cam_icp_free_hfi_mem();
+ hw_mgr->fw_download = false;
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+
+ return 0;
+}
+
+static int cam_icp_mgr_download_fw(void *hw_mgr_priv, void *download_fw_args)
+{
+ struct cam_hw_intf *a5_dev_intf = NULL;
+ struct cam_hw_intf *ipe0_dev_intf = NULL;
+ struct cam_hw_intf *ipe1_dev_intf = NULL;
+ struct cam_hw_intf *bps_dev_intf = NULL;
+ struct cam_hw_info *a5_dev = NULL;
+ struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv;
+ struct cam_icp_a5_set_irq_cb irq_cb;
+ struct cam_icp_a5_set_fw_buf_info fw_buf_info;
+ struct hfi_mem_info hfi_mem;
+ int rc = 0;
+
+ if (!hw_mgr) {
+ pr_err("hw_mgr is NULL\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&hw_mgr->hw_mgr_mutex);
+ if (hw_mgr->fw_download) {
+ ICP_DBG("FW already downloaded\n");
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ return rc;
+ }
+
+ /* Allocate memory for FW and shared memory */
+ rc = cam_icp_allocate_hfi_mem();
+ if (rc < 0) {
+ pr_err("hfi mem alloc failed\n");
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ return rc;
+ }
+
+ a5_dev_intf = hw_mgr->devices[CAM_ICP_DEV_A5][0];
+ ipe0_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][0];
+ ipe1_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][1];
+ bps_dev_intf = hw_mgr->devices[CAM_ICP_DEV_BPS][0];
+
+ if ((!a5_dev_intf) || (!ipe0_dev_intf) || (!bps_dev_intf)) {
+ pr_err("dev intfs are wrong\n");
+ goto dev_intf_fail;
+ }
+
+ a5_dev = (struct cam_hw_info *)a5_dev_intf->hw_priv;
+
+ rc = a5_dev_intf->hw_ops.init(a5_dev_intf->hw_priv, NULL, 0);
+ if (rc < 0) {
+ pr_err("a5 dev init failed\n");
+ goto a5_dev_init_failed;
+ }
+ rc = bps_dev_intf->hw_ops.init(bps_dev_intf->hw_priv, NULL, 0);
+ if (rc < 0) {
+ pr_err("bps dev init failed\n");
+ goto bps_dev_init_failed;
+ }
+ rc = ipe0_dev_intf->hw_ops.init(ipe0_dev_intf->hw_priv, NULL, 0);
+ if (rc < 0) {
+ pr_err("ipe0 dev init failed\n");
+ goto ipe0_dev_init_failed;
+ }
+
+ if (ipe1_dev_intf) {
+ rc = ipe1_dev_intf->hw_ops.init(ipe1_dev_intf->hw_priv,
+ NULL, 0);
+ if (rc < 0) {
+ pr_err("ipe1 dev init failed\n");
+ goto ipe1_dev_init_failed;
+ }
+ }
+ /* Set IRQ callback */
+ irq_cb.icp_hw_mgr_cb = cam_icp_hw_mgr_cb;
+ irq_cb.data = hw_mgr_priv;
+ rc = a5_dev_intf->hw_ops.process_cmd(
+ a5_dev_intf->hw_priv,
+ CAM_ICP_A5_SET_IRQ_CB,
+ &irq_cb, sizeof(irq_cb));
+ if (rc < 0) {
+ pr_err("CAM_ICP_A5_SET_IRQ_CB failed\n");
+ rc = -EINVAL;
+ goto set_irq_failed;
+ }
+
+ fw_buf_info.kva = icp_hw_mgr.hfi_mem.fw_buf.kva;
+ fw_buf_info.iova = icp_hw_mgr.hfi_mem.fw_buf.iova;
+ fw_buf_info.len = icp_hw_mgr.hfi_mem.fw_buf.len;
+
+ rc = a5_dev_intf->hw_ops.process_cmd(
+ a5_dev_intf->hw_priv,
+ CAM_ICP_A5_CMD_SET_FW_BUF,
+ &fw_buf_info,
+ sizeof(fw_buf_info));
+ if (rc < 0) {
+ pr_err("CAM_ICP_A5_CMD_SET_FW_BUF failed\n");
+ goto set_irq_failed;
+ }
+
+ cam_hfi_enable_cpu(a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base);
+
+ rc = a5_dev_intf->hw_ops.process_cmd(
+ a5_dev_intf->hw_priv,
+ CAM_ICP_A5_CMD_FW_DOWNLOAD,
+ NULL, 0);
+ if (rc < 0) {
+ pr_err("FW download is failed\n");
+ goto set_irq_failed;
+ }
+
+ hfi_mem.qtbl.kva = icp_hw_mgr.hfi_mem.qtbl.kva;
+ hfi_mem.qtbl.iova = icp_hw_mgr.hfi_mem.qtbl.iova;
+ hfi_mem.qtbl.len = icp_hw_mgr.hfi_mem.qtbl.len;
+ ICP_DBG("kva = %llX\n", hfi_mem.qtbl.kva);
+ ICP_DBG("IOVA = %X\n", hfi_mem.qtbl.iova);
+ ICP_DBG("length = %lld\n", hfi_mem.qtbl.len);
+
+ hfi_mem.cmd_q.kva = icp_hw_mgr.hfi_mem.cmd_q.kva;
+ hfi_mem.cmd_q.iova = icp_hw_mgr.hfi_mem.cmd_q.iova;
+ hfi_mem.cmd_q.len = icp_hw_mgr.hfi_mem.cmd_q.len;
+ ICP_DBG("kva = %llX\n", hfi_mem.cmd_q.kva);
+ ICP_DBG("IOVA = %X\n", hfi_mem.cmd_q.iova);
+ ICP_DBG("length = %lld\n", hfi_mem.cmd_q.len);
+
+ hfi_mem.msg_q.kva = icp_hw_mgr.hfi_mem.msg_q.kva;
+ hfi_mem.msg_q.iova = icp_hw_mgr.hfi_mem.msg_q.iova;
+ hfi_mem.msg_q.len = icp_hw_mgr.hfi_mem.msg_q.len;
+ ICP_DBG("kva = %llX\n", hfi_mem.msg_q.kva);
+ ICP_DBG("IOVA = %X\n", hfi_mem.msg_q.iova);
+ ICP_DBG("length = %lld\n", hfi_mem.msg_q.len);
+
+ hfi_mem.dbg_q.kva = icp_hw_mgr.hfi_mem.dbg_q.kva;
+ hfi_mem.dbg_q.iova = icp_hw_mgr.hfi_mem.dbg_q.iova;
+ hfi_mem.dbg_q.len = icp_hw_mgr.hfi_mem.dbg_q.len;
+ ICP_DBG("kva = %llX\n", hfi_mem.dbg_q.kva);
+ ICP_DBG("IOVA = %X\n", hfi_mem.dbg_q.iova);
+ ICP_DBG("length = %lld\n", hfi_mem.dbg_q.len);
+
+ hfi_mem.sec_heap.kva = icp_hw_mgr.hfi_mem.sec_heap.kva;
+ hfi_mem.sec_heap.iova = icp_hw_mgr.hfi_mem.sec_heap.iova;
+ hfi_mem.sec_heap.len = icp_hw_mgr.hfi_mem.sec_heap.len;
+
+ rc = cam_hfi_init(0, &hfi_mem,
+ a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base,
+ false);
+ if (rc < 0) {
+ pr_err("hfi_init is failed\n");
+ goto set_irq_failed;
+ }
+
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+
+ ICP_DBG("Sending HFI init command\n");
+ reinit_completion(&hw_mgr->a5_complete);
+
+ rc = a5_dev_intf->hw_ops.process_cmd(
+ a5_dev_intf->hw_priv,
+ CAM_ICP_A5_SEND_INIT,
+ NULL, 0);
+
+ ICP_DBG("Wait for INIT DONE Message\n");
+ wait_for_completion(&hw_mgr->a5_complete);
+
+ ICP_DBG("Done Waiting for INIT DONE Message\n");
+
+ rc = a5_dev_intf->hw_ops.process_cmd(
+ a5_dev_intf->hw_priv,
+ CAM_ICP_A5_CMD_POWER_COLLAPSE,
+ NULL, 0);
+
+ hw_mgr->fw_download = true;
+
+ rc = cam_icp_stop_cpas(hw_mgr);
+ if (rc) {
+ pr_err("cpas stop failed\n");
+ goto set_irq_failed;
+ }
+
+ hw_mgr->ctxt_cnt = 0;
+
+ return rc;
+
+set_irq_failed:
+ if (ipe1_dev_intf)
+ rc = ipe1_dev_intf->hw_ops.deinit(ipe1_dev_intf->hw_priv,
+ NULL, 0);
+ipe1_dev_init_failed:
+ rc = ipe0_dev_intf->hw_ops.deinit(ipe0_dev_intf->hw_priv, NULL, 0);
+ipe0_dev_init_failed:
+ rc = bps_dev_intf->hw_ops.deinit(bps_dev_intf->hw_priv, NULL, 0);
+bps_dev_init_failed:
+ rc = a5_dev_intf->hw_ops.deinit(a5_dev_intf->hw_priv, NULL, 0);
+a5_dev_init_failed:
+dev_intf_fail:
+ cam_icp_free_hfi_mem();
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ return rc;
+}
+
+static int cam_icp_mgr_config_hw(void *hw_mgr_priv, void *config_hw_args)
+{
+ int rc = 0;
+ struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv;
+ struct cam_hw_config_args *config_args = config_hw_args;
+ uint32_t fw_handle;
+ int ctx_id = 0;
+ struct cam_icp_hw_ctx_data *ctx_data = NULL;
+ int32_t request_id = 0;
+ struct cam_hw_update_entry *hw_update_entries;
+ struct crm_workq_task *task;
+ struct hfi_cmd_work_data *task_data;
+ struct hfi_cmd_ipebps_async *hfi_cmd;
+
+ if (!hw_mgr || !config_args) {
+ pr_err("Invalid arguments %pK %pK\n",
+ hw_mgr, config_args);
+ return -EINVAL;
+ }
+
+ if (!config_args->num_hw_update_entries) {
+ pr_err("No hw update enteries are available\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&hw_mgr->hw_mgr_mutex);
+ fw_handle = *(uint32_t *)config_args->ctxt_to_hw_map;
+ ctx_id = cam_icp_mgr_get_ctx_from_fw_handle(hw_mgr, fw_handle);
+ if (ctx_id < 0) {
+ pr_err("Fw handle to ctx mapping is failed\n");
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ return -EINVAL;
+ }
+
+ ctx_data = &hw_mgr->ctx_data[ctx_id];
+ if (!ctx_data->in_use) {
+ pr_err("ctx is not in use\n");
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ return -EINVAL;
+ }
+
+ request_id = *(uint32_t *)config_args->priv;
+ hw_update_entries = config_args->hw_update_entries;
+ ICP_DBG("req_id = %d\n", request_id);
+ ICP_DBG("fw_handle = %x req_id = %d %pK\n",
+ fw_handle, request_id, config_args->priv);
+ task = cam_req_mgr_workq_get_task(icp_hw_mgr.cmd_work);
+ if (!task) {
+ pr_err("no empty task\n");
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ return -ENOMEM;
+ }
+
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+
+ task_data = (struct hfi_cmd_work_data *)task->payload;
+ if (!task_data) {
+ pr_err("task_data is NULL\n");
+ return -EINVAL;
+ }
+
+ task_data->data = (void *)hw_update_entries->addr;
+ hfi_cmd = (struct hfi_cmd_ipebps_async *)hw_update_entries->addr;
+ ICP_DBG("request from hfi_cmd :%llu, hfi_cmd: %pK\n",
+ hfi_cmd->user_data2, hfi_cmd);
+ task_data->request_id = request_id;
+ task_data->type = ICP_WORKQ_TASK_CMD_TYPE;
+ task->process_cb = cam_icp_mgr_process_cmd;
+ rc = cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr,
+ CRM_TASK_PRIORITY_0);
+ return rc;
+}
+
+static int cam_icp_mgr_prepare_frame_process_cmd(
+ struct cam_icp_hw_ctx_data *ctx_data,
+ struct hfi_cmd_ipebps_async *hfi_cmd,
+ uint32_t request_id,
+ uint32_t fw_cmd_buf_iova_addr)
+{
+ hfi_cmd->size = sizeof(struct hfi_cmd_ipebps_async);
+ hfi_cmd->pkt_type = HFI_CMD_IPEBPS_ASYNC_COMMAND_INDIRECT;
+ if (ctx_data->icp_dev_acquire_info.dev_type == CAM_ICP_RES_TYPE_BPS)
+ hfi_cmd->opcode = HFI_IPEBPS_CMD_OPCODE_BPS_FRAME_PROCESS;
+ else
+ hfi_cmd->opcode = HFI_IPEBPS_CMD_OPCODE_IPE_FRAME_PROCESS;
+ hfi_cmd->num_fw_handles = 1;
+ hfi_cmd->fw_handles[0] = ctx_data->fw_handle;
+ hfi_cmd->payload.indirect = fw_cmd_buf_iova_addr;
+ hfi_cmd->user_data1 = (uint64_t)ctx_data;
+ hfi_cmd->user_data2 = request_id;
+
+ ICP_DBG("ctx_data : %pK, request_id :%d cmd_buf %x\n",
+ (void *)ctx_data->context_priv,
+ request_id, fw_cmd_buf_iova_addr);
+
+ return 0;
+}
+
+static int cam_icp_mgr_prepare_hw_update(void *hw_mgr_priv,
+ void *prepare_hw_update_args)
+{
+ int rc = 0, i, j;
+ int ctx_id = 0;
+ uint32_t fw_handle;
+ int32_t idx;
+ uint64_t iova_addr, cpu_addr;
+ uint32_t fw_cmd_buf_iova_addr;
+ uint32_t temp;
+ uint32_t *dst_cpu_addr;
+ uint32_t *src_buf_iova_addr;
+ size_t fw_cmd_buf_len;
+ size_t dst_buf_len;
+ size_t src_buf_size;
+ int32_t sync_in_obj[CAM_ICP_IPE_IMAGE_MAX];
+ int32_t merged_sync_in_obj;
+
+
+ struct cam_hw_prepare_update_args *prepare_args =
+ prepare_hw_update_args;
+ struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv;
+ struct cam_icp_hw_ctx_data *ctx_data = NULL;
+ struct cam_packet *packet = NULL;
+ struct cam_cmd_buf_desc *cmd_desc = NULL;
+ struct cam_buf_io_cfg *io_cfg_ptr = NULL;
+ struct cam_patch_desc *patch_desc = NULL;
+ struct hfi_cmd_ipebps_async *hfi_cmd = NULL;
+
+ if ((!prepare_args) || (!hw_mgr)) {
+ pr_err("Invalid args\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&hw_mgr->hw_mgr_mutex);
+ fw_handle = *(uint32_t *)prepare_args->ctxt_to_hw_map;
+ ctx_id = cam_icp_mgr_get_ctx_from_fw_handle(hw_mgr, fw_handle);
+ if (ctx_id < 0) {
+ pr_err("Fw handle to ctx mapping is failed\n");
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ return -EINVAL;
+ }
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+
+ ctx_data = &hw_mgr->ctx_data[ctx_id];
+ if (!ctx_data->in_use) {
+ pr_err("ctx is not in use\n");
+ return -EINVAL;
+ }
+
+ packet = prepare_args->packet;
+ if (!packet) {
+ pr_err("received packet is NULL\n");
+ return -EINVAL;
+ }
+
+ ICP_DBG("packet header : opcode = %x size = %x",
+ packet->header.op_code,
+ packet->header.size);
+
+ ICP_DBG(" req_id = %x flags = %x\n",
+ (uint32_t)packet->header.request_id,
+ packet->header.flags);
+
+ ICP_DBG("packet data : c_off = %x c_num = %x\n",
+ packet->cmd_buf_offset,
+ packet->num_cmd_buf);
+
+ ICP_DBG("io_off = %x io_num = %x p_off = %x p_num = %x %x %x\n",
+ packet->io_configs_offset,
+ packet->num_io_configs, packet->patch_offset,
+ packet->num_patches, packet->kmd_cmd_buf_index,
+ packet->kmd_cmd_buf_offset);
+
+ if (((packet->header.op_code & 0xff) !=
+ CAM_ICP_OPCODE_IPE_UPDATE) &&
+ ((packet->header.op_code & 0xff) !=
+ CAM_ICP_OPCODE_BPS_UPDATE)) {
+ pr_err("Invalid Opcode in pkt: %d\n",
+ packet->header.op_code & 0xff);
+ return -EINVAL;
+ }
+
+ if ((packet->num_cmd_buf > 1) || (!packet->num_patches) ||
+ (!packet->num_io_configs)) {
+ pr_err("wrong number of cmd/patch info: %u %u\n",
+ packet->num_cmd_buf,
+ packet->num_patches);
+ return -EINVAL;
+ }
+
+ /* process command buffer descriptors */
+ cmd_desc = (struct cam_cmd_buf_desc *)
+ ((uint32_t *) &packet->payload +
+ packet->cmd_buf_offset/4);
+ ICP_DBG("packet = %pK cmd_desc = %pK size = %lu\n",
+ (void *)packet, (void *)cmd_desc,
+ sizeof(struct cam_cmd_buf_desc));
+
+ rc = cam_mem_get_io_buf(cmd_desc->mem_handle,
+ hw_mgr->iommu_hdl, &iova_addr, &fw_cmd_buf_len);
+ if (rc < 0) {
+ pr_err("unable to get src buf info for cmd buf: %x\n",
+ hw_mgr->iommu_hdl);
+ return rc;
+ }
+ ICP_DBG("cmd_buf desc cpu and iova address: %pK %zu\n",
+ (void *)iova_addr, fw_cmd_buf_len);
+ fw_cmd_buf_iova_addr = iova_addr;
+ fw_cmd_buf_iova_addr = (fw_cmd_buf_iova_addr + cmd_desc->offset);
+
+ /* process patch descriptor */
+ patch_desc = (struct cam_patch_desc *)
+ ((uint32_t *) &packet->payload +
+ packet->patch_offset/4);
+ ICP_DBG("packet = %pK patch_desc = %pK size = %lu\n",
+ (void *)packet, (void *)patch_desc,
+ sizeof(struct cam_patch_desc));
+
+ for (i = 0; i < packet->num_patches; i++) {
+ rc = cam_mem_get_io_buf(patch_desc[i].src_buf_hdl,
+ hw_mgr->iommu_hdl, &iova_addr, &src_buf_size);
+ if (rc < 0) {
+ pr_err("unable to get src buf address\n");
+ return rc;
+ }
+ src_buf_iova_addr = (uint32_t *)iova_addr;
+ temp = iova_addr;
+
+ rc = cam_mem_get_cpu_buf(patch_desc[i].dst_buf_hdl,
+ &cpu_addr, &dst_buf_len);
+ if (rc < 0) {
+ pr_err("unable to get dst buf address\n");
+ return rc;
+ }
+ dst_cpu_addr = (uint32_t *)cpu_addr;
+
+ ICP_DBG("i = %d patch info = %x %x %x %x\n", i,
+ patch_desc[i].dst_buf_hdl, patch_desc[i].dst_offset,
+ patch_desc[i].src_buf_hdl, patch_desc[i].src_offset);
+
+ dst_cpu_addr = (uint32_t *)((uint8_t *)dst_cpu_addr +
+ patch_desc[i].dst_offset);
+ temp += patch_desc[i].src_offset;
+
+ *dst_cpu_addr = temp;
+
+ ICP_DBG("patch is done for dst %pK with src %pK value %llx\n",
+ dst_cpu_addr, src_buf_iova_addr,
+ *((uint64_t *)dst_cpu_addr));
+ }
+
+ /* process io config out descriptors */
+ io_cfg_ptr = (struct cam_buf_io_cfg *) ((uint32_t *) &packet->payload +
+ packet->io_configs_offset/4);
+ ICP_DBG("packet = %pK io_cfg_ptr = %pK size = %lu\n",
+ (void *)packet, (void *)io_cfg_ptr,
+ sizeof(struct cam_buf_io_cfg));
+
+ prepare_args->num_out_map_entries = 0;
+ for (i = 0, j = 0; i < packet->num_io_configs; i++) {
+ if (io_cfg_ptr[i].direction == CAM_BUF_INPUT) {
+ ICP_DBG("direction is i : %d :%u\n",
+ i, io_cfg_ptr[i].direction);
+ ICP_DBG("fence is i : %d :%d\n",
+ i, io_cfg_ptr[i].fence);
+ continue;
+ }
+
+ prepare_args->out_map_entries[j].sync_id = io_cfg_ptr[i].fence;
+ prepare_args->out_map_entries[j++].resource_handle =
+ io_cfg_ptr[i].fence;
+ prepare_args->num_out_map_entries++;
+ ICP_DBG(" out fence = %x index = %d\n", io_cfg_ptr[i].fence, i);
+ }
+ ICP_DBG("out buf entries processing is done\n");
+
+ /* process io config in descriptors */
+ for (i = 0, j = 0; i < packet->num_io_configs; i++) {
+ if (io_cfg_ptr[i].direction == CAM_BUF_INPUT) {
+ sync_in_obj[j++] = io_cfg_ptr[i].fence;
+ ICP_DBG(" in fence = %x index = %d\n",
+ io_cfg_ptr[i].fence, i);
+ }
+ }
+
+ if (j == 1)
+ merged_sync_in_obj = sync_in_obj[j - 1];
+ else if (j > 1) {
+ rc = cam_sync_merge(&sync_in_obj[0], j, &merged_sync_in_obj);
+ if (rc < 0) {
+ pr_err("unable to create in merged object: %d\n",
+ rc);
+ return rc;
+ }
+ } else {
+ pr_err("no input fence provided %u\n", j);
+ return -EINVAL;
+ }
+
+ prepare_args->in_map_entries[0].sync_id = merged_sync_in_obj;
+ prepare_args->in_map_entries[0].resource_handle =
+ ctx_data->icp_dev_acquire_info.dev_type;
+ prepare_args->num_in_map_entries = 1;
+ ICP_DBG("out buf entries processing is done\n");
+
+ mutex_lock(&ctx_data->hfi_frame_process.lock);
+ idx = find_first_zero_bit(ctx_data->hfi_frame_process.bitmap,
+ ctx_data->hfi_frame_process.bits);
+ if (idx < 0 || idx >= CAM_FRAME_CMD_MAX) {
+ pr_err("request idx is wrong: %d\n", idx);
+ mutex_unlock(&ctx_data->hfi_frame_process.lock);
+ return -EINVAL;
+ }
+ set_bit(idx, ctx_data->hfi_frame_process.bitmap);
+ mutex_unlock(&ctx_data->hfi_frame_process.lock);
+
+ ctx_data->hfi_frame_process.request_id[idx] = packet->header.request_id;
+ ICP_DBG("slot[%d]: %d\n", idx,
+ ctx_data->hfi_frame_process.request_id[idx]);
+ ctx_data->hfi_frame_process.num_out_resources[idx] =
+ prepare_args->num_out_map_entries;
+ for (i = 0; i < prepare_args->num_out_map_entries; i++)
+ ctx_data->hfi_frame_process.out_resource[idx][i] =
+ prepare_args->out_map_entries[i].resource_handle;
+
+ hfi_cmd = (struct hfi_cmd_ipebps_async *)
+ &ctx_data->hfi_frame_process.hfi_frame_cmd[idx];
+
+ cam_icp_mgr_prepare_frame_process_cmd(
+ ctx_data, hfi_cmd, packet->header.request_id,
+ fw_cmd_buf_iova_addr);
+
+ prepare_args->num_hw_update_entries = 1;
+ prepare_args->hw_update_entries[0].addr = (uint64_t)hfi_cmd;
+
+ prepare_args->priv = &ctx_data->hfi_frame_process.request_id[idx];
+
+ ICP_DBG("slot : %d, hfi_cmd : %pK, request : %d\n", idx,
+ (void *)hfi_cmd,
+ ctx_data->hfi_frame_process.request_id[idx]);
+
+ return rc;
+}
+
+static int cam_icp_mgr_release_hw(void *hw_mgr_priv, void *release_hw_args)
+{
+ int rc = 0;
+ int ctx_id = 0;
+ int i;
+ uint32_t fw_handle;
+ struct cam_hw_release_args *release_hw = release_hw_args;
+ struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv;
+ struct cam_icp_hw_ctx_data *ctx_data = NULL;
+
+ if (!release_hw || !hw_mgr) {
+ pr_err("Invalid args\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < CAM_ICP_CTX_MAX; i++) {
+ ctx_data = &hw_mgr->ctx_data[i];
+ ICP_DBG("i = %d in_use = %u fw_handle = %u\n", i,
+ ctx_data->in_use, ctx_data->fw_handle);
+ }
+
+ mutex_lock(&hw_mgr->hw_mgr_mutex);
+ fw_handle = *(uint32_t *)release_hw->ctxt_to_hw_map;
+ ctx_id = cam_icp_mgr_get_ctx_from_fw_handle(hw_mgr, fw_handle);
+ if (ctx_id < 0) {
+ pr_err("Invalid ctx id\n");
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ return -EINVAL;
+ }
+
+ rc = cam_icp_mgr_release_ctx(hw_mgr, ctx_id);
+ if (rc) {
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ return -EINVAL;
+ }
+
+ --hw_mgr->ctxt_cnt;
+ if (!hw_mgr->ctxt_cnt) {
+ ICP_DBG("stop cpas for last context\n");
+ cam_icp_stop_cpas(hw_mgr);
+ }
+ ICP_DBG("context count : %u\n", hw_mgr->ctxt_cnt);
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+
+ ICP_DBG("fw handle %d\n", fw_handle);
+ return rc;
+}
+
+static int cam_icp_mgr_send_config_io(struct cam_icp_hw_ctx_data *ctx_data,
+ struct crm_workq_task *task, uint32_t io_buf_addr)
+{
+ int rc = 0;
+ struct hfi_cmd_work_data *task_data;
+ struct hfi_cmd_ipebps_async ioconfig_cmd;
+
+ ioconfig_cmd.size = sizeof(struct hfi_cmd_ipebps_async);
+ ioconfig_cmd.pkt_type = HFI_CMD_IPEBPS_ASYNC_COMMAND_INDIRECT;
+ if (ctx_data->icp_dev_acquire_info.dev_type == CAM_ICP_RES_TYPE_BPS)
+ ioconfig_cmd.opcode = HFI_IPEBPS_CMD_OPCODE_BPS_CONFIG_IO;
+ else
+ ioconfig_cmd.opcode = HFI_IPEBPS_CMD_OPCODE_IPE_CONFIG_IO;
+
+ reinit_completion(&ctx_data->wait_complete);
+ ICP_DBG("Sending HFI_CMD_IPEBPS_ASYNC_COMMAND: opcode :%u\n",
+ ioconfig_cmd.opcode);
+ ioconfig_cmd.num_fw_handles = 1;
+ ioconfig_cmd.fw_handles[0] = ctx_data->fw_handle;
+ ioconfig_cmd.payload.indirect = io_buf_addr;
+ ioconfig_cmd.user_data1 = (uint64_t)ctx_data;
+ ioconfig_cmd.user_data2 = (uint64_t)0x0;
+ task_data = (struct hfi_cmd_work_data *)task->payload;
+ task_data->data = (void *)&ioconfig_cmd;
+ task_data->request_id = 0;
+ task_data->type = ICP_WORKQ_TASK_CMD_TYPE;
+ task->process_cb = cam_icp_mgr_process_cmd;
+ cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr, CRM_TASK_PRIORITY_0);
+ ICP_DBG("fw_hdl = %x ctx_data = %pK\n", ctx_data->fw_handle, ctx_data);
+ wait_for_completion(&ctx_data->wait_complete);
+
+ return rc;
+}
+
+static int cam_icp_mgr_create_handle(uint32_t dev_type,
+ struct cam_icp_hw_ctx_data *ctx_data,
+ struct crm_workq_task *task)
+{
+ struct hfi_cmd_create_handle create_handle;
+ struct hfi_cmd_work_data *task_data;
+ int rc = 0;
+
+ create_handle.size = sizeof(struct hfi_cmd_create_handle);
+ create_handle.pkt_type = HFI_CMD_IPEBPS_CREATE_HANDLE;
+ create_handle.handle_type = dev_type;
+ create_handle.user_data1 = (uint64_t)ctx_data;
+ ICP_DBG("%x %x %x %pK\n", create_handle.size, create_handle.pkt_type,
+ create_handle.handle_type, (void *)create_handle.user_data1);
+ ICP_DBG("Sending HFI_CMD_IPEBPS_CREATE_HANDLE\n");
+
+ reinit_completion(&ctx_data->wait_complete);
+ task_data = (struct hfi_cmd_work_data *)task->payload;
+ task_data->data = (void *)&create_handle;
+ task_data->request_id = 0;
+ task_data->type = ICP_WORKQ_TASK_CMD_TYPE;
+ task->process_cb = cam_icp_mgr_process_cmd;
+ cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr, CRM_TASK_PRIORITY_0);
+ wait_for_completion(&ctx_data->wait_complete);
+
+ return rc;
+}
+
+static int cam_icp_mgr_send_ping(struct cam_icp_hw_ctx_data *ctx_data,
+ struct crm_workq_task *task)
+{
+ struct hfi_cmd_ping_pkt ping_pkt;
+ struct hfi_cmd_work_data *task_data;
+ int rc = 0;
+
+ ping_pkt.size = sizeof(struct hfi_cmd_ping_pkt);
+ ping_pkt.pkt_type = HFI_CMD_SYS_PING;
+ ping_pkt.user_data = (uint64_t)ctx_data;
+ ICP_DBG("Sending HFI_CMD_SYS_PING\n");
+ ICP_DBG("%x %x %pK\n", ping_pkt.size, ping_pkt.pkt_type,
+ (void *)ping_pkt.user_data);
+
+ init_completion(&ctx_data->wait_complete);
+ task_data = (struct hfi_cmd_work_data *)task->payload;
+ task_data->data = (void *)&ping_pkt;
+ task_data->request_id = 0;
+ task_data->type = ICP_WORKQ_TASK_CMD_TYPE;
+ task->process_cb = cam_icp_mgr_process_cmd;
+ cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr, CRM_TASK_PRIORITY_0);
+ wait_for_completion(&ctx_data->wait_complete);
+
+ return rc;
+}
+
+static int cam_icp_mgr_acquire_hw(void *hw_mgr_priv, void *acquire_hw_args)
+{
+ int rc = 0, i, bitmap_size = 0, tmp_size;
+ uint32_t ctx_id = 0;
+ uint64_t io_buf_addr;
+ size_t io_buf_size;
+ struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv;
+ struct cam_icp_hw_ctx_data *ctx_data = NULL;
+ struct cam_hw_acquire_args *args = acquire_hw_args;
+ struct cam_icp_acquire_dev_info icp_dev_acquire_info;
+ struct cam_icp_res_info *p_icp_out = NULL;
+ struct crm_workq_task *task;
+ uint8_t *tmp_acquire;
+
+ if ((!hw_mgr_priv) || (!acquire_hw_args)) {
+ pr_err("Invalid params: %pK %pK\n", hw_mgr_priv,
+ acquire_hw_args);
+ return -EINVAL;
+ }
+
+ if (args->num_acq > 1) {
+ pr_err("number of resources are wrong: %u\n", args->num_acq);
+ return -EINVAL;
+ }
+
+ if (copy_from_user(&icp_dev_acquire_info,
+ (void __user *)args->acquire_info,
+ sizeof(icp_dev_acquire_info)))
+ return -EFAULT;
+
+ if (icp_dev_acquire_info.num_out_res > ICP_IPE_MAX_OUTPUT_SUPPORTED) {
+ pr_err("num of out resources exceeding : %u\n",
+ icp_dev_acquire_info.num_out_res);
+ return -EINVAL;
+ }
+
+ ICP_DBG("%x %x %x %x %x %x %x\n",
+ icp_dev_acquire_info.dev_type,
+ icp_dev_acquire_info.in_res.format,
+ icp_dev_acquire_info.in_res.width,
+ icp_dev_acquire_info.in_res.height,
+ icp_dev_acquire_info.in_res.fps,
+ icp_dev_acquire_info.num_out_res,
+ icp_dev_acquire_info.scratch_mem_size);
+
+ tmp_size = sizeof(icp_dev_acquire_info) +
+ icp_dev_acquire_info.num_out_res *
+ sizeof(struct cam_icp_res_info);
+
+ tmp_acquire = kzalloc(tmp_size, GFP_KERNEL);
+ if (!tmp_acquire)
+ return -EINVAL;
+
+ if (copy_from_user(tmp_acquire,
+ (void __user *)args->acquire_info,
+ tmp_size)) {
+ kfree(tmp_acquire);
+ return -EFAULT;
+ }
+
+ p_icp_out =
+ (struct cam_icp_res_info *)(tmp_acquire +
+ sizeof(icp_dev_acquire_info)-
+ sizeof(struct cam_icp_res_info));
+ ICP_DBG("out[0] %x %x %x %x\n",
+ p_icp_out[0].format,
+ p_icp_out[0].width,
+ p_icp_out[0].height,
+ p_icp_out[0].fps);
+
+ ICP_DBG("out[1] %x %x %x %x\n",
+ p_icp_out[1].format,
+ p_icp_out[1].width,
+ p_icp_out[1].height,
+ p_icp_out[1].fps);
+
+ mutex_lock(&hw_mgr->hw_mgr_mutex);
+ ctx_id = cam_icp_mgr_get_free_ctx(hw_mgr);
+ if (ctx_id >= CAM_ICP_CTX_MAX) {
+ pr_err("No free ctx space in hw_mgr\n");
+ kfree(tmp_acquire);
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ return -EFAULT;
+ }
+
+ /* Fill ctx with acquire info */
+ ctx_data = &hw_mgr->ctx_data[ctx_id];
+
+ if (!hw_mgr->ctxt_cnt++) {
+ ICP_DBG("starting cpas\n");
+ cam_icp_start_cpas(hw_mgr);
+ }
+ ICP_DBG("context count : %u\n", hw_mgr->ctxt_cnt);
+
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+
+ /* Fill ctx with acquire info */
+ mutex_lock(&ctx_data->ctx_mutex);
+ ctx_data->icp_dev_acquire_info = icp_dev_acquire_info;
+ for (i = 0; i < icp_dev_acquire_info.num_out_res; i++)
+ ctx_data->icp_out_acquire_info[i] = p_icp_out[i];
+ mutex_unlock(&ctx_data->ctx_mutex);
+
+ /* Get IOCONFIG command info */
+ if (ctx_data->icp_dev_acquire_info.secure_mode)
+ rc = cam_mem_get_io_buf(
+ icp_dev_acquire_info.io_config_cmd_handle,
+ hw_mgr->iommu_sec_hdl,
+ &io_buf_addr, &io_buf_size);
+ else
+ rc = cam_mem_get_io_buf(
+ icp_dev_acquire_info.io_config_cmd_handle,
+ hw_mgr->iommu_hdl,
+ &io_buf_addr, &io_buf_size);
+
+ ICP_DBG("io_config_cmd_handle : %d\n",
+ icp_dev_acquire_info.io_config_cmd_handle);
+ ICP_DBG("io_buf_addr : %pK\n", (void *)io_buf_addr);
+ ICP_DBG("io_buf_size : %zu\n", io_buf_size);
+ if (rc < 0) {
+ pr_err("unable to get src buf info from io desc\n");
+ goto cmd_cpu_buf_failed;
+ }
+
+ mutex_lock(&icp_hw_mgr.hw_mgr_mutex);
+ task = cam_req_mgr_workq_get_task(icp_hw_mgr.cmd_work);
+ if (!task) {
+ pr_err("no free task\n");
+ mutex_unlock(&icp_hw_mgr.hw_mgr_mutex);
+ goto get_create_task_failed;
+ }
+ mutex_unlock(&icp_hw_mgr.hw_mgr_mutex);
+
+ rc = cam_icp_mgr_send_ping(ctx_data, task);
+ if (rc) {
+ pr_err("ping ack not received\n");
+ goto create_handle_failed;
+ }
+ mutex_lock(&icp_hw_mgr.hw_mgr_mutex);
+ task = cam_req_mgr_workq_get_task(icp_hw_mgr.cmd_work);
+ if (!task) {
+ pr_err("no free task\n");
+ mutex_unlock(&icp_hw_mgr.hw_mgr_mutex);
+ goto get_create_task_failed;
+ }
+ mutex_unlock(&icp_hw_mgr.hw_mgr_mutex);
+
+ /* Send create fw handle command */
+ rc = cam_icp_mgr_create_handle(icp_dev_acquire_info.dev_type,
+ ctx_data, task);
+ if (rc) {
+ pr_err("create handle failed\n");
+ goto create_handle_failed;
+ }
+
+ /* Send IOCONFIG command */
+ mutex_lock(&icp_hw_mgr.hw_mgr_mutex);
+ task = cam_req_mgr_workq_get_task(icp_hw_mgr.cmd_work);
+ if (!task) {
+ pr_err("no empty task\n");
+ mutex_unlock(&icp_hw_mgr.hw_mgr_mutex);
+ goto get_ioconfig_task_failed;
+ }
+ mutex_unlock(&icp_hw_mgr.hw_mgr_mutex);
+
+ rc = cam_icp_mgr_send_config_io(ctx_data, task, io_buf_addr);
+ if (rc) {
+ pr_err("IO Config command failed\n");
+ goto ioconfig_failed;
+ }
+
+ mutex_lock(&ctx_data->ctx_mutex);
+ ctx_data->context_priv = args->context_data;
+ args->ctxt_to_hw_map = &ctx_data->fw_handle;
+
+ bitmap_size = BITS_TO_LONGS(CAM_FRAME_CMD_MAX) * sizeof(long);
+ ctx_data->hfi_frame_process.bitmap =
+ kzalloc(sizeof(bitmap_size), GFP_KERNEL);
+ ctx_data->hfi_frame_process.bits = bitmap_size * BITS_PER_BYTE;
+ mutex_init(&ctx_data->hfi_frame_process.lock);
+ mutex_unlock(&ctx_data->ctx_mutex);
+
+ hw_mgr->ctx_data[ctx_id].ctxt_event_cb = args->event_cb;
+
+ icp_dev_acquire_info.scratch_mem_size = ctx_data->scratch_mem_size;
+ if (copy_to_user((void __user *)args->acquire_info,
+ &icp_dev_acquire_info,
+ sizeof(icp_dev_acquire_info)))
+ goto copy_to_user_failed;
+
+ ICP_DBG("scratch mem size = %x fw_handle = %x\n",
+ (unsigned int)icp_dev_acquire_info.scratch_mem_size,
+ (unsigned int)ctx_data->fw_handle);
+ kfree(tmp_acquire);
+ return 0;
+
+copy_to_user_failed:
+ioconfig_failed:
+get_ioconfig_task_failed:
+ mutex_lock(&icp_hw_mgr.hw_mgr_mutex);
+ task = cam_req_mgr_workq_get_task(icp_hw_mgr.cmd_work);
+ mutex_unlock(&icp_hw_mgr.hw_mgr_mutex);
+ if (task)
+ cam_icp_mgr_destroy_handle(ctx_data, task);
+create_handle_failed:
+get_create_task_failed:
+cmd_cpu_buf_failed:
+ --hw_mgr->ctxt_cnt;
+ if (!hw_mgr->ctxt_cnt)
+ cam_icp_stop_cpas(hw_mgr);
+ cam_icp_mgr_release_ctx(hw_mgr, ctx_id);
+ kfree(tmp_acquire);
+ return rc;
+}
+
+static int cam_icp_mgr_get_hw_caps(void *hw_mgr_priv, void *hw_caps_args)
+{
+ int rc = 0;
+ struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv;
+ struct cam_query_cap_cmd *query_cap = hw_caps_args;
+
+ if ((!hw_mgr_priv) || (!hw_caps_args)) {
+ pr_err("Invalid params: %pK %pK\n", hw_mgr_priv, hw_caps_args);
+ return -EINVAL;
+ }
+
+ if (copy_from_user(&icp_hw_mgr.icp_caps,
+ (void __user *)query_cap->caps_handle,
+ sizeof(struct cam_icp_query_cap_cmd))) {
+ pr_err("copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ mutex_lock(&hw_mgr->hw_mgr_mutex);
+ rc = hfi_get_hw_caps(&icp_hw_mgr.icp_caps);
+ if (rc < 0) {
+ pr_err("Unable to get caps from HFI: %d\n", rc);
+ goto hfi_get_caps_fail;
+ }
+
+ icp_hw_mgr.icp_caps.dev_iommu_handle.non_secure = hw_mgr->iommu_hdl;
+ icp_hw_mgr.icp_caps.dev_iommu_handle.secure = hw_mgr->iommu_sec_hdl;
+
+ if (copy_to_user((void __user *)query_cap->caps_handle,
+ &icp_hw_mgr.icp_caps,
+ sizeof(struct cam_icp_query_cap_cmd))) {
+ pr_err("copy_to_user failed\n");
+ rc = -EFAULT;
+ goto hfi_get_caps_fail;
+ }
+
+hfi_get_caps_fail:
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ return rc;
+}
+
+int cam_icp_hw_mgr_init(struct device_node *of_node, uint64_t *hw_mgr_hdl)
+{
+ int count, i, rc = 0;
+ uint32_t num_dev;
+ uint32_t num_ipe_dev;
+ const char *name = NULL;
+ struct device_node *child_node = NULL;
+ struct platform_device *child_pdev = NULL;
+ struct cam_hw_intf *child_dev_intf = NULL;
+ struct cam_hw_mgr_intf *hw_mgr_intf;
+
+
+ hw_mgr_intf = (struct cam_hw_mgr_intf *)hw_mgr_hdl;
+ if (!of_node || !hw_mgr_intf) {
+ pr_err("Invalid args of_node %pK hw_mgr %pK\n",
+ of_node, hw_mgr_intf);
+ return -EINVAL;
+ }
+
+ hw_mgr_intf->hw_mgr_priv = &icp_hw_mgr;
+ hw_mgr_intf->hw_get_caps = cam_icp_mgr_get_hw_caps;
+ hw_mgr_intf->hw_acquire = cam_icp_mgr_acquire_hw;
+ hw_mgr_intf->hw_release = cam_icp_mgr_release_hw;
+ hw_mgr_intf->hw_prepare_update = cam_icp_mgr_prepare_hw_update;
+ hw_mgr_intf->hw_config = cam_icp_mgr_config_hw;
+ hw_mgr_intf->download_fw = cam_icp_mgr_download_fw;
+ hw_mgr_intf->hw_close = cam_icp_mgr_hw_close;
+
+ mutex_init(&icp_hw_mgr.hw_mgr_mutex);
+ spin_lock_init(&icp_hw_mgr.hw_mgr_lock);
+
+ for (i = 0; i < CAM_ICP_CTX_MAX; i++)
+ mutex_init(&icp_hw_mgr.ctx_data[i].ctx_mutex);
+
+ /* Get number of device objects */
+ count = of_property_count_strings(of_node, "compat-hw-name");
+ if (!count) {
+ pr_err("no compat hw found in dev tree, count = %d\n", count);
+ rc = -EINVAL;
+ goto num_dev_failed;
+ }
+
+ /* Get number of a5 device nodes and a5 mem allocation */
+ rc = of_property_read_u32(of_node, "num-a5", &num_dev);
+ if (rc < 0) {
+ pr_err("getting num of a5 failed\n");
+ goto num_dev_failed;
+ }
+
+ icp_hw_mgr.devices[CAM_ICP_DEV_A5] = kzalloc(
+ sizeof(struct cam_hw_intf *) * num_dev, GFP_KERNEL);
+ if (!icp_hw_mgr.devices[CAM_ICP_DEV_A5]) {
+ rc = -ENOMEM;
+ goto num_dev_failed;
+ }
+
+ /* Get number of ipe device nodes and ipe mem allocation */
+ rc = of_property_read_u32(of_node, "num-ipe", &num_ipe_dev);
+ if (rc < 0) {
+ pr_err("getting number of ipe dev nodes failed\n");
+ goto num_ipe_failed;
+ }
+
+ icp_hw_mgr.devices[CAM_ICP_DEV_IPE] = kzalloc(
+ sizeof(struct cam_hw_intf *) * num_ipe_dev, GFP_KERNEL);
+ if (!icp_hw_mgr.devices[CAM_ICP_DEV_IPE]) {
+ rc = -ENOMEM;
+ goto num_ipe_failed;
+ }
+
+ /* Get number of bps device nodes and bps mem allocation */
+ rc = of_property_read_u32(of_node, "num-bps", &num_dev);
+ if (rc < 0) {
+ pr_err("read num bps devices failed\n");
+ goto num_bps_failed;
+ }
+ icp_hw_mgr.devices[CAM_ICP_DEV_BPS] = kzalloc(
+ sizeof(struct cam_hw_intf *) * num_dev, GFP_KERNEL);
+ if (!icp_hw_mgr.devices[CAM_ICP_DEV_BPS]) {
+ rc = -ENOMEM;
+ goto num_bps_failed;
+ }
+
+ for (i = 0; i < count; i++) {
+ rc = of_property_read_string_index(of_node, "compat-hw-name",
+ i, &name);
+ if (rc < 0) {
+ pr_err("getting dev object name failed\n");
+ goto compat_hw_name_failed;
+ }
+
+ child_node = of_find_node_by_name(NULL, name);
+ if (!child_node) {
+ pr_err("error! Cannot find node in dtsi %s\n", name);
+ rc = -ENODEV;
+ goto compat_hw_name_failed;
+ }
+
+ child_pdev = of_find_device_by_node(child_node);
+ if (!child_pdev) {
+ pr_err("failed to find device on bus %s\n",
+ child_node->name);
+ rc = -ENODEV;
+ of_node_put(child_node);
+ goto compat_hw_name_failed;
+ }
+
+ child_dev_intf = (struct cam_hw_intf *)platform_get_drvdata(
+ child_pdev);
+ if (!child_dev_intf) {
+ pr_err("no child device\n");
+ of_node_put(child_node);
+ goto compat_hw_name_failed;
+ }
+ ICP_DBG("child_intf %pK\n", child_dev_intf);
+ ICP_DBG("child type %d index %d\n", child_dev_intf->hw_type,
+ child_dev_intf->hw_idx);
+
+ icp_hw_mgr.devices[child_dev_intf->hw_type]
+ [child_dev_intf->hw_idx] = child_dev_intf;
+
+ of_node_put(child_node);
+ }
+
+ rc = cam_smmu_get_handle("icp", &icp_hw_mgr.iommu_hdl);
+ if (rc < 0) {
+ pr_err("icp get iommu handle failed\n");
+ goto compat_hw_name_failed;
+ }
+
+ pr_err("mmu handle :%d\n", icp_hw_mgr.iommu_hdl);
+ rc = cam_smmu_ops(icp_hw_mgr.iommu_hdl, CAM_SMMU_ATTACH);
+ if (rc < 0) {
+ pr_err("icp attach failed: %d\n", rc);
+ goto icp_attach_failed;
+ }
+
+ rc = cam_req_mgr_workq_create("icp_command_queue", ICP_WORKQ_NUM_TASK,
+ &icp_hw_mgr.cmd_work);
+ if (rc < 0) {
+ pr_err("unable to create a worker\n");
+ goto cmd_work_failed;
+ }
+
+ rc = cam_req_mgr_workq_create("icp_message_queue", ICP_WORKQ_NUM_TASK,
+ &icp_hw_mgr.msg_work);
+ if (rc < 0) {
+ pr_err("unable to create a worker\n");
+ goto msg_work_failed;
+ }
+
+ icp_hw_mgr.cmd_work_data = (struct hfi_cmd_work_data *)
+ kzalloc(sizeof(struct hfi_cmd_work_data) * ICP_WORKQ_NUM_TASK,
+ GFP_KERNEL);
+ if (!icp_hw_mgr.cmd_work_data)
+ goto cmd_work_data_failed;
+
+ icp_hw_mgr.msg_work_data = (struct hfi_msg_work_data *)
+ kzalloc(sizeof(struct hfi_msg_work_data) * ICP_WORKQ_NUM_TASK,
+ GFP_KERNEL);
+ if (!icp_hw_mgr.msg_work_data)
+ goto msg_work_data_failed;
+
+
+ for (i = 0; i < ICP_WORKQ_NUM_TASK; i++)
+ icp_hw_mgr.msg_work->task.pool[i].payload =
+ &icp_hw_mgr.msg_work_data[i];
+
+ for (i = 0; i < ICP_WORKQ_NUM_TASK; i++)
+ icp_hw_mgr.cmd_work->task.pool[i].payload =
+ &icp_hw_mgr.cmd_work_data[i];
+
+ init_completion(&icp_hw_mgr.a5_complete);
+
+ pr_err("Exit\n");
+ return rc;
+
+msg_work_data_failed:
+ kfree(icp_hw_mgr.cmd_work_data);
+cmd_work_data_failed:
+ cam_req_mgr_workq_destroy(&icp_hw_mgr.msg_work);
+msg_work_failed:
+ cam_req_mgr_workq_destroy(&icp_hw_mgr.cmd_work);
+cmd_work_failed:
+ cam_smmu_ops(icp_hw_mgr.iommu_hdl, CAM_SMMU_DETACH);
+icp_attach_failed:
+ icp_hw_mgr.iommu_hdl = 0;
+compat_hw_name_failed:
+ kfree(icp_hw_mgr.devices[CAM_ICP_DEV_BPS]);
+num_bps_failed:
+ kfree(icp_hw_mgr.devices[CAM_ICP_DEV_IPE]);
+num_ipe_failed:
+ kfree(icp_hw_mgr.devices[CAM_ICP_DEV_A5]);
+num_dev_failed:
+ mutex_destroy(&icp_hw_mgr.hw_mgr_mutex);
+ for (i = 0; i < CAM_ICP_CTX_MAX; i++)
+ mutex_destroy(&icp_hw_mgr.ctx_data[i].ctx_mutex);
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h
new file mode 100644
index 0000000..e5ffa7a
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h
@@ -0,0 +1,181 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CAM_ICP_HW_MGR_H
+#define CAM_ICP_HW_MGR_H
+
+#include <linux/types.h>
+#include <linux/completion.h>
+#include <media/cam_icp.h>
+#include "cam_icp_hw_intf.h"
+#include "cam_hw_mgr_intf.h"
+#include "cam_hw_intf.h"
+#include "cam_a5_hw_intf.h"
+#include "hfi_session_defs.h"
+#include "cam_req_mgr_workq.h"
+#include "cam_mem_mgr.h"
+
+#define CAM_ICP_ROLE_PARENT 1
+#define CAM_ICP_ROLE_CHILD 2
+
+#define CAM_FRAME_CMD_MAX 20
+
+#define CAM_MAX_OUT_RES 6
+
+#define ICP_WORKQ_NUM_TASK 30
+#define ICP_WORKQ_TASK_CMD_TYPE 1
+#define ICP_WORKQ_TASK_MSG_TYPE 2
+
+#define ICP_PACKET_SIZE 0
+#define ICP_PACKET_TYPE 1
+#define ICP_PACKET_IPCODE 2
+#define ICP_IPE_MAX_OUTPUT_SUPPORTED 6
+
+/**
+ * struct icp_hfi_mem_info
+ * @qtbl: Memory info of queue table
+ * @cmd_q: Memory info of command queue
+ * @msg_q: Memory info of message queue
+ * @dbg_q: Memory info of debug queue
+ * @sec_heap: Memory info of secondary heap
+ * @fw_buf: Memory info of firmware
+ */
+struct icp_hfi_mem_info {
+ struct cam_mem_mgr_memory_desc qtbl;
+ struct cam_mem_mgr_memory_desc cmd_q;
+ struct cam_mem_mgr_memory_desc msg_q;
+ struct cam_mem_mgr_memory_desc dbg_q;
+ struct cam_mem_mgr_memory_desc sec_heap;
+ struct cam_mem_mgr_memory_desc fw_buf;
+};
+
+/**
+ * struct hfi_cmd_work_data
+ * @type: Task type
+ * @data: Pointer to command data
+ * @request_id: Request id
+ */
+struct hfi_cmd_work_data {
+ uint32_t type;
+ void *data;
+ int32_t request_id;
+};
+
+/**
+ * struct hfi_msg_work_data
+ * @type: Task type
+ * @data: Pointer to message data
+ * @irq_status: IRQ status
+ */
+struct hfi_msg_work_data {
+ uint32_t type;
+ void *data;
+ uint32_t irq_status;
+};
+
+/**
+ * struct hfi_frame_process_info
+ * @hfi_frame_cmd: Frame process command info
+ * @bitmap: Bitmap for hfi_frame_cmd
+ * @bits: Used in hfi_frame_cmd bitmap
+ * @lock: Lock for hfi_frame_cmd
+ * @request_id: Request id list
+ * @num_out_resources: Number of out syncs
+ * @out_resource: Out sync info
+ */
+struct hfi_frame_process_info {
+ struct hfi_cmd_ipebps_async hfi_frame_cmd[CAM_FRAME_CMD_MAX];
+ void *bitmap;
+ size_t bits;
+ struct mutex lock;
+ int32_t request_id[CAM_FRAME_CMD_MAX];
+ uint32_t num_out_resources[CAM_FRAME_CMD_MAX];
+ uint32_t out_resource[CAM_FRAME_CMD_MAX][CAM_MAX_OUT_RES];
+};
+
+/**
+ * struct cam_icp_hw_ctx_data
+ * @context_priv: Context private data
+ * @ctx_mutex: Mutex for context
+ * @fw_handle: Firmware handle
+ * @scratch_mem_size: Scratch memory size
+ * @acquire_dev_cmd: Acquire command
+ * @icp_dev_acquire_info: Acquire device info
+ * @icp_out_acquire_info: Acquire out resource info
+ * @ctxt_event_cb: Context callback function
+ * @in_use: Flag for context usage
+ * @role: Role of a context in case of chaining
+ * @chain_ctx: Peer context
+ * @hfi_frame_process: Frame process command
+ * @wait_complete: Completion info
+ * @temp_payload: Payload for destroy handle data
+ */
+struct cam_icp_hw_ctx_data {
+ void *context_priv;
+ struct mutex ctx_mutex;
+ uint32_t fw_handle;
+ uint32_t scratch_mem_size;
+ struct cam_acquire_dev_cmd acquire_dev_cmd;
+ struct cam_icp_acquire_dev_info icp_dev_acquire_info;
+ struct cam_icp_res_info icp_out_acquire_info[CAM_MAX_OUT_RES];
+ cam_hw_event_cb_func ctxt_event_cb;
+ uint32_t in_use;
+ uint32_t role;
+ struct cam_icp_hw_ctx_data *chain_ctx;
+ struct hfi_frame_process_info hfi_frame_process;
+ struct completion wait_complete;
+ struct ipe_bps_destroy temp_payload;
+};
+
+/**
+ * struct cam_icp_hw_mgr
+ * @hw_mgr_mutex: Mutex for ICP hardware manager
+ * @hw_mgr_lock: Spinlock for ICP hardware manager
+ * @devices: Devices of ICP hardware manager
+ * @ctx_data: Context data
+ * @icp_caps: ICP capabilities
+ * @fw_download: Firmware download state
+ * @iommu_hdl: Non secure IOMMU handle
+ * @iommu_sec_hdl: Secure IOMMU handle
+ * @hfi_mem: Memory for hfi
+ * @cmd_work: Work queue for hfi commands
+ * @msg_work: Work queue for hfi messages
+ * @msg_buf: Buffer for message data from firmware
+ * @dbg_buf: Buffer for debug data from firmware
+ * @a5_complete: Completion info
+ * @cmd_work_data: Pointer to command work queue task
+ * @msg_work_data: Pointer to message work queue task
+ * @ctxt_cnt: Active context count
+ */
+struct cam_icp_hw_mgr {
+ struct mutex hw_mgr_mutex;
+ spinlock_t hw_mgr_lock;
+
+ struct cam_hw_intf **devices[CAM_ICP_DEV_MAX];
+ struct cam_icp_hw_ctx_data ctx_data[CAM_ICP_CTX_MAX];
+ struct cam_icp_query_cap_cmd icp_caps;
+
+ bool fw_download;
+ int32_t iommu_hdl;
+ int32_t iommu_sec_hdl;
+ struct icp_hfi_mem_info hfi_mem;
+ struct cam_req_mgr_core_workq *cmd_work;
+ struct cam_req_mgr_core_workq *msg_work;
+ uint32_t msg_buf[256];
+ uint32_t dbg_buf[256];
+ struct completion a5_complete;
+ struct hfi_cmd_work_data *cmd_work_data;
+ struct hfi_msg_work_data *msg_work_data;
+ uint32_t ctxt_cnt;
+};
+
+#endif /* CAM_ICP_HW_MGR_H */
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/include/cam_a5_hw_intf.h b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/include/cam_a5_hw_intf.h
new file mode 100644
index 0000000..2686877
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/include/cam_a5_hw_intf.h
@@ -0,0 +1,79 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CAM_A5_HW_INTF_H
+#define CAM_A5_HW_INTF_H
+
+#include <linux/timer.h>
+#include <uapi/media/cam_defs.h>
+#include <media/cam_icp.h>
+#include "cam_hw_mgr_intf.h"
+#include "cam_icp_hw_intf.h"
+
+enum cam_icp_a5_cmd_type {
+ CAM_ICP_A5_CMD_FW_DOWNLOAD,
+ CAM_ICP_A5_CMD_POWER_COLLAPSE,
+ CAM_ICP_A5_CMD_POWER_RESUME,
+ CAM_ICP_A5_CMD_SET_FW_BUF,
+ CAM_ICP_A5_CMD_ACQUIRE,
+ CAM_ICP_A5_SET_IRQ_CB,
+ CAM_ICP_A5_TEST_IRQ,
+ CAM_ICP_A5_SEND_INIT,
+ CAM_ICP_A5_CMD_VOTE_CPAS,
+ CAM_ICP_A5_CMD_CPAS_START,
+ CAM_ICP_A5_CMD_CPAS_STOP,
+ CAM_ICP_A5_CMD_MAX,
+};
+
+struct cam_icp_a5_set_fw_buf_info {
+ uint32_t iova;
+ uint64_t kva;
+ uint64_t len;
+};
+
+/**
+ * struct cam_icp_a5_query_cap - ICP query device capability payload
+ * @fw_version: firmware version info
+ * @api_version: api version info
+ * @num_ipe: number of ipes
+ * @num_bps: number of bps
+ * @num_dev: number of device capabilities in dev_caps
+ * @reserved: reserved
+ * @dev_ver: returned device capability array
+ * @CAM_QUERY_CAP IOCTL
+ */
+struct cam_icp_a5_query_cap {
+ struct cam_icp_ver fw_version;
+ struct cam_icp_ver api_version;
+ uint32_t num_ipe;
+ uint32_t num_bps;
+ uint32_t num_dev;
+ uint32_t reserved;
+ struct cam_icp_dev_ver dev_ver[CAM_ICP_DEV_TYPE_MAX];
+};
+
+struct cam_icp_a5_acquire_dev {
+ uint32_t ctx_id;
+ struct cam_icp_acquire_dev_info icp_acquire_info;
+ struct cam_icp_res_info icp_out_acquire_info[2];
+ uint32_t fw_handle;
+};
+
+struct cam_icp_a5_set_irq_cb {
+ int32_t (*icp_hw_mgr_cb)(uint32_t irq_status, void *data);
+ void *data;
+};
+
+struct cam_icp_a5_test_irq {
+ uint32_t test_irq;
+};
+#endif /* CAM_A5_HW_INTF_H */
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/include/cam_bps_hw_intf.h b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/include/cam_bps_hw_intf.h
new file mode 100644
index 0000000..4427a30
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/include/cam_bps_hw_intf.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CAM_BPS_HW_INTF_H
+#define CAM_BPS_HW_INTF_H
+
+#include <uapi/media/cam_defs.h>
+#include <media/cam_icp.h>
+#include "cam_hw_mgr_intf.h"
+#include "cam_icp_hw_intf.h"
+
+enum cam_icp_bps_cmd_type {
+ CAM_ICP_BPS_CMD_FW_DOWNLOAD,
+ CAM_ICP_BPS_CMD_POWER_COLLAPSE,
+ CAM_ICP_BPS_CMD_POWER_RESUME,
+ CAM_ICP_BPS_CMD_SET_FW_BUF,
+ CAM_ICP_BPS_CMD_VOTE_CPAS,
+ CAM_ICP_BPS_CMD_CPAS_START,
+ CAM_ICP_BPS_CMD_CPAS_STOP,
+ CAM_ICP_BPS_CMD_MAX,
+};
+
+#endif /* CAM_BPS_HW_INTF_H */
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/include/cam_icp_hw_intf.h b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/include/cam_icp_hw_intf.h
new file mode 100644
index 0000000..9300ea8
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/include/cam_icp_hw_intf.h
@@ -0,0 +1,27 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CAM_ICP_HW_INTF_H
+#define CAM_ICP_HW_INTF_H
+
+#define CAM_ICP_CTX_MAX 8
+
+#define CAM_ICP_CMD_BUF_MAX_SIZE 128
+#define CAM_ICP_MSG_BUF_MAX_SIZE CAM_ICP_CMD_BUF_MAX_SIZE
+
+enum cam_a5_hw_type {
+ CAM_ICP_DEV_A5,
+ CAM_ICP_DEV_IPE,
+ CAM_ICP_DEV_BPS,
+ CAM_ICP_DEV_MAX,
+};
+#endif
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/include/cam_ipe_hw_intf.h b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/include/cam_ipe_hw_intf.h
new file mode 100644
index 0000000..0db66c0
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/include/cam_ipe_hw_intf.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CAM_IPE_HW_INTF_H
+#define CAM_IPE_HW_INTF_H
+
+#include <uapi/media/cam_defs.h>
+#include <media/cam_icp.h>
+#include "cam_hw_mgr_intf.h"
+#include "cam_icp_hw_intf.h"
+
+enum cam_icp_ipe_cmd_type {
+ CAM_ICP_IPE_CMD_FW_DOWNLOAD,
+ CAM_ICP_IPE_CMD_POWER_COLLAPSE,
+ CAM_ICP_IPE_CMD_POWER_RESUME,
+ CAM_ICP_IPE_CMD_SET_FW_BUF,
+ CAM_ICP_IPE_CMD_VOTE_CPAS,
+ CAM_ICP_IPE_CMD_CPAS_START,
+ CAM_ICP_IPE_CMD_CPAS_STOP,
+ CAM_ICP_IPE_CMD_MAX,
+};
+
+#endif /* CAM_IPE_HW_INTF_H */
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/include/cam_icp_hw_mgr_intf.h b/drivers/media/platform/msm/camera/icp/icp_hw/include/cam_icp_hw_mgr_intf.h
new file mode 100644
index 0000000..2f100ca
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/include/cam_icp_hw_mgr_intf.h
@@ -0,0 +1,40 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CAM_ICP_HW_MGR_INTF_H
+#define CAM_ICP_HW_MGR_INTF_H
+
+#include <uapi/media/cam_icp.h>
+#include <uapi/media/cam_defs.h>
+#include <linux/of.h>
+#include "cam_cpas_api.h"
+
+#define ICP_TURBO_VOTE 640000000
+
+int cam_icp_hw_mgr_init(struct device_node *of_node,
+ uint64_t *hw_mgr_hdl);
+
+/**
+ * struct cam_icp_cpas_vote
+ * @ahb_vote: AHB vote info
+ * @axi_vote: AXI vote info
+ * @ahb_vote_valid: Flag for ahb vote data
+ * @axi_vote_valid: flag for axi vote data
+ */
+struct cam_icp_cpas_vote {
+ struct cam_ahb_vote ahb_vote;
+ struct cam_axi_vote axi_vote;
+ uint32_t ahb_vote_valid;
+ uint32_t axi_vote_valid;
+};
+
+#endif /* CAM_ICP_HW_MGR_INTF_H */
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/Makefile b/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/Makefile
new file mode 100644
index 0000000..8af20ae
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/Makefile
@@ -0,0 +1,11 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_core
+ccflags-y += -Idrivers/media/platform/msm/camera/icp
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/icp_hw/include
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/include
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/icp_hw/ipe_hw
+ccflags-y += -Idrivers/media/platform/msm/camera/icp/fw_inc
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
+
+obj-$(CONFIG_SPECTRA_CAMERA) += ipe_dev.o ipe_core.o ipe_soc.o
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.c b/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.c
new file mode 100644
index 0000000..15cb943
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.c
@@ -0,0 +1,183 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "IPE-CORE %s:%d " fmt, __func__, __LINE__
+
+#include <linux/of.h>
+#include <linux/debugfs.h>
+#include <linux/videodev2.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include "cam_io_util.h"
+#include "cam_hw.h"
+#include "cam_hw_intf.h"
+#include "ipe_core.h"
+#include "ipe_soc.h"
+#include "cam_soc_util.h"
+#include "cam_io_util.h"
+#include "cam_ipe_hw_intf.h"
+#include "cam_icp_hw_mgr_intf.h"
+#include "cam_cpas_api.h"
+
+static int cam_ipe_caps_vote(struct cam_ipe_device_core_info *core_info,
+ struct cam_icp_cpas_vote *cpas_vote)
+{
+ int rc = 0;
+
+ if (cpas_vote->ahb_vote_valid)
+ rc = cam_cpas_update_ahb_vote(core_info->cpas_handle,
+ &cpas_vote->ahb_vote);
+ if (cpas_vote->axi_vote_valid)
+ rc = cam_cpas_update_axi_vote(core_info->cpas_handle,
+ &cpas_vote->axi_vote);
+
+ if (rc < 0)
+ pr_err("cpas vote is failed: %d\n", rc);
+
+ return rc;
+}
+
+int cam_ipe_init_hw(void *device_priv,
+ void *init_hw_args, uint32_t arg_size)
+{
+ struct cam_hw_info *ipe_dev = device_priv;
+ struct cam_hw_soc_info *soc_info = NULL;
+ struct cam_ipe_device_core_info *core_info = NULL;
+ struct cam_icp_cpas_vote cpas_vote;
+ int rc = 0;
+
+ if (!device_priv) {
+ pr_err("Invalid cam_dev_info\n");
+ return -EINVAL;
+ }
+
+ soc_info = &ipe_dev->soc_info;
+ core_info = (struct cam_ipe_device_core_info *)ipe_dev->core_info;
+
+ if ((!soc_info) || (!core_info)) {
+ pr_err("soc_info = %pK core_info = %pK\n", soc_info, core_info);
+ return -EINVAL;
+ }
+
+ cpas_vote.ahb_vote.type = CAM_VOTE_ABSOLUTE;
+ cpas_vote.ahb_vote.vote.level = CAM_TURBO_VOTE;
+ cpas_vote.axi_vote.compressed_bw = ICP_TURBO_VOTE;
+ cpas_vote.axi_vote.uncompressed_bw = ICP_TURBO_VOTE;
+
+ rc = cam_cpas_start(core_info->cpas_handle,
+ &cpas_vote.ahb_vote, &cpas_vote.axi_vote);
+ if (rc < 0) {
+ pr_err("cpass start failed: %d\n", rc);
+ return rc;
+ }
+
+ rc = cam_ipe_enable_soc_resources(soc_info);
+ if (rc < 0) {
+ pr_err("soc enable is failed\n");
+ rc = cam_cpas_stop(core_info->cpas_handle);
+ }
+
+ return rc;
+}
+
+int cam_ipe_deinit_hw(void *device_priv,
+ void *init_hw_args, uint32_t arg_size)
+{
+ struct cam_hw_info *ipe_dev = device_priv;
+ struct cam_hw_soc_info *soc_info = NULL;
+ struct cam_ipe_device_core_info *core_info = NULL;
+ int rc = 0;
+
+ if (!device_priv) {
+ pr_err("Invalid cam_dev_info\n");
+ return -EINVAL;
+ }
+
+ soc_info = &ipe_dev->soc_info;
+ core_info = (struct cam_ipe_device_core_info *)ipe_dev->core_info;
+ if ((!soc_info) || (!core_info)) {
+ pr_err("soc_info = %pK core_info = %pK\n", soc_info, core_info);
+ return -EINVAL;
+ }
+
+ rc = cam_ipe_disable_soc_resources(soc_info);
+ if (rc < 0)
+ pr_err("soc enable is failed\n");
+
+ rc = cam_cpas_stop(core_info->cpas_handle);
+ if (rc < 0)
+ pr_err("cpas stop is failed: %d\n", rc);
+
+ return rc;
+}
+
+int cam_ipe_process_cmd(void *device_priv, uint32_t cmd_type,
+ void *cmd_args, uint32_t arg_size)
+{
+ struct cam_hw_info *ipe_dev = device_priv;
+ struct cam_hw_soc_info *soc_info = NULL;
+ struct cam_ipe_device_core_info *core_info = NULL;
+ struct cam_ipe_device_hw_info *hw_info = NULL;
+ int rc = 0;
+
+ if (!device_priv) {
+ pr_err("Invalid arguments\n");
+ return -EINVAL;
+ }
+
+ if (cmd_type >= CAM_ICP_IPE_CMD_MAX) {
+ pr_err("Invalid command : %x\n", cmd_type);
+ return -EINVAL;
+ }
+
+ soc_info = &ipe_dev->soc_info;
+ core_info = (struct cam_ipe_device_core_info *)ipe_dev->core_info;
+ hw_info = core_info->ipe_hw_info;
+
+ switch (cmd_type) {
+ case CAM_ICP_IPE_CMD_VOTE_CPAS: {
+ struct cam_icp_cpas_vote *cpas_vote = cmd_args;
+
+ if (!cmd_args)
+ return -EINVAL;
+
+ cam_ipe_caps_vote(core_info, cpas_vote);
+ break;
+ }
+
+ case CAM_ICP_IPE_CMD_CPAS_START: {
+ struct cam_icp_cpas_vote *cpas_vote = cmd_args;
+
+ if (!cmd_args)
+ return -EINVAL;
+
+ rc = cam_cpas_start(core_info->cpas_handle,
+ &cpas_vote->ahb_vote, &cpas_vote->axi_vote);
+ break;
+ }
+
+ case CAM_ICP_IPE_CMD_CPAS_STOP:
+ cam_cpas_stop(core_info->cpas_handle);
+ break;
+ default:
+ break;
+ }
+ return rc;
+}
+
+irqreturn_t cam_ipe_irq(int irq_num, void *data)
+{
+ return IRQ_HANDLED;
+}
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.h b/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.h
new file mode 100644
index 0000000..4818846
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CAM_IPE_CORE_H
+#define CAM_IPE_CORE_H
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/dma-buf.h>
+
+struct cam_ipe_device_hw_info {
+ uint32_t reserved;
+};
+
+struct cam_ipe_device_core_info {
+ struct cam_ipe_device_hw_info *ipe_hw_info;
+ uint32_t cpas_handle;
+};
+
+int cam_ipe_init_hw(void *device_priv,
+ void *init_hw_args, uint32_t arg_size);
+int cam_ipe_deinit_hw(void *device_priv,
+ void *init_hw_args, uint32_t arg_size);
+int cam_ipe_process_cmd(void *device_priv, uint32_t cmd_type,
+ void *cmd_args, uint32_t arg_size);
+irqreturn_t cam_ipe_irq(int irq_num, void *data);
+
+#endif /* CAM_IPE_CORE_H */
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_dev.c b/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_dev.c
new file mode 100644
index 0000000..0efb1de
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_dev.c
@@ -0,0 +1,176 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of_device.h>
+#include <linux/timer.h>
+#include "ipe_core.h"
+#include "ipe_soc.h"
+#include "cam_hw.h"
+#include "cam_hw_intf.h"
+#include "cam_io_util.h"
+#include "cam_icp_hw_intf.h"
+#include "cam_icp_hw_mgr_intf.h"
+#include "cam_cpas_api.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+struct cam_ipe_device_hw_info cam_ipe_hw_info = {
+ .reserved = 0,
+};
+EXPORT_SYMBOL(cam_ipe_hw_info);
+
+int cam_ipe_register_cpas(struct cam_hw_soc_info *soc_info,
+ struct cam_ipe_device_core_info *core_info,
+ uint32_t hw_idx)
+{
+ struct cam_cpas_register_params cpas_register_params;
+ int rc;
+
+ cpas_register_params.dev = &soc_info->pdev->dev;
+ memcpy(cpas_register_params.identifier, "ipe", sizeof("ipe"));
+ cpas_register_params.cam_cpas_client_cb = NULL;
+ cpas_register_params.cell_index = hw_idx;
+ cpas_register_params.userdata = NULL;
+
+ rc = cam_cpas_register_client(&cpas_register_params);
+ if (rc < 0) {
+ pr_err("cam_cpas_register_client is failed: %d\n", rc);
+ return rc;
+ }
+ core_info->cpas_handle = cpas_register_params.client_handle;
+
+ return rc;
+}
+
+int cam_ipe_probe(struct platform_device *pdev)
+{
+ struct cam_hw_info *ipe_dev = NULL;
+ struct cam_hw_intf *ipe_dev_intf = NULL;
+ const struct of_device_id *match_dev = NULL;
+ struct cam_ipe_device_core_info *core_info = NULL;
+ struct cam_ipe_device_hw_info *hw_info = NULL;
+ int rc = 0;
+
+ ipe_dev_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL);
+ if (!ipe_dev_intf)
+ return -ENOMEM;
+
+ of_property_read_u32(pdev->dev.of_node,
+ "cell-index", &ipe_dev_intf->hw_idx);
+
+ ipe_dev = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL);
+ if (!ipe_dev) {
+ kfree(ipe_dev_intf);
+ return -ENOMEM;
+ }
+ ipe_dev->soc_info.pdev = pdev;
+ ipe_dev_intf->hw_priv = ipe_dev;
+ ipe_dev_intf->hw_ops.init = cam_ipe_init_hw;
+ ipe_dev_intf->hw_ops.deinit = cam_ipe_deinit_hw;
+ ipe_dev_intf->hw_ops.process_cmd = cam_ipe_process_cmd;
+ ipe_dev_intf->hw_type = CAM_ICP_DEV_IPE;
+
+ pr_debug("%s: type %d index %d\n", __func__,
+ ipe_dev_intf->hw_type,
+ ipe_dev_intf->hw_idx);
+
+ platform_set_drvdata(pdev, ipe_dev_intf);
+
+ ipe_dev->core_info = kzalloc(sizeof(struct cam_ipe_device_core_info),
+ GFP_KERNEL);
+ if (!ipe_dev->core_info) {
+ kfree(ipe_dev);
+ kfree(ipe_dev_intf);
+ return -ENOMEM;
+ }
+ core_info = (struct cam_ipe_device_core_info *)ipe_dev->core_info;
+
+ match_dev = of_match_device(pdev->dev.driver->of_match_table,
+ &pdev->dev);
+ if (!match_dev) {
+ pr_debug("%s: No ipe hardware info\n", __func__);
+ kfree(ipe_dev->core_info);
+ kfree(ipe_dev);
+ kfree(ipe_dev_intf);
+ rc = -EINVAL;
+ return rc;
+ }
+ hw_info = (struct cam_ipe_device_hw_info *)match_dev->data;
+ core_info->ipe_hw_info = hw_info;
+
+ rc = cam_ipe_init_soc_resources(&ipe_dev->soc_info, cam_ipe_irq,
+ ipe_dev);
+ if (rc < 0) {
+ pr_err("%s: failed to init_soc\n", __func__);
+ kfree(ipe_dev->core_info);
+ kfree(ipe_dev);
+ kfree(ipe_dev_intf);
+ return rc;
+ }
+
+ pr_debug("cam_ipe_init_soc_resources : %pK\n",
+ (void *)&ipe_dev->soc_info);
+ rc = cam_ipe_register_cpas(&ipe_dev->soc_info,
+ core_info, ipe_dev_intf->hw_idx);
+ if (rc < 0) {
+ kfree(ipe_dev->core_info);
+ kfree(ipe_dev);
+ kfree(ipe_dev_intf);
+ return rc;
+ }
+ ipe_dev->hw_state = CAM_HW_STATE_POWER_DOWN;
+ mutex_init(&ipe_dev->hw_mutex);
+ spin_lock_init(&ipe_dev->hw_lock);
+ init_completion(&ipe_dev->hw_complete);
+
+ pr_debug("%s: IPE%d probe successful\n", __func__,
+ ipe_dev_intf->hw_idx);
+
+ return rc;
+}
+
+static const struct of_device_id cam_ipe_dt_match[] = {
+ {
+ .compatible = "qcom,cam_ipe",
+ .data = &cam_ipe_hw_info,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cam_ipe_dt_match);
+
+static struct platform_driver cam_ipe_driver = {
+ .probe = cam_ipe_probe,
+ .driver = {
+ .name = "cam_ipe",
+ .owner = THIS_MODULE,
+ .of_match_table = cam_ipe_dt_match,
+ },
+};
+
+static int __init cam_ipe_init_module(void)
+{
+ return platform_driver_register(&cam_ipe_driver);
+}
+
+static void __exit cam_ipe_exit_module(void)
+{
+ platform_driver_unregister(&cam_ipe_driver);
+}
+
+module_init(cam_ipe_init_module);
+module_exit(cam_ipe_exit_module);
+MODULE_DESCRIPTION("CAM IPE driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_soc.c b/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_soc.c
new file mode 100644
index 0000000..527e716
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_soc.c
@@ -0,0 +1,87 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/dma-buf.h>
+#include <media/cam_defs.h>
+#include <media/cam_icp.h>
+#include "ipe_soc.h"
+#include "cam_soc_util.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+static int cam_ipe_get_dt_properties(struct cam_hw_soc_info *soc_info)
+{
+ int rc = 0;
+
+ rc = cam_soc_util_get_dt_properties(soc_info);
+ if (rc < 0)
+ pr_err("get ipe dt prop is failed\n");
+
+ return rc;
+}
+
+static int cam_ipe_request_platform_resource(
+ struct cam_hw_soc_info *soc_info,
+ irq_handler_t ipe_irq_handler, void *irq_data)
+{
+ int rc = 0;
+
+ rc = cam_soc_util_request_platform_resource(soc_info, ipe_irq_handler,
+ irq_data);
+
+ return rc;
+}
+
+int cam_ipe_init_soc_resources(struct cam_hw_soc_info *soc_info,
+ irq_handler_t ipe_irq_handler, void *irq_data)
+{
+ int rc = 0;
+
+ rc = cam_ipe_get_dt_properties(soc_info);
+ if (rc < 0)
+ return rc;
+
+ rc = cam_ipe_request_platform_resource(soc_info, ipe_irq_handler,
+ irq_data);
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+int cam_ipe_enable_soc_resources(struct cam_hw_soc_info *soc_info)
+{
+ int rc = 0;
+
+ rc = cam_soc_util_enable_platform_resource(soc_info, true, false);
+ if (rc) {
+ pr_err("%s: enable platform failed\n", __func__);
+ return rc;
+ }
+
+ return rc;
+}
+
+int cam_ipe_disable_soc_resources(struct cam_hw_soc_info *soc_info)
+{
+ int rc = 0;
+
+ rc = cam_soc_util_disable_platform_resource(soc_info, true, false);
+ if (rc)
+ pr_err("%s: enable platform failed\n", __func__);
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_soc.h b/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_soc.h
new file mode 100644
index 0000000..12ab444
--- /dev/null
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_soc.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CAM_IPE_SOC_H
+#define CAM_IPE_SOC_H
+
+#include "cam_soc_util.h"
+
+int cam_ipe_init_soc_resources(struct cam_hw_soc_info *soc_info,
+ irq_handler_t ipe_irq_handler, void *irq_data);
+
+int cam_ipe_enable_soc_resources(struct cam_hw_soc_info *soc_info);
+
+int cam_ipe_disable_soc_resources(struct cam_hw_soc_info *soc_info);
+
+#endif /* CAM_IPE_SOC_H */
diff --git a/drivers/misc/memory_state_time.c b/drivers/misc/memory_state_time.c
index 34c797a..ba94dcf 100644
--- a/drivers/misc/memory_state_time.c
+++ b/drivers/misc/memory_state_time.c
@@ -296,27 +296,31 @@
struct device_node *node = dev->of_node;
of_property_read_u32(node, NUM_SOURCES, &num_sources);
- if (of_find_property(node, BW_TBL, &lenb)) {
- bandwidths = devm_kzalloc(dev,
- sizeof(*bandwidths) * num_sources, GFP_KERNEL);
- if (!bandwidths)
- return -ENOMEM;
- lenb /= sizeof(*bw_buckets);
- bw_buckets = devm_kzalloc(dev, lenb * sizeof(*bw_buckets),
- GFP_KERNEL);
- if (!bw_buckets) {
- devm_kfree(dev, bandwidths);
- return -ENOMEM;
- }
- ret = of_property_read_u32_array(node, BW_TBL, bw_buckets,
- lenb);
- if (ret < 0) {
- devm_kfree(dev, bandwidths);
- devm_kfree(dev, bw_buckets);
- pr_err("Unable to read bandwidth table from device tree.\n");
- return ret;
- }
+ if (!of_find_property(node, BW_TBL, &lenb)) {
+ pr_err("Missing %s property\n", BW_TBL);
+ return -ENODATA;
}
+
+ bandwidths = devm_kzalloc(dev,
+ sizeof(*bandwidths) * num_sources, GFP_KERNEL);
+ if (!bandwidths)
+ return -ENOMEM;
+ lenb /= sizeof(*bw_buckets);
+ bw_buckets = devm_kzalloc(dev, lenb * sizeof(*bw_buckets),
+ GFP_KERNEL);
+ if (!bw_buckets) {
+ devm_kfree(dev, bandwidths);
+ return -ENOMEM;
+ }
+ ret = of_property_read_u32_array(node, BW_TBL, bw_buckets,
+ lenb);
+ if (ret < 0) {
+ devm_kfree(dev, bandwidths);
+ devm_kfree(dev, bw_buckets);
+ pr_err("Unable to read bandwidth table from device tree.\n");
+ return ret;
+ }
+
curr_bw = 0;
num_buckets = lenb;
return 0;
@@ -332,22 +336,26 @@
int ret, lenf;
struct device_node *node = dev->of_node;
- if (of_find_property(node, FREQ_TBL, &lenf)) {
- lenf /= sizeof(*freq_buckets);
- freq_buckets = devm_kzalloc(dev, lenf * sizeof(*freq_buckets),
- GFP_KERNEL);
- if (!freq_buckets)
- return -ENOMEM;
- pr_debug("freqs found len %d\n", lenf);
- ret = of_property_read_u32_array(node, FREQ_TBL, freq_buckets,
- lenf);
- if (ret < 0) {
- devm_kfree(dev, freq_buckets);
- pr_err("Unable to read frequency table from device tree.\n");
- return ret;
- }
- pr_debug("ret freq %d\n", ret);
+ if (!of_find_property(node, FREQ_TBL, &lenf)) {
+ pr_err("Missing %s property\n", FREQ_TBL);
+ return -ENODATA;
}
+
+ lenf /= sizeof(*freq_buckets);
+ freq_buckets = devm_kzalloc(dev, lenf * sizeof(*freq_buckets),
+ GFP_KERNEL);
+ if (!freq_buckets)
+ return -ENOMEM;
+ pr_debug("freqs found len %d\n", lenf);
+ ret = of_property_read_u32_array(node, FREQ_TBL, freq_buckets,
+ lenf);
+ if (ret < 0) {
+ devm_kfree(dev, freq_buckets);
+ pr_err("Unable to read frequency table from device tree.\n");
+ return ret;
+ }
+ pr_debug("ret freq %d\n", ret);
+
num_freqs = lenf;
curr_freq = freq_buckets[LOWEST_FREQ];
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 1397d03..790f191 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -4321,6 +4321,7 @@
mmc_power_up(host, host->ocr_avail);
mmc_gpiod_request_cd_irq(host);
+ mmc_register_extcon(host);
mmc_release_host(host);
_mmc_detect_change(host, 0, false);
}
@@ -4357,6 +4358,8 @@
BUG_ON(host->card);
+ mmc_register_extcon(host);
+
mmc_claim_host(host);
mmc_power_off(host);
mmc_release_host(host);
diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c
index 27117ba..b5c81e4 100644
--- a/drivers/mmc/core/slot-gpio.c
+++ b/drivers/mmc/core/slot-gpio.c
@@ -17,6 +17,7 @@
#include <linux/mmc/slot-gpio.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/extcon.h>
#include "slot-gpio.h"
@@ -154,6 +155,53 @@
}
EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
+static int mmc_card_detect_notifier(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct mmc_host *host = container_of(nb, struct mmc_host,
+ card_detect_nb);
+
+ host->trigger_card_event = true;
+ mmc_detect_change(host, 0);
+
+ return NOTIFY_DONE;
+}
+
+void mmc_register_extcon(struct mmc_host *host)
+{
+ struct extcon_dev *extcon = host->extcon;
+ int err;
+
+ if (!extcon)
+ return;
+
+ host->card_detect_nb.notifier_call = mmc_card_detect_notifier;
+ err = extcon_register_notifier(extcon, EXTCON_MECHANICAL,
+ &host->card_detect_nb);
+ if (err) {
+ dev_err(mmc_dev(host), "%s: extcon_register_notifier() failed ret=%d\n",
+ __func__, err);
+ host->caps |= MMC_CAP_NEEDS_POLL;
+ }
+}
+EXPORT_SYMBOL(mmc_register_extcon);
+
+void mmc_unregister_extcon(struct mmc_host *host)
+{
+ struct extcon_dev *extcon = host->extcon;
+ int err;
+
+ if (!extcon)
+ return;
+
+ err = extcon_unregister_notifier(extcon, EXTCON_MECHANICAL,
+ &host->card_detect_nb);
+ if (err)
+ dev_err(mmc_dev(host), "%s: extcon_unregister_notifier() failed ret=%d\n",
+ __func__, err);
+}
+EXPORT_SYMBOL(mmc_unregister_extcon);
+
/* Register an alternate interrupt service routine for
* the card-detect GPIO.
*/
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 515abb2..dff6631 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -401,6 +401,8 @@
depends on MMC_SDHCI_PLTFM
select PM_DEVFREQ
select DEVFREQ_GOV_SIMPLE_ONDEMAND
+ select EXTCON
+ select EXTCON_GPIO
help
This selects the Secure Digital Host Controller Interface (SDHCI)
support present in Qualcomm Technologies, Inc. SOCs. The controller
diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c
index ad49bfa..b6122e9 100644
--- a/drivers/mmc/host/sdhci-pltfm.c
+++ b/drivers/mmc/host/sdhci-pltfm.c
@@ -121,6 +121,7 @@
struct resource *iomem;
void __iomem *ioaddr;
int irq, ret;
+ struct extcon_dev *extcon;
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ioaddr = devm_ioremap_resource(&pdev->dev, iomem);
@@ -156,6 +157,15 @@
host->quirks2 = pdata->quirks2;
}
+ extcon = extcon_get_edev_by_phandle(&pdev->dev, 0);
+ if (IS_ERR(extcon) && PTR_ERR(extcon) != -ENODEV) {
+ ret = PTR_ERR(extcon);
+ goto err;
+ }
+ if (!IS_ERR(extcon))
+ host->mmc->extcon = extcon;
+
+
platform_set_drvdata(pdev, host);
return host;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 53a6ae8..83be863 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -4095,7 +4095,7 @@
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
mmc_card_is_removable(mmc) &&
mmc_gpio_get_cd(host->mmc) < 0 &&
- !(mmc->caps2 & MMC_CAP2_NONHOTPLUG))
+ !(mmc->caps2 & MMC_CAP2_NONHOTPLUG) && !host->mmc->extcon)
mmc->caps |= MMC_CAP_NEEDS_POLL;
/* If vqmmc regulator and no 1.8V signalling, then there's no UHS */
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index f08a20b..48ee411 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -2867,7 +2867,8 @@
INIT_HLIST_HEAD(&bp->ntp_fltr_hash_tbl[i]);
bp->ntp_fltr_count = 0;
- bp->ntp_fltr_bmap = kzalloc(BITS_TO_LONGS(BNXT_NTP_FLTR_MAX_FLTR),
+ bp->ntp_fltr_bmap = kcalloc(BITS_TO_LONGS(BNXT_NTP_FLTR_MAX_FLTR),
+ sizeof(long),
GFP_KERNEL);
if (!bp->ntp_fltr_bmap)
diff --git a/drivers/net/ethernet/msm/ecm_ipa.c b/drivers/net/ethernet/msm/ecm_ipa.c
index ce8f7ac..105294a 100644
--- a/drivers/net/ethernet/msm/ecm_ipa.c
+++ b/drivers/net/ethernet/msm/ecm_ipa.c
@@ -97,8 +97,8 @@
};
#define ECM_IPA_STATE_DEBUG(ecm_ipa_ctx) \
- (ECM_IPA_DEBUG("Driver state - %s\n",\
- ecm_ipa_state_string((ecm_ipa_ctx)->state)))
+ ECM_IPA_DEBUG("Driver state - %s\n",\
+ ecm_ipa_state_string((ecm_ipa_ctx)->state))
/**
* struct ecm_ipa_dev - main driver context parameters
@@ -163,8 +163,6 @@
static netdev_tx_t ecm_ipa_start_xmit
(struct sk_buff *skb, struct net_device *net);
static int ecm_ipa_debugfs_atomic_open(struct inode *inode, struct file *file);
-static ssize_t ecm_ipa_debugfs_enable_read
- (struct file *file, char __user *ubuf, size_t count, loff_t *ppos);
static ssize_t ecm_ipa_debugfs_atomic_read
(struct file *file, char __user *ubuf, size_t count, loff_t *ppos);
static void ecm_ipa_debugfs_init(struct ecm_ipa_dev *ecm_ipa_ctx);
@@ -558,7 +556,7 @@
netdev_tx_t status = NETDEV_TX_BUSY;
struct ecm_ipa_dev *ecm_ipa_ctx = netdev_priv(net);
- net->trans_start = jiffies;
+ netif_trans_update(net);
ECM_IPA_DEBUG
("Tx, len=%d, skb->protocol=%d, outstanding=%d\n",
diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c
index f7c6a40..a5d66e2 100644
--- a/drivers/net/macsec.c
+++ b/drivers/net/macsec.c
@@ -617,7 +617,8 @@
static struct aead_request *macsec_alloc_req(struct crypto_aead *tfm,
unsigned char **iv,
- struct scatterlist **sg)
+ struct scatterlist **sg,
+ int num_frags)
{
size_t size, iv_offset, sg_offset;
struct aead_request *req;
@@ -629,7 +630,7 @@
size = ALIGN(size, __alignof__(struct scatterlist));
sg_offset = size;
- size += sizeof(struct scatterlist) * (MAX_SKB_FRAGS + 1);
+ size += sizeof(struct scatterlist) * num_frags;
tmp = kmalloc(size, GFP_ATOMIC);
if (!tmp)
@@ -649,6 +650,7 @@
{
int ret;
struct scatterlist *sg;
+ struct sk_buff *trailer;
unsigned char *iv;
struct ethhdr *eth;
struct macsec_eth_header *hh;
@@ -723,7 +725,14 @@
return ERR_PTR(-EINVAL);
}
- req = macsec_alloc_req(tx_sa->key.tfm, &iv, &sg);
+ ret = skb_cow_data(skb, 0, &trailer);
+ if (unlikely(ret < 0)) {
+ macsec_txsa_put(tx_sa);
+ kfree_skb(skb);
+ return ERR_PTR(ret);
+ }
+
+ req = macsec_alloc_req(tx_sa->key.tfm, &iv, &sg, ret);
if (!req) {
macsec_txsa_put(tx_sa);
kfree_skb(skb);
@@ -732,7 +741,7 @@
macsec_fill_iv(iv, secy->sci, pn);
- sg_init_table(sg, MAX_SKB_FRAGS + 1);
+ sg_init_table(sg, ret);
skb_to_sgvec(skb, sg, 0, skb->len);
if (tx_sc->encrypt) {
@@ -914,6 +923,7 @@
{
int ret;
struct scatterlist *sg;
+ struct sk_buff *trailer;
unsigned char *iv;
struct aead_request *req;
struct macsec_eth_header *hdr;
@@ -924,7 +934,12 @@
if (!skb)
return ERR_PTR(-ENOMEM);
- req = macsec_alloc_req(rx_sa->key.tfm, &iv, &sg);
+ ret = skb_cow_data(skb, 0, &trailer);
+ if (unlikely(ret < 0)) {
+ kfree_skb(skb);
+ return ERR_PTR(ret);
+ }
+ req = macsec_alloc_req(rx_sa->key.tfm, &iv, &sg, ret);
if (!req) {
kfree_skb(skb);
return ERR_PTR(-ENOMEM);
@@ -933,7 +948,7 @@
hdr = (struct macsec_eth_header *)skb->data;
macsec_fill_iv(iv, sci, ntohl(hdr->packet_number));
- sg_init_table(sg, MAX_SKB_FRAGS + 1);
+ sg_init_table(sg, ret);
skb_to_sgvec(skb, sg, 0, skb->len);
if (hdr->tci_an & MACSEC_TCI_E) {
@@ -2709,7 +2724,7 @@
}
#define MACSEC_FEATURES \
- (NETIF_F_SG | NETIF_F_HIGHDMA)
+ (NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST)
static struct lock_class_key macsec_netdev_addr_lock_key;
static int macsec_dev_init(struct net_device *dev)
diff --git a/drivers/net/phy/mdio-mux-bcm-iproc.c b/drivers/net/phy/mdio-mux-bcm-iproc.c
index 0a04125..0a5f62e 100644
--- a/drivers/net/phy/mdio-mux-bcm-iproc.c
+++ b/drivers/net/phy/mdio-mux-bcm-iproc.c
@@ -203,11 +203,14 @@
&md->mux_handle, md, md->mii_bus);
if (rc) {
dev_info(md->dev, "mdiomux initialization failed\n");
- goto out;
+ goto out_register;
}
dev_info(md->dev, "iProc mdiomux registered\n");
return 0;
+
+out_register:
+ mdiobus_unregister(bus);
out:
mdiobus_free(bus);
return rc;
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 0d519a9..34d997c 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -902,6 +902,7 @@
{QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */
{QMI_FIXED_INTF(0x2357, 0x9000, 4)}, /* TP-LINK MA260 */
{QMI_QUIRK_SET_DTR(0x1bc7, 0x1040, 2)}, /* Telit LE922A */
+ {QMI_FIXED_INTF(0x1bc7, 0x1100, 3)}, /* Telit ME910 */
{QMI_FIXED_INTF(0x1bc7, 0x1200, 5)}, /* Telit LE920 */
{QMI_FIXED_INTF(0x1bc7, 0x1201, 2)}, /* Telit LE920 */
{QMI_FIXED_INTF(0x1c9e, 0x9b01, 3)}, /* XS Stick W100-2 from 4G Systems */
diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig
index 8f0bde5..0e66348 100644
--- a/drivers/net/wireless/ath/wil6210/Kconfig
+++ b/drivers/net/wireless/ath/wil6210/Kconfig
@@ -44,7 +44,7 @@
config WIL6210_WRITE_IOCTL
bool "wil6210 write ioctl to the device"
depends on WIL6210
- default n
+ default y
---help---
Say Y here to allow write-access from user-space to
the device memory through ioctl. This is useful for
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 4e111cb..a83f8f6 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -25,6 +25,10 @@
module_param(disable_ap_sme, bool, 0444);
MODULE_PARM_DESC(disable_ap_sme, " let user space handle AP mode SME");
+static bool ignore_reg_hints = true;
+module_param(ignore_reg_hints, bool, 0444);
+MODULE_PARM_DESC(ignore_reg_hints, " Ignore OTA regulatory hints (Default: true)");
+
#define CHAN60G(_channel, _flags) { \
.band = NL80211_BAND_60GHZ, \
.center_freq = 56160 + (2160 * (_channel)), \
@@ -1413,6 +1417,8 @@
wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
wil_set_recovery_state(wil, fw_recovery_idle);
+ set_bit(wil_status_resetting, wil->status);
+
mutex_lock(&wil->mutex);
wmi_pcp_stop(wil);
@@ -1647,12 +1653,6 @@
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
enum wmi_ps_profile_type ps_profile;
- int rc;
-
- if (!test_bit(WMI_FW_CAPABILITY_PS_CONFIG, wil->fw_capabilities)) {
- wil_err(wil, "set_power_mgmt not supported\n");
- return -EOPNOTSUPP;
- }
wil_dbg_misc(wil, "enabled=%d, timeout=%d\n",
enabled, timeout);
@@ -1662,11 +1662,7 @@
else
ps_profile = WMI_PS_PROFILE_TYPE_PS_DISABLED;
- rc = wmi_ps_dev_profile_cfg(wil, ps_profile);
- if (rc)
- wil_err(wil, "wmi_ps_dev_profile_cfg failed (%d)\n", rc);
-
- return rc;
+ return wil_ps_update(wil, ps_profile);
}
static struct cfg80211_ops wil_cfg80211_ops = {
@@ -1740,6 +1736,11 @@
wiphy->vendor_commands = wil_nl80211_vendor_commands;
wiphy->vendor_events = wil_nl80211_vendor_events;
wiphy->n_vendor_events = ARRAY_SIZE(wil_nl80211_vendor_events);
+
+ if (ignore_reg_hints) {
+ wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS;
+ wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
+ }
}
struct wireless_dev *wil_cfg80211_init(struct device *dev)
diff --git a/drivers/net/wireless/ath/wil6210/fw_inc.c b/drivers/net/wireless/ath/wil6210/fw_inc.c
index f490158..e01acac 100644
--- a/drivers/net/wireless/ath/wil6210/fw_inc.c
+++ b/drivers/net/wireless/ath/wil6210/fw_inc.c
@@ -554,5 +554,7 @@
rc = request_firmware(&fw, name, wil_to_dev(wil));
if (!rc)
release_firmware(fw);
- return rc != -ENOENT;
+ else
+ wil_dbg_fw(wil, "<%s> not available: %d\n", name, rc);
+ return !rc;
}
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 36959a3..1fc4580 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -130,9 +130,15 @@
u32 *d = dst;
const volatile u32 __iomem *s = src;
- /* size_t is unsigned, if (count%4 != 0) it will wrap */
- for (count += 4; count > 4; count -= 4)
+ for (; count >= 4; count -= 4)
*d++ = __raw_readl(s++);
+
+ if (unlikely(count)) {
+ /* count can be 1..3 */
+ u32 tmp = __raw_readl(s);
+
+ memcpy(d, &tmp, count);
+ }
}
void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
@@ -141,8 +147,16 @@
volatile u32 __iomem *d = dst;
const u32 *s = src;
- for (count += 4; count > 4; count -= 4)
+ for (; count >= 4; count -= 4)
__raw_writel(*s++, d++);
+
+ if (unlikely(count)) {
+ /* count can be 1..3 */
+ u32 tmp = 0;
+
+ memcpy(&tmp, s, count);
+ __raw_writel(tmp, d);
+ }
}
static void wil_disconnect_cid(struct wil6210_priv *wil, int cid,
@@ -176,6 +190,7 @@
break;
}
sta->status = wil_sta_unused;
+ sta->fst_link_loss = false;
}
/* reorder buffers */
for (i = 0; i < WIL_STA_TID_NUM; i++) {
@@ -561,6 +576,9 @@
if (rx_ring_overflow_thrsh == WIL6210_RX_HIGH_TRSH_INIT)
rx_ring_overflow_thrsh = WIL6210_RX_HIGH_TRSH_DEFAULT;
+
+ wil->ps_profile = WMI_PS_PROFILE_TYPE_DEFAULT;
+
return 0;
out_wmi_wq:
@@ -889,6 +907,24 @@
}
}
+int wil_ps_update(struct wil6210_priv *wil, enum wmi_ps_profile_type ps_profile)
+{
+ int rc;
+
+ if (!test_bit(WMI_FW_CAPABILITY_PS_CONFIG, wil->fw_capabilities)) {
+ wil_err(wil, "set_power_mgmt not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ rc = wmi_ps_dev_profile_cfg(wil, ps_profile);
+ if (rc)
+ wil_err(wil, "wmi_ps_dev_profile_cfg failed (%d)\n", rc);
+ else
+ wil->ps_profile = ps_profile;
+
+ return rc;
+}
+
/*
* We reset all the structures, and we reset the UMAC.
* After calling this routine, you're expected to reload
@@ -938,15 +974,15 @@
/* Disable device led before reset*/
wmi_led_cfg(wil, false);
+ mutex_lock(&wil->p2p_wdev_mutex);
+ wil_abort_scan(wil, false);
+ mutex_unlock(&wil->p2p_wdev_mutex);
+
/* prevent NAPI from being scheduled and prevent wmi commands */
mutex_lock(&wil->wmi_mutex);
bitmap_zero(wil->status, wil_status_last);
mutex_unlock(&wil->wmi_mutex);
- mutex_lock(&wil->p2p_wdev_mutex);
- wil_abort_scan(wil, false);
- mutex_unlock(&wil->p2p_wdev_mutex);
-
wil_mask_irq(wil);
wmi_event_flush(wil);
@@ -1023,6 +1059,12 @@
return rc;
}
+ if (wil->ps_profile != WMI_PS_PROFILE_TYPE_DEFAULT)
+ wil_ps_update(wil, wil->ps_profile);
+
+ if (wil->tt_data_set)
+ wmi_set_tt_cfg(wil, &wil->tt_data);
+
wil_collect_fw_info(wil);
if (wil->platform_ops.notify) {
diff --git a/drivers/net/wireless/ath/wil6210/pmc.c b/drivers/net/wireless/ath/wil6210/pmc.c
index b067fdf..2e301b6 100644
--- a/drivers/net/wireless/ath/wil6210/pmc.c
+++ b/drivers/net/wireless/ath/wil6210/pmc.c
@@ -200,7 +200,7 @@
release_pmc_skbs:
wil_err(wil, "exit on error: Releasing skbs...\n");
- for (i = 0; pmc->descriptors[i].va && i < num_descriptors; i++) {
+ for (i = 0; i < num_descriptors && pmc->descriptors[i].va; i++) {
dma_free_coherent(dev,
descriptor_size,
pmc->descriptors[i].va,
@@ -283,7 +283,7 @@
int i;
for (i = 0;
- pmc->descriptors[i].va && i < pmc->num_descriptors; i++) {
+ i < pmc->num_descriptors && pmc->descriptors[i].va; i++) {
dma_free_coherent(dev,
pmc->descriptor_size,
pmc->descriptors[i].va,
diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c
index 7404b6f..a43cffc 100644
--- a/drivers/net/wireless/ath/wil6210/rx_reorder.c
+++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c
@@ -343,8 +343,16 @@
wil_err(wil, "BACK requested unsupported ba_policy == 1\n");
status = WLAN_STATUS_INVALID_QOS_PARAM;
}
- if (status == WLAN_STATUS_SUCCESS)
- agg_wsize = wil_agg_size(wil, req_agg_wsize);
+ if (status == WLAN_STATUS_SUCCESS) {
+ if (req_agg_wsize == 0) {
+ wil_dbg_misc(wil, "Suggest BACK wsize %d\n",
+ WIL_MAX_AGG_WSIZE);
+ agg_wsize = WIL_MAX_AGG_WSIZE;
+ } else {
+ agg_wsize = min_t(u16,
+ WIL_MAX_AGG_WSIZE, req_agg_wsize);
+ }
+ }
rc = wmi_addba_rx_resp(wil, cid, tid, dialog_token, status,
agg_amsdu, agg_wsize, agg_timeout);
diff --git a/drivers/net/wireless/ath/wil6210/sysfs.c b/drivers/net/wireless/ath/wil6210/sysfs.c
index 0faa26c..b4c4d09 100644
--- a/drivers/net/wireless/ath/wil6210/sysfs.c
+++ b/drivers/net/wireless/ath/wil6210/sysfs.c
@@ -94,8 +94,184 @@
wil_ftm_txrx_offset_sysfs_show,
wil_ftm_txrx_offset_sysfs_store);
+static ssize_t
+wil_tt_sysfs_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct wil6210_priv *wil = dev_get_drvdata(dev);
+ ssize_t len;
+ struct wmi_tt_data tt_data;
+ int i, rc;
+
+ rc = wmi_get_tt_cfg(wil, &tt_data);
+ if (rc)
+ return rc;
+
+ len = snprintf(buf, PAGE_SIZE, " high max critical\n");
+
+ len += snprintf(buf + len, PAGE_SIZE - len, "bb: ");
+ if (tt_data.bb_enabled)
+ for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i)
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "%03d-%03d ",
+ tt_data.bb_zones[i].temperature_high,
+ tt_data.bb_zones[i].temperature_low);
+ else
+ len += snprintf(buf + len, PAGE_SIZE - len, "* disabled *");
+ len += snprintf(buf + len, PAGE_SIZE - len, "\nrf: ");
+ if (tt_data.rf_enabled)
+ for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i)
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "%03d-%03d ",
+ tt_data.rf_zones[i].temperature_high,
+ tt_data.rf_zones[i].temperature_low);
+ else
+ len += snprintf(buf + len, PAGE_SIZE - len, "* disabled *");
+ len += snprintf(buf + len, PAGE_SIZE - len, "\n");
+
+ return len;
+}
+
+static ssize_t
+wil_tt_sysfs_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct wil6210_priv *wil = dev_get_drvdata(dev);
+ int i, rc = -EINVAL;
+ char *token, *dupbuf, *tmp;
+ struct wmi_tt_data tt_data = {
+ .bb_enabled = 0,
+ .rf_enabled = 0,
+ };
+
+ tmp = kmemdup(buf, count + 1, GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+ tmp[count] = '\0';
+ dupbuf = tmp;
+
+ /* Format for writing is 12 unsigned bytes separated by spaces:
+ * <bb_z1_h> <bb_z1_l> <bb_z2_h> <bb_z2_l> <bb_z3_h> <bb_z3_l> \
+ * <rf_z1_h> <rf_z1_l> <rf_z2_h> <rf_z2_l> <rf_z3_h> <rf_z3_l>
+ * To disable thermal throttling for bb or for rf, use 0 for all
+ * its six set points.
+ */
+
+ /* bb */
+ for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i) {
+ token = strsep(&dupbuf, " ");
+ if (!token)
+ goto out;
+ if (kstrtou8(token, 0, &tt_data.bb_zones[i].temperature_high))
+ goto out;
+ token = strsep(&dupbuf, " ");
+ if (!token)
+ goto out;
+ if (kstrtou8(token, 0, &tt_data.bb_zones[i].temperature_low))
+ goto out;
+
+ if (tt_data.bb_zones[i].temperature_high > 0 ||
+ tt_data.bb_zones[i].temperature_low > 0)
+ tt_data.bb_enabled = 1;
+ }
+ /* rf */
+ for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i) {
+ token = strsep(&dupbuf, " ");
+ if (!token)
+ goto out;
+ if (kstrtou8(token, 0, &tt_data.rf_zones[i].temperature_high))
+ goto out;
+ token = strsep(&dupbuf, " ");
+ if (!token)
+ goto out;
+ if (kstrtou8(token, 0, &tt_data.rf_zones[i].temperature_low))
+ goto out;
+
+ if (tt_data.rf_zones[i].temperature_high > 0 ||
+ tt_data.rf_zones[i].temperature_low > 0)
+ tt_data.rf_enabled = 1;
+ }
+
+ rc = wmi_set_tt_cfg(wil, &tt_data);
+ if (rc)
+ goto out;
+
+ rc = count;
+out:
+ kfree(tmp);
+ return rc;
+}
+
+static DEVICE_ATTR(thermal_throttling, 0644,
+ wil_tt_sysfs_show, wil_tt_sysfs_store);
+
+static ssize_t
+wil_fst_link_loss_sysfs_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct wil6210_priv *wil = dev_get_drvdata(dev);
+ ssize_t len = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wil->sta); i++)
+ if (wil->sta[i].status == wil_sta_connected)
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "[%d] %pM %s\n", i, wil->sta[i].addr,
+ wil->sta[i].fst_link_loss ?
+ "On" : "Off");
+
+ return len;
+}
+
+static ssize_t
+wil_fst_link_loss_sysfs_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct wil6210_priv *wil = dev_get_drvdata(dev);
+ u8 addr[ETH_ALEN];
+ char *token, *dupbuf, *tmp;
+ int rc = -EINVAL;
+ bool fst_link_loss;
+
+ tmp = kmemdup(buf, count + 1, GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ tmp[count] = '\0';
+ dupbuf = tmp;
+
+ token = strsep(&dupbuf, " ");
+ if (!token)
+ goto out;
+
+ /* mac address */
+ if (sscanf(token, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+ &addr[0], &addr[1], &addr[2],
+ &addr[3], &addr[4], &addr[5]) != 6)
+ goto out;
+
+ /* On/Off */
+ if (strtobool(dupbuf, &fst_link_loss))
+ goto out;
+
+ wil_dbg_misc(wil, "set [%pM] with %d\n", addr, fst_link_loss);
+
+ rc = wmi_link_maintain_cfg_write(wil, addr, fst_link_loss);
+ if (!rc)
+ rc = count;
+
+out:
+ kfree(tmp);
+ return rc;
+}
+
+static DEVICE_ATTR(fst_link_loss, 0644,
+ wil_fst_link_loss_sysfs_show,
+ wil_fst_link_loss_sysfs_store);
+
static struct attribute *wil6210_sysfs_entries[] = {
&dev_attr_ftm_txrx_offset.attr,
+ &dev_attr_thermal_throttling.attr,
+ &dev_attr_fst_link_loss.attr,
NULL
};
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 8b5411e..35bbf3a 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -37,6 +37,10 @@
module_param(rx_align_2, bool, 0444);
MODULE_PARM_DESC(rx_align_2, " align Rx buffers on 4*n+2, default - no");
+bool rx_large_buf;
+module_param(rx_large_buf, bool, 0444);
+MODULE_PARM_DESC(rx_large_buf, " allocate 8KB RX buffers, default - no");
+
static inline uint wil_rx_snaplen(void)
{
return rx_align_2 ? 6 : 0;
@@ -255,7 +259,7 @@
u32 i, int headroom)
{
struct device *dev = wil_to_dev(wil);
- unsigned int sz = mtu_max + ETH_HLEN + wil_rx_snaplen();
+ unsigned int sz = wil->rx_buf_len + ETH_HLEN + wil_rx_snaplen();
struct vring_rx_desc dd, *d = ⅆ
volatile struct vring_rx_desc *_d = &vring->va[i].rx;
dma_addr_t pa;
@@ -419,7 +423,7 @@
struct sk_buff *skb;
dma_addr_t pa;
unsigned int snaplen = wil_rx_snaplen();
- unsigned int sz = mtu_max + ETH_HLEN + snaplen;
+ unsigned int sz = wil->rx_buf_len + ETH_HLEN + snaplen;
u16 dmalen;
u8 ftype;
int cid;
@@ -780,6 +784,20 @@
wil_rx_refill(wil, v->size);
}
+static void wil_rx_buf_len_init(struct wil6210_priv *wil)
+{
+ wil->rx_buf_len = rx_large_buf ?
+ WIL_MAX_ETH_MTU : TXRX_BUF_LEN_DEFAULT - WIL_MAX_MPDU_OVERHEAD;
+ if (mtu_max > wil->rx_buf_len) {
+ /* do not allow RX buffers to be smaller than mtu_max, for
+ * backward compatibility (mtu_max parameter was also used
+ * to support receiving large packets)
+ */
+ wil_info(wil, "Override RX buffer to mtu_max(%d)\n", mtu_max);
+ wil->rx_buf_len = mtu_max;
+ }
+}
+
int wil_rx_init(struct wil6210_priv *wil, u16 size)
{
struct vring *vring = &wil->vring_rx;
@@ -792,6 +810,8 @@
return -EINVAL;
}
+ wil_rx_buf_len_init(wil);
+
vring->size = size;
rc = wil_vring_alloc(wil, vring);
if (rc)
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 734449d..d05bb36 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -33,6 +33,7 @@
extern int agg_wsize;
extern u32 vring_idle_trsh;
extern bool rx_align_2;
+extern bool rx_large_buf;
extern bool debug_fw;
extern bool disable_ap_sme;
@@ -531,6 +532,7 @@
struct wil_tid_crypto_rx tid_crypto_rx[WIL_STA_TID_NUM];
struct wil_tid_crypto_rx group_crypto_rx;
u8 aid; /* 1-254; 0 if unknown/not reported */
+ bool fst_link_loss;
};
enum {
@@ -661,6 +663,7 @@
struct work_struct probe_client_worker;
/* DMA related */
struct vring vring_rx;
+ unsigned int rx_buf_len;
struct vring vring_tx[WIL6210_MAX_TX_RINGS];
struct vring_tx_data vring_tx_data[WIL6210_MAX_TX_RINGS];
u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */
@@ -696,7 +699,11 @@
/* High Access Latency Policy voting */
struct wil_halp halp;
+ enum wmi_ps_profile_type ps_profile;
+
struct wil_ftm_priv ftm;
+ bool tt_data_set;
+ struct wmi_tt_data tt_data;
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
@@ -813,6 +820,8 @@
void wil_if_remove(struct wil6210_priv *wil);
int wil_priv_init(struct wil6210_priv *wil);
void wil_priv_deinit(struct wil6210_priv *wil);
+int wil_ps_update(struct wil6210_priv *wil,
+ enum wmi_ps_profile_type ps_profile);
int wil_reset(struct wil6210_priv *wil, bool no_fw);
void wil_fw_error_recovery(struct wil6210_priv *wil);
void wil_set_recovery_state(struct wil6210_priv *wil, int state);
@@ -861,6 +870,8 @@
int wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short);
int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short);
int wmi_new_sta(struct wil6210_priv *wil, const u8 *mac, u8 aid);
+int wmi_set_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data);
+int wmi_get_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data);
int wil_addba_rx_request(struct wil6210_priv *wil, u8 cidxtid,
u8 dialog_token, __le16 ba_param_set,
__le16 ba_timeout, __le16 ba_seq_ctrl);
@@ -983,5 +994,9 @@
void wil_aoa_evt_meas(struct wil6210_priv *wil,
struct wmi_aoa_meas_event *evt,
int len);
+/* link loss */
+int wmi_link_maintain_cfg_write(struct wil6210_priv *wil,
+ const u8 *addr,
+ bool fst_link_loss);
#endif /* __WIL6210_H__ */
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 31d6ab9..f085fb3 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -519,16 +519,16 @@
assoc_resp_ielen = 0;
}
- mutex_lock(&wil->mutex);
if (test_bit(wil_status_resetting, wil->status) ||
!test_bit(wil_status_fwready, wil->status)) {
wil_err(wil, "status_resetting, cancel connect event, CID %d\n",
evt->cid);
- mutex_unlock(&wil->mutex);
/* no need for cleanup, wil_reset will do that */
return;
}
+ mutex_lock(&wil->mutex);
+
if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
(wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
if (!test_bit(wil_status_fwconnecting, wil->status)) {
@@ -632,6 +632,13 @@
wil->sinfo_gen++;
+ if (test_bit(wil_status_resetting, wil->status) ||
+ !test_bit(wil_status_fwready, wil->status)) {
+ wil_err(wil, "status_resetting, cancel disconnect event\n");
+ /* no need for cleanup, wil_reset will do that */
+ return;
+ }
+
mutex_lock(&wil->mutex);
wil6210_disconnect(wil, evt->bssid, reason_code, true);
mutex_unlock(&wil->mutex);
@@ -1430,7 +1437,8 @@
struct wmi_cfg_rx_chain_cmd cmd = {
.action = WMI_RX_CHAIN_ADD,
.rx_sw_ring = {
- .max_mpdu_size = cpu_to_le16(wil_mtu2macbuf(mtu_max)),
+ .max_mpdu_size = cpu_to_le16(
+ wil_mtu2macbuf(wil->rx_buf_len)),
.ring_mem_base = cpu_to_le64(vring->pa),
.ring_size = cpu_to_le16(vring->size),
},
@@ -1769,6 +1777,67 @@
return rc;
}
+int wmi_set_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data)
+{
+ int rc;
+ struct wmi_set_thermal_throttling_cfg_cmd cmd = {
+ .tt_data = *tt_data,
+ };
+ struct {
+ struct wmi_cmd_hdr wmi;
+ struct wmi_set_thermal_throttling_cfg_event evt;
+ } __packed reply;
+
+ if (!test_bit(WMI_FW_CAPABILITY_THERMAL_THROTTLING,
+ wil->fw_capabilities))
+ return -EOPNOTSUPP;
+
+ memset(&reply, 0, sizeof(reply));
+ rc = wmi_call(wil, WMI_SET_THERMAL_THROTTLING_CFG_CMDID, &cmd,
+ sizeof(cmd), WMI_SET_THERMAL_THROTTLING_CFG_EVENTID,
+ &reply, sizeof(reply), 100);
+ if (rc) {
+ wil_err(wil, "failed to set thermal throttling\n");
+ return rc;
+ }
+ if (reply.evt.status) {
+ wil_err(wil, "set thermal throttling failed, error %d\n",
+ reply.evt.status);
+ return -EIO;
+ }
+
+ wil->tt_data = *tt_data;
+ wil->tt_data_set = true;
+
+ return 0;
+}
+
+int wmi_get_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data)
+{
+ int rc;
+ struct {
+ struct wmi_cmd_hdr wmi;
+ struct wmi_get_thermal_throttling_cfg_event evt;
+ } __packed reply;
+
+ if (!test_bit(WMI_FW_CAPABILITY_THERMAL_THROTTLING,
+ wil->fw_capabilities))
+ return -EOPNOTSUPP;
+
+ rc = wmi_call(wil, WMI_GET_THERMAL_THROTTLING_CFG_CMDID, NULL, 0,
+ WMI_GET_THERMAL_THROTTLING_CFG_EVENTID, &reply,
+ sizeof(reply), 100);
+ if (rc) {
+ wil_err(wil, "failed to get thermal throttling\n");
+ return rc;
+ }
+
+ if (tt_data)
+ *tt_data = reply.evt.tt_data;
+
+ return 0;
+}
+
void wmi_event_flush(struct wil6210_priv *wil)
{
ulong flags;
@@ -1786,6 +1855,61 @@
spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
}
+int wmi_link_maintain_cfg_write(struct wil6210_priv *wil,
+ const u8 *addr,
+ bool fst_link_loss)
+{
+ int rc;
+ int cid = wil_find_cid(wil, addr);
+ u32 cfg_type;
+ struct wmi_link_maintain_cfg_write_cmd cmd;
+ struct {
+ struct wmi_cmd_hdr wmi;
+ struct wmi_link_maintain_cfg_write_done_event evt;
+ } __packed reply;
+
+ if (cid < 0)
+ return cid;
+
+ switch (wil->wdev->iftype) {
+ case NL80211_IFTYPE_STATION:
+ cfg_type = fst_link_loss ?
+ WMI_LINK_MAINTAIN_CFG_TYPE_DEFAULT_FST_STA :
+ WMI_LINK_MAINTAIN_CFG_TYPE_DEFAULT_NORMAL_STA;
+ break;
+ case NL80211_IFTYPE_AP:
+ cfg_type = fst_link_loss ?
+ WMI_LINK_MAINTAIN_CFG_TYPE_DEFAULT_FST_AP :
+ WMI_LINK_MAINTAIN_CFG_TYPE_DEFAULT_NORMAL_AP;
+ break;
+ default:
+ wil_err(wil, "Unsupported for iftype %d", wil->wdev->iftype);
+ return -EINVAL;
+ }
+
+ wil_dbg_misc(wil, "Setting cid:%d with cfg_type:%d\n", cid, cfg_type);
+
+ cmd.cfg_type = cpu_to_le32(cfg_type);
+ cmd.cid = cpu_to_le32(cid);
+
+ reply.evt.status = cpu_to_le32(WMI_FW_STATUS_FAILURE);
+
+ rc = wmi_call(wil, WMI_LINK_MAINTAIN_CFG_WRITE_CMDID, &cmd, sizeof(cmd),
+ WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENTID, &reply,
+ sizeof(reply), 250);
+ if (rc) {
+ wil_err(wil, "Failed to %s FST link loss",
+ fst_link_loss ? "enable" : "disable");
+ } else if (reply.evt.status == WMI_FW_STATUS_SUCCESS) {
+ wil->sta[cid].fst_link_loss = fst_link_loss;
+ } else {
+ wil_err(wil, "WMI_LINK_MAINTAIN_CFG_WRITE_CMDID returned status %d",
+ reply.evt.status);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
static bool wmi_evt_call_handler(struct wil6210_priv *wil, int id,
void *d, int len)
{
diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h
index 7c9fee5..f7f5f4f 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.h
+++ b/drivers/net/wireless/ath/wil6210/wmi.h
@@ -58,6 +58,7 @@
WMI_FW_CAPABILITY_MGMT_RETRY_LIMIT = 3,
WMI_FW_CAPABILITY_DISABLE_AP_SME = 4,
WMI_FW_CAPABILITY_WMI_ONLY = 5,
+ WMI_FW_CAPABILITY_THERMAL_THROTTLING = 7,
WMI_FW_CAPABILITY_MAX,
};
@@ -142,8 +143,6 @@
WMI_MAINTAIN_RESUME_CMDID = 0x851,
WMI_RS_MGMT_CMDID = 0x852,
WMI_RF_MGMT_CMDID = 0x853,
- WMI_THERMAL_THROTTLING_CTRL_CMDID = 0x854,
- WMI_THERMAL_THROTTLING_GET_STATUS_CMDID = 0x855,
WMI_OTP_READ_CMDID = 0x856,
WMI_OTP_WRITE_CMDID = 0x857,
WMI_LED_CFG_CMDID = 0x858,
@@ -192,6 +191,8 @@
WMI_GET_MGMT_RETRY_LIMIT_CMDID = 0x931,
WMI_NEW_STA_CMDID = 0x935,
WMI_DEL_STA_CMDID = 0x936,
+ WMI_SET_THERMAL_THROTTLING_CFG_CMDID = 0x940,
+ WMI_GET_THERMAL_THROTTLING_CFG_CMDID = 0x941,
WMI_TOF_SESSION_START_CMDID = 0x991,
WMI_TOF_GET_CAPABILITIES_CMDID = 0x992,
WMI_TOF_SET_LCR_CMDID = 0x993,
@@ -438,16 +439,6 @@
__le32 rf_mgmt_type;
} __packed;
-/* WMI_THERMAL_THROTTLING_CTRL_CMDID */
-#define THERMAL_THROTTLING_USE_DEFAULT_MAX_TXOP_LENGTH (0xFFFFFFFF)
-
-/* WMI_THERMAL_THROTTLING_CTRL_CMDID */
-struct wmi_thermal_throttling_ctrl_cmd {
- __le32 time_on_usec;
- __le32 time_off_usec;
- __le32 max_txop_length_usec;
-} __packed;
-
/* WMI_RF_RX_TEST_CMDID */
struct wmi_rf_rx_test_cmd {
__le32 sector;
@@ -549,7 +540,7 @@
u8 hidden_ssid;
u8 is_go;
u8 reserved0[5];
- /* abft_len override if non-0 */
+ /* A-BFT length override if non-0 */
u8 abft_len;
u8 disable_ap_sme;
u8 network_type;
@@ -910,6 +901,39 @@
u8 reserved[3];
} __packed;
+/* Zones: HIGH, MAX, CRITICAL */
+#define WMI_NUM_OF_TT_ZONES (3)
+
+struct wmi_tt_zone_limits {
+ /* Above this temperature this zone is active */
+ u8 temperature_high;
+ /* Below this temperature the adjacent lower zone is active */
+ u8 temperature_low;
+ u8 reserved[2];
+} __packed;
+
+/* Struct used for both configuration and status commands of thermal
+ * throttling
+ */
+struct wmi_tt_data {
+ /* Enable/Disable TT algorithm for baseband */
+ u8 bb_enabled;
+ u8 reserved0[3];
+ /* Define zones for baseband */
+ struct wmi_tt_zone_limits bb_zones[WMI_NUM_OF_TT_ZONES];
+ /* Enable/Disable TT algorithm for radio */
+ u8 rf_enabled;
+ u8 reserved1[3];
+ /* Define zones for all radio chips */
+ struct wmi_tt_zone_limits rf_zones[WMI_NUM_OF_TT_ZONES];
+} __packed;
+
+/* WMI_SET_THERMAL_THROTTLING_CFG_CMDID */
+struct wmi_set_thermal_throttling_cfg_cmd {
+ /* Command data */
+ struct wmi_tt_data tt_data;
+} __packed;
+
/* WMI_NEW_STA_CMDID */
struct wmi_new_sta_cmd {
u8 dst_mac[WMI_MAC_LEN];
@@ -1040,7 +1064,6 @@
WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839,
WMI_RS_MGMT_DONE_EVENTID = 0x1852,
WMI_RF_MGMT_STATUS_EVENTID = 0x1853,
- WMI_THERMAL_THROTTLING_STATUS_EVENTID = 0x1855,
WMI_BF_SM_MGMT_DONE_EVENTID = 0x1838,
WMI_RX_MGMT_PACKET_EVENTID = 0x1840,
WMI_TX_MGMT_PACKET_EVENTID = 0x1841,
@@ -1090,6 +1113,8 @@
WMI_BRP_SET_ANT_LIMIT_EVENTID = 0x1924,
WMI_SET_MGMT_RETRY_LIMIT_EVENTID = 0x1930,
WMI_GET_MGMT_RETRY_LIMIT_EVENTID = 0x1931,
+ WMI_SET_THERMAL_THROTTLING_CFG_EVENTID = 0x1940,
+ WMI_GET_THERMAL_THROTTLING_CFG_EVENTID = 0x1941,
WMI_TOF_SESSION_END_EVENTID = 0x1991,
WMI_TOF_GET_CAPABILITIES_EVENTID = 0x1992,
WMI_TOF_SET_LCR_EVENTID = 0x1993,
@@ -1133,13 +1158,6 @@
__le32 rf_status;
} __packed;
-/* WMI_THERMAL_THROTTLING_STATUS_EVENTID */
-struct wmi_thermal_throttling_status_event {
- __le32 time_on_usec;
- __le32 time_off_usec;
- __le32 max_txop_length_usec;
-} __packed;
-
/* WMI_GET_STATUS_DONE_EVENTID */
struct wmi_get_status_done_event {
__le32 is_associated;
@@ -2206,6 +2224,19 @@
__le32 aoa_supported_types;
} __packed;
+/* WMI_SET_THERMAL_THROTTLING_CFG_EVENTID */
+struct wmi_set_thermal_throttling_cfg_event {
+ /* wmi_fw_status */
+ u8 status;
+ u8 reserved[3];
+} __packed;
+
+/* WMI_GET_THERMAL_THROTTLING_CFG_EVENTID */
+struct wmi_get_thermal_throttling_cfg_event {
+ /* Status data */
+ struct wmi_tt_data tt_data;
+} __packed;
+
enum wmi_tof_session_end_status {
WMI_TOF_SESSION_END_NO_ERROR = 0x00,
WMI_TOF_SESSION_END_FAIL = 0x01,
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
index 5eaac13..f877301 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
@@ -198,7 +198,7 @@
int ret;
struct brcmf_if *ifp = netdev_priv(ndev);
struct brcmf_pub *drvr = ifp->drvr;
- struct ethhdr *eh = (struct ethhdr *)(skb->data);
+ struct ethhdr *eh;
brcmf_dbg(DATA, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
@@ -211,22 +211,13 @@
goto done;
}
- /* Make sure there's enough room for any header */
- if (skb_headroom(skb) < drvr->hdrlen) {
- struct sk_buff *skb2;
-
- brcmf_dbg(INFO, "%s: insufficient headroom\n",
+ /* Make sure there's enough writable headroom*/
+ ret = skb_cow_head(skb, drvr->hdrlen);
+ if (ret < 0) {
+ brcmf_err("%s: skb_cow_head failed\n",
brcmf_ifname(ifp));
- drvr->bus_if->tx_realloc++;
- skb2 = skb_realloc_headroom(skb, drvr->hdrlen);
dev_kfree_skb(skb);
- skb = skb2;
- if (skb == NULL) {
- brcmf_err("%s: skb_realloc_headroom failed\n",
- brcmf_ifname(ifp));
- ret = -ENOMEM;
- goto done;
- }
+ goto done;
}
/* validate length for ether packet */
@@ -236,6 +227,8 @@
goto done;
}
+ eh = (struct ethhdr *)(skb->data);
+
if (eh->h_proto == htons(ETH_P_PAE))
atomic_inc(&ifp->pend_8021x_cnt);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-6000.c b/drivers/net/wireless/intel/iwlwifi/iwl-6000.c
index 0b9f6a7..39335b7 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-6000.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-6000.c
@@ -371,4 +371,4 @@
MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL6050_MODULE_FIRMWARE(IWL6050_UCODE_API_MAX));
MODULE_FIRMWARE(IWL6005_MODULE_FIRMWARE(IWL6000G2_UCODE_API_MAX));
-MODULE_FIRMWARE(IWL6030_MODULE_FIRMWARE(IWL6000G2B_UCODE_API_MAX));
+MODULE_FIRMWARE(IWL6030_MODULE_FIRMWARE(IWL6000G2_UCODE_API_MAX));
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
index b88e204..207d8ae 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
@@ -1262,12 +1262,15 @@
iwl_trans_d3_suspend(mvm->trans, test, !unified_image);
out:
if (ret < 0) {
- iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
- if (mvm->restart_fw > 0) {
- mvm->restart_fw--;
- ieee80211_restart_hw(mvm->hw);
- }
iwl_mvm_free_nd(mvm);
+
+ if (!unified_image) {
+ iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
+ if (mvm->restart_fw > 0) {
+ mvm->restart_fw--;
+ ieee80211_restart_hw(mvm->hw);
+ }
+ }
}
out_noreset:
mutex_unlock(&mvm->mutex);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
index 7b7d2a1..0bda91f 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
@@ -1056,6 +1056,8 @@
if (ret)
return ret;
+ if (count == 0)
+ return 0;
iwl_mvm_fw_dbg_collect(mvm, FW_DBG_TRIGGER_USER, buf,
(count - 1), NULL);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c
index d89d0a1..700d244 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c
@@ -784,12 +784,16 @@
struct iwl_fw_error_dump_paging *paging;
struct page *pages =
mvm->fw_paging_db[i].fw_paging_block;
+ dma_addr_t addr = mvm->fw_paging_db[i].fw_paging_phys;
dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING);
dump_data->len = cpu_to_le32(sizeof(*paging) +
PAGING_BLOCK_SIZE);
paging = (void *)dump_data->data;
paging->index = cpu_to_le32(i);
+ dma_sync_single_for_cpu(mvm->trans->dev, addr,
+ PAGING_BLOCK_SIZE,
+ DMA_BIDIRECTIONAL);
memcpy(paging->data, page_address(pages),
PAGING_BLOCK_SIZE);
dump_data = iwl_fw_error_next_data(dump_data);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
index 8720663..2ec3a91 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
@@ -214,6 +214,10 @@
memcpy(page_address(mvm->fw_paging_db[0].fw_paging_block),
image->sec[sec_idx].data,
mvm->fw_paging_db[0].fw_paging_size);
+ dma_sync_single_for_device(mvm->trans->dev,
+ mvm->fw_paging_db[0].fw_paging_phys,
+ mvm->fw_paging_db[0].fw_paging_size,
+ DMA_BIDIRECTIONAL);
IWL_DEBUG_FW(mvm,
"Paging: copied %d CSS bytes to first block\n",
@@ -228,9 +232,16 @@
* loop stop at num_of_paging_blk since that last block is not full.
*/
for (idx = 1; idx < mvm->num_of_paging_blk; idx++) {
- memcpy(page_address(mvm->fw_paging_db[idx].fw_paging_block),
+ struct iwl_fw_paging *block = &mvm->fw_paging_db[idx];
+
+ memcpy(page_address(block->fw_paging_block),
image->sec[sec_idx].data + offset,
- mvm->fw_paging_db[idx].fw_paging_size);
+ block->fw_paging_size);
+ dma_sync_single_for_device(mvm->trans->dev,
+ block->fw_paging_phys,
+ block->fw_paging_size,
+ DMA_BIDIRECTIONAL);
+
IWL_DEBUG_FW(mvm,
"Paging: copied %d paging bytes to block %d\n",
@@ -242,9 +253,15 @@
/* copy the last paging block */
if (mvm->num_of_pages_in_last_blk > 0) {
- memcpy(page_address(mvm->fw_paging_db[idx].fw_paging_block),
+ struct iwl_fw_paging *block = &mvm->fw_paging_db[idx];
+
+ memcpy(page_address(block->fw_paging_block),
image->sec[sec_idx].data + offset,
FW_PAGING_SIZE * mvm->num_of_pages_in_last_blk);
+ dma_sync_single_for_device(mvm->trans->dev,
+ block->fw_paging_phys,
+ block->fw_paging_size,
+ DMA_BIDIRECTIONAL);
IWL_DEBUG_FW(mvm,
"Paging: copied %d pages in the last block %d\n",
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
index 6c802ce..a481eb4 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
@@ -409,7 +409,7 @@
/* ignore nssn smaller than head sn - this can happen due to timeout */
if (iwl_mvm_is_sn_less(nssn, ssn, reorder_buf->buf_size))
- return;
+ goto set_timer;
while (iwl_mvm_is_sn_less(ssn, nssn, reorder_buf->buf_size)) {
int index = ssn % reorder_buf->buf_size;
@@ -432,6 +432,7 @@
}
reorder_buf->head_sn = nssn;
+set_timer:
if (reorder_buf->num_stored && !reorder_buf->removed) {
u16 index = reorder_buf->head_sn % reorder_buf->buf_size;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
index 52de3c6..e64aeb4 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
@@ -1466,6 +1466,7 @@
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+ u8 sta_id = mvm_sta->sta_id;
int ret;
lockdep_assert_held(&mvm->mutex);
@@ -1474,7 +1475,7 @@
kfree(mvm_sta->dup_data);
if ((vif->type == NL80211_IFTYPE_STATION &&
- mvmvif->ap_sta_id == mvm_sta->sta_id) ||
+ mvmvif->ap_sta_id == sta_id) ||
iwl_mvm_is_dqa_supported(mvm)){
ret = iwl_mvm_drain_sta(mvm, mvm_sta, true);
if (ret)
@@ -1497,6 +1498,15 @@
iwl_mvm_disable_sta_queues(mvm, vif, mvm_sta);
/*
+ * If pending_frames is set at this point - it must be
+ * driver internal logic error, since queues are empty
+ * and removed successuly.
+ * warn on it but set it to 0 anyway to avoid station
+ * not being removed later in the function
+ */
+ WARN_ON(atomic_xchg(&mvm->pending_frames[sta_id], 0));
+
+ /*
* If no traffic has gone through the reserved TXQ - it
* is still marked as IWL_MVM_QUEUE_RESERVED, and
* should be manually marked as free again
@@ -1506,7 +1516,7 @@
if (WARN((*status != IWL_MVM_QUEUE_RESERVED) &&
(*status != IWL_MVM_QUEUE_FREE),
"sta_id %d reserved txq %d status %d",
- mvm_sta->sta_id, reserved_txq, *status)) {
+ sta_id, reserved_txq, *status)) {
spin_unlock_bh(&mvm->queue_info_lock);
return -EINVAL;
}
@@ -1516,7 +1526,7 @@
}
if (vif->type == NL80211_IFTYPE_STATION &&
- mvmvif->ap_sta_id == mvm_sta->sta_id) {
+ mvmvif->ap_sta_id == sta_id) {
/* if associated - we can't remove the AP STA now */
if (vif->bss_conf.assoc)
return ret;
@@ -1525,7 +1535,7 @@
mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
/* clear d0i3_ap_sta_id if no longer relevant */
- if (mvm->d0i3_ap_sta_id == mvm_sta->sta_id)
+ if (mvm->d0i3_ap_sta_id == sta_id)
mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
}
}
@@ -1534,7 +1544,7 @@
* This shouldn't happen - the TDLS channel switch should be canceled
* before the STA is removed.
*/
- if (WARN_ON_ONCE(mvm->tdls_cs.peer.sta_id == mvm_sta->sta_id)) {
+ if (WARN_ON_ONCE(mvm->tdls_cs.peer.sta_id == sta_id)) {
mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT;
cancel_delayed_work(&mvm->tdls_cs.dwork);
}
@@ -1544,21 +1554,20 @@
* calls the drain worker.
*/
spin_lock_bh(&mvm_sta->lock);
+
/*
* There are frames pending on the AC queues for this station.
* We need to wait until all the frames are drained...
*/
- if (atomic_read(&mvm->pending_frames[mvm_sta->sta_id])) {
- rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
+ if (atomic_read(&mvm->pending_frames[sta_id])) {
+ rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id],
ERR_PTR(-EBUSY));
spin_unlock_bh(&mvm_sta->lock);
/* disable TDLS sta queues on drain complete */
if (sta->tdls) {
- mvm->tfd_drained[mvm_sta->sta_id] =
- mvm_sta->tfd_queue_msk;
- IWL_DEBUG_TDLS(mvm, "Draining TDLS sta %d\n",
- mvm_sta->sta_id);
+ mvm->tfd_drained[sta_id] = mvm_sta->tfd_queue_msk;
+ IWL_DEBUG_TDLS(mvm, "Draining TDLS sta %d\n", sta_id);
}
ret = iwl_mvm_drain_sta(mvm, mvm_sta, true);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
index 66957ac..0556d13 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
@@ -202,7 +202,6 @@
struct iwl_tx_cmd *tx_cmd,
struct ieee80211_tx_info *info, u8 sta_id)
{
- struct ieee80211_tx_info *skb_info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (void *)skb->data;
__le16 fc = hdr->frame_control;
u32 tx_flags = le32_to_cpu(tx_cmd->tx_flags);
@@ -284,9 +283,8 @@
tx_flags |= TX_CMD_FLG_WRITE_TX_POWER;
tx_cmd->tx_flags = cpu_to_le32(tx_flags);
- /* Total # bytes to be transmitted */
- tx_cmd->len = cpu_to_le16((u16)skb->len +
- (uintptr_t)skb_info->driver_data[0]);
+ /* Total # bytes to be transmitted - PCIe code will adjust for A-MSDU */
+ tx_cmd->len = cpu_to_le16((u16)skb->len);
tx_cmd->life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
tx_cmd->sta_id = sta_id;
@@ -459,7 +457,6 @@
struct ieee80211_sta *sta, u8 sta_id)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- struct ieee80211_tx_info *skb_info = IEEE80211_SKB_CB(skb);
struct iwl_device_cmd *dev_cmd;
struct iwl_tx_cmd *tx_cmd;
@@ -479,12 +476,18 @@
iwl_mvm_set_tx_cmd_rate(mvm, tx_cmd, info, sta, hdr->frame_control);
+ return dev_cmd;
+}
+
+static void iwl_mvm_skb_prepare_status(struct sk_buff *skb,
+ struct iwl_device_cmd *cmd)
+{
+ struct ieee80211_tx_info *skb_info = IEEE80211_SKB_CB(skb);
+
memset(&skb_info->status, 0, sizeof(skb_info->status));
memset(skb_info->driver_data, 0, sizeof(skb_info->driver_data));
- skb_info->driver_data[1] = dev_cmd;
-
- return dev_cmd;
+ skb_info->driver_data[1] = cmd;
}
static int iwl_mvm_get_ctrl_vif_queue(struct iwl_mvm *mvm,
@@ -550,9 +553,6 @@
info.hw_queue != info.control.vif->cab_queue)))
return -1;
- /* This holds the amsdu headers length */
- skb_info->driver_data[0] = (void *)(uintptr_t)0;
-
queue = info.hw_queue;
/*
@@ -563,9 +563,10 @@
* (this is not possible for unicast packets as a TLDS discovery
* response are sent without a station entry); otherwise use the
* AUX station.
- * In DQA mode, if vif is of type STATION and frames are not multicast,
- * they should be sent from the BSS queue. For example, TDLS setup
- * frames should be sent on this queue, as they go through the AP.
+ * In DQA mode, if vif is of type STATION and frames are not multicast
+ * or offchannel, they should be sent from the BSS queue.
+ * For example, TDLS setup frames should be sent on this queue,
+ * as they go through the AP.
*/
sta_id = mvm->aux_sta.sta_id;
if (info.control.vif) {
@@ -587,7 +588,8 @@
if (ap_sta_id != IWL_MVM_STATION_COUNT)
sta_id = ap_sta_id;
} else if (iwl_mvm_is_dqa_supported(mvm) &&
- info.control.vif->type == NL80211_IFTYPE_STATION) {
+ info.control.vif->type == NL80211_IFTYPE_STATION &&
+ queue != mvm->aux_queue) {
queue = IWL_MVM_DQA_BSS_CLIENT_QUEUE;
}
}
@@ -598,6 +600,9 @@
if (!dev_cmd)
return -1;
+ /* From now on, we cannot access info->control */
+ iwl_mvm_skb_prepare_status(skb, dev_cmd);
+
tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
/* Copy MAC header from skb into command buffer */
@@ -634,7 +639,7 @@
unsigned int num_subframes, tcp_payload_len, subf_len, max_amsdu_len;
bool ipv4 = (skb->protocol == htons(ETH_P_IP));
u16 ip_base_id = ipv4 ? ntohs(ip_hdr(skb)->id) : 0;
- u16 amsdu_add, snap_ip_tcp, pad, i = 0;
+ u16 snap_ip_tcp, pad, i = 0;
unsigned int dbg_max_amsdu_len;
netdev_features_t netdev_features = NETIF_F_CSUM_MASK | NETIF_F_SG;
u8 *qc, tid, txf;
@@ -736,21 +741,6 @@
/* This skb fits in one single A-MSDU */
if (num_subframes * mss >= tcp_payload_len) {
- struct ieee80211_tx_info *skb_info = IEEE80211_SKB_CB(skb);
-
- /*
- * Compute the length of all the data added for the A-MSDU.
- * This will be used to compute the length to write in the TX
- * command. We have: SNAP + IP + TCP for n -1 subframes and
- * ETH header for n subframes. Note that the original skb
- * already had one set of SNAP / IP / TCP headers.
- */
- num_subframes = DIV_ROUND_UP(tcp_payload_len, mss);
- amsdu_add = num_subframes * sizeof(struct ethhdr) +
- (num_subframes - 1) * (snap_ip_tcp + pad);
- /* This holds the amsdu headers length */
- skb_info->driver_data[0] = (void *)(uintptr_t)amsdu_add;
-
__skb_queue_tail(mpdus_skb, skb);
return 0;
}
@@ -789,14 +779,6 @@
ip_hdr(tmp)->id = htons(ip_base_id + i * num_subframes);
if (tcp_payload_len > mss) {
- struct ieee80211_tx_info *skb_info =
- IEEE80211_SKB_CB(tmp);
-
- num_subframes = DIV_ROUND_UP(tcp_payload_len, mss);
- amsdu_add = num_subframes * sizeof(struct ethhdr) +
- (num_subframes - 1) * (snap_ip_tcp + pad);
- skb_info->driver_data[0] =
- (void *)(uintptr_t)amsdu_add;
skb_shinfo(tmp)->gso_size = mss;
} else {
qc = ieee80211_get_qos_ctl((void *)tmp->data);
@@ -908,7 +890,6 @@
goto drop;
tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
- /* From now on, we cannot access info->control */
/*
* we handle that entirely ourselves -- for uAPSD the firmware
@@ -1015,6 +996,9 @@
IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x\n", mvmsta->sta_id,
tid, txq_id, IEEE80211_SEQ_TO_SN(seq_number));
+ /* From now on, we cannot access info->control */
+ iwl_mvm_skb_prepare_status(skb, dev_cmd);
+
if (iwl_trans_tx(mvm->trans, skb, dev_cmd, txq_id))
goto drop_unlock_sta;
@@ -1024,7 +1008,10 @@
spin_unlock(&mvmsta->lock);
/* Increase pending frames count if this isn't AMPDU */
- if (!is_ampdu)
+ if ((iwl_mvm_is_dqa_supported(mvm) &&
+ mvmsta->tid_data[tx_cmd->tid_tspec].state != IWL_AGG_ON &&
+ mvmsta->tid_data[tx_cmd->tid_tspec].state != IWL_AGG_STARTING) ||
+ (!iwl_mvm_is_dqa_supported(mvm) && !is_ampdu))
atomic_inc(&mvm->pending_frames[mvmsta->sta_id]);
return 0;
@@ -1040,7 +1027,6 @@
struct ieee80211_sta *sta)
{
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- struct ieee80211_tx_info *skb_info = IEEE80211_SKB_CB(skb);
struct ieee80211_tx_info info;
struct sk_buff_head mpdus_skbs;
unsigned int payload_len;
@@ -1054,9 +1040,6 @@
memcpy(&info, skb->cb, sizeof(info));
- /* This holds the amsdu headers length */
- skb_info->driver_data[0] = (void *)(uintptr_t)0;
-
if (!skb_is_gso(skb))
return iwl_mvm_tx_mpdu(mvm, skb, &info, sta);
@@ -1295,8 +1278,6 @@
memset(&info->status, 0, sizeof(info->status));
- info->flags &= ~IEEE80211_TX_CTL_AMPDU;
-
/* inform mac80211 about what happened with the frame */
switch (status & TX_STATUS_MSK) {
case TX_STATUS_SUCCESS:
@@ -1319,10 +1300,11 @@
(void *)(uintptr_t)le32_to_cpu(tx_resp->initial_rate);
/* Single frame failure in an AMPDU queue => send BAR */
- if (txq_id >= mvm->first_agg_queue &&
+ if (info->flags & IEEE80211_TX_CTL_AMPDU &&
!(info->flags & IEEE80211_TX_STAT_ACK) &&
!(info->flags & IEEE80211_TX_STAT_TX_FILTERED))
info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
+ info->flags &= ~IEEE80211_TX_CTL_AMPDU;
/* W/A FW bug: seq_ctl is wrong when the status isn't success */
if (status != TX_STATUS_SUCCESS) {
@@ -1357,7 +1339,7 @@
ieee80211_tx_status(mvm->hw, skb);
}
- if (txq_id >= mvm->first_agg_queue) {
+ if (iwl_mvm_is_dqa_supported(mvm) || txq_id >= mvm->first_agg_queue) {
/* If this is an aggregation queue, we use the ssn since:
* ssn = wifi seq_num % 256.
* The seq_ctl is the sequence control of the packet to which
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
index cac6d99..e3cede9 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
@@ -279,7 +279,7 @@
bool frozen;
u8 active;
bool ampdu;
- bool block;
+ int block;
unsigned long wd_timeout;
struct sk_buff_head overflow_q;
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
index ae95533..10ef44e 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
@@ -868,17 +868,13 @@
int cpu,
int *first_ucode_section)
{
- int shift_param;
int i, ret = 0;
u32 last_read_idx = 0;
- if (cpu == 1) {
- shift_param = 0;
+ if (cpu == 1)
*first_ucode_section = 0;
- } else {
- shift_param = 16;
+ else
(*first_ucode_section)++;
- }
for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) {
last_read_idx = i;
@@ -2933,16 +2929,12 @@
PCIE_LINK_STATE_CLKPM);
}
- if (cfg->mq_rx_supported)
- addr_size = 64;
- else
- addr_size = 36;
-
if (cfg->use_tfh) {
+ addr_size = 64;
trans_pcie->max_tbs = IWL_TFH_NUM_TBS;
trans_pcie->tfd_size = sizeof(struct iwl_tfh_tfd);
-
} else {
+ addr_size = 36;
trans_pcie->max_tbs = IWL_NUM_OF_TBS;
trans_pcie->tfd_size = sizeof(struct iwl_tfd);
}
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
index 5f840f1..e1bfc95 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
@@ -2096,6 +2096,7 @@
struct iwl_cmd_meta *out_meta,
struct iwl_device_cmd *dev_cmd, u16 tb1_len)
{
+ struct iwl_tx_cmd *tx_cmd = (void *)dev_cmd->payload;
struct iwl_trans_pcie *trans_pcie = txq->trans_pcie;
struct ieee80211_hdr *hdr = (void *)skb->data;
unsigned int snap_ip_tcp_hdrlen, ip_hdrlen, total_len, hdr_room;
@@ -2145,6 +2146,13 @@
*/
skb_pull(skb, hdr_len + iv_len);
+ /*
+ * Remove the length of all the headers that we don't actually
+ * have in the MPDU by themselves, but that we duplicate into
+ * all the different MSDUs inside the A-MSDU.
+ */
+ le16_add_cpu(&tx_cmd->len, -snap_ip_tcp_hdrlen);
+
tso_start(skb, &tso);
while (total_len) {
@@ -2155,7 +2163,7 @@
unsigned int hdr_tb_len;
dma_addr_t hdr_tb_phys;
struct tcphdr *tcph;
- u8 *iph;
+ u8 *iph, *subf_hdrs_start = hdr_page->pos;
total_len -= data_left;
@@ -2216,6 +2224,8 @@
hdr_tb_len, false);
trace_iwlwifi_dev_tx_tso_chunk(trans->dev, start_hdr,
hdr_tb_len);
+ /* add this subframe's headers' length to the tx_cmd */
+ le16_add_cpu(&tx_cmd->len, hdr_page->pos - subf_hdrs_start);
/* prepare the start_hdr for the next subframe */
start_hdr = hdr_page->pos;
@@ -2408,9 +2418,10 @@
tb1_len = len;
}
- /* The first TB points to bi-directional DMA data */
- memcpy(&txq->first_tb_bufs[txq->write_ptr], &dev_cmd->hdr,
- IWL_FIRST_TB_SIZE);
+ /*
+ * The first TB points to bi-directional DMA data, we'll
+ * memcpy the data into it later.
+ */
iwl_pcie_txq_build_tfd(trans, txq, tb0_phys,
IWL_FIRST_TB_SIZE, true);
@@ -2434,6 +2445,10 @@
goto out_err;
}
+ /* building the A-MSDU might have changed this data, so memcpy it now */
+ memcpy(&txq->first_tb_bufs[txq->write_ptr], &dev_cmd->hdr,
+ IWL_FIRST_TB_SIZE);
+
tfd = iwl_pcie_get_tfd(trans_pcie, txq, txq->write_ptr);
/* Set up entry for this TFD in Tx byte-count array */
iwl_pcie_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len),
diff --git a/drivers/net/wireless/marvell/mwifiex/11n_aggr.c b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c
index c47d636..a75013a 100644
--- a/drivers/net/wireless/marvell/mwifiex/11n_aggr.c
+++ b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c
@@ -101,13 +101,6 @@
{
struct txpd *local_tx_pd;
struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb);
- unsigned int pad;
- int headroom = (priv->adapter->iface_type ==
- MWIFIEX_USB) ? 0 : INTF_HEADER_LEN;
-
- pad = ((void *)skb->data - sizeof(*local_tx_pd) -
- headroom - NULL) & (MWIFIEX_DMA_ALIGN_SZ - 1);
- skb_push(skb, pad);
skb_push(skb, sizeof(*local_tx_pd));
@@ -121,12 +114,10 @@
local_tx_pd->bss_num = priv->bss_num;
local_tx_pd->bss_type = priv->bss_type;
/* Always zero as the data is followed by struct txpd */
- local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd) +
- pad);
+ local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd));
local_tx_pd->tx_pkt_type = cpu_to_le16(PKT_TYPE_AMSDU);
local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len -
- sizeof(*local_tx_pd) -
- pad);
+ sizeof(*local_tx_pd));
if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT)
local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET;
@@ -190,7 +181,11 @@
ra_list_flags);
return -1;
}
- skb_reserve(skb_aggr, MWIFIEX_MIN_DATA_HEADER_LEN);
+
+ /* skb_aggr->data already 64 byte align, just reserve bus interface
+ * header and txpd.
+ */
+ skb_reserve(skb_aggr, headroom + sizeof(struct txpd));
tx_info_aggr = MWIFIEX_SKB_TXCB(skb_aggr);
memset(tx_info_aggr, 0, sizeof(*tx_info_aggr));
diff --git a/drivers/net/wireless/marvell/mwifiex/debugfs.c b/drivers/net/wireless/marvell/mwifiex/debugfs.c
index b9284b5..ae2b69d 100644
--- a/drivers/net/wireless/marvell/mwifiex/debugfs.c
+++ b/drivers/net/wireless/marvell/mwifiex/debugfs.c
@@ -114,7 +114,8 @@
if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) {
p += sprintf(p, "multicast_count=\"%d\"\n",
netdev_mc_count(netdev));
- p += sprintf(p, "essid=\"%s\"\n", info.ssid.ssid);
+ p += sprintf(p, "essid=\"%.*s\"\n", info.ssid.ssid_len,
+ info.ssid.ssid);
p += sprintf(p, "bssid=\"%pM\"\n", info.bssid);
p += sprintf(p, "channel=\"%d\"\n", (int) info.bss_chan);
p += sprintf(p, "country_code = \"%s\"\n", info.country_code);
diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
index 644f3a2..1532ac9 100644
--- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
@@ -1159,8 +1159,6 @@
encrypt_key.is_rx_seq_valid = true;
}
} else {
- if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
- return 0;
encrypt_key.key_disable = true;
if (mac_addr)
memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c
index b36ce18..86fa0fc 100644
--- a/drivers/net/wireless/ti/wl18xx/event.c
+++ b/drivers/net/wireless/ti/wl18xx/event.c
@@ -218,5 +218,33 @@
if (vector & FW_LOGGER_INDICATION)
wlcore_event_fw_logger(wl);
+ if (vector & RX_BA_WIN_SIZE_CHANGE_EVENT_ID) {
+ struct wl12xx_vif *wlvif;
+ struct ieee80211_vif *vif;
+ struct ieee80211_sta *sta;
+ u8 link_id = mbox->rx_ba_link_id;
+ u8 win_size = mbox->rx_ba_win_size;
+ const u8 *addr;
+
+ wlvif = wl->links[link_id].wlvif;
+ vif = wl12xx_wlvif_to_vif(wlvif);
+
+ /* Update RX aggregation window size and call
+ * MAC routine to stop active RX aggregations for this link
+ */
+ if (wlvif->bss_type != BSS_TYPE_AP_BSS)
+ addr = vif->bss_conf.bssid;
+ else
+ addr = wl->links[link_id].addr;
+
+ sta = ieee80211_find_sta(vif, addr);
+ if (sta) {
+ sta->max_rx_aggregation_subframes = win_size;
+ ieee80211_stop_rx_ba_session(vif,
+ wl->links[link_id].ba_bitmap,
+ addr);
+ }
+ }
+
return 0;
}
diff --git a/drivers/net/wireless/ti/wl18xx/event.h b/drivers/net/wireless/ti/wl18xx/event.h
index ce8ea9c0..4af297f 100644
--- a/drivers/net/wireless/ti/wl18xx/event.h
+++ b/drivers/net/wireless/ti/wl18xx/event.h
@@ -38,6 +38,7 @@
REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID = BIT(18),
DFS_CHANNELS_CONFIG_COMPLETE_EVENT = BIT(19),
PERIODIC_SCAN_REPORT_EVENT_ID = BIT(20),
+ RX_BA_WIN_SIZE_CHANGE_EVENT_ID = BIT(21),
SMART_CONFIG_SYNC_EVENT_ID = BIT(22),
SMART_CONFIG_DECODE_EVENT_ID = BIT(23),
TIME_SYNC_EVENT_ID = BIT(24),
diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
index 06d6943..5bdf7a0 100644
--- a/drivers/net/wireless/ti/wl18xx/main.c
+++ b/drivers/net/wireless/ti/wl18xx/main.c
@@ -1041,7 +1041,8 @@
SMART_CONFIG_SYNC_EVENT_ID |
SMART_CONFIG_DECODE_EVENT_ID |
TIME_SYNC_EVENT_ID |
- FW_LOGGER_INDICATION;
+ FW_LOGGER_INDICATION |
+ RX_BA_WIN_SIZE_CHANGE_EVENT_ID;
wl->ap_event_mask = MAX_TX_FAILURE_EVENT_ID;
diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c
index 26cc23f..a485999 100644
--- a/drivers/net/wireless/ti/wlcore/acx.c
+++ b/drivers/net/wireless/ti/wlcore/acx.c
@@ -1419,7 +1419,8 @@
/* setup BA session receiver setting in the FW. */
int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index,
- u16 ssn, bool enable, u8 peer_hlid)
+ u16 ssn, bool enable, u8 peer_hlid,
+ u8 win_size)
{
struct wl1271_acx_ba_receiver_setup *acx;
int ret;
@@ -1435,7 +1436,7 @@
acx->hlid = peer_hlid;
acx->tid = tid_index;
acx->enable = enable;
- acx->win_size = wl->conf.ht.rx_ba_win_size;
+ acx->win_size = win_size;
acx->ssn = ssn;
ret = wlcore_cmd_configure_failsafe(wl, ACX_BA_SESSION_RX_SETUP, acx,
diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h
index 6321ed4..f46d7fd 100644
--- a/drivers/net/wireless/ti/wlcore/acx.h
+++ b/drivers/net/wireless/ti/wlcore/acx.h
@@ -1113,7 +1113,8 @@
int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl,
struct wl12xx_vif *wlvif);
int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index,
- u16 ssn, bool enable, u8 peer_hlid);
+ u16 ssn, bool enable, u8 peer_hlid,
+ u8 win_size);
int wl12xx_acx_tsf_info(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u64 *mactime);
int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif,
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 471521a..5438975 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -5285,7 +5285,9 @@
}
ret = wl12xx_acx_set_ba_receiver_session(wl, tid, *ssn, true,
- hlid);
+ hlid,
+ params->buf_size);
+
if (!ret) {
*ba_bitmap |= BIT(tid);
wl->ba_rx_session_count++;
@@ -5306,7 +5308,7 @@
}
ret = wl12xx_acx_set_ba_receiver_session(wl, tid, 0, false,
- hlid);
+ hlid, 0);
if (!ret) {
*ba_bitmap &= ~BIT(tid);
wl->ba_rx_session_count--;
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index fe00f91..7dc726d 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -456,6 +456,7 @@
config PHY_TUSB1210
tristate "TI TUSB1210 ULPI PHY module"
depends on USB_ULPI_BUS
+ depends on EXTCON || !EXTCON # if EXTCON=m, this cannot be built-in
select GENERIC_PHY
help
Support for TI TUSB1210 USB ULPI PHY.
diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c
index 5fdb4e9..7fca7aa 100644
--- a/drivers/platform/msm/gsi/gsi.c
+++ b/drivers/platform/msm/gsi/gsi.c
@@ -649,6 +649,13 @@
GSI_V1_3_EE_n_GSI_HW_PARAM_2_GSI_NUM_CH_PER_EE_BMSK) >>
GSI_V1_3_EE_n_GSI_HW_PARAM_2_GSI_NUM_CH_PER_EE_SHFT;
break;
+ case GSI_VER_2_0:
+ reg = gsi_readl(gsi_ctx->base +
+ GSI_V2_0_EE_n_GSI_HW_PARAM_2_OFFS(gsi_ctx->per.ee));
+ reg = (reg &
+ GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_NUM_CH_PER_EE_BMSK) >>
+ GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_NUM_CH_PER_EE_SHFT;
+ break;
default:
GSIERR("bad gsi version %d\n", ver);
WARN_ON(1);
@@ -684,6 +691,13 @@
GSI_V1_3_EE_n_GSI_HW_PARAM_2_GSI_NUM_EV_PER_EE_BMSK) >>
GSI_V1_3_EE_n_GSI_HW_PARAM_2_GSI_NUM_EV_PER_EE_SHFT;
break;
+ case GSI_VER_2_0:
+ reg = gsi_readl(gsi_ctx->base +
+ GSI_V2_0_EE_n_GSI_HW_PARAM_2_OFFS(gsi_ctx->per.ee));
+ reg = (reg &
+ GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_NUM_EV_PER_EE_BMSK) >>
+ GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_NUM_EV_PER_EE_SHFT;
+ break;
default:
GSIERR("bad gsi version %d\n", ver);
WARN_ON(1);
diff --git a/drivers/platform/msm/gsi/gsi_dbg.c b/drivers/platform/msm/gsi/gsi_dbg.c
index f5e23c68..154ac26 100644
--- a/drivers/platform/msm/gsi/gsi_dbg.c
+++ b/drivers/platform/msm/gsi/gsi_dbg.c
@@ -293,6 +293,16 @@
val = gsi_readl(gsi_ctx->base +
GSI_V1_3_EE_n_GSI_HW_PARAM_2_OFFS(gsi_ctx->per.ee));
TERR("EE%2d HW_PARAM_2 0x%x\n", gsi_ctx->per.ee, val);
+ } else if (gsi_ctx->per.ver == GSI_VER_2_0) {
+ val = gsi_readl(gsi_ctx->base +
+ GSI_V1_3_EE_n_GSI_HW_PARAM_0_OFFS(gsi_ctx->per.ee));
+ TERR("EE%2d HW_PARAM_0 0x%x\n", gsi_ctx->per.ee, val);
+ val = gsi_readl(gsi_ctx->base +
+ GSI_V1_3_EE_n_GSI_HW_PARAM_1_OFFS(gsi_ctx->per.ee));
+ TERR("EE%2d HW_PARAM_1 0x%x\n", gsi_ctx->per.ee, val);
+ val = gsi_readl(gsi_ctx->base +
+ GSI_V2_0_EE_n_GSI_HW_PARAM_2_OFFS(gsi_ctx->per.ee));
+ TERR("EE%2d HW_PARAM_2 0x%x\n", gsi_ctx->per.ee, val);
} else {
WARN_ON(1);
}
diff --git a/drivers/platform/msm/gsi/gsi_reg.h b/drivers/platform/msm/gsi/gsi_reg.h
index 653cdd4..7817613 100644
--- a/drivers/platform/msm/gsi/gsi_reg.h
+++ b/drivers/platform/msm/gsi/gsi_reg.h
@@ -1518,6 +1518,34 @@
#define GSI_V1_3_EE_n_GSI_HW_PARAM_2_GSI_IRAM_SIZE_ONE_KB_FVAL 0x0
#define GSI_V1_3_EE_n_GSI_HW_PARAM_2_GSI_IRAM_SIZE_TWO_KB_FVAL 0x1
+/* v2.0 */
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_OFFS(n) \
+ (GSI_GSI_REG_BASE_OFFS + 0x0001f040 + 0x4000 * (n))
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_RMSK 0x7fff
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_MAXn 2
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_SDMA_N_IOVEC_BMSK 0x38000000
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_SDMA_N_IOVEC_SHFT 0x1b
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_SDMA_MAX_BURST_BMSK 0x7F80000
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_SDMA_MAX_BURST_SHFT 0x13
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_SDMA_N_INT_BMSK 0x70000
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_SDMA_N_INT_SHFT 0x10
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_USE_SDMA_BMSK 0x8000
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_USE_SDMA_SHFT 0xf
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_CH_FULL_LOGIC_BMSK 0x4000
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_CH_FULL_LOGIC_SHFT 0xe
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_CH_PEND_TRANSLATE_BMSK 0x2000
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_CH_PEND_TRANSLATE_SHFT 0xd
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_NUM_EV_PER_EE_BMSK 0x1f00
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_NUM_EV_PER_EE_SHFT 0x8
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_NUM_CH_PER_EE_BMSK 0xf8
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_NUM_CH_PER_EE_SHFT 0x3
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_IRAM_SIZE_BMSK 0x7
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_IRAM_SIZE_SHFT 0x0
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_IRAM_SIZE_ONE_KB_FVAL 0x0
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_IRAM_SIZE_TWO_KB_FVAL 0x1
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_IRAM_SIZE_TWO_N_HALF_KB_FVAL 0x2
+#define GSI_V2_0_EE_n_GSI_HW_PARAM_2_GSI_IRAM_SIZE_THREE_KB_FVAL 0x3
+
#define GSI_EE_n_GSI_SW_VERSION_OFFS(n) \
(GSI_GSI_REG_BASE_OFFS + 0x0001f044 + 0x4000 * (n))
#define GSI_EE_n_GSI_SW_VERSION_RMSK 0xffffffff
diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c
index e958433..50171fd 100644
--- a/drivers/power/supply/bq24190_charger.c
+++ b/drivers/power/supply/bq24190_charger.c
@@ -144,10 +144,7 @@
* so the first read after a fault returns the latched value and subsequent
* reads return the current value. In order to return the fault status
* to the user, have the interrupt handler save the reg's value and retrieve
- * it in the appropriate health/status routine. Each routine has its own
- * flag indicating whether it should use the value stored by the last run
- * of the interrupt handler or do an actual reg read. That way each routine
- * can report back whatever fault may have occured.
+ * it in the appropriate health/status routine.
*/
struct bq24190_dev_info {
struct i2c_client *client;
@@ -159,10 +156,6 @@
unsigned int gpio_int;
unsigned int irq;
struct mutex f_reg_lock;
- bool first_time;
- bool charger_health_valid;
- bool battery_health_valid;
- bool battery_status_valid;
u8 f_reg;
u8 ss_reg;
u8 watchdog;
@@ -636,21 +629,11 @@
union power_supply_propval *val)
{
u8 v;
- int health, ret;
+ int health;
mutex_lock(&bdi->f_reg_lock);
-
- if (bdi->charger_health_valid) {
- v = bdi->f_reg;
- bdi->charger_health_valid = false;
- mutex_unlock(&bdi->f_reg_lock);
- } else {
- mutex_unlock(&bdi->f_reg_lock);
-
- ret = bq24190_read(bdi, BQ24190_REG_F, &v);
- if (ret < 0)
- return ret;
- }
+ v = bdi->f_reg;
+ mutex_unlock(&bdi->f_reg_lock);
if (v & BQ24190_REG_F_BOOST_FAULT_MASK) {
/*
@@ -937,18 +920,8 @@
int status, ret;
mutex_lock(&bdi->f_reg_lock);
-
- if (bdi->battery_status_valid) {
- chrg_fault = bdi->f_reg;
- bdi->battery_status_valid = false;
- mutex_unlock(&bdi->f_reg_lock);
- } else {
- mutex_unlock(&bdi->f_reg_lock);
-
- ret = bq24190_read(bdi, BQ24190_REG_F, &chrg_fault);
- if (ret < 0)
- return ret;
- }
+ chrg_fault = bdi->f_reg;
+ mutex_unlock(&bdi->f_reg_lock);
chrg_fault &= BQ24190_REG_F_CHRG_FAULT_MASK;
chrg_fault >>= BQ24190_REG_F_CHRG_FAULT_SHIFT;
@@ -996,21 +969,11 @@
union power_supply_propval *val)
{
u8 v;
- int health, ret;
+ int health;
mutex_lock(&bdi->f_reg_lock);
-
- if (bdi->battery_health_valid) {
- v = bdi->f_reg;
- bdi->battery_health_valid = false;
- mutex_unlock(&bdi->f_reg_lock);
- } else {
- mutex_unlock(&bdi->f_reg_lock);
-
- ret = bq24190_read(bdi, BQ24190_REG_F, &v);
- if (ret < 0)
- return ret;
- }
+ v = bdi->f_reg;
+ mutex_unlock(&bdi->f_reg_lock);
if (v & BQ24190_REG_F_BAT_FAULT_MASK) {
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
@@ -1197,9 +1160,12 @@
static irqreturn_t bq24190_irq_handler_thread(int irq, void *data)
{
struct bq24190_dev_info *bdi = data;
- bool alert_userspace = false;
+ const u8 battery_mask_ss = BQ24190_REG_SS_CHRG_STAT_MASK;
+ const u8 battery_mask_f = BQ24190_REG_F_BAT_FAULT_MASK
+ | BQ24190_REG_F_NTC_FAULT_MASK;
+ bool alert_charger = false, alert_battery = false;
u8 ss_reg = 0, f_reg = 0;
- int ret;
+ int i, ret;
pm_runtime_get_sync(bdi->dev);
@@ -1209,6 +1175,32 @@
goto out;
}
+ i = 0;
+ do {
+ ret = bq24190_read(bdi, BQ24190_REG_F, &f_reg);
+ if (ret < 0) {
+ dev_err(bdi->dev, "Can't read F reg: %d\n", ret);
+ goto out;
+ }
+ } while (f_reg && ++i < 2);
+
+ if (f_reg != bdi->f_reg) {
+ dev_info(bdi->dev,
+ "Fault: boost %d, charge %d, battery %d, ntc %d\n",
+ !!(f_reg & BQ24190_REG_F_BOOST_FAULT_MASK),
+ !!(f_reg & BQ24190_REG_F_CHRG_FAULT_MASK),
+ !!(f_reg & BQ24190_REG_F_BAT_FAULT_MASK),
+ !!(f_reg & BQ24190_REG_F_NTC_FAULT_MASK));
+
+ mutex_lock(&bdi->f_reg_lock);
+ if ((bdi->f_reg & battery_mask_f) != (f_reg & battery_mask_f))
+ alert_battery = true;
+ if ((bdi->f_reg & ~battery_mask_f) != (f_reg & ~battery_mask_f))
+ alert_charger = true;
+ bdi->f_reg = f_reg;
+ mutex_unlock(&bdi->f_reg_lock);
+ }
+
if (ss_reg != bdi->ss_reg) {
/*
* The device is in host mode so when PG_STAT goes from 1->0
@@ -1225,47 +1217,17 @@
ret);
}
+ if ((bdi->ss_reg & battery_mask_ss) != (ss_reg & battery_mask_ss))
+ alert_battery = true;
+ if ((bdi->ss_reg & ~battery_mask_ss) != (ss_reg & ~battery_mask_ss))
+ alert_charger = true;
bdi->ss_reg = ss_reg;
- alert_userspace = true;
}
- mutex_lock(&bdi->f_reg_lock);
-
- ret = bq24190_read(bdi, BQ24190_REG_F, &f_reg);
- if (ret < 0) {
- mutex_unlock(&bdi->f_reg_lock);
- dev_err(bdi->dev, "Can't read F reg: %d\n", ret);
- goto out;
- }
-
- if (f_reg != bdi->f_reg) {
- bdi->f_reg = f_reg;
- bdi->charger_health_valid = true;
- bdi->battery_health_valid = true;
- bdi->battery_status_valid = true;
-
- alert_userspace = true;
- }
-
- mutex_unlock(&bdi->f_reg_lock);
-
- /*
- * Sometimes bq24190 gives a steady trickle of interrupts even
- * though the watchdog timer is turned off and neither the STATUS
- * nor FAULT registers have changed. Weed out these sprurious
- * interrupts so userspace isn't alerted for no reason.
- * In addition, the chip always generates an interrupt after
- * register reset so we should ignore that one (the very first
- * interrupt received).
- */
- if (alert_userspace) {
- if (!bdi->first_time) {
- power_supply_changed(bdi->charger);
- power_supply_changed(bdi->battery);
- } else {
- bdi->first_time = false;
- }
- }
+ if (alert_charger)
+ power_supply_changed(bdi->charger);
+ if (alert_battery)
+ power_supply_changed(bdi->battery);
out:
pm_runtime_put_sync(bdi->dev);
@@ -1300,6 +1262,10 @@
goto out;
ret = bq24190_set_mode_host(bdi);
+ if (ret < 0)
+ goto out;
+
+ ret = bq24190_read(bdi, BQ24190_REG_SS, &bdi->ss_reg);
out:
pm_runtime_put_sync(bdi->dev);
return ret;
@@ -1375,10 +1341,8 @@
bdi->model = id->driver_data;
strncpy(bdi->model_name, id->name, I2C_NAME_SIZE);
mutex_init(&bdi->f_reg_lock);
- bdi->first_time = true;
- bdi->charger_health_valid = false;
- bdi->battery_health_valid = false;
- bdi->battery_status_valid = false;
+ bdi->f_reg = 0;
+ bdi->ss_reg = BQ24190_REG_SS_VBUS_STAT_MASK; /* impossible state */
i2c_set_clientdata(client, bdi);
@@ -1392,22 +1356,13 @@
return -EINVAL;
}
- ret = devm_request_threaded_irq(dev, bdi->irq, NULL,
- bq24190_irq_handler_thread,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
- "bq24190-charger", bdi);
- if (ret < 0) {
- dev_err(dev, "Can't set up irq handler\n");
- goto out1;
- }
-
pm_runtime_enable(dev);
pm_runtime_resume(dev);
ret = bq24190_hw_init(bdi);
if (ret < 0) {
dev_err(dev, "Hardware init failed\n");
- goto out2;
+ goto out1;
}
charger_cfg.drv_data = bdi;
@@ -1418,7 +1373,7 @@
if (IS_ERR(bdi->charger)) {
dev_err(dev, "Can't register charger\n");
ret = PTR_ERR(bdi->charger);
- goto out2;
+ goto out1;
}
battery_cfg.drv_data = bdi;
@@ -1427,24 +1382,34 @@
if (IS_ERR(bdi->battery)) {
dev_err(dev, "Can't register battery\n");
ret = PTR_ERR(bdi->battery);
- goto out3;
+ goto out2;
}
ret = bq24190_sysfs_create_group(bdi);
if (ret) {
dev_err(dev, "Can't create sysfs entries\n");
+ goto out3;
+ }
+
+ ret = devm_request_threaded_irq(dev, bdi->irq, NULL,
+ bq24190_irq_handler_thread,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "bq24190-charger", bdi);
+ if (ret < 0) {
+ dev_err(dev, "Can't set up irq handler\n");
goto out4;
}
return 0;
out4:
- power_supply_unregister(bdi->battery);
+ bq24190_sysfs_remove_group(bdi);
out3:
- power_supply_unregister(bdi->charger);
+ power_supply_unregister(bdi->battery);
out2:
- pm_runtime_disable(dev);
+ power_supply_unregister(bdi->charger);
out1:
+ pm_runtime_disable(dev);
if (bdi->gpio_int)
gpio_free(bdi->gpio_int);
@@ -1488,12 +1453,13 @@
struct i2c_client *client = to_i2c_client(dev);
struct bq24190_dev_info *bdi = i2c_get_clientdata(client);
- bdi->charger_health_valid = false;
- bdi->battery_health_valid = false;
- bdi->battery_status_valid = false;
+ bdi->f_reg = 0;
+ bdi->ss_reg = BQ24190_REG_SS_VBUS_STAT_MASK; /* impossible state */
pm_runtime_get_sync(bdi->dev);
bq24190_register_reset(bdi);
+ bq24190_set_mode_host(bdi);
+ bq24190_read(bdi, BQ24190_REG_SS, &bdi->ss_reg);
pm_runtime_put_sync(bdi->dev);
/* Things may have changed while suspended so alert upper layer */
diff --git a/drivers/power/supply/lp8788-charger.c b/drivers/power/supply/lp8788-charger.c
index 7321b72..cd614fe 100644
--- a/drivers/power/supply/lp8788-charger.c
+++ b/drivers/power/supply/lp8788-charger.c
@@ -654,7 +654,7 @@
{
struct lp8788_charger *pchg = dev_get_drvdata(dev);
char *stime[] = { "400ms", "5min", "10min", "15min",
- "20min", "25min", "30min" "No timeout" };
+ "20min", "25min", "30min", "No timeout" };
u8 val;
lp8788_read_byte(pchg->lp, LP8788_CHG_EOC, &val);
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 3e2bdb9..17b1574 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -1497,7 +1497,7 @@
config MAC_SCSI
tristate "Macintosh NCR5380 SCSI"
- depends on MAC && SCSI=y
+ depends on MAC && SCSI
select SCSI_SPI_ATTRS
help
This is the NCR 5380 SCSI controller included on most of the 68030
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 734e592..f9b52a4 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -1464,7 +1464,8 @@
/* Don't abort commands in adapter during EEH
* recovery as it's not accessible/responding.
*/
- if (GET_CMD_SP(sp) && !ha->flags.eeh_busy) {
+ if (GET_CMD_SP(sp) && !ha->flags.eeh_busy &&
+ (sp->type == SRB_SCSI_CMD)) {
/* Get a reference to the sp and drop the lock.
* The reference ensures this sp->done() call
* - and not the call in qla2xxx_eh_abort() -
diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c
index a535b26..96a343e 100644
--- a/drivers/scsi/smartpqi/smartpqi_init.c
+++ b/drivers/scsi/smartpqi/smartpqi_init.c
@@ -533,8 +533,7 @@
size_t buffer_length;
time64_t local_time;
unsigned int year;
- struct timeval time;
- struct rtc_time tm;
+ struct tm tm;
buffer_length = sizeof(*buffer);
@@ -551,9 +550,8 @@
put_unaligned_le16(sizeof(buffer->time),
&buffer->time_length);
- do_gettimeofday(&time);
- local_time = time.tv_sec - (sys_tz.tz_minuteswest * 60);
- rtc_time64_to_tm(local_time, &tm);
+ local_time = ktime_get_real_seconds();
+ time64_to_tm(local_time, -sys_tz.tz_minuteswest * 60, &tm);
year = tm.tm_year + 1900;
buffer->time[0] = bin2bcd(tm.tm_hour);
diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig
new file mode 100644
index 0000000..0e2da79
--- /dev/null
+++ b/drivers/sensors/Kconfig
@@ -0,0 +1,6 @@
+config SENSORS_SSC
+ bool "Enable Sensors Driver Support for SSC"
+ help
+ Add support for sensors SSC driver.
+ This driver is used for exercising sensors use case,
+ time syncing with ADSP clock.
diff --git a/drivers/sensors/Makefile b/drivers/sensors/Makefile
new file mode 100644
index 0000000..08d8a63
--- /dev/null
+++ b/drivers/sensors/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SENSORS_SSC) += sensors_ssc.o
diff --git a/drivers/sensors/sensors_ssc.c b/drivers/sensors/sensors_ssc.c
new file mode 100644
index 0000000..d738767
--- /dev/null
+++ b/drivers/sensors/sensors_ssc.c
@@ -0,0 +1,419 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/msm_dsps.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/of_device.h>
+#include <asm/arch_timer.h>
+#include <linux/uaccess.h>
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/workqueue.h>
+
+#include <soc/qcom/subsystem_restart.h>
+
+#define IMAGE_LOAD_CMD 1
+#define IMAGE_UNLOAD_CMD 0
+#define CLASS_NAME "ssc"
+#define DRV_NAME "sensors"
+#define DRV_VERSION "2.00"
+#ifdef CONFIG_COMPAT
+#define DSPS_IOCTL_READ_SLOW_TIMER32 _IOR(DSPS_IOCTL_MAGIC, 3, compat_uint_t)
+#endif
+
+#define QTICK_DIV_FACTOR 0x249F
+
+struct sns_ssc_control_s {
+ struct class *dev_class;
+ dev_t dev_num;
+ struct device *dev;
+ struct cdev *cdev;
+};
+static struct sns_ssc_control_s sns_ctl;
+
+static ssize_t slpi_boot_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count);
+
+struct slpi_loader_private {
+ void *pil_h;
+ struct kobject *boot_slpi_obj;
+ struct attribute_group *attr_group;
+};
+
+static struct kobj_attribute slpi_boot_attribute =
+ __ATTR(boot, 0220, NULL, slpi_boot_store);
+
+static struct attribute *attrs[] = {
+ &slpi_boot_attribute.attr,
+ NULL,
+};
+
+static struct platform_device *slpi_private;
+static struct work_struct slpi_ldr_work;
+
+static void slpi_load_fw(struct work_struct *slpi_ldr_work)
+{
+ struct platform_device *pdev = slpi_private;
+ struct slpi_loader_private *priv = NULL;
+ int ret;
+ const char *firmware_name = NULL;
+
+ if (!pdev) {
+ dev_err(&pdev->dev, "%s: Platform device null\n", __func__);
+ goto fail;
+ }
+
+ if (!pdev->dev.of_node) {
+ dev_err(&pdev->dev,
+ "%s: Device tree information missing\n", __func__);
+ goto fail;
+ }
+
+ ret = of_property_read_string(pdev->dev.of_node,
+ "qcom,firmware-name", &firmware_name);
+ if (ret < 0) {
+ pr_err("can't get fw name.\n");
+ goto fail;
+ }
+
+ priv = platform_get_drvdata(pdev);
+ if (!priv) {
+ dev_err(&pdev->dev,
+ " %s: Private data get failed\n", __func__);
+ goto fail;
+ }
+
+ priv->pil_h = subsystem_get_with_fwname("slpi", firmware_name);
+ if (IS_ERR(priv->pil_h)) {
+ dev_err(&pdev->dev, "%s: pil get failed,\n",
+ __func__);
+ goto fail;
+ }
+
+ dev_dbg(&pdev->dev, "%s: SLPI image is loaded\n", __func__);
+ return;
+
+fail:
+ dev_err(&pdev->dev, "%s: SLPI image loading failed\n", __func__);
+}
+
+static void slpi_loader_do(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "%s: scheduling work to load SLPI fw\n", __func__);
+ schedule_work(&slpi_ldr_work);
+}
+
+static void slpi_loader_unload(struct platform_device *pdev)
+{
+ struct slpi_loader_private *priv = NULL;
+
+ priv = platform_get_drvdata(pdev);
+
+ if (!priv)
+ return;
+
+ if (priv->pil_h) {
+ dev_dbg(&pdev->dev, "%s: calling subsystem put\n", __func__);
+ subsystem_put(priv->pil_h);
+ priv->pil_h = NULL;
+ }
+}
+
+static ssize_t slpi_boot_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ int boot = 0;
+
+ if (sscanf(buf, "%du", &boot) != 1)
+ return -EINVAL;
+
+ if (boot == IMAGE_LOAD_CMD) {
+ pr_debug("%s: going to call slpi_loader_do\n", __func__);
+ slpi_loader_do(slpi_private);
+ } else if (boot == IMAGE_UNLOAD_CMD) {
+ pr_debug("%s: going to call slpi_unloader\n", __func__);
+ slpi_loader_unload(slpi_private);
+ }
+ return count;
+}
+
+static int slpi_loader_init_sysfs(struct platform_device *pdev)
+{
+ int ret = -EINVAL;
+ struct slpi_loader_private *priv = NULL;
+
+ slpi_private = NULL;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ priv->pil_h = NULL;
+ priv->boot_slpi_obj = NULL;
+ priv->attr_group = devm_kzalloc(&pdev->dev,
+ sizeof(*(priv->attr_group)),
+ GFP_KERNEL);
+ if (!priv->attr_group) {
+ dev_err(&pdev->dev, "%s: malloc attr_group failed\n",
+ __func__);
+ ret = -ENOMEM;
+ goto error_return;
+ }
+
+ priv->attr_group->attrs = attrs;
+
+ priv->boot_slpi_obj = kobject_create_and_add("boot_slpi", kernel_kobj);
+ if (!priv->boot_slpi_obj) {
+ dev_err(&pdev->dev, "%s: sysfs create and add failed\n",
+ __func__);
+ ret = -ENOMEM;
+ goto error_return;
+ }
+
+ ret = sysfs_create_group(priv->boot_slpi_obj, priv->attr_group);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: sysfs create group failed %d\n",
+ __func__, ret);
+ goto error_return;
+ }
+
+ slpi_private = pdev;
+
+ return 0;
+
+error_return:
+
+ if (priv->boot_slpi_obj) {
+ kobject_del(priv->boot_slpi_obj);
+ priv->boot_slpi_obj = NULL;
+ }
+
+ return ret;
+}
+
+static int slpi_loader_remove(struct platform_device *pdev)
+{
+ struct slpi_loader_private *priv = NULL;
+
+ priv = platform_get_drvdata(pdev);
+
+ if (!priv)
+ return 0;
+
+ if (priv->pil_h) {
+ subsystem_put(priv->pil_h);
+ priv->pil_h = NULL;
+ }
+
+ if (priv->boot_slpi_obj) {
+ sysfs_remove_group(priv->boot_slpi_obj, priv->attr_group);
+ kobject_del(priv->boot_slpi_obj);
+ priv->boot_slpi_obj = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * Read virtual QTimer clock ticks and scale down to 32KHz clock as used
+ * in DSPS
+ */
+static u32 sns_read_qtimer(void)
+{
+ u64 val;
+
+ val = arch_counter_get_cntvct();
+ /*
+ * To convert ticks from 19.2 Mhz clock to 32768 Hz clock:
+ * x = (value * 32768) / 19200000
+ * This is same as first left shift the value by 4 bits, i.e. multiply
+ * by 16, and then divide by 0x249F. The latter is preferable since
+ * QTimer tick (value) is 56-bit, so (value * 32768) could overflow,
+ * while (value * 16) will never do
+ */
+ val <<= 4;
+ do_div(val, QTICK_DIV_FACTOR);
+
+ return (u32)val;
+}
+
+static int sensors_ssc_open(struct inode *ip, struct file *fp)
+{
+ return 0;
+}
+
+static int sensors_ssc_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static long sensors_ssc_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ u32 val = 0;
+
+ switch (cmd) {
+ case DSPS_IOCTL_READ_SLOW_TIMER:
+#ifdef CONFIG_COMPAT
+ case DSPS_IOCTL_READ_SLOW_TIMER32:
+#endif
+ val = sns_read_qtimer();
+ ret = put_user(val, (u32 __user *) arg);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+const struct file_operations sensors_ssc_fops = {
+ .owner = THIS_MODULE,
+ .open = sensors_ssc_open,
+ .release = sensors_ssc_release,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = sensors_ssc_ioctl,
+#endif
+ .unlocked_ioctl = sensors_ssc_ioctl
+};
+
+static int sensors_ssc_probe(struct platform_device *pdev)
+{
+ int ret = slpi_loader_init_sysfs(pdev);
+
+ if (ret != 0) {
+ dev_err(&pdev->dev, "%s: Error in initing sysfs\n", __func__);
+ return ret;
+ }
+
+ sns_ctl.dev_class = class_create(THIS_MODULE, CLASS_NAME);
+ if (sns_ctl.dev_class == NULL) {
+ pr_err("%s: class_create fail.\n", __func__);
+ goto res_err;
+ }
+
+ ret = alloc_chrdev_region(&sns_ctl.dev_num, 0, 1, DRV_NAME);
+ if (ret) {
+ pr_err("%s: alloc_chrdev_region fail.\n", __func__);
+ goto alloc_chrdev_region_err;
+ }
+
+ sns_ctl.dev = device_create(sns_ctl.dev_class, NULL,
+ sns_ctl.dev_num,
+ &sns_ctl, DRV_NAME);
+ if (IS_ERR(sns_ctl.dev)) {
+ pr_err("%s: device_create fail.\n", __func__);
+ goto device_create_err;
+ }
+
+ sns_ctl.cdev = cdev_alloc();
+ if (sns_ctl.cdev == NULL) {
+ pr_err("%s: cdev_alloc fail.\n", __func__);
+ goto cdev_alloc_err;
+ }
+ cdev_init(sns_ctl.cdev, &sensors_ssc_fops);
+ sns_ctl.cdev->owner = THIS_MODULE;
+
+ ret = cdev_add(sns_ctl.cdev, sns_ctl.dev_num, 1);
+ if (ret) {
+ pr_err("%s: cdev_add fail.\n", __func__);
+ goto cdev_add_err;
+ }
+
+ INIT_WORK(&slpi_ldr_work, slpi_load_fw);
+
+ return 0;
+
+cdev_add_err:
+ kfree(sns_ctl.cdev);
+cdev_alloc_err:
+ device_destroy(sns_ctl.dev_class, sns_ctl.dev_num);
+device_create_err:
+ unregister_chrdev_region(sns_ctl.dev_num, 1);
+alloc_chrdev_region_err:
+ class_destroy(sns_ctl.dev_class);
+res_err:
+ return -ENODEV;
+}
+
+static int sensors_ssc_remove(struct platform_device *pdev)
+{
+ slpi_loader_remove(pdev);
+ cdev_del(sns_ctl.cdev);
+ kfree(sns_ctl.cdev);
+ sns_ctl.cdev = NULL;
+ device_destroy(sns_ctl.dev_class, sns_ctl.dev_num);
+ unregister_chrdev_region(sns_ctl.dev_num, 1);
+ class_destroy(sns_ctl.dev_class);
+
+ return 0;
+}
+
+static const struct of_device_id msm_ssc_sensors_dt_match[] = {
+ {.compatible = "qcom,msm-ssc-sensors"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, msm_ssc_sensors_dt_match);
+
+static struct platform_driver sensors_ssc_driver = {
+ .driver = {
+ .name = "sensors-ssc",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_ssc_sensors_dt_match,
+ },
+ .probe = sensors_ssc_probe,
+ .remove = sensors_ssc_remove,
+};
+
+static int __init sensors_ssc_init(void)
+{
+ int rc;
+
+ pr_debug("%s driver version %s.\n", DRV_NAME, DRV_VERSION);
+ rc = platform_driver_register(&sensors_ssc_driver);
+ if (rc) {
+ pr_err("%s: Failed to register sensors ssc driver\n",
+ __func__);
+ return rc;
+ }
+
+ return 0;
+}
+
+static void __exit sensors_ssc_exit(void)
+{
+ platform_driver_unregister(&sensors_ssc_driver);
+}
+
+module_init(sensors_ssc_init);
+module_exit(sensors_ssc_exit);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Sensors SSC driver");
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index b759776..69e0ebc 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -197,6 +197,7 @@
ICNSS_MSA0_ASSIGNED,
ICNSS_WLFW_EXISTS,
ICNSS_WDOG_BITE,
+ ICNSS_SHUTDOWN_DONE,
};
struct ce_irq_list {
@@ -1983,9 +1984,13 @@
if (!priv->ops || !priv->ops->shutdown)
goto out;
+ if (test_bit(ICNSS_SHUTDOWN_DONE, &penv->state))
+ goto out;
+
icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state);
priv->ops->shutdown(&priv->pdev->dev);
+ set_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
out:
return 0;
@@ -2023,6 +2028,7 @@
}
out:
+ clear_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
return 0;
call_probe:
@@ -2101,7 +2107,6 @@
power_off:
icnss_hw_power_off(penv);
- penv->ops = NULL;
out:
return ret;
}
@@ -2633,7 +2638,7 @@
}
ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
- ICNSS_EVENT_SYNC, ops);
+ 0, ops);
if (ret == -EINTR)
ret = 0;
@@ -3655,6 +3660,9 @@
case ICNSS_WDOG_BITE:
seq_puts(s, "MODEM WDOG BITE");
continue;
+ case ICNSS_SHUTDOWN_DONE:
+ seq_puts(s, "SHUTDOWN DONE");
+ continue;
}
seq_printf(s, "UNKNOWN-%d", i);
diff --git a/drivers/soc/qcom/icnss_utils.c b/drivers/soc/qcom/icnss_utils.c
index 5e187d5..a7a0ffa 100644
--- a/drivers/soc/qcom/icnss_utils.c
+++ b/drivers/soc/qcom/icnss_utils.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -16,7 +16,7 @@
#define ICNSS_MAX_CH_NUM 45
static DEFINE_MUTEX(unsafe_channel_list_lock);
-static DEFINE_MUTEX(dfs_nol_info_lock);
+static DEFINE_SPINLOCK(dfs_nol_info_lock);
static struct icnss_unsafe_channel_list {
u16 unsafe_ch_count;
@@ -77,27 +77,24 @@
int icnss_wlan_set_dfs_nol(const void *info, u16 info_len)
{
void *temp;
+ void *old_nol_info;
struct icnss_dfs_nol_info *dfs_info;
- mutex_lock(&dfs_nol_info_lock);
- if (!info || !info_len) {
- mutex_unlock(&dfs_nol_info_lock);
+ if (!info || !info_len)
return -EINVAL;
- }
- temp = kmalloc(info_len, GFP_KERNEL);
- if (!temp) {
- mutex_unlock(&dfs_nol_info_lock);
+ temp = kmalloc(info_len, GFP_ATOMIC);
+ if (!temp)
return -ENOMEM;
- }
memcpy(temp, info, info_len);
+ spin_lock_bh(&dfs_nol_info_lock);
dfs_info = &dfs_nol_info;
- kfree(dfs_info->dfs_nol_info);
-
+ old_nol_info = dfs_info->dfs_nol_info;
dfs_info->dfs_nol_info = temp;
dfs_info->dfs_nol_info_len = info_len;
- mutex_unlock(&dfs_nol_info_lock);
+ spin_unlock_bh(&dfs_nol_info_lock);
+ kfree(old_nol_info);
return 0;
}
@@ -108,24 +105,21 @@
int len;
struct icnss_dfs_nol_info *dfs_info;
- mutex_lock(&dfs_nol_info_lock);
- if (!info || !info_len) {
- mutex_unlock(&dfs_nol_info_lock);
+ if (!info || !info_len)
return -EINVAL;
- }
+
+ spin_lock_bh(&dfs_nol_info_lock);
dfs_info = &dfs_nol_info;
-
if (dfs_info->dfs_nol_info == NULL ||
dfs_info->dfs_nol_info_len == 0) {
- mutex_unlock(&dfs_nol_info_lock);
+ spin_unlock_bh(&dfs_nol_info_lock);
return -ENOENT;
}
len = min(info_len, dfs_info->dfs_nol_info_len);
-
memcpy(info, dfs_info->dfs_nol_info, len);
- mutex_unlock(&dfs_nol_info_lock);
+ spin_unlock_bh(&dfs_nol_info_lock);
return len;
}
diff --git a/drivers/soc/qcom/rpm_stats.c b/drivers/soc/qcom/rpm_stats.c
index 15d8b1b..8bf990e 100644
--- a/drivers/soc/qcom/rpm_stats.c
+++ b/drivers/soc/qcom/rpm_stats.c
@@ -231,34 +231,28 @@
static ssize_t rpmstats_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
- struct msm_rpmstats_private_data *prvdata = NULL;
+ struct msm_rpmstats_private_data prvdata;
struct msm_rpmstats_platform_data *pdata = NULL;
pdata = GET_PDATA_OF_ATTR(attr);
- prvdata =
- kmalloc(sizeof(*prvdata), GFP_KERNEL);
- if (!prvdata)
- return -ENOMEM;
-
- prvdata->reg_base = ioremap_nocache(pdata->phys_addr_base,
+ prvdata.reg_base = ioremap_nocache(pdata->phys_addr_base,
pdata->phys_size);
- if (!prvdata->reg_base) {
- kfree(prvdata);
+ if (!prvdata.reg_base) {
pr_err("%s: ERROR could not ioremap start=%pa, len=%u\n",
__func__, &pdata->phys_addr_base,
pdata->phys_size);
return -EBUSY;
}
- prvdata->read_idx = prvdata->len = 0;
- prvdata->platform_data = pdata;
- prvdata->num_records = RPM_STATS_NUM_REC;
+ prvdata.read_idx = prvdata.len = 0;
+ prvdata.platform_data = pdata;
+ prvdata.num_records = RPM_STATS_NUM_REC;
- if (prvdata->read_idx < prvdata->num_records)
- prvdata->len = msm_rpmstats_copy_stats(prvdata);
+ if (prvdata.read_idx < prvdata.num_records)
+ prvdata.len = msm_rpmstats_copy_stats(&prvdata);
- return snprintf(buf, prvdata->len, prvdata->buf);
+ return snprintf(buf, prvdata.len, prvdata.buf);
}
static int msm_rpmstats_create_sysfs(struct msm_rpmstats_platform_data *pd)
diff --git a/drivers/staging/emxx_udc/emxx_udc.c b/drivers/staging/emxx_udc/emxx_udc.c
index c3e2988..1055649 100644
--- a/drivers/staging/emxx_udc/emxx_udc.c
+++ b/drivers/staging/emxx_udc/emxx_udc.c
@@ -3160,7 +3160,7 @@
};
/*-------------------------------------------------------------------------*/
-static void __init nbu2ss_drv_ep_init(struct nbu2ss_udc *udc)
+static void nbu2ss_drv_ep_init(struct nbu2ss_udc *udc)
{
int i;
@@ -3191,7 +3191,7 @@
/*-------------------------------------------------------------------------*/
/* platform_driver */
-static int __init nbu2ss_drv_contest_init(
+static int nbu2ss_drv_contest_init(
struct platform_device *pdev,
struct nbu2ss_udc *udc)
{
diff --git a/drivers/staging/lustre/lustre/llite/lproc_llite.c b/drivers/staging/lustre/lustre/llite/lproc_llite.c
index 23fda9d..13ec24d 100644
--- a/drivers/staging/lustre/lustre/llite/lproc_llite.c
+++ b/drivers/staging/lustre/lustre/llite/lproc_llite.c
@@ -924,27 +924,29 @@
}
LPROC_SEQ_FOPS(ll_unstable_stats);
-static ssize_t root_squash_show(struct kobject *kobj, struct attribute *attr,
- char *buf)
+static int ll_root_squash_seq_show(struct seq_file *m, void *v)
{
- struct ll_sb_info *sbi = container_of(kobj, struct ll_sb_info,
- ll_kobj);
+ struct super_block *sb = m->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
struct root_squash_info *squash = &sbi->ll_squash;
- return sprintf(buf, "%u:%u\n", squash->rsi_uid, squash->rsi_gid);
+ seq_printf(m, "%u:%u\n", squash->rsi_uid, squash->rsi_gid);
+ return 0;
}
-static ssize_t root_squash_store(struct kobject *kobj, struct attribute *attr,
- const char *buffer, size_t count)
+static ssize_t ll_root_squash_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
{
- struct ll_sb_info *sbi = container_of(kobj, struct ll_sb_info,
- ll_kobj);
+ struct seq_file *m = file->private_data;
+ struct super_block *sb = m->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
struct root_squash_info *squash = &sbi->ll_squash;
return lprocfs_wr_root_squash(buffer, count, squash,
- ll_get_fsname(sbi->ll_sb, NULL, 0));
+ ll_get_fsname(sb, NULL, 0));
}
-LUSTRE_RW_ATTR(root_squash);
+LPROC_SEQ_FOPS(ll_root_squash);
static int ll_nosquash_nids_seq_show(struct seq_file *m, void *v)
{
@@ -997,6 +999,8 @@
{ "statahead_stats", &ll_statahead_stats_fops, NULL, 0 },
{ "unstable_stats", &ll_unstable_stats_fops, NULL },
{ "sbi_flags", &ll_sbi_flags_fops, NULL, 0 },
+ { .name = "root_squash",
+ .fops = &ll_root_squash_fops },
{ .name = "nosquash_nids",
.fops = &ll_nosquash_nids_fops },
{ NULL }
@@ -1027,7 +1031,6 @@
&lustre_attr_max_easize.attr,
&lustre_attr_default_easize.attr,
&lustre_attr_xattr_cache.attr,
- &lustre_attr_root_squash.attr,
NULL,
};
diff --git a/drivers/staging/wlan-ng/p80211netdev.c b/drivers/staging/wlan-ng/p80211netdev.c
index 825a63a..2e075a6 100644
--- a/drivers/staging/wlan-ng/p80211netdev.c
+++ b/drivers/staging/wlan-ng/p80211netdev.c
@@ -232,7 +232,7 @@
struct p80211_hdr_a3 *hdr;
hdr = (struct p80211_hdr_a3 *)skb->data;
- if (p80211_rx_typedrop(wlandev, hdr->fc))
+ if (p80211_rx_typedrop(wlandev, le16_to_cpu(hdr->fc)))
return CONV_TO_ETHER_SKIPPED;
/* perform mcast filtering: allow my local address through but reject
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index f4eb807..da31159 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -1237,7 +1237,8 @@
pm_runtime_put_autosuspend(&pdev->dev);
return 0;
err:
- pm_runtime_put(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
+ pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return ret;
}
@@ -1246,6 +1247,7 @@
{
struct omap8250_priv *priv = platform_get_drvdata(pdev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
serial8250_unregister_port(priv->line);
@@ -1345,6 +1347,10 @@
struct omap8250_priv *priv = dev_get_drvdata(dev);
struct uart_8250_port *up;
+ /* In case runtime-pm tries this before we are setup */
+ if (!priv)
+ return 0;
+
up = serial8250_get_port(priv->line);
/*
* When using 'no_console_suspend', the console UART must not be
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index cd41455..05bc4d6 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -428,9 +428,6 @@
u8 hw_port_test_get(struct ci_hdrc *ci);
-int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask,
- u32 value, unsigned int timeout_ms);
-
void ci_platform_configure(struct ci_hdrc *ci);
int dbg_create_files(struct ci_hdrc *ci);
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 3dbb4a2..6e0d614 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -516,38 +516,6 @@
return 0;
}
-/**
- * hw_wait_reg: wait the register value
- *
- * Sometimes, it needs to wait register value before going on.
- * Eg, when switch to device mode, the vbus value should be lower
- * than OTGSC_BSV before connects to host.
- *
- * @ci: the controller
- * @reg: register index
- * @mask: mast bit
- * @value: the bit value to wait
- * @timeout_ms: timeout in millisecond
- *
- * This function returns an error code if timeout
- */
-int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask,
- u32 value, unsigned int timeout_ms)
-{
- unsigned long elapse = jiffies + msecs_to_jiffies(timeout_ms);
-
- while (hw_read(ci, reg, mask) != value) {
- if (time_after(jiffies, elapse)) {
- dev_err(ci->dev, "timeout waiting for %08x in %d\n",
- mask, reg);
- return -ETIMEDOUT;
- }
- msleep(20);
- }
-
- return 0;
-}
-
static irqreturn_t ci_irq(int irq, void *data)
{
struct ci_hdrc *ci = data;
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index 03b6743..0cf149e 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -44,12 +44,15 @@
else
val &= ~OTGSC_BSVIS;
- cable->changed = false;
-
if (cable->state)
val |= OTGSC_BSV;
else
val &= ~OTGSC_BSV;
+
+ if (cable->enabled)
+ val |= OTGSC_BSVIE;
+ else
+ val &= ~OTGSC_BSVIE;
}
cable = &ci->platdata->id_extcon;
@@ -59,15 +62,18 @@
else
val &= ~OTGSC_IDIS;
- cable->changed = false;
-
if (cable->state)
val |= OTGSC_ID;
else
val &= ~OTGSC_ID;
+
+ if (cable->enabled)
+ val |= OTGSC_IDIE;
+ else
+ val &= ~OTGSC_IDIE;
}
- return val;
+ return val & mask;
}
/**
@@ -77,6 +83,36 @@
*/
void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data)
{
+ struct ci_hdrc_cable *cable;
+
+ cable = &ci->platdata->vbus_extcon;
+ if (!IS_ERR(cable->edev)) {
+ if (data & mask & OTGSC_BSVIS)
+ cable->changed = false;
+
+ /* Don't enable vbus interrupt if using external notifier */
+ if (data & mask & OTGSC_BSVIE) {
+ cable->enabled = true;
+ data &= ~OTGSC_BSVIE;
+ } else if (mask & OTGSC_BSVIE) {
+ cable->enabled = false;
+ }
+ }
+
+ cable = &ci->platdata->id_extcon;
+ if (!IS_ERR(cable->edev)) {
+ if (data & mask & OTGSC_IDIS)
+ cable->changed = false;
+
+ /* Don't enable id interrupt if using external notifier */
+ if (data & mask & OTGSC_IDIE) {
+ cable->enabled = true;
+ data &= ~OTGSC_IDIE;
+ } else if (mask & OTGSC_IDIE) {
+ cable->enabled = false;
+ }
+ }
+
hw_write(ci, OP_OTGSC, mask | OTGSC_INT_STATUS_BITS, data);
}
@@ -104,7 +140,31 @@
usb_gadget_vbus_disconnect(&ci->gadget);
}
-#define CI_VBUS_STABLE_TIMEOUT_MS 5000
+/**
+ * When we switch to device mode, the vbus value should be lower
+ * than OTGSC_BSV before connecting to host.
+ *
+ * @ci: the controller
+ *
+ * This function returns an error code if timeout
+ */
+static int hw_wait_vbus_lower_bsv(struct ci_hdrc *ci)
+{
+ unsigned long elapse = jiffies + msecs_to_jiffies(5000);
+ u32 mask = OTGSC_BSV;
+
+ while (hw_read_otgsc(ci, mask)) {
+ if (time_after(jiffies, elapse)) {
+ dev_err(ci->dev, "timeout waiting for %08x in OTGSC\n",
+ mask);
+ return -ETIMEDOUT;
+ }
+ msleep(20);
+ }
+
+ return 0;
+}
+
static void ci_handle_id_switch(struct ci_hdrc *ci)
{
enum ci_role role = ci_otg_role(ci);
@@ -116,9 +176,11 @@
ci_role_stop(ci);
if (role == CI_ROLE_GADGET)
- /* wait vbus lower than OTGSC_BSV */
- hw_wait_reg(ci, OP_OTGSC, OTGSC_BSV, 0,
- CI_VBUS_STABLE_TIMEOUT_MS);
+ /*
+ * wait vbus lower than OTGSC_BSV before connecting
+ * to host
+ */
+ hw_wait_vbus_lower_bsv(ci);
ci_role_start(ci, role);
}
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 4c0fa0b..f6759c6 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -455,7 +455,7 @@
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
if (dwc2_iddig_filter_enabled(hsotg))
- usleep_range(100000, 110000);
+ msleep(100);
}
/*
diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c
index 42e5b66..7a603f6 100644
--- a/drivers/usb/host/ehci-exynos.c
+++ b/drivers/usb/host/ehci-exynos.c
@@ -77,10 +77,12 @@
if (IS_ERR(phy)) {
ret = PTR_ERR(phy);
if (ret == -EPROBE_DEFER) {
+ of_node_put(child);
return ret;
} else if (ret != -ENOSYS && ret != -ENODEV) {
dev_err(dev,
"Error retrieving usb2 phy: %d\n", ret);
+ of_node_put(child);
return ret;
}
}
diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c
index 2cd105b..6865b91 100644
--- a/drivers/usb/host/ohci-exynos.c
+++ b/drivers/usb/host/ohci-exynos.c
@@ -66,10 +66,12 @@
if (IS_ERR(phy)) {
ret = PTR_ERR(phy);
if (ret == -EPROBE_DEFER) {
+ of_node_put(child);
return ret;
} else if (ret != -ENOSYS && ret != -ENODEV) {
dev_err(dev,
"Error retrieving usb2 phy: %d\n", ret);
+ of_node_put(child);
return ret;
}
}
diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c
index 7812052..754fc3e 100644
--- a/drivers/usb/serial/ark3116.c
+++ b/drivers/usb/serial/ark3116.c
@@ -373,23 +373,29 @@
dev_dbg(&port->dev,
"%s - usb_serial_generic_open failed: %d\n",
__func__, result);
- goto err_out;
+ goto err_free;
}
/* remove any data still left: also clears error state */
ark3116_read_reg(serial, UART_RX, buf);
/* read modem status */
- priv->msr = ark3116_read_reg(serial, UART_MSR, buf);
+ result = ark3116_read_reg(serial, UART_MSR, buf);
+ if (result < 0)
+ goto err_close;
+ priv->msr = *buf;
+
/* read line status */
- priv->lsr = ark3116_read_reg(serial, UART_LSR, buf);
+ result = ark3116_read_reg(serial, UART_LSR, buf);
+ if (result < 0)
+ goto err_close;
+ priv->lsr = *buf;
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (result) {
dev_err(&port->dev, "submit irq_in urb failed %d\n",
result);
- ark3116_close(port);
- goto err_out;
+ goto err_close;
}
/* activate interrupts */
@@ -402,8 +408,15 @@
if (tty)
ark3116_set_termios(tty, port, NULL);
-err_out:
kfree(buf);
+
+ return 0;
+
+err_close:
+ usb_serial_generic_close(port);
+err_free:
+ kfree(buf);
+
return result;
}
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index 30bf0f5..7ab3235 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -1398,25 +1398,30 @@
{
struct usb_serial_port *port = urb->context;
struct digi_port *priv = usb_get_serial_port_data(port);
- int opcode = ((unsigned char *)urb->transfer_buffer)[0];
- int len = ((unsigned char *)urb->transfer_buffer)[1];
- int port_status = ((unsigned char *)urb->transfer_buffer)[2];
- unsigned char *data = ((unsigned char *)urb->transfer_buffer) + 3;
+ unsigned char *buf = urb->transfer_buffer;
+ int opcode;
+ int len;
+ int port_status;
+ unsigned char *data;
int flag, throttled;
- int status = urb->status;
-
- /* do not process callbacks on closed ports */
- /* but do continue the read chain */
- if (urb->status == -ENOENT)
- return 0;
/* short/multiple packet check */
+ if (urb->actual_length < 2) {
+ dev_warn(&port->dev, "short packet received\n");
+ return -1;
+ }
+
+ opcode = buf[0];
+ len = buf[1];
+
if (urb->actual_length != len + 2) {
- dev_err(&port->dev, "%s: INCOMPLETE OR MULTIPLE PACKET, "
- "status=%d, port=%d, opcode=%d, len=%d, "
- "actual_length=%d, status=%d\n", __func__, status,
- priv->dp_port_num, opcode, len, urb->actual_length,
- port_status);
+ dev_err(&port->dev, "malformed packet received: port=%d, opcode=%d, len=%d, actual_length=%u\n",
+ priv->dp_port_num, opcode, len, urb->actual_length);
+ return -1;
+ }
+
+ if (opcode == DIGI_CMD_RECEIVE_DATA && len < 1) {
+ dev_err(&port->dev, "malformed data packet received\n");
return -1;
}
@@ -1430,6 +1435,9 @@
/* receive data */
if (opcode == DIGI_CMD_RECEIVE_DATA) {
+ port_status = buf[2];
+ data = &buf[3];
+
/* get flag from port_status */
flag = 0;
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 99a0a5f..d8d13ee 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -1439,10 +1439,13 @@
FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE,
0, priv->interface,
buf, 1, WDR_TIMEOUT);
- if (rv < 0)
+ if (rv < 1) {
dev_err(&port->dev, "Unable to read latency timer: %i\n", rv);
- else
+ if (rv >= 0)
+ rv = -EIO;
+ } else {
priv->latency = buf[0];
+ }
kfree(buf);
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 36dfe99..464db17 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -492,20 +492,24 @@
int result;
struct usb_serial *serial = ep->serial;
struct edgeport_product_info *product_info = &ep->product_info;
- struct edge_compatibility_descriptor *epic = &ep->epic_descriptor;
+ struct edge_compatibility_descriptor *epic;
struct edge_compatibility_bits *bits;
struct device *dev = &serial->dev->dev;
ep->is_epic = 0;
+
+ epic = kmalloc(sizeof(*epic), GFP_KERNEL);
+ if (!epic)
+ return -ENOMEM;
+
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
USB_REQUEST_ION_GET_EPIC_DESC,
0xC0, 0x00, 0x00,
- &ep->epic_descriptor,
- sizeof(struct edge_compatibility_descriptor),
+ epic, sizeof(*epic),
300);
-
- if (result > 0) {
+ if (result == sizeof(*epic)) {
ep->is_epic = 1;
+ memcpy(&ep->epic_descriptor, epic, sizeof(*epic));
memset(product_info, 0, sizeof(struct edgeport_product_info));
product_info->NumPorts = epic->NumPorts;
@@ -534,8 +538,16 @@
dev_dbg(dev, " IOSPWriteLCR : %s\n", bits->IOSPWriteLCR ? "TRUE": "FALSE");
dev_dbg(dev, " IOSPSetBaudRate : %s\n", bits->IOSPSetBaudRate ? "TRUE": "FALSE");
dev_dbg(dev, " TrueEdgeport : %s\n", bits->TrueEdgeport ? "TRUE": "FALSE");
+
+ result = 0;
+ } else if (result >= 0) {
+ dev_warn(&serial->interface->dev, "short epic descriptor received: %d\n",
+ result);
+ result = -EIO;
}
+ kfree(epic);
+
return result;
}
@@ -2093,8 +2105,7 @@
* rom_read
* reads a number of bytes from the Edgeport device starting at the given
* address.
- * If successful returns the number of bytes read, otherwise it returns
- * a negative error number of the problem.
+ * Returns zero on success or a negative error number.
****************************************************************************/
static int rom_read(struct usb_serial *serial, __u16 extAddr,
__u16 addr, __u16 length, __u8 *data)
@@ -2119,12 +2130,17 @@
USB_REQUEST_ION_READ_ROM,
0xC0, addr, extAddr, transfer_buffer,
current_length, 300);
- if (result < 0)
+ if (result < current_length) {
+ if (result >= 0)
+ result = -EIO;
break;
+ }
memcpy(data, transfer_buffer, current_length);
length -= current_length;
addr += current_length;
data += current_length;
+
+ result = 0;
}
kfree(transfer_buffer);
@@ -2578,9 +2594,10 @@
EDGE_MANUF_DESC_LEN,
(__u8 *)(&edge_serial->manuf_descriptor));
- if (response < 1)
- dev_err(dev, "error in getting manufacturer descriptor\n");
- else {
+ if (response < 0) {
+ dev_err(dev, "error in getting manufacturer descriptor: %d\n",
+ response);
+ } else {
char string[30];
dev_dbg(dev, "**Manufacturer Descriptor\n");
dev_dbg(dev, " RomSize: %dK\n",
@@ -2637,9 +2654,10 @@
EDGE_BOOT_DESC_LEN,
(__u8 *)(&edge_serial->boot_descriptor));
- if (response < 1)
- dev_err(dev, "error in getting boot descriptor\n");
- else {
+ if (response < 0) {
+ dev_err(dev, "error in getting boot descriptor: %d\n",
+ response);
+ } else {
dev_dbg(dev, "**Boot Descriptor:\n");
dev_dbg(dev, " BootCodeLength: %d\n",
le16_to_cpu(edge_serial->boot_descriptor.BootCodeLength));
@@ -2782,7 +2800,7 @@
dev_info(&serial->dev->dev, "%s detected\n", edge_serial->name);
/* Read the epic descriptor */
- if (get_epic_descriptor(edge_serial) <= 0) {
+ if (get_epic_descriptor(edge_serial) < 0) {
/* memcpy descriptor to Supports structures */
memcpy(&edge_serial->epic_descriptor.Supports, descriptor,
sizeof(struct edge_compatibility_bits));
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index 83523fc..d2dab2a 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -139,6 +139,7 @@
{
struct usb_serial_port *port = urb->context;
unsigned char *data = urb->transfer_buffer;
+ unsigned int len = urb->actual_length;
int retval;
int status = urb->status;
struct keyspan_pda_private *priv;
@@ -159,18 +160,26 @@
goto exit;
}
+ if (len < 1) {
+ dev_warn(&port->dev, "short message received\n");
+ goto exit;
+ }
+
/* see if the message is data or a status interrupt */
switch (data[0]) {
case 0:
/* rest of message is rx data */
- if (urb->actual_length) {
- tty_insert_flip_string(&port->port, data + 1,
- urb->actual_length - 1);
- tty_flip_buffer_push(&port->port);
- }
+ if (len < 2)
+ break;
+ tty_insert_flip_string(&port->port, data + 1, len - 1);
+ tty_flip_buffer_push(&port->port);
break;
case 1:
/* status interrupt */
+ if (len < 3) {
+ dev_warn(&port->dev, "short interrupt message received\n");
+ break;
+ }
dev_dbg(&port->dev, "rx int, d1=%d, d2=%d\n", data[1], data[2]);
switch (data[1]) {
case 1: /* modemline change */
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index 8856553..edbc81f 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -322,8 +322,12 @@
MCT_U232_GET_REQUEST_TYPE,
0, 0, buf, MCT_U232_GET_MODEM_STAT_SIZE,
WDR_TIMEOUT);
- if (rc < 0) {
+ if (rc < MCT_U232_GET_MODEM_STAT_SIZE) {
dev_err(&port->dev, "Get MODEM STATus failed (error = %d)\n", rc);
+
+ if (rc >= 0)
+ rc = -EIO;
+
*msr = 0;
} else {
*msr = buf[0];
diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c
index bd1a130..1d17779 100644
--- a/drivers/usb/serial/quatech2.c
+++ b/drivers/usb/serial/quatech2.c
@@ -188,22 +188,22 @@
}
-static inline int qt2_getdevice(struct usb_device *dev, u8 *data)
-{
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- QT_SET_GET_DEVICE, 0xc0, 0, 0,
- data, 3, QT2_USB_TIMEOUT);
-}
-
static inline int qt2_getregister(struct usb_device *dev,
u8 uart,
u8 reg,
u8 *data)
{
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- QT_SET_GET_REGISTER, 0xc0, reg,
- uart, data, sizeof(*data), QT2_USB_TIMEOUT);
+ int ret;
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ QT_SET_GET_REGISTER, 0xc0, reg,
+ uart, data, sizeof(*data), QT2_USB_TIMEOUT);
+ if (ret < sizeof(*data)) {
+ if (ret >= 0)
+ ret = -EIO;
+ }
+
+ return ret;
}
static inline int qt2_setregister(struct usb_device *dev,
@@ -372,9 +372,11 @@
0xc0, 0,
device_port, data, 2, QT2_USB_TIMEOUT);
- if (status < 0) {
+ if (status < 2) {
dev_err(&port->dev, "%s - open port failed %i\n", __func__,
status);
+ if (status >= 0)
+ status = -EIO;
kfree(data);
return status;
}
diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c
index 70a098d..886e129 100644
--- a/drivers/usb/serial/ssu100.c
+++ b/drivers/usb/serial/ssu100.c
@@ -80,9 +80,17 @@
static inline int ssu100_getdevice(struct usb_device *dev, u8 *data)
{
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- QT_SET_GET_DEVICE, 0xc0, 0, 0,
- data, 3, 300);
+ int ret;
+
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ QT_SET_GET_DEVICE, 0xc0, 0, 0,
+ data, 3, 300);
+ if (ret < 3) {
+ if (ret >= 0)
+ ret = -EIO;
+ }
+
+ return ret;
}
static inline int ssu100_getregister(struct usb_device *dev,
@@ -90,10 +98,17 @@
unsigned short reg,
u8 *data)
{
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- QT_SET_GET_REGISTER, 0xc0, reg,
- uart, data, sizeof(*data), 300);
+ int ret;
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ QT_SET_GET_REGISTER, 0xc0, reg,
+ uart, data, sizeof(*data), 300);
+ if (ret < sizeof(*data)) {
+ if (ret >= 0)
+ ret = -EIO;
+ }
+
+ return ret;
}
@@ -289,8 +304,10 @@
QT_OPEN_CLOSE_CHANNEL,
QT_TRANSFER_IN, 0x01,
0, data, 2, 300);
- if (result < 0) {
+ if (result < 2) {
dev_dbg(&port->dev, "%s - open failed %i\n", __func__, result);
+ if (result >= 0)
+ result = -EIO;
kfree(data);
return result;
}
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index bdbddbc..6bcb874b 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -1556,13 +1556,10 @@
(USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT),
value, moduleid, data, size, 1000);
- if (status == size)
- status = 0;
+ if (status < 0)
+ return status;
- if (status > 0)
- status = -ECOMM;
-
- return status;
+ return 0;
}
@@ -1578,8 +1575,7 @@
if (status == size)
status = 0;
-
- if (status > 0)
+ else if (status >= 0)
status = -ECOMM;
return status;
diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c
index 9ecfcdc..d5dbdb9 100644
--- a/drivers/xen/events/events_base.c
+++ b/drivers/xen/events/events_base.c
@@ -1314,6 +1314,9 @@
if (!VALID_EVTCHN(evtchn))
return -1;
+ if (!xen_support_evtchn_rebind())
+ return -1;
+
/* Send future instances of this interrupt to other vcpu. */
bind_vcpu.port = evtchn;
bind_vcpu.vcpu = xen_vcpu_nr(tcpu);
@@ -1647,15 +1650,20 @@
{
int rc;
uint64_t callback_via;
-
- callback_via = HVM_CALLBACK_VECTOR(HYPERVISOR_CALLBACK_VECTOR);
- rc = xen_set_callback_via(callback_via);
- BUG_ON(rc);
- pr_info("Xen HVM callback vector for event delivery is enabled\n");
- /* in the restore case the vector has already been allocated */
- if (!test_bit(HYPERVISOR_CALLBACK_VECTOR, used_vectors))
- alloc_intr_gate(HYPERVISOR_CALLBACK_VECTOR,
- xen_hvm_callback_vector);
+ if (xen_have_vector_callback) {
+ callback_via = HVM_CALLBACK_VECTOR(HYPERVISOR_CALLBACK_VECTOR);
+ rc = xen_set_callback_via(callback_via);
+ if (rc) {
+ pr_err("Request for Xen HVM callback vector failed\n");
+ xen_have_vector_callback = 0;
+ return;
+ }
+ pr_info("Xen HVM callback vector for event delivery is enabled\n");
+ /* in the restore case the vector has already been allocated */
+ if (!test_bit(HYPERVISOR_CALLBACK_VECTOR, used_vectors))
+ alloc_intr_gate(HYPERVISOR_CALLBACK_VECTOR,
+ xen_hvm_callback_vector);
+ }
}
#else
void xen_callback_vector(void) {}
diff --git a/drivers/xen/platform-pci.c b/drivers/xen/platform-pci.c
index b59c9455..cf96666 100644
--- a/drivers/xen/platform-pci.c
+++ b/drivers/xen/platform-pci.c
@@ -42,6 +42,7 @@
static unsigned long platform_mmio;
static unsigned long platform_mmio_alloc;
static unsigned long platform_mmiolen;
+static uint64_t callback_via;
static unsigned long alloc_xen_mmio(unsigned long len)
{
@@ -54,6 +55,51 @@
return addr;
}
+static uint64_t get_callback_via(struct pci_dev *pdev)
+{
+ u8 pin;
+ int irq;
+
+ irq = pdev->irq;
+ if (irq < 16)
+ return irq; /* ISA IRQ */
+
+ pin = pdev->pin;
+
+ /* We don't know the GSI. Specify the PCI INTx line instead. */
+ return ((uint64_t)0x01 << 56) | /* PCI INTx identifier */
+ ((uint64_t)pci_domain_nr(pdev->bus) << 32) |
+ ((uint64_t)pdev->bus->number << 16) |
+ ((uint64_t)(pdev->devfn & 0xff) << 8) |
+ ((uint64_t)(pin - 1) & 3);
+}
+
+static irqreturn_t do_hvm_evtchn_intr(int irq, void *dev_id)
+{
+ xen_hvm_evtchn_do_upcall();
+ return IRQ_HANDLED;
+}
+
+static int xen_allocate_irq(struct pci_dev *pdev)
+{
+ return request_irq(pdev->irq, do_hvm_evtchn_intr,
+ IRQF_NOBALANCING | IRQF_TRIGGER_RISING,
+ "xen-platform-pci", pdev);
+}
+
+static int platform_pci_resume(struct pci_dev *pdev)
+{
+ int err;
+ if (xen_have_vector_callback)
+ return 0;
+ err = xen_set_callback_via(callback_via);
+ if (err) {
+ dev_err(&pdev->dev, "platform_pci_resume failure!\n");
+ return err;
+ }
+ return 0;
+}
+
static int platform_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -92,6 +138,21 @@
platform_mmio = mmio_addr;
platform_mmiolen = mmio_len;
+ if (!xen_have_vector_callback) {
+ ret = xen_allocate_irq(pdev);
+ if (ret) {
+ dev_warn(&pdev->dev, "request_irq failed err=%d\n", ret);
+ goto out;
+ }
+ callback_via = get_callback_via(pdev);
+ ret = xen_set_callback_via(callback_via);
+ if (ret) {
+ dev_warn(&pdev->dev, "Unable to set the evtchn callback "
+ "err=%d\n", ret);
+ goto out;
+ }
+ }
+
max_nr_gframes = gnttab_max_grant_frames();
grant_frames = alloc_xen_mmio(PAGE_SIZE * max_nr_gframes);
ret = gnttab_setup_auto_xlat_frames(grant_frames);
@@ -123,6 +184,9 @@
.name = DRV_NAME,
.probe = platform_pci_probe,
.id_table = platform_pci_tbl,
+#ifdef CONFIG_PM
+ .resume_early = platform_pci_resume,
+#endif
};
static int __init platform_pci_init(void)
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 092a2eed..9ad527f 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -1165,7 +1165,6 @@
if (disk->fops->revalidate_disk)
ret = disk->fops->revalidate_disk(disk);
- blk_integrity_revalidate(disk);
bdev = bdget_disk(disk, 0);
if (!bdev)
return ret;
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 013c6a5..7e0c002 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -1405,6 +1405,13 @@
return 1;
}
+ if (le32_to_cpu(raw_super->segment_count) > F2FS_MAX_SEGMENT) {
+ f2fs_msg(sb, KERN_INFO,
+ "Invalid segment count (%u)",
+ le32_to_cpu(raw_super->segment_count));
+ return 1;
+ }
+
/* check CP/SIT/NAT/SSA/MAIN_AREA area boundary */
if (sanity_check_area_boundary(sbi, bh))
return 1;
diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c
index afd9771..ae2b4ba 100644
--- a/fs/sdcardfs/dentry.c
+++ b/fs/sdcardfs/dentry.c
@@ -60,6 +60,14 @@
lower_dentry = lower_path.dentry;
lower_cur_parent_dentry = dget_parent(lower_dentry);
+ if ((lower_dentry->d_flags & DCACHE_OP_REVALIDATE)) {
+ err = lower_dentry->d_op->d_revalidate(lower_dentry, flags);
+ if (err == 0) {
+ d_drop(dentry);
+ goto out;
+ }
+ }
+
spin_lock(&lower_dentry->d_lock);
if (d_unhashed(lower_dentry)) {
spin_unlock(&lower_dentry->d_lock);
diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c
index 2964527..5a0ef38 100644
--- a/fs/sdcardfs/derived_perm.c
+++ b/fs/sdcardfs/derived_perm.c
@@ -215,16 +215,16 @@
gid = AID_MEDIA_OBB;
break;
case PERM_ANDROID_PACKAGE:
- if (info->d_uid != 0)
+ if (uid_is_app(info->d_uid))
gid = multiuser_get_ext_gid(info->d_uid);
else
- gid = multiuser_get_uid(info->userid, uid);
+ gid = multiuser_get_uid(info->userid, AID_MEDIA_RW);
break;
case PERM_ANDROID_PACKAGE_CACHE:
- if (info->d_uid != 0)
+ if (uid_is_app(info->d_uid))
gid = multiuser_get_ext_cache_gid(info->d_uid);
else
- gid = multiuser_get_uid(info->userid, uid);
+ gid = multiuser_get_uid(info->userid, AID_MEDIA_RW);
break;
case PERM_PRE_ROOT:
default:
diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c
index 1f6921e..6076c34 100644
--- a/fs/sdcardfs/file.c
+++ b/fs/sdcardfs/file.c
@@ -358,9 +358,12 @@
get_file(lower_file); /* prevent lower_file from being released */
iocb->ki_filp = lower_file;
err = lower_file->f_op->read_iter(iocb, iter);
- /* ? wait IO finish to update atime as ecryptfs ? */
iocb->ki_filp = file;
fput(lower_file);
+ /* update upper inode atime as needed */
+ if (err >= 0 || err == -EIOCBQUEUED)
+ fsstack_copy_attr_atime(file->f_path.dentry->d_inode,
+ file_inode(lower_file));
out:
return err;
}
@@ -384,6 +387,13 @@
err = lower_file->f_op->write_iter(iocb, iter);
iocb->ki_filp = file;
fput(lower_file);
+ /* update upper inode times/sizes as needed */
+ if (err >= 0 || err == -EIOCBQUEUED) {
+ fsstack_copy_inode_size(file->f_path.dentry->d_inode,
+ file_inode(lower_file));
+ fsstack_copy_attr_times(file->f_path.dentry->d_inode,
+ file_inode(lower_file));
+ }
out:
return err;
}
diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c
index 92afceb..4d558b8 100644
--- a/fs/sdcardfs/inode.c
+++ b/fs/sdcardfs/inode.c
@@ -622,11 +622,8 @@
struct inode tmp;
struct inode *top = grab_top(SDCARDFS_I(inode));
- if (!top) {
- release_top(SDCARDFS_I(inode));
- WARN(1, "Top value was null!\n");
+ if (!top)
return -EINVAL;
- }
/*
* Permission check on sdcardfs inode.
@@ -701,10 +698,8 @@
inode = d_inode(dentry);
top = grab_top(SDCARDFS_I(inode));
- if (!top) {
- release_top(SDCARDFS_I(inode));
+ if (!top)
return -EINVAL;
- }
/*
* Permission check on sdcardfs inode.
diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c
index 19154b7..706329d 100644
--- a/fs/sdcardfs/lookup.c
+++ b/fs/sdcardfs/lookup.c
@@ -366,19 +366,22 @@
/* instatiate a new negative dentry */
dname.name = name->name;
dname.len = name->len;
- dname.hash = full_name_hash(lower_dir_dentry, dname.name, dname.len);
- lower_dentry = d_lookup(lower_dir_dentry, &dname);
- if (lower_dentry)
- goto setup_lower;
- lower_dentry = d_alloc(lower_dir_dentry, &dname);
+ /* See if the low-level filesystem might want
+ * to use its own hash
+ */
+ lower_dentry = d_hash_and_lookup(lower_dir_dentry, &dname);
+ if (IS_ERR(lower_dentry))
+ return lower_dentry;
if (!lower_dentry) {
- err = -ENOMEM;
+ /* We called vfs_path_lookup earlier, and did not get a negative
+ * dentry then. Don't confuse the lower filesystem by forcing
+ * one on it now...
+ */
+ err = -ENOENT;
goto out;
}
- d_add(lower_dentry, NULL); /* instantiate and hash */
-setup_lower:
lower_path.dentry = lower_dentry;
lower_path.mnt = mntget(lower_dir_mnt);
sdcardfs_set_lower_path(dentry, &lower_path);
diff --git a/fs/sdcardfs/multiuser.h b/fs/sdcardfs/multiuser.h
index d0c925c..85341e7 100644
--- a/fs/sdcardfs/multiuser.h
+++ b/fs/sdcardfs/multiuser.h
@@ -35,6 +35,13 @@
return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);
}
+static inline bool uid_is_app(uid_t uid)
+{
+ appid_t appid = uid % AID_USER_OFFSET;
+
+ return appid >= AID_APP_START && appid <= AID_APP_END;
+}
+
static inline gid_t multiuser_get_ext_cache_gid(uid_t uid)
{
return uid - AID_APP_START + AID_EXT_CACHE_GID_START;
diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c
index a3393e9..8a9c9c7 100644
--- a/fs/sdcardfs/super.c
+++ b/fs/sdcardfs/super.c
@@ -192,9 +192,16 @@
return &i->vfs_inode;
}
+static void i_callback(struct rcu_head *head)
+{
+ struct inode *inode = container_of(head, struct inode, i_rcu);
+
+ kmem_cache_free(sdcardfs_inode_cachep, SDCARDFS_I(inode));
+}
+
static void sdcardfs_destroy_inode(struct inode *inode)
{
- kmem_cache_free(sdcardfs_inode_cachep, SDCARDFS_I(inode));
+ call_rcu(&inode->i_rcu, i_callback);
}
/* sdcardfs inode cache constructor */
diff --git a/include/dt-bindings/clock/qcom,gcc-sdm845.h b/include/dt-bindings/clock/qcom,gcc-sdm845.h
index e411e8e..a95d494 100644
--- a/include/dt-bindings/clock/qcom,gcc-sdm845.h
+++ b/include/dt-bindings/clock/qcom,gcc-sdm845.h
@@ -197,6 +197,7 @@
#define GCC_AGGRE_UFS_PHY_AXI_HW_CTL_CLK 179
#define GCC_UFS_CARD_PHY_AUX_HW_CTL_CLK 180
#define GCC_UFS_PHY_PHY_AUX_HW_CTL_CLK 181
+#define GCC_GPU_IREF_CLK 182
/* GCC reset clocks */
#define GCC_GPU_BCR 0
diff --git a/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h b/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h
index 6243588..915ac08 100644
--- a/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h
+++ b/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h
@@ -63,37 +63,39 @@
#define GCC_PCIE_AUX_CLK 45
#define GCC_PCIE_AUX_PHY_CLK_SRC 46
#define GCC_PCIE_CFG_AHB_CLK 47
-#define GCC_PCIE_MSTR_AXI_CLK 48
-#define GCC_PCIE_PHY_REFGEN_CLK 49
-#define GCC_PCIE_PHY_REFGEN_CLK_SRC 50
-#define GCC_PCIE_PIPE_CLK 51
-#define GCC_PCIE_SLEEP_CLK 52
-#define GCC_PCIE_SLV_AXI_CLK 53
-#define GCC_PCIE_SLV_Q2A_AXI_CLK 54
-#define GCC_PDM2_CLK 55
-#define GCC_PDM2_CLK_SRC 56
-#define GCC_PDM_AHB_CLK 57
-#define GCC_PDM_XO4_CLK 58
-#define GCC_PRNG_AHB_CLK 59
-#define GCC_SDCC1_AHB_CLK 60
-#define GCC_SDCC1_APPS_CLK 61
-#define GCC_SDCC1_APPS_CLK_SRC 62
-#define GCC_SPMI_FETCHER_AHB_CLK 63
-#define GCC_SPMI_FETCHER_CLK 64
-#define GCC_SPMI_FETCHER_CLK_SRC 65
-#define GCC_SYS_NOC_CPUSS_AHB_CLK 66
-#define GCC_USB30_MASTER_CLK 67
-#define GCC_USB30_MASTER_CLK_SRC 68
-#define GCC_USB30_MOCK_UTMI_CLK 69
-#define GCC_USB30_MOCK_UTMI_CLK_SRC 70
-#define GCC_USB30_SLEEP_CLK 71
-#define GCC_USB3_PHY_AUX_CLK 72
-#define GCC_USB3_PHY_AUX_CLK_SRC 73
-#define GCC_USB3_PHY_PIPE_CLK 74
-#define GCC_USB_PHY_CFG_AHB2PHY_CLK 75
-#define GCC_XO_DIV4_CLK 76
-#define GPLL0 77
-#define GPLL0_OUT_EVEN 78
+#define GCC_PCIE_0_CLKREF_EN 48
+#define GCC_PCIE_MSTR_AXI_CLK 49
+#define GCC_PCIE_PHY_REFGEN_CLK 50
+#define GCC_PCIE_PHY_REFGEN_CLK_SRC 51
+#define GCC_PCIE_PIPE_CLK 52
+#define GCC_PCIE_SLEEP_CLK 53
+#define GCC_PCIE_SLV_AXI_CLK 54
+#define GCC_PCIE_SLV_Q2A_AXI_CLK 55
+#define GCC_PDM2_CLK 56
+#define GCC_PDM2_CLK_SRC 57
+#define GCC_PDM_AHB_CLK 58
+#define GCC_PDM_XO4_CLK 59
+#define GCC_PRNG_AHB_CLK 60
+#define GCC_SDCC1_AHB_CLK 61
+#define GCC_SDCC1_APPS_CLK 62
+#define GCC_SDCC1_APPS_CLK_SRC 63
+#define GCC_SPMI_FETCHER_AHB_CLK 64
+#define GCC_SPMI_FETCHER_CLK 65
+#define GCC_SPMI_FETCHER_CLK_SRC 66
+#define GCC_SYS_NOC_CPUSS_AHB_CLK 67
+#define GCC_USB30_MASTER_CLK 68
+#define GCC_USB30_MASTER_CLK_SRC 69
+#define GCC_USB30_MOCK_UTMI_CLK 70
+#define GCC_USB30_MOCK_UTMI_CLK_SRC 71
+#define GCC_USB30_SLEEP_CLK 72
+#define GCC_USB3_PRIM_CLKREF_CLK 73
+#define GCC_USB3_PHY_AUX_CLK 74
+#define GCC_USB3_PHY_AUX_CLK_SRC 75
+#define GCC_USB3_PHY_PIPE_CLK 76
+#define GCC_USB_PHY_CFG_AHB2PHY_CLK 77
+#define GCC_XO_DIV4_CLK 78
+#define GPLL0 79
+#define GPLL0_OUT_EVEN 80
/* GDSCs */
#define PCIE_GDSC 0
@@ -118,6 +120,9 @@
#define GCC_SDCC1_BCR 12
#define GCC_SPMI_FETCHER_BCR 13
#define GCC_USB30_BCR 14
-#define GCC_USB_PHY_CFG_AHB2PHY_BCR 15
+#define GCC_USB3_PHY_BCR 15
+#define GCC_USB3PHY_PHY_BCR 16
+#define GCC_QUSB2PHY_BCR 17
+#define GCC_USB_PHY_CFG_AHB2PHY_BCR 18
#endif
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 1325b23..094b152 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -430,6 +430,16 @@
*/
int clk_set_flags(struct clk *clk, unsigned long flags);
+/**
+ * clk_list_frequnecy - enumerate supported frequencies
+ * @clk: clock source
+ * @index: identify frequency to list
+ *
+ * Returns a non-negative integer frequency for success
+ * or negative errno in case of failure.
+ */
+unsigned long clk_list_frequency(struct clk *clk, unsigned int index);
+
#else /* !CONFIG_HAVE_CLK */
static inline struct clk *clk_get(struct device *dev, const char *id)
diff --git a/include/linux/extcon/extcon-gpio.h b/include/linux/extcon/extcon-gpio.h
index 7cacafb..8c463a8 100644
--- a/include/linux/extcon/extcon-gpio.h
+++ b/include/linux/extcon/extcon-gpio.h
@@ -33,6 +33,8 @@
* @irq_flags: IRQ Flags (e.g., IRQF_TRIGGER_LOW).
* @check_on_resume: Boolean describing whether to check the state of gpio
* while resuming from sleep.
+ * @pctrl: GPIO pinctrl handle
+ * @pctrl_default: GPIO pinctrl default state handle
*/
struct gpio_extcon_pdata {
unsigned int extcon_id;
@@ -42,6 +44,9 @@
unsigned long irq_flags;
bool check_on_resume;
+
+ struct pinctrl *pctrl;
+ struct pinctrl_state *pins_default;
};
#endif /* __EXTCON_GPIO_H__ */
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index 422630b..e46e7d1 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -286,6 +286,12 @@
#define SIT_ENTRY_PER_BLOCK (PAGE_SIZE / sizeof(struct f2fs_sit_entry))
/*
+ * F2FS uses 4 bytes to represent block address. As a result, supported size of
+ * disk is 16 TB and it equals to 16 * 1024 * 1024 / 2 segments.
+ */
+#define F2FS_MAX_SEGMENT ((16 * 1024 * 1024) / 2)
+
+/*
* Note that f2fs_sit_entry->vblocks has the following bit-field information.
* [15:10] : allocation type such as CURSEG_XXXX_TYPE
* [9:0] : valid block count
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index e0341af..3c99fb6 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -731,11 +731,9 @@
#if defined(CONFIG_BLK_DEV_INTEGRITY)
extern void blk_integrity_add(struct gendisk *);
extern void blk_integrity_del(struct gendisk *);
-extern void blk_integrity_revalidate(struct gendisk *);
#else /* CONFIG_BLK_DEV_INTEGRITY */
static inline void blk_integrity_add(struct gendisk *disk) { }
static inline void blk_integrity_del(struct gendisk *disk) { }
-static inline void blk_integrity_revalidate(struct gendisk *disk) { }
#endif /* CONFIG_BLK_DEV_INTEGRITY */
#else /* CONFIG_BLOCK */
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index ecfc173..0e6a54c 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -18,6 +18,7 @@
#include <linux/devfreq.h>
#include <linux/fault-inject.h>
#include <linux/blkdev.h>
+#include <linux/extcon.h>
#include <linux/mmc/core.h>
#include <linux/mmc/card.h>
@@ -594,6 +595,8 @@
* actually disabling the clock from it's source.
*/
bool card_clock_off;
+ struct extcon_dev *extcon;
+ struct notifier_block card_detect_nb;
#ifdef CONFIG_MMC_PERF_PROFILING
struct {
diff --git a/include/linux/mmc/slot-gpio.h b/include/linux/mmc/slot-gpio.h
index 3945a8c..f2c8d13 100644
--- a/include/linux/mmc/slot-gpio.h
+++ b/include/linux/mmc/slot-gpio.h
@@ -29,5 +29,7 @@
void mmc_gpio_set_cd_isr(struct mmc_host *host,
irqreturn_t (*isr)(int irq, void *dev_id));
void mmc_gpiod_request_cd_irq(struct mmc_host *host);
+void mmc_register_extcon(struct mmc_host *host);
+void mmc_unregister_extcon(struct mmc_host *host);
#endif
diff --git a/include/linux/msm_gsi.h b/include/linux/msm_gsi.h
index f5d2f72..0c460a0 100644
--- a/include/linux/msm_gsi.h
+++ b/include/linux/msm_gsi.h
@@ -18,6 +18,7 @@
GSI_VER_1_0 = 1,
GSI_VER_1_2 = 2,
GSI_VER_1_3 = 3,
+ GSI_VER_2_0 = 4,
GSI_VER_MAX,
};
diff --git a/include/linux/netfilter/xt_qtaguid.h b/include/linux/netfilter/xt_qtaguid.h
index ca60fbd..1c67155 100644
--- a/include/linux/netfilter/xt_qtaguid.h
+++ b/include/linux/netfilter/xt_qtaguid.h
@@ -10,4 +10,5 @@
#define XT_QTAGUID_SOCKET XT_OWNER_SOCKET
#define xt_qtaguid_match_info xt_owner_match_info
+int qtaguid_untag(struct socket *sock, bool kernel);
#endif /* _XT_QTAGUID_MATCH_H */
diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h
index 5dd75fa..f9be467 100644
--- a/include/linux/usb/chipidea.h
+++ b/include/linux/usb/chipidea.h
@@ -14,6 +14,7 @@
* struct ci_hdrc_cable - structure for external connector cable state tracking
* @state: current state of the line
* @changed: set to true when extcon event happen
+ * @enabled: set to true if we've enabled the vbus or id interrupt
* @edev: device which generate events
* @ci: driver state of the chipidea device
* @nb: hold event notification callback
@@ -22,6 +23,7 @@
struct ci_hdrc_cable {
bool state;
bool changed;
+ bool enabled;
struct extcon_dev *edev;
struct ci_hdrc *ci;
struct notifier_block nb;
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index e1bd2bc..858f308 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -20,6 +20,8 @@
#define ADDRCONF_TIMER_FUZZ (HZ / 4)
#define ADDRCONF_TIMER_FUZZ_MAX (HZ)
+#define ADDRCONF_NOTIFY_PRIORITY 0
+
#include <linux/in.h>
#include <linux/in6.h>
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index 9dc2c18..f5e625f 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -84,6 +84,7 @@
struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
int ifindex, struct flowi6 *fl6, int flags);
+void ip6_route_init_special_entries(void);
int ip6_route_init(void);
void ip6_route_cleanup(void);
diff --git a/include/xen/xen.h b/include/xen/xen.h
index f0f0252..0c0e3ef 100644
--- a/include/xen/xen.h
+++ b/include/xen/xen.h
@@ -38,7 +38,8 @@
*/
#include <xen/features.h>
#define xen_pvh_domain() (xen_pv_domain() && \
- xen_feature(XENFEAT_auto_translated_physmap))
+ xen_feature(XENFEAT_auto_translated_physmap) && \
+ xen_have_vector_callback)
#else
#define xen_pvh_domain() (0)
#endif
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 7c9f94c..44c17f4 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -279,7 +279,8 @@
[BPF_EXIT >> 4] = "exit",
};
-static void print_bpf_insn(struct bpf_insn *insn)
+static void print_bpf_insn(const struct bpf_verifier_env *env,
+ const struct bpf_insn *insn)
{
u8 class = BPF_CLASS(insn->code);
@@ -343,9 +344,19 @@
insn->code,
bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
insn->src_reg, insn->imm);
- } else if (BPF_MODE(insn->code) == BPF_IMM) {
- verbose("(%02x) r%d = 0x%x\n",
- insn->code, insn->dst_reg, insn->imm);
+ } else if (BPF_MODE(insn->code) == BPF_IMM &&
+ BPF_SIZE(insn->code) == BPF_DW) {
+ /* At this point, we already made sure that the second
+ * part of the ldimm64 insn is accessible.
+ */
+ u64 imm = ((u64)(insn + 1)->imm << 32) | (u32)insn->imm;
+ bool map_ptr = insn->src_reg == BPF_PSEUDO_MAP_FD;
+
+ if (map_ptr && !env->allow_ptr_leaks)
+ imm = 0;
+
+ verbose("(%02x) r%d = 0x%llx\n", insn->code,
+ insn->dst_reg, (unsigned long long)imm);
} else {
verbose("BUG_ld_%02x\n", insn->code);
return;
@@ -1749,6 +1760,17 @@
return 0;
} else if (opcode == BPF_ADD &&
BPF_CLASS(insn->code) == BPF_ALU64 &&
+ dst_reg->type == PTR_TO_STACK &&
+ ((BPF_SRC(insn->code) == BPF_X &&
+ regs[insn->src_reg].type == CONST_IMM) ||
+ BPF_SRC(insn->code) == BPF_K)) {
+ if (BPF_SRC(insn->code) == BPF_X)
+ dst_reg->imm += regs[insn->src_reg].imm;
+ else
+ dst_reg->imm += insn->imm;
+ return 0;
+ } else if (opcode == BPF_ADD &&
+ BPF_CLASS(insn->code) == BPF_ALU64 &&
(dst_reg->type == PTR_TO_PACKET ||
(BPF_SRC(insn->code) == BPF_X &&
regs[insn->src_reg].type == PTR_TO_PACKET))) {
@@ -2663,7 +2685,7 @@
if (log_level) {
verbose("%d: ", insn_idx);
- print_bpf_insn(insn);
+ print_bpf_insn(env, insn);
}
err = ext_analyzer_insn_hook(env, insn_idx, prev_insn_idx);
diff --git a/kernel/configs/android-base.config b/kernel/configs/android-base.config
index 3c32c74..b7b997f 100644
--- a/kernel/configs/android-base.config
+++ b/kernel/configs/android-base.config
@@ -3,7 +3,6 @@
# CONFIG_DEVMEM is not set
# CONFIG_FHANDLE is not set
# CONFIG_INET_LRO is not set
-# CONFIG_MODULES is not set
# CONFIG_OABI_COMPAT is not set
# CONFIG_SYSVIPC is not set
# CONFIG_USELIB is not set
@@ -86,7 +85,6 @@
CONFIG_NETFILTER_XT_MATCH_POLICY=y
CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
-CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
CONFIG_NETFILTER_XT_MATCH_QUOTA=y
CONFIG_NETFILTER_XT_MATCH_SOCKET=y
CONFIG_NETFILTER_XT_MATCH_STATE=y
diff --git a/lib/test_bpf.c b/lib/test_bpf.c
index 0362da0..2e38502 100644
--- a/lib/test_bpf.c
+++ b/lib/test_bpf.c
@@ -4656,6 +4656,51 @@
{ },
{ { 0, 1 } },
},
+ {
+ /* Mainly testing JIT + imm64 here. */
+ "JMP_JGE_X: ldimm64 test 1",
+ .u.insns_int = {
+ BPF_ALU32_IMM(BPF_MOV, R0, 0),
+ BPF_LD_IMM64(R1, 3),
+ BPF_LD_IMM64(R2, 2),
+ BPF_JMP_REG(BPF_JGE, R1, R2, 2),
+ BPF_LD_IMM64(R0, 0xffffffffffffffffUL),
+ BPF_LD_IMM64(R0, 0xeeeeeeeeeeeeeeeeUL),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 0xeeeeeeeeU } },
+ },
+ {
+ "JMP_JGE_X: ldimm64 test 2",
+ .u.insns_int = {
+ BPF_ALU32_IMM(BPF_MOV, R0, 0),
+ BPF_LD_IMM64(R1, 3),
+ BPF_LD_IMM64(R2, 2),
+ BPF_JMP_REG(BPF_JGE, R1, R2, 0),
+ BPF_LD_IMM64(R0, 0xffffffffffffffffUL),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 0xffffffffU } },
+ },
+ {
+ "JMP_JGE_X: ldimm64 test 3",
+ .u.insns_int = {
+ BPF_ALU32_IMM(BPF_MOV, R0, 1),
+ BPF_LD_IMM64(R1, 3),
+ BPF_LD_IMM64(R2, 2),
+ BPF_JMP_REG(BPF_JGE, R1, R2, 4),
+ BPF_LD_IMM64(R0, 0xffffffffffffffffUL),
+ BPF_LD_IMM64(R0, 0xeeeeeeeeeeeeeeeeUL),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 1 } },
+ },
/* BPF_JMP | BPF_JNE | BPF_X */
{
"JMP_JNE_X: if (3 != 2) return 1",
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index b7f9ae7..b490af6 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -1059,7 +1059,7 @@
return err;
}
- if (nla_put(skb, IFLA_PHYS_PORT_NAME, strlen(name), name))
+ if (nla_put_string(skb, IFLA_PHYS_PORT_NAME, name))
return -EMSGSIZE;
return 0;
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 8bc6c4e..39e9acf 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -89,6 +89,7 @@
#include <linux/netfilter_ipv4.h>
#include <linux/random.h>
#include <linux/slab.h>
+#include <linux/netfilter/xt_qtaguid.h>
#include <asm/uaccess.h>
@@ -412,6 +413,9 @@
if (sk) {
long timeout;
+#ifdef CONFIG_NETFILTER_XT_MATCH_QTAGUID
+ qtaguid_untag(sock, true);
+#endif
/* Applications forget to leave groups before exiting */
ip_mc_drop_socket(sk);
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 7525f5e..ab0bbcb 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -356,6 +356,9 @@
rt->dst.dev->mtu);
return -EMSGSIZE;
}
+ if (length < sizeof(struct iphdr))
+ return -EINVAL;
+
if (flags&MSG_PROBE)
goto out;
diff --git a/net/ipv4/tcp_lp.c b/net/ipv4/tcp_lp.c
index c67ece1..7d86fc5 100644
--- a/net/ipv4/tcp_lp.c
+++ b/net/ipv4/tcp_lp.c
@@ -264,13 +264,15 @@
{
struct tcp_sock *tp = tcp_sk(sk);
struct lp *lp = inet_csk_ca(sk);
+ u32 delta;
if (sample->rtt_us > 0)
tcp_lp_rtt_sample(sk, sample->rtt_us);
/* calc inference */
- if (tcp_time_stamp > tp->rx_opt.rcv_tsecr)
- lp->inference = 3 * (tcp_time_stamp - tp->rx_opt.rcv_tsecr);
+ delta = tcp_time_stamp - tp->rx_opt.rcv_tsecr;
+ if ((s32)delta > 0)
+ lp->inference = 3 * delta;
/* test if within inference */
if (lp->last_drop && (tcp_time_stamp - lp->last_drop < lp->inference))
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 8615a6b..64e1ba4 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -543,6 +543,7 @@
newicsk->icsk_ack.last_seg_size = skb->len - newtp->tcp_header_len;
newtp->rx_opt.mss_clamp = req->mss;
tcp_ecn_openreq_child(newtp, req);
+ newtp->fastopen_req = NULL;
newtp->fastopen_rsk = NULL;
newtp->syn_data_acked = 0;
newtp->rack.mstamp.v64 = 0;
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 0e7c05b..e6bf011 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -1246,7 +1246,7 @@
* eventually). The difference is that pulled data not copied, but
* immediately discarded.
*/
-static void __pskb_trim_head(struct sk_buff *skb, int len)
+static int __pskb_trim_head(struct sk_buff *skb, int len)
{
struct skb_shared_info *shinfo;
int i, k, eat;
@@ -1256,7 +1256,7 @@
__skb_pull(skb, eat);
len -= eat;
if (!len)
- return;
+ return 0;
}
eat = len;
k = 0;
@@ -1282,23 +1282,28 @@
skb_reset_tail_pointer(skb);
skb->data_len -= len;
skb->len = skb->data_len;
+ return len;
}
/* Remove acked data from a packet in the transmit queue. */
int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len)
{
+ u32 delta_truesize;
+
if (skb_unclone(skb, GFP_ATOMIC))
return -ENOMEM;
- __pskb_trim_head(skb, len);
+ delta_truesize = __pskb_trim_head(skb, len);
TCP_SKB_CB(skb)->seq += len;
skb->ip_summed = CHECKSUM_PARTIAL;
- skb->truesize -= len;
- sk->sk_wmem_queued -= len;
- sk_mem_uncharge(sk, len);
- sock_set_flag(sk, SOCK_QUEUE_SHRUNK);
+ if (delta_truesize) {
+ skb->truesize -= delta_truesize;
+ sk->sk_wmem_queued -= delta_truesize;
+ sk_mem_uncharge(sk, delta_truesize);
+ sock_set_flag(sk, SOCK_QUEUE_SHRUNK);
+ }
/* Any change of skb->len requires recalculation of tso factor. */
if (tcp_skb_pcount(skb) > 1)
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 553138d..58d7c1d 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -3318,7 +3318,8 @@
idev->dev, 0, 0);
}
- addrconf_dad_start(ifp);
+ if (ifp->state == INET6_IFADDR_STATE_PREDAD)
+ addrconf_dad_start(ifp);
return 0;
}
@@ -3531,6 +3532,7 @@
*/
static struct notifier_block ipv6_dev_notf = {
.notifier_call = addrconf_notify,
+ .priority = ADDRCONF_NOTIFY_PRIORITY,
};
static void addrconf_type_change(struct net_device *dev, unsigned long event)
@@ -3667,7 +3669,7 @@
if (keep) {
/* set state to skip the notifier below */
state = INET6_IFADDR_STATE_DEAD;
- ifa->state = 0;
+ ifa->state = INET6_IFADDR_STATE_PREDAD;
if (!(ifa->flags & IFA_F_NODAD))
ifa->flags |= IFA_F_TENTATIVE;
@@ -6319,6 +6321,8 @@
goto errlo;
}
+ ip6_route_init_special_entries();
+
for (i = 0; i < IN6_ADDR_HSIZE; i++)
INIT_HLIST_HEAD(&inet6_addr_lst[i]);
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 1a34da0..83c7d2b 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -630,6 +630,8 @@
ipv6_local_error(sk, EMSGSIZE, fl6, rt->dst.dev->mtu);
return -EMSGSIZE;
}
+ if (length < sizeof(struct ipv6hdr))
+ return -EINVAL;
if (flags&MSG_PROBE)
goto out;
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 7d17670..0b21d61 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -3455,7 +3455,10 @@
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct net *net = dev_net(dev);
- if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
+ if (!(dev->flags & IFF_LOOPBACK))
+ return NOTIFY_OK;
+
+ if (event == NETDEV_REGISTER) {
net->ipv6.ip6_null_entry->dst.dev = dev;
net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
#ifdef CONFIG_IPV6_MULTIPLE_TABLES
@@ -3464,6 +3467,12 @@
net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
#endif
+ } else if (event == NETDEV_UNREGISTER) {
+ in6_dev_put(net->ipv6.ip6_null_entry->rt6i_idev);
+#ifdef CONFIG_IPV6_MULTIPLE_TABLES
+ in6_dev_put(net->ipv6.ip6_prohibit_entry->rt6i_idev);
+ in6_dev_put(net->ipv6.ip6_blk_hole_entry->rt6i_idev);
+#endif
}
return NOTIFY_OK;
@@ -3770,9 +3779,24 @@
static struct notifier_block ip6_route_dev_notifier = {
.notifier_call = ip6_route_dev_notify,
- .priority = 0,
+ .priority = ADDRCONF_NOTIFY_PRIORITY - 10,
};
+void __init ip6_route_init_special_entries(void)
+{
+ /* Registering of the loopback is done before this portion of code,
+ * the loopback reference in rt6_info will not be taken, do it
+ * manually for init_net */
+ init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
+ init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
+ #ifdef CONFIG_IPV6_MULTIPLE_TABLES
+ init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
+ init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
+ init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
+ init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
+ #endif
+}
+
int __init ip6_route_init(void)
{
int ret;
@@ -3799,17 +3823,6 @@
ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
- /* Registering of the loopback is done before this portion of code,
- * the loopback reference in rt6_info will not be taken, do it
- * manually for init_net */
- init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
- init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
- #ifdef CONFIG_IPV6_MULTIPLE_TABLES
- init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
- init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
- init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
- init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
- #endif
ret = fib6_init();
if (ret)
goto out_register_subsys;
diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c
index 3c7ae04..1fbe4b6 100644
--- a/net/netfilter/xt_qtaguid.c
+++ b/net/netfilter/xt_qtaguid.c
@@ -321,7 +321,7 @@
st_entry->tag,
get_uid_from_tag(st_entry->tag));
rb_erase(&st_entry->sock_node, st_to_free_tree);
- sockfd_put(st_entry->socket);
+ sock_put(st_entry->sk);
kfree(st_entry);
}
}
@@ -1922,12 +1922,12 @@
{
struct sock_tag *sock_tag_entry = v;
uid_t uid;
- long f_count;
CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u\n",
current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
if (sock_tag_entry != SEQ_START_TOKEN) {
+ int sk_ref_count;
uid = get_uid_from_tag(sock_tag_entry->tag);
CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%u) "
"pid=%u\n",
@@ -1936,13 +1936,13 @@
uid,
sock_tag_entry->pid
);
- f_count = atomic_long_read(
- &sock_tag_entry->socket->file->f_count);
+ sk_ref_count = atomic_read(
+ &sock_tag_entry->sk->sk_refcnt);
seq_printf(m, "sock=%pK tag=0x%llx (uid=%u) pid=%u "
- "f_count=%lu\n",
+ "f_count=%d\n",
sock_tag_entry->sk,
sock_tag_entry->tag, uid,
- sock_tag_entry->pid, f_count);
+ sock_tag_entry->pid, sk_ref_count);
} else {
seq_printf(m, "events: sockets_tagged=%llu "
"sockets_untagged=%llu "
@@ -2238,8 +2238,8 @@
from_kuid(&init_user_ns, current_fsuid()));
goto err;
}
- CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n",
- input, atomic_long_read(&el_socket->file->f_count),
+ CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->sk_refcnt=%d ->sk=%p\n",
+ input, atomic_read(&el_socket->sk->sk_refcnt),
el_socket->sk);
if (argc < 3) {
acct_tag = make_atag_from_value(0);
@@ -2283,16 +2283,9 @@
struct tag_ref *prev_tag_ref_entry;
CT_DEBUG("qtaguid: ctrl_tag(%s): retag for sk=%p "
- "st@%p ...->f_count=%ld\n",
+ "st@%p ...->sk_refcnt=%d\n",
input, el_socket->sk, sock_tag_entry,
- atomic_long_read(&el_socket->file->f_count));
- /*
- * This is a re-tagging, so release the sock_fd that was
- * locked at the time of the 1st tagging.
- * There is still the ref from this call's sockfd_lookup() so
- * it can be done within the spinlock.
- */
- sockfd_put(sock_tag_entry->socket);
+ atomic_read(&el_socket->sk->sk_refcnt));
prev_tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag,
&uid_tag_data_entry);
BUG_ON(IS_ERR_OR_NULL(prev_tag_ref_entry));
@@ -2312,8 +2305,12 @@
res = -ENOMEM;
goto err_tag_unref_put;
}
+ /*
+ * Hold the sk refcount here to make sure the sk pointer cannot
+ * be freed and reused
+ */
+ sock_hold(el_socket->sk);
sock_tag_entry->sk = el_socket->sk;
- sock_tag_entry->socket = el_socket;
sock_tag_entry->pid = current->tgid;
sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid_int);
spin_lock_bh(&uid_tag_data_tree_lock);
@@ -2340,10 +2337,11 @@
atomic64_inc(&qtu_events.sockets_tagged);
}
spin_unlock_bh(&sock_tag_list_lock);
- /* We keep the ref to the socket (file) until it is untagged */
- CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->f_count=%ld\n",
+ /* We keep the ref to the sk until it is untagged */
+ CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->sk_refcnt=%d\n",
input, sock_tag_entry,
- atomic_long_read(&el_socket->file->f_count));
+ atomic_read(&el_socket->sk->sk_refcnt));
+ sockfd_put(el_socket);
return 0;
err_tag_unref_put:
@@ -2351,8 +2349,8 @@
tag_ref_entry->num_sock_tags--;
free_tag_ref_from_utd_entry(tag_ref_entry, uid_tag_data_entry);
err_put:
- CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->f_count=%ld\n",
- input, atomic_long_read(&el_socket->file->f_count) - 1);
+ CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->sk_refcnt=%d\n",
+ input, atomic_read(&el_socket->sk->sk_refcnt) - 1);
/* Release the sock_fd that was grabbed by sockfd_lookup(). */
sockfd_put(el_socket);
return res;
@@ -2368,17 +2366,13 @@
int sock_fd = 0;
struct socket *el_socket;
int res, argc;
- struct sock_tag *sock_tag_entry;
- struct tag_ref *tag_ref_entry;
- struct uid_tag_data *utd_entry;
- struct proc_qtu_data *pqd_entry;
argc = sscanf(input, "%c %d", &cmd, &sock_fd);
CT_DEBUG("qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n",
input, argc, cmd, sock_fd);
if (argc < 2) {
res = -EINVAL;
- goto err;
+ return res;
}
el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */
if (!el_socket) {
@@ -2386,17 +2380,31 @@
" sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n",
input, sock_fd, res, current->pid, current->tgid,
from_kuid(&init_user_ns, current_fsuid()));
- goto err;
+ return res;
}
CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n",
input, atomic_long_read(&el_socket->file->f_count),
el_socket->sk);
+ res = qtaguid_untag(el_socket, false);
+ sockfd_put(el_socket);
+ return res;
+}
+
+int qtaguid_untag(struct socket *el_socket, bool kernel)
+{
+ int res;
+ pid_t pid;
+ struct sock_tag *sock_tag_entry;
+ struct tag_ref *tag_ref_entry;
+ struct uid_tag_data *utd_entry;
+ struct proc_qtu_data *pqd_entry;
+
spin_lock_bh(&sock_tag_list_lock);
sock_tag_entry = get_sock_stat_nl(el_socket->sk);
if (!sock_tag_entry) {
spin_unlock_bh(&sock_tag_list_lock);
res = -EINVAL;
- goto err_put;
+ return res;
}
/*
* The socket already belongs to the current process
@@ -2408,20 +2416,26 @@
BUG_ON(!tag_ref_entry);
BUG_ON(tag_ref_entry->num_sock_tags <= 0);
spin_lock_bh(&uid_tag_data_tree_lock);
+ if (kernel)
+ pid = sock_tag_entry->pid;
+ else
+ pid = current->tgid;
pqd_entry = proc_qtu_data_tree_search(
- &proc_qtu_data_tree, current->tgid);
+ &proc_qtu_data_tree, pid);
/*
* TODO: remove if, and start failing.
* At first, we want to catch user-space code that is not
* opening the /dev/xt_qtaguid.
*/
- if (IS_ERR_OR_NULL(pqd_entry))
+ if (IS_ERR_OR_NULL(pqd_entry) || !sock_tag_entry->list.next) {
pr_warn_once("qtaguid: %s(): "
"User space forgot to open /dev/xt_qtaguid? "
- "pid=%u tgid=%u uid=%u\n", __func__,
- current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
- else
+ "pid=%u tgid=%u sk_pid=%u, uid=%u\n", __func__,
+ current->pid, current->tgid, sock_tag_entry->pid,
+ from_kuid(&init_user_ns, current_fsuid()));
+ } else {
list_del(&sock_tag_entry->list);
+ }
spin_unlock_bh(&uid_tag_data_tree_lock);
/*
* We don't free tag_ref from the utd_entry here,
@@ -2430,30 +2444,17 @@
tag_ref_entry->num_sock_tags--;
spin_unlock_bh(&sock_tag_list_lock);
/*
- * Release the sock_fd that was grabbed at tag time,
- * and once more for the sockfd_lookup() here.
+ * Release the sock_fd that was grabbed at tag time.
*/
- sockfd_put(sock_tag_entry->socket);
- CT_DEBUG("qtaguid: ctrl_untag(%s): done. st@%p ...->f_count=%ld\n",
- input, sock_tag_entry,
- atomic_long_read(&el_socket->file->f_count) - 1);
- sockfd_put(el_socket);
+ sock_put(sock_tag_entry->sk);
+ CT_DEBUG("qtaguid: done. st@%p ...->sk_refcnt=%d\n",
+ sock_tag_entry,
+ atomic_read(&el_socket->sk->sk_refcnt));
kfree(sock_tag_entry);
atomic64_inc(&qtu_events.sockets_untagged);
return 0;
-
-err_put:
- CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%ld\n",
- input, atomic_long_read(&el_socket->file->f_count) - 1);
- /* Release the sock_fd that was grabbed by sockfd_lookup(). */
- sockfd_put(el_socket);
- return res;
-
-err:
- CT_DEBUG("qtaguid: ctrl_untag(%s): done.\n", input);
- return res;
}
static ssize_t qtaguid_ctrl_parse(const char *input, size_t count)
diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h
index 6dc14a9..8178fbd 100644
--- a/net/netfilter/xt_qtaguid_internal.h
+++ b/net/netfilter/xt_qtaguid_internal.h
@@ -256,8 +256,6 @@
struct sock_tag {
struct rb_node sock_node;
struct sock *sk; /* Only used as a number, never dereferenced */
- /* The socket is needed for sockfd_put() */
- struct socket *socket;
/* Used to associate with a given pid */
struct list_head list; /* in proc_qtu_data.sock_tag_list */
pid_t pid;
diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c
index f6a00a3..2a7190d 100644
--- a/net/netfilter/xt_qtaguid_print.c
+++ b/net/netfilter/xt_qtaguid_print.c
@@ -24,7 +24,7 @@
#include <linux/rbtree.h>
#include <linux/slab.h>
#include <linux/spinlock_types.h>
-
+#include <net/sock.h>
#include "xt_qtaguid_internal.h"
#include "xt_qtaguid_print.h"
@@ -237,10 +237,10 @@
tag_str = pp_tag_t(&st->tag);
res = kasprintf(GFP_ATOMIC, "sock_tag@%p{"
"sock_node=rb_node{...}, "
- "sk=%p socket=%p (f_count=%lu), list=list_head{...}, "
+ "sk=%p (f_count=%d), list=list_head{...}, "
"pid=%u, tag=%s}",
- st, st->sk, st->socket, atomic_long_read(
- &st->socket->file->f_count),
+ st, st->sk, atomic_read(
+ &st->sk->sk_refcnt),
st->pid, tag_str);
_bug_on_err_or_null(res);
kfree(tag_str);
diff --git a/net/rfkill/core.c b/net/rfkill/core.c
index 9b4260d..a9a7128 100644
--- a/net/rfkill/core.c
+++ b/net/rfkill/core.c
@@ -796,8 +796,7 @@
}
EXPORT_SYMBOL(rfkill_resume_polling);
-#ifdef CONFIG_RFKILL_PM
-static int rfkill_suspend(struct device *dev)
+static __maybe_unused int rfkill_suspend(struct device *dev)
{
struct rfkill *rfkill = to_rfkill(dev);
@@ -807,7 +806,7 @@
return 0;
}
-static int rfkill_resume(struct device *dev)
+static __maybe_unused int rfkill_resume(struct device *dev)
{
struct rfkill *rfkill = to_rfkill(dev);
bool cur;
@@ -827,19 +826,13 @@
}
static SIMPLE_DEV_PM_OPS(rfkill_pm_ops, rfkill_suspend, rfkill_resume);
-#define RFKILL_PM_OPS (&rfkill_pm_ops)
-#else
-#define RFKILL_PM_OPS NULL
-#endif
static struct class rfkill_class = {
.name = "rfkill",
.dev_release = rfkill_release,
.dev_groups = rfkill_dev_groups,
.dev_uevent = rfkill_dev_uevent,
-#ifdef CONFIG_RFKILL_PM
- .pm = RFKILL_PM_OPS,
-#endif
+ .pm = IS_ENABLED(CONFIG_RFKILL_PM) ? &rfkill_pm_ops : NULL,
};
bool rfkill_blocked(struct rfkill *rfkill)
diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c
index 44ac85f..d0ca0db 100644
--- a/net/xfrm/xfrm_algo.c
+++ b/net/xfrm/xfrm_algo.c
@@ -241,7 +241,7 @@
.uinfo = {
.auth = {
- .icv_truncbits = 96,
+ .icv_truncbits = 128,
.icv_fullbits = 256,
}
},
diff --git a/samples/bpf/test_verifier.c b/samples/bpf/test_verifier.c
index 369ffaa..dc7dec9 100644
--- a/samples/bpf/test_verifier.c
+++ b/samples/bpf/test_verifier.c
@@ -1218,16 +1218,22 @@
.result = ACCEPT,
},
{
- "unpriv: obfuscate stack pointer",
+ "stack pointer arithmetic",
.insns = {
- BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
- BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
- BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+ BPF_MOV64_IMM(BPF_REG_1, 4),
+ BPF_JMP_IMM(BPF_JA, 0, 0, 0),
+ BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -10),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -10),
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
+ BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_1),
+ BPF_ST_MEM(0, BPF_REG_2, 4, 0),
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 8),
+ BPF_ST_MEM(0, BPF_REG_2, 4, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
},
- .errstr_unpriv = "R2 pointer arithmetic",
- .result_unpriv = REJECT,
.result = ACCEPT,
},
{
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index bc44626..5cb7e04 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -2155,7 +2155,20 @@
/* cancel the pending probing work */
chip = card->private_data;
hda = container_of(chip, struct hda_intel, chip);
+ /* FIXME: below is an ugly workaround.
+ * Both device_release_driver() and driver_probe_device()
+ * take *both* the device's and its parent's lock before
+ * calling the remove() and probe() callbacks. The codec
+ * probe takes the locks of both the codec itself and its
+ * parent, i.e. the PCI controller dev. Meanwhile, when
+ * the PCI controller is unbound, it takes its lock, too
+ * ==> ouch, a deadlock!
+ * As a workaround, we unlock temporarily here the controller
+ * device during cancel_work_sync() call.
+ */
+ device_unlock(&pci->dev);
cancel_work_sync(&hda->probe_work);
+ device_lock(&pci->dev);
snd_card_free(card);
}
diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c
index cf014d7..0c2f41a 100644
--- a/sound/soc/codecs/wcd_cpe_core.c
+++ b/sound/soc/codecs/wcd_cpe_core.c
@@ -1725,10 +1725,10 @@
if (pos)
copy_count = pos - buf;
- if (copy_count > WCD_CPE_IMAGE_FNAME_MAX) {
+ if (copy_count > (WCD_CPE_IMAGE_FNAME_MAX - 1)) {
dev_err(core->dev,
"%s: Invalid length %d, max allowed %d\n",
- __func__, copy_count, WCD_CPE_IMAGE_FNAME_MAX);
+ __func__, copy_count, WCD_CPE_IMAGE_FNAME_MAX - 1);
return -EINVAL;
}
diff --git a/tools/power/cpupower/utils/helpers/cpuid.c b/tools/power/cpupower/utils/helpers/cpuid.c
index 93b0aa7..39c2c7d 100644
--- a/tools/power/cpupower/utils/helpers/cpuid.c
+++ b/tools/power/cpupower/utils/helpers/cpuid.c
@@ -156,6 +156,7 @@
*/
case 0x2C: /* Westmere EP - Gulftown */
cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO;
+ break;
case 0x2A: /* SNB */
case 0x2D: /* SNB Xeon */
case 0x3A: /* IVB */
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index a89f80a..6300c1a 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -5,7 +5,7 @@
.PHONY: all all_32 all_64 warn_32bit_failure clean
TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_syscall test_mremap_vdso \
- check_initial_reg_state sigreturn ldt_gdt iopl \
+ check_initial_reg_state sigreturn ldt_gdt iopl mpx-mini-test \
protection_keys
TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \
test_FCMOV test_FCOMI test_FISTTP \