Merge "msm: kgsl: Lock ringbuffer translation in TLB"
diff --git a/Documentation/devicetree/bindings/arm/msm/rpm-regulator-smd.txt b/Documentation/devicetree/bindings/arm/msm/rpm-regulator-smd.txt
index 93b5144..d930799 100644
--- a/Documentation/devicetree/bindings/arm/msm/rpm-regulator-smd.txt
+++ b/Documentation/devicetree/bindings/arm/msm/rpm-regulator-smd.txt
@@ -113,8 +113,14 @@
14 = 1.37
15 = 1.28
16 = 1.20
-- qcom,init-head-room: Voltage head room in uV required for the
- regulator
+- qcom,init-head-room: Voltage head room in mV required for the
+ regulator. This head room value should be used
+ in situations where the device connected to the
+ output of the regulator has low noise tolerance.
+ Note that the RPM independently enforces a
+ safety head room value for subregulated LDOs
+ which is sufficient to account for LDO drop-out
+ voltage.
- qcom,init-quiet-mode: Specify that quiet mode is needed for an SMPS
regulator in order to have lower output noise.
Supported values are:
diff --git a/Documentation/devicetree/bindings/iommu/msm_iommu.txt b/Documentation/devicetree/bindings/iommu/msm_iommu.txt
index 313f0a3..2a631ed 100644
--- a/Documentation/devicetree/bindings/iommu/msm_iommu.txt
+++ b/Documentation/devicetree/bindings/iommu/msm_iommu.txt
@@ -7,6 +7,7 @@
offset and length for clock register for additional clock that
needs to be turned on for access to this IOMMU.
- reg-names: "iommu_base", "clk_base" (optional)
+- label: name of this IOMMU instance.
Optional properties:
- qcom,iommu-secure-id : Secure identifier for the IOMMU block
@@ -14,6 +15,7 @@
programmed by the secure environment.
- qcom,alt-vdd-supply : Alternative regulator needed to access IOMMU
configuration registers.
+- interrupts : should contain the performance monitor overflow interrupt number.
- List of sub nodes, one for each of the translation context banks supported.
Each sub node has the following required properties:
@@ -51,6 +53,7 @@
vdd-supply = <&gdsc_iommu>;
qcom,iommu-bfb-regs = <0x204c 0x2050>;
qcom,iommu-bfb-data = <0xffff 0xffce>;
+ label = "iommu_0";
qcom,iommu-ctx@fda6c000 {
reg = <0xfda6c000 0x1000>;
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-server.txt b/Documentation/devicetree/bindings/media/video/msm-cam-server.txt
deleted file mode 100644
index 2b6f513..0000000
--- a/Documentation/devicetree/bindings/media/video/msm-cam-server.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-* Qualcomm MSM Camera Server
-
-Required properties:
-- compatible :
- - "qcom,cam_server"
-
-Example:
-
- qcom,cam_server {
- compatible = "qcom,cam_server";
- };
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam.txt b/Documentation/devicetree/bindings/media/video/msm-cam.txt
new file mode 100644
index 0000000..b5b6cf6
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-cam.txt
@@ -0,0 +1,15 @@
+* Qualcomm MSM Camera
+
+Required properties:
+- compatible :
+ - "qcom,msm-cam"
+- reg : offset and length of msm camera device registers.
+- reg-names : should specify relevant names for each reg property defined.
+
+Example:
+
+ qcom,msm-cam@fd8c0000 {
+ compatible = "qcom,msm-cam";
+ reg = <0xfd8C0000 0x10000>;
+ reg-names = "msm-cam";
+ };
diff --git a/Documentation/devicetree/bindings/media/video/msm-cci.txt b/Documentation/devicetree/bindings/media/video/msm-cci.txt
index e256265..cf33f50 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cci.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cci.txt
@@ -11,6 +11,25 @@
- interrupts : should contain the cci interrupt.
- interrupt-names : should specify relevant names to each interrupts
property defined.
+- gpios : should contain phandle to gpio controller node and array of
+ #gpio-cells specifying specific gpio (controller specific)
+- qcom,gpio-req-tbl-num : should contain index to gpios specific to this sensor
+- qcom,gpio-req-tbl-flags : should contain direction of gpios present in
+ qcom,gpio-req-tbl-num property (in the same order)
+- qcom,gpio-req-tbl-label : should contain name of gpios present in
+ qcom,gpio-req-tbl-num property (in the same order)
+- qcom,hw-thigh : should contain high period of the SCL clock in terms of CCI
+ clock cycle
+- qcom,hw-tlow : should contain high period of the SCL clock in terms of CCI
+ clock cycle
+- qcom,hw-tsu-sto : should contain setup time for STOP condition
+- qcom,hw-tsu-sta : should contain setup time for Repeated START condition
+- qcom,hw-thd-dat : should contain hold time for the data
+- qcom,hw-thd-sta : should contain hold time for START condition
+- qcom,hw-tbuf : should contain free time between a STOP and a START condition
+- qcom,hw-scl-stretch-en : should contain enable or disable clock stretching
+- qcom,hw-trdhld : should contain internal hold time for SDA
+- qcom,hw-tsp : should contain filtering of glitches
[Second level nodes]
* Qualcomm MSM Sensor
@@ -22,16 +41,14 @@
- "qcom,s5k3l1yx"
- reg : should contain i2c slave address of the camera sensor and
length of data field which is 0x0
-- qcom,csi-if : should contain number of csid cores required at the receiver
- side
- - 1 for 2D sensor
- - 2 for 3D sensor
-- qcom,csid-core : should contain csid core instance that will used to receive
- sensor data
+- qcom,slave-id : should contain i2c slave address, device id address
+ and expected id read value
+- qcom,csiphy-sd-index : should contain csiphy instance that will used to
+ receive sensor data
+ - 0, 1, 2
+- qcom,csid-sd-index : should contain csid core instance that will used to
+ receive sensor data
- 0, 1, 2, 3
-- qcom,is-vpe : should be enabled if VPE module is required for post processing
- of this sensor
- - 1 if required, 0 otherwise
- qcom,sensor-name : should contain unique sensor name to differentiate from
other sensor
- "s5k3l1yx"
@@ -52,7 +69,7 @@
regulators mentioned in qcom,cam-vreg-name property (in the same order)
- qcom,cam-vreg-op-mode : should contain optimum voltage level for regulators
mentioned in qcom,cam-vreg-name property (in the same order)
-- qcom,camera-type : should contain sensor type
+- qcom,sensor-mode : should contain sensor mode supported
- 0 -> back camera 2D
- 1 -> front camera 2D
- 2 -> back camera 3D
@@ -62,7 +79,10 @@
- 1 -> yuv format
Optional properties:
-- qcom,flash-src-index : should contain phandle to flash source node if flash
+- qcom,is-vpe : should be enabled if VPE module is required for post processing
+ of this sensor
+ - 1 if required, 0 otherwise
+- qcom,led-flash-sd-index : should contain phandle to flash source node if flash
is supported for this sensor
- led_flash0, led_flash1
- qcom,mount-angle : should contain the physical mount angle of the sensor on
@@ -74,12 +94,7 @@
- cam_vaf-supply : should contain regulator from which AF voltage is supplied
- gpios : should contain phandle to gpio controller node and array of
#gpio-cells specifying specific gpio (controller specific)
-- qcom,gpio-common-tbl-num : should contain index to gpios shared between
- different sensors
-- qcom,gpio-common-tbl-flags : should contain direction of gpios present in
- qcom,gpio-common-tbl-num property (in the same order)
-- qcom,gpio-common-tbl-label : should contain name of gpios present in
- qcom,gpio-common-tbl-num property (in the same order)
+- qcom,gpio-reset : should contain index to gpio used by sensors reset_n
- qcom,gpio-req-tbl-num : should contain index to gpios specific to this sensor
- qcom,gpio-req-tbl-flags : should contain direction of gpios present in
qcom,gpio-req-tbl-num property (in the same order)
@@ -107,7 +122,9 @@
for actuator
- qcom,actuator-vcm-enable : should contain value to be set for actuator vcm
gpio
-
+- qcom,sensor-position : should contain the mount angle of the camera sensor
+ - 0 -> back camera
+ - 1 -> front camera
Example:
qcom,cci@0xfda0c000 {
@@ -117,13 +134,33 @@
reg-names = "cci";
interrupts = <0 50 0>;
interrupt-names = "cci";
+ gpios = <&msmgpio 19 0>,
+ <&msmgpio 20 0>,
+ <&msmgpio 21 0>,
+ <&msmgpio 22 0>;
+ qcom,gpio-tbl-num = <0 1 2 3>;
+ qcom,gpio-tbl-flags = <1 1 1 1>;
+ qcom,gpio-tbl-label = "CCI_I2C_DATA0",
+ "CCI_I2C_CLK0",
+ "CCI_I2C_DATA1",
+ "CCI_I2C_CLK1";
+ qcom,hw-thigh = <78>;
+ qcom,hw-tlow = <114>;
+ qcom,hw-tsu-sto = <28>;
+ qcom,hw-tsu-sta = <28>;
+ qcom,hw-thd-dat = <10>;
+ qcom,hw-thd-sta = <77>;
+ qcom,hw-tbuf = <118>;
+ qcom,hw-scl-stretch-en = <0>;
+ qcom,hw-trdhld = <6>;
+ qcom,hw-tsp = <1>;
qcom,s5k3l1yx@6e {
compatible = "qcom,s5k3l1yx";
reg = <0x6e 0x0>;
- qcom,csi-if = <1>;
- qcom,csid-core = <0>;
- qcom,is-vpe = <1>;
- qcom,flash-type = <0>;
+ qcom,slave-id = <0x6e 0x0 0x3121>;
+ qcom,led-flash-sd-index = <0>;
+ qcom,csiphy-sd-index = <2>;
+ qcom,csid-sd-index = <0>;
qcom,mount-angle = <90>;
qcom,sensor-name = "s5k3l1yx";
cam_vdig-supply = <&pm8941_l3>;
@@ -137,23 +174,18 @@
qcom,cam-vreg-op-mode = <105000 80000 0 100000>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 15 0>,
- <&msmgpio 19 0>,
- <&msmgpio 20 0>,
- <&msmgpio 90 0>;
- qcom,gpio-common-tbl-num = <0 1 2>;
- qcom,gpio-common-tbl-flags = <1 1 1>;
- qcom,gpio-common-tbl-label = "CAMIF_MCLK", "CAMIF_I2C_DATA",
- "CAMIF_I2C_CLK";
- qcom,gpio-req-tbl-num = <3>;
- qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_RESET1";
- qcom,gpio-set-tbl-num = <3 3>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-req-tbl-num = <0 1>;
+ qcom,gpio-req-tbl-flags = <1 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1";
+ qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 4000>;
qcom,csi-lane-assign = <0x4320>;
qcom,csi-lane-mask = <0x1F>;
qcom,csi-phy-sel = <0>;
- qcom,camera-type = <0>;
- qcom,sensor-type = <0>;
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <1>;
};
};
diff --git a/Documentation/devicetree/bindings/media/video/msm-vfe.txt b/Documentation/devicetree/bindings/media/video/msm-vfe.txt
index 7a70cac..f02f35e 100644
--- a/Documentation/devicetree/bindings/media/video/msm-vfe.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-vfe.txt
@@ -4,6 +4,7 @@
- cell-index: vfe hardware core index
- compatible :
- "qcom,vfe"
+ - "qcom,vfe40"
- reg : offset and length of the register set for the device
for the vfe operating in compatible mode.
- reg-names : should specify relevant names to each reg property defined.
diff --git a/Documentation/devicetree/bindings/tty/serial/msm_serial_hs.txt b/Documentation/devicetree/bindings/tty/serial/msm_serial_hs.txt
new file mode 100644
index 0000000..3534823
--- /dev/null
+++ b/Documentation/devicetree/bindings/tty/serial/msm_serial_hs.txt
@@ -0,0 +1,70 @@
+* Qualcomm MSM HSUART
+
+Required properties:
+- compatible :
+ - "qcom,msm-hsuart-v14" to be used for UARTDM Core v1.4
+- reg : offset and length of the register set for both the device,
+ uart core and bam core
+- reg-names : names of the uart core and bam core.
+- interrupts : should contain the uart interrupt.
+- interrupt-names : names of interrupts to be used.
+- bam-tx-ep-pipe-index : BAM TX Endpoint Pipe Index for HSUART
+- bam-rx-ep-pipe-index : BAM RX Endpoint Pipe Index for HSUART
+
+BLSP has a static pipe allocation and assumes a pair-pipe for each uart core.
+Pipes [2*i : 2*i+1] are allocated for UART cores where i = [0 : 5].
+Hence, Minimum and Maximum permitted value of endpoint pipe index to be used
+with uart core is 0 and 11 respectively.
+
+There is one HSUART block used in MSM devices,
+"qcom,msm-hsuart-v14". The msm-serial-hs driver is
+able to handle this, and matches against the "qcom,msm-hsuart-v14"
+as the compatibility.
+
+The registers for the "qcom,msm-hsuart-v14" device need to specify both
+register blocks - uart core and bam core.
+
+Example:
+
+ uart7@f995d000 {
+ compatible = "qcom,msm-hsuart-v14";
+ reg = <0xf995d000 0x1000>,
+ <0xf9944000 0x5000>;
+ reg-names = "core_mem", "bam_mem";
+ interrupts = <0 113 0>, <0 239 0>;
+ interrupt-names = "core_irq", "bam_irq";
+ };
+
+Optional properties:
+- qcom,<gpio-name>-gpio : handle to the GPIO node, see "gpios property" in
+Documentation/devicetree/bindings/gpio/gpio.txt.
+"gpio-name" can be "tx", "rx", "cts" and "rfr" based on number of UART GPIOs
+need to configured.
+Gpio's are optional if it is required to be not configured by UART driver or
+case where there is nothing connected and we want to use internal loopback mode
+for uart.
+- qcom, wakeup_irq : UART RX GPIO IRQ line to be configured as wakeup source.
+- qcom,inject_rx_on_wakeup : inject_rx_on_wakeup enables feature where on
+receiving interrupt with UART RX GPIO IRQ line (i.e. above wakeup_irq property),
+HSUART driver injects provided character with property rx_to_inject.
+- qcom, rx_to_inject : The character to be inserted on wakeup.
+
+
+Example:
+
+ uart7: uart@f995d000 {
+ compatible = "qcom,msm-hsuart-v14"
+ reg = <0x19c40000 0x1000">,
+ <0xf9944000 0x5000>;
+ reg-names = "core_mem", "bam_mem";
+ interrupts = <0 113 0>, <0 239 0>;
+ interrupt-names = "core_irq", "bam_irq";
+
+ qcom,tx-gpio = <&msmgpio 41 0x00>;
+ qcom,rx-gpio = <&msmgpio 42 0x00>;
+ qcom,cts-gpio = <&msmgpio 43 0x00>;
+ qcom,rfr-gpio = <&msmgpio 44 0x00>;
+
+ qcom,bam-tx-ep-pipe-index = <0>;
+ qcom,bam-rx-ep-pipe-index = <1>;
+ };
diff --git a/arch/arm/boot/dts/msm-iommu.dtsi b/arch/arm/boot/dts/msm-iommu.dtsi
index 3c65491..5a9e1ee 100755
--- a/arch/arm/boot/dts/msm-iommu.dtsi
+++ b/arch/arm/boot/dts/msm-iommu.dtsi
@@ -18,8 +18,10 @@
ranges;
reg = <0xfda64000 0x10000>;
reg-names = "iommu_base";
+ interrupts = <0 67 0>;
vdd-supply = <&gdsc_jpeg>;
qcom,needs-alt-core-clk;
+ label = "jpeg_iommu";
status = "disabled";
qcom,iommu-bfb-regs = <0x204c
@@ -83,8 +85,10 @@
ranges;
reg = <0xfd928000 0x10000>;
reg-names = "iommu_base";
+ interrupts = <0 73 0>;
vdd-supply = <&gdsc_mdss>;
qcom,iommu-secure-id = <1>;
+ label = "mdp_iommu";
status = "disabled";
qcom,iommu-bfb-regs = <0x204c
@@ -149,9 +153,11 @@
reg = <0xfdc84000 0x10000
0xfdce0004 0x4>;
reg-names = "iommu_base", "clk_base";
+ interrupts = <0 45 0>;
vdd-supply = <&gdsc_venus>;
qcom,iommu-secure-id = <0>;
qcom,needs-alt-core-clk;
+ label = "venus_iommu";
status = "disabled";
qcom,iommu-bfb-regs = <0x204c
@@ -235,9 +241,11 @@
ranges;
reg = <0xfdb10000 0x10000>;
reg-names = "iommu_base";
+ interrupts = <0 38 0>;
vdd-supply = <&gdsc_oxili_cx>;
qcom,alt-vdd-supply = <&gdsc_oxili_gx>;
qcom,needs-alt-core-clk;
+ label = "kgsl_iommu";
status = "disabled";
qcom,iommu-bfb-regs = <0x204c
@@ -288,8 +296,10 @@
ranges;
reg = <0xfda44000 0x10000>;
reg-names = "iommu_base";
+ interrupts = <0 62 0>;
vdd-supply = <&gdsc_vfe>;
qcom,needs-alt-core-clk;
+ label = "vfe_iommu";
status = "disabled";
qcom,iommu-bfb-regs = <0x204c
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi
index c9b999f..7035bb4 100644
--- a/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -17,8 +17,9 @@
qcom,camera@6e {
compatible = "qcom,s5k3l1yx";
reg = <0x6e 0x0>;
- qcom,csi-if = <1>;
- qcom,csid-core = <0>;
+ qcom,slave-id = <0x6e 0x0 0x3121>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
qcom,flash-src-index = <&led_flash0>;
qcom,mount-angle = <0>;
qcom,sensor-name = "s5k3l1yx";
@@ -26,46 +27,42 @@
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
cam_vaf-supply = <&pm8941_l23>;
- qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio",
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
"cam_vaf";
- qcom,cam-vreg-type = <0 0 1 0>;
- qcom,cam-vreg-min-voltage = <1225000 2850000 0 3000000>;
- qcom,cam-vreg-max-voltage = <1225000 2850000 0 3000000>;
- qcom,cam-vreg-op-mode = <105000 80000 0 100000>;
+ qcom,cam-vreg-type = <0 1 0 0>;
+ qcom,cam-vreg-min-voltage = <1225000 0 2850000 3000000>;
+ qcom,cam-vreg-max-voltage = <1225000 0 2850000 3000000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 15 0>,
- <&msmgpio 19 0>,
- <&msmgpio 20 0>,
<&msmgpio 90 0>;
- qcom,gpio-common-tbl-num = <0 1 2>;
- qcom,gpio-common-tbl-flags = <1 1 1>;
- qcom,gpio-common-tbl-label = "CAMIF_MCLK",
- "CAMIF_I2C_DATA",
- "CAMIF_I2C_CLK";
- qcom,gpio-req-tbl-num = <3>;
- qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_RESET1";
- qcom,gpio-set-tbl-num = <3 3>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-req-tbl-num = <0 1>;
+ qcom,gpio-req-tbl-flags = <1 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1";
+ qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 30000>;
qcom,csi-lane-assign = <0x4320>;
qcom,csi-lane-mask = <0x1F>;
- qcom,csi-phy-sel = <0>;
- qcom,camera-type = <0>;
- qcom,sensor-type = <0>;
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <1>;
status = "ok";
};
qcom,camera@6c {
compatible = "qcom,ov2720";
reg = <0x6c 0x0>;
- qcom,csi-if = <1>;
- qcom,csid-core = <0>;
+ qcom,slave-id = <0x6c 0x300A 0x2720>;
+ qcom,led-flash-sd-index = <0>;
+ qcom,csiphy-sd-index = <2>;
+ qcom,csid-sd-index = <0>;
qcom,mount-angle = <180>;
qcom,sensor-name = "ov2720";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
- cam_vio-supply = <&pm8941_lvs2>;
+ cam_vio-supply = <&pm8941_lvs3>;
qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio";
qcom,cam-vreg-type = <0 0 1>;
qcom,cam-vreg-min-voltage = <1225000 2850000 0>;
@@ -73,25 +70,19 @@
qcom,cam-vreg-op-mode = <105000 80000 0>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 17 0>,
- <&msmgpio 19 0>,
- <&msmgpio 20 0>,
<&msmgpio 18 0>;
- qcom,gpio-common-tbl-num = <0 1 2>;
- qcom,gpio-common-tbl-flags = <1 1 1>;
- qcom,gpio-common-tbl-label = "CAMIF_MCLK",
- "CAMIF_I2C_DATA",
- "CAMIF_I2C_CLK";
- qcom,gpio-req-tbl-num = <3>;
- qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_RESET1";
- qcom,gpio-set-tbl-num = <3 3>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-req-tbl-num = <0 1>;
+ qcom,gpio-req-tbl-flags = <1 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1";
+ qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 4000>;
qcom,csi-lane-assign = <0x4320>;
qcom,csi-lane-mask = <0x7>;
- qcom,csi-phy-sel = <2>;
- qcom,camera-type = <1>;
- qcom,sensor-type = <0>;
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
status = "ok";
};
};
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor.dtsi
index 68da844..948cdf5 100644
--- a/arch/arm/boot/dts/msm8974-camera-sensor.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera-sensor.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -16,8 +16,9 @@
qcom,camera@6e {
compatible = "qcom,s5k3l1yx";
reg = <0x6e 0x0>;
- qcom,csi-if = <1>;
- qcom,csid-core = <0>;
+ qcom,slave-id = <0x6e 0x0 0x3121>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
qcom,flash-src-index = <&led_flash0>;
qcom,mount-angle = <90>;
qcom,sensor-name = "s5k3l1yx";
@@ -25,41 +26,37 @@
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
cam_vaf-supply = <&pm8941_l23>;
- qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio",
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
"cam_vaf";
- qcom,cam-vreg-type = <0 0 1 0>;
- qcom,cam-vreg-min-voltage = <1225000 2850000 0 3000000>;
- qcom,cam-vreg-max-voltage = <1225000 2850000 0 3000000>;
- qcom,cam-vreg-op-mode = <105000 80000 0 100000>;
+ qcom,cam-vreg-type = <0 1 0 0>;
+ qcom,cam-vreg-min-voltage = <1225000 0 2850000 3000000>;
+ qcom,cam-vreg-max-voltage = <1225000 0 2850000 3000000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 15 0>,
- <&msmgpio 19 0>,
- <&msmgpio 20 0>,
<&msmgpio 90 0>;
- qcom,gpio-common-tbl-num = <0 1 2>;
- qcom,gpio-common-tbl-flags = <1 1 1>;
- qcom,gpio-common-tbl-label = "CAMIF_MCLK",
- "CAMIF_I2C_DATA",
- "CAMIF_I2C_CLK";
- qcom,gpio-req-tbl-num = <3>;
- qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_RESET1";
- qcom,gpio-set-tbl-num = <3 3>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-req-tbl-num = <0 1>;
+ qcom,gpio-req-tbl-flags = <1 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1";
+ qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 30000>;
qcom,csi-lane-assign = <0x4320>;
qcom,csi-lane-mask = <0x1F>;
- qcom,csi-phy-sel = <0>;
- qcom,camera-type = <0>;
- qcom,sensor-type = <0>;
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <1>;
status = "ok";
};
qcom,camera@6c {
compatible = "qcom,ov2720";
reg = <0x6c 0x0>;
- qcom,csi-if = <1>;
- qcom,csid-core = <0>;
+ qcom,slave-id = <0x6c 0x300A 0x2720>;
+ qcom,led-flash-sd-index = <0>;
+ qcom,csiphy-sd-index = <2>;
+ qcom,csid-sd-index = <0>;
qcom,mount-angle = <180>;
qcom,sensor-name = "ov2720";
cam_vdig-supply = <&pm8941_l3>;
@@ -72,25 +69,19 @@
qcom,cam-vreg-op-mode = <105000 80000 0>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 17 0>,
- <&msmgpio 19 0>,
- <&msmgpio 20 0>,
<&msmgpio 18 0>;
- qcom,gpio-common-tbl-num = <0 1 2>;
- qcom,gpio-common-tbl-flags = <1 1 1>;
- qcom,gpio-common-tbl-label = "CAMIF_MCLK",
- "CAMIF_I2C_DATA",
- "CAMIF_I2C_CLK";
- qcom,gpio-req-tbl-num = <3>;
- qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_RESET1";
- qcom,gpio-set-tbl-num = <3 3>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-req-tbl-num = <0 1>;
+ qcom,gpio-req-tbl-flags = <1 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1";
+ qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 4000>;
qcom,csi-lane-assign = <0x4320>;
qcom,csi-lane-mask = <0x7>;
- qcom,csi-phy-sel = <2>;
- qcom,camera-type = <1>;
- qcom,sensor-type = <0>;
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
status = "ok";
};
};
diff --git a/arch/arm/boot/dts/msm8974-camera.dtsi b/arch/arm/boot/dts/msm8974-camera.dtsi
index 48dd4dc..6002f85 100644
--- a/arch/arm/boot/dts/msm8974-camera.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -14,10 +14,10 @@
/include/ "skeleton.dtsi"
/ {
- qcom,cam_server {
- compatible = "qcom,cam_server";
+ qcom,msm-cam@fd8C0000 {
+ compatible = "qcom,msm-cam";
reg = <0xfd8C0000 0x10000>;
- reg-names = "server";
+ reg-names = "msm-cam";
};
qcom,csiphy@fda0ac00 {
@@ -90,7 +90,7 @@
qcom,ispif@fda0A000 {
cell-index = <0>;
compatible = "qcom,ispif";
- reg = <0xfda0A000 0x300>;
+ reg = <0xfda0A000 0x500>;
reg-names = "ispif";
interrupts = <0 55 0>;
interrupt-names = "ispif";
@@ -182,6 +182,26 @@
reg-names = "cci";
interrupts = <0 50 0>;
interrupt-names = "cci";
+ gpios = <&msmgpio 19 0>,
+ <&msmgpio 20 0>,
+ <&msmgpio 21 0>,
+ <&msmgpio 22 0>;
+ qcom,gpio-tbl-num = <0 1 2 3>;
+ qcom,gpio-tbl-flags = <1 1 1 1>;
+ qcom,gpio-tbl-label = "CCI_I2C_DATA0",
+ "CCI_I2C_CLK0",
+ "CCI_I2C_DATA1",
+ "CCI_I2C_CLK1";
+ qcom,hw-thigh = <78>;
+ qcom,hw-tlow = <114>;
+ qcom,hw-tsu-sto = <28>;
+ qcom,hw-tsu-sta = <28>;
+ qcom,hw-thd-dat = <10>;
+ qcom,hw-thd-sta = <77>;
+ qcom,hw-tbuf = <118>;
+ qcom,hw-scl-stretch-en = <0>;
+ qcom,hw-trdhld = <6>;
+ qcom,hw-tsp = <1>;
qcom,camera@6e {
status = "disable";
@@ -194,8 +214,9 @@
qcom,camera@90 {
compatible = "qcom,mt9m114";
reg = <0x90 0x0>;
- qcom,csi-if = <1>;
- qcom,csid-core = <0>;
+ qcom,slave-id = <0x90 0x0 0x2481>;
+ qcom,csiphy-sd-index = <1>;
+ qcom,csid-sd-index = <0>;
qcom,mount-angle = <0>;
qcom,sensor-name = "mt9m114";
cam_vdig-supply = <&pm8941_l3>;
@@ -209,20 +230,18 @@
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 16 0>,
<&msmgpio 92 0>;
- qcom,gpio-common-tbl-num = <0>;
- qcom,gpio-common-tbl-flags = <1>;
- qcom,gpio-common-tbl-label = "CAMIF_MCLK";
- qcom,gpio-req-tbl-num = <1>;
- qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_RESET1";
+ qcom,gpio-reset = <1>;
+ qcom,gpio-req-tbl-num = <0 1>;
+ qcom,gpio-req-tbl-flags = <1 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1";
qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 4000>;
qcom,csi-lane-assign = <0x4320>;
qcom,csi-lane-mask = <0x3>;
- qcom,csi-phy-sel = <1>;
- qcom,camera-type = <1>;
- qcom,sensor-type = <1>;
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
};
};
};
diff --git a/arch/arm/configs/msm8610_defconfig b/arch/arm/configs/msm8610_defconfig
new file mode 100644
index 0000000..3db20a2
--- /dev/null
+++ b/arch/arm/configs/msm8610_defconfig
@@ -0,0 +1,179 @@
+# CONFIG_ARM_PATCH_PHYS_VIRT is not set
+CONFIG_EXPERIMENTAL=y
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SYSVIPC=y
+CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_RD_BZIP2=y
+CONFIG_RD_LZMA=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_KALLSYMS_ALL=y
+CONFIG_ASHMEM=y
+CONFIG_EMBEDDED=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=m
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_EFI_PARTITION=y
+CONFIG_ARCH_MSM=y
+CONFIG_ARCH_MSM8910=y
+CONFIG_ARCH_MSM8610=y
+CONFIG_ARCH_MSM8226=y
+CONFIG_SND_SOC_MSM8226=y
+# CONFIG_MSM_STACKED_MEMORY is not set
+CONFIG_CPU_HAS_L2_PMU=y
+# CONFIG_MSM_FIQ_SUPPORT is not set
+# CONFIG_MSM_PROC_COMM is not set
+CONFIG_MSM_SMD=y
+CONFIG_MSM_SMD_PKG4=y
+# CONFIG_MSM_HW3D is not set
+CONFIG_MSM_DIRECT_SCLK_ACCESS=y
+CONFIG_MSM_WATCHDOG_V2=y
+CONFIG_MSM_ADSP_LOADER=m
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+CONFIG_SCHED_MC=y
+CONFIG_ARM_ARCH_TIMER=y
+CONFIG_HOTPLUG_CPU=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+CONFIG_VMALLOC_RESERVE=0x19000000
+CONFIG_USE_OF=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_SUSPEND is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+CONFIG_IPV6=y
+CONFIG_IPV6_PRIVACY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_NETFILTER=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_EVBUG=m
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=m
+CONFIG_SERIAL_MSM_HSL=y
+CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+CONFIG_DIAG_CHAR=y
+CONFIG_HW_RANDOM=y
+CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB=y
+CONFIG_MSM_QPNP_INT=y
+CONFIG_SLIMBUS=y
+CONFIG_SLIMBUS_MSM_NGD=y
+CONFIG_DEBUG_GPIO=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_I2C=y
+CONFIG_WCD9306_CODEC=y
+# CONFIG_HWMON is not set
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_STUB=y
+CONFIG_ION=y
+CONFIG_ION_MSM=y
+CONFIG_FB=y
+CONFIG_FB_VIRTUAL=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+CONFIG_USB_GADGET_DEBUG_FS=y
+CONFIG_USB_CI13XXX_MSM=y
+CONFIG_USB_G_ANDROID=y
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_TEST=m
+CONFIG_MMC_MSM=y
+CONFIG_STAGING=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_RAM_CONSOLE=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_MSM_IOMMU=y
+CONFIG_SPS=y
+CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_MMC_MSM_SPS_SUPPORT=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT4_FS=y
+CONFIG_FUSE_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_FS=y
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_USER=y
+CONFIG_KEYS=y
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_ARC4=y
+CONFIG_CRYPTO_TWOFISH=y
+# CONFIG_CRYPTO_HW is not set
+CONFIG_CRC_CCITT=y
+CONFIG_LIBCRC32C=y
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index f464a55..53c97ce 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -253,7 +253,6 @@
CONFIG_PMIC8XXX_VIBRATOR=y
CONFIG_QSEECOM=y
CONFIG_USB_HSIC_SMSC_HUB=y
-CONFIG_SCSI=y
CONFIG_SCSI_TGT=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -262,6 +261,8 @@
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI_MSM=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
CONFIG_DM_CRYPT=y
@@ -370,6 +371,8 @@
CONFIG_MSM_CSI20_HEADER=y
CONFIG_S5K3L1YX=y
CONFIG_IMX091=y
+CONFIG_MSM_CSIPHY=y
+CONFIG_MSM_CSID=y
CONFIG_MSM_WFD=y
CONFIG_RADIO_IRIS=y
CONFIG_RADIO_IRIS_TRANSPORT=m
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index e54459d..2d97769 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -258,7 +258,6 @@
CONFIG_PMIC8XXX_VIBRATOR=y
CONFIG_QSEECOM=y
CONFIG_USB_HSIC_SMSC_HUB=y
-CONFIG_SCSI=y
CONFIG_SCSI_TGT=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -267,6 +266,8 @@
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI_MSM=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
CONFIG_DM_CRYPT=y
@@ -374,6 +375,8 @@
CONFIG_MSM_CSI20_HEADER=y
CONFIG_S5K3L1YX=y
CONFIG_IMX091=y
+CONFIG_MSM_CSIPHY=y
+CONFIG_MSM_CSID=y
CONFIG_MSM_WFD=y
CONFIG_RADIO_IRIS=y
CONFIG_RADIO_IRIS_TRANSPORT=m
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index 71b6990..be2e483 100644
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -304,12 +304,9 @@
CONFIG_VIDEOBUF2_MSM_MEM=y
CONFIG_USB_VIDEO_CLASS=y
CONFIG_V4L_PLATFORM_DRIVERS=y
-CONFIG_MSM_CAMERA_V4L2=y
-CONFIG_MT9M114=y
-CONFIG_MSM_CAMERA_LED_TRIGGER_FLASH=y
-CONFIG_OV2720=y
+CONFIG_MSM_CAMERA=n
+CONFIG_MSMB_CAMERA=y
CONFIG_MSM_CAMERA_SENSOR=y
-CONFIG_MSM_ACTUATOR=y
CONFIG_MSM_JPEG=y
CONFIG_MSM_CCI=y
CONFIG_MSM_CSI30_HEADER=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index 1479954..c882db5 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -74,6 +74,8 @@
CONFIG_MSM_L1_RECOV_ERR_PANIC=y
CONFIG_MSM_L1_ERR_LOG=y
CONFIG_MSM_L2_ERP_PRINT_ACCESS_ERRORS=y
+CONFIG_MSM_L2_ERP_PORT_PANIC=y
+CONFIG_MSM_L2_ERP_1BIT_PANIC=y
CONFIG_MSM_L2_ERP_2BIT_PANIC=y
CONFIG_MSM_CACHE_DUMP=y
CONFIG_MSM_CACHE_DUMP_ON_PANIC=y
@@ -305,12 +307,9 @@
CONFIG_VIDEOBUF2_MSM_MEM=y
CONFIG_USB_VIDEO_CLASS=y
CONFIG_V4L_PLATFORM_DRIVERS=y
-CONFIG_MSM_CAMERA_V4L2=y
-CONFIG_MT9M114=y
-CONFIG_MSM_CAMERA_LED_TRIGGER_FLASH=y
-CONFIG_OV2720=y
+CONFIG_MSM_CAMERA=n
+CONFIG_MSMB_CAMERA=y
CONFIG_MSM_CAMERA_SENSOR=y
-CONFIG_MSM_ACTUATOR=y
CONFIG_MSM_JPEG=y
CONFIG_MSM_CCI=y
CONFIG_MSM_CSI30_HEADER=y
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 83a5fb0..3f22221 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -288,6 +288,7 @@
select QMI_ENCDEC
select DONT_MAP_HOLE_AFTER_MEMBANK0
select SENSORS_ADSP
+ select MSM_ULTRASOUND_B
config ARCH_MPQ8092
bool "MPQ8092"
@@ -2426,6 +2427,16 @@
HW and services, calculating input events
upon the ultrasound data.
+config MSM_ULTRASOUND_B
+ bool "QDSP6V2 HW Ultrasound support"
+ help
+ Enable HW Ultrasound support in QDSP6V2.
+ QDSP6V2 can support HW encoder & decoder and
+ ultrasound processing. It will enable
+ ultrasound data paths between
+ HW and services, calculating input events
+ upon the ultrasound data.
+
config MSM_RPC_VIBRATOR
bool "RPC based MSM Vibrator Support"
depends on MSM_ONCRPCROUTER
diff --git a/arch/arm/mach-msm/board-msm7627a-bt.c b/arch/arm/mach-msm/board-msm7627a-bt.c
index 5f29adb..3e90a15 100644
--- a/arch/arm/mach-msm/board-msm7627a-bt.c
+++ b/arch/arm/mach-msm/board-msm7627a-bt.c
@@ -485,7 +485,7 @@
const struct bahama_config_register *p;
- u8 version;
+ int version;
const struct bahama_config_register v10_bt_on[] = {
{ 0xE9, 0x00, 0xFF },
@@ -567,7 +567,7 @@
u8 offset = 0; /* index into bahama configs */
on = on ? 1 : 0;
version = marimba_read_bahama_ver(&config);
- if ((int)version < 0 || version == BAHAMA_VER_UNSUPPORTED) {
+ if (version < 0 || version == BAHAMA_VER_UNSUPPORTED) {
dev_err(&msm_bt_power_device.dev, "%s : Bahama "
"version read Error, version = %d\n",
__func__, version);
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index 389ac62..66c01d6 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -5182,18 +5182,24 @@
CLK_LOOKUP("cci_src_clk", cci_clk_src.c, "fda0c000.qcom,cci"),
CLK_LOOKUP("cci_clk", camss_cci_cci_clk.c, "fda0c000.qcom,cci"),
/* CSIPHY clocks */
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda0ac00.qcom,csiphy"),
CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
"fda0ac00.qcom,csiphy"),
CLK_LOOKUP("csiphy_timer_src_clk", csi0phytimer_clk_src.c,
"fda0ac00.qcom,csiphy"),
CLK_LOOKUP("csiphy_timer_clk", camss_phy0_csi0phytimer_clk.c,
"fda0ac00.qcom,csiphy"),
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda0b000.qcom,csiphy"),
CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
"fda0b000.qcom,csiphy"),
CLK_LOOKUP("csiphy_timer_src_clk", csi1phytimer_clk_src.c,
"fda0b000.qcom,csiphy"),
CLK_LOOKUP("csiphy_timer_clk", camss_phy1_csi1phytimer_clk.c,
"fda0b000.qcom,csiphy"),
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda0b400.qcom,csiphy"),
CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
"fda0b400.qcom,csiphy"),
CLK_LOOKUP("csiphy_timer_src_clk", csi2phytimer_clk_src.c,
@@ -5201,6 +5207,10 @@
CLK_LOOKUP("csiphy_timer_clk", camss_phy2_csi2phytimer_clk.c,
"fda0b400.qcom,csiphy"),
/* CSID clocks */
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda08000.qcom,csid"),
+ CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
+ "fda08000.qcom,csid"),
CLK_LOOKUP("csi0_ahb_clk", camss_csi0_ahb_clk.c, "fda08000.qcom,csid"),
CLK_LOOKUP("csi0_src_clk", csi0_clk_src.c, "fda08000.qcom,csid"),
CLK_LOOKUP("csi0_phy_clk", camss_csi0phy_clk.c, "fda08000.qcom,csid"),
@@ -5208,6 +5218,10 @@
CLK_LOOKUP("csi0_pix_clk", camss_csi0pix_clk.c, "fda08000.qcom,csid"),
CLK_LOOKUP("csi0_rdi_clk", camss_csi0rdi_clk.c, "fda08000.qcom,csid"),
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda08400.qcom,csid"),
+ CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
+ "fda08400.qcom,csid"),
CLK_LOOKUP("csi0_ahb_clk", camss_csi0_ahb_clk.c, "fda08400.qcom,csid"),
CLK_LOOKUP("csi1_ahb_clk", camss_csi1_ahb_clk.c, "fda08400.qcom,csid"),
CLK_LOOKUP("csi0_src_clk", csi0_clk_src.c, "fda08400.qcom,csid"),
@@ -5221,6 +5235,10 @@
CLK_LOOKUP("csi0_rdi_clk", camss_csi0rdi_clk.c, "fda08400.qcom,csid"),
CLK_LOOKUP("csi1_rdi_clk", camss_csi1rdi_clk.c, "fda08400.qcom,csid"),
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda08800.qcom,csid"),
+ CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
+ "fda08800.qcom,csid"),
CLK_LOOKUP("csi0_ahb_clk", camss_csi0_ahb_clk.c, "fda08800.qcom,csid"),
CLK_LOOKUP("csi2_ahb_clk", camss_csi2_ahb_clk.c, "fda08800.qcom,csid"),
CLK_LOOKUP("csi0_src_clk", csi0_clk_src.c, "fda08800.qcom,csid"),
@@ -5234,6 +5252,10 @@
CLK_LOOKUP("csi0_rdi_clk", camss_csi0rdi_clk.c, "fda08800.qcom,csid"),
CLK_LOOKUP("csi2_rdi_clk", camss_csi2rdi_clk.c, "fda08800.qcom,csid"),
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda08c00.qcom,csid"),
+ CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
+ "fda08c00.qcom,csid"),
CLK_LOOKUP("csi0_ahb_clk", camss_csi0_ahb_clk.c, "fda08c00.qcom,csid"),
CLK_LOOKUP("csi3_ahb_clk", camss_csi3_ahb_clk.c, "fda08c00.qcom,csid"),
CLK_LOOKUP("csi0_src_clk", csi0_clk_src.c, "fda08c00.qcom,csid"),
@@ -5247,6 +5269,16 @@
CLK_LOOKUP("csi0_rdi_clk", camss_csi0rdi_clk.c, "fda08c00.qcom,csid"),
CLK_LOOKUP("csi3_rdi_clk", camss_csi3rdi_clk.c, "fda08c00.qcom,csid"),
+ /* ISPIF clocks */
+ CLK_LOOKUP("camss_vfe_vfe_clk", camss_vfe_vfe0_clk.c,
+ "fda0a000.qcom,ispif"),
+ CLK_LOOKUP("camss_csi_vfe_clk", camss_csi_vfe0_clk.c,
+ "fda0a000.qcom,ispif"),
+ CLK_LOOKUP("camss_vfe_vfe_clk1", camss_vfe_vfe1_clk.c,
+ "fda0a000.qcom,ispif"),
+ CLK_LOOKUP("camss_csi_vfe_clk1", camss_csi_vfe1_clk.c,
+ "fda0a000.qcom,ispif"),
+
/*VFE clocks*/
CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
"fda10000.qcom,vfe"),
diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h
index 8b5c70f..0a8afa9 100644
--- a/arch/arm/mach-msm/include/mach/board.h
+++ b/arch/arm/mach-msm/include/mach/board.h
@@ -1,7 +1,7 @@
/* arch/arm/mach-msm/include/mach/board.h
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
* Author: Brian Swetland <swetland@google.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -182,9 +182,8 @@
uint32_t delay;
};
-struct msm_camera_csi_lane_params {
- uint16_t csi_lane_assign;
- uint16_t csi_lane_mask;
+struct msm_camera_gpio_num_info {
+ uint16_t gpio_num[2];
};
struct msm_camera_gpio_conf {
@@ -201,6 +200,7 @@
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;
};
enum msm_camera_i2c_mux_mode {
@@ -215,13 +215,6 @@
enum msm_camera_i2c_mux_mode i2c_mux_mode;
};
-enum msm_camera_vreg_name_t {
- CAM_VDIG,
- CAM_VIO,
- CAM_VANA,
- CAM_VAF,
-};
-
struct msm_camera_sensor_platform_info {
int mount_angle;
int sensor_reset;
diff --git a/arch/arm/mach-msm/include/mach/camera2.h b/arch/arm/mach-msm/include/mach/camera2.h
new file mode 100644
index 0000000..e624131
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/camera2.h
@@ -0,0 +1,91 @@
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 __CAMERA2_H__
+#define __CAMERA2_H__
+
+#include <media/msm_cam_sensor.h>
+#include <mach/board.h>
+
+enum msm_sensor_device_type_t {
+ MSM_SENSOR_I2C_DEVICE,
+ MSM_SENSOR_PLATFORM_DEVICE,
+};
+
+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
+};
+
+struct msm_camera_slave_info {
+ uint16_t sensor_slave_addr;
+ uint16_t sensor_id_reg_addr;
+ uint16_t sensor_id;
+};
+
+struct msm_cam_clk_info {
+ const char *clk_name;
+ long clk_rate;
+ uint32_t delay;
+};
+
+struct msm_cam_clk_setting {
+ struct msm_cam_clk_info *clk_info;
+ uint16_t num_clk_info;
+ uint8_t enable;
+};
+
+struct v4l2_subdev_info {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+ uint16_t fmt;
+ uint16_t order;
+};
+
+struct msm_camera_sensor_board_info {
+ const char *sensor_name;
+ struct msm_camera_slave_info *slave_info;
+ struct msm_camera_csi_lane_params *csi_lane_params;
+ struct camera_vreg_t *cam_vreg;
+ int num_vreg;
+ struct msm_camera_sensor_strobe_flash_data *strobe_flash_data;
+ struct msm_camera_gpio_conf *gpio_conf;
+ struct msm_actuator_info *actuator_info;
+ struct msm_camera_i2c_conf *i2c_conf;
+ struct msm_sensor_info_t *sensor_info;
+ struct msm_sensor_init_params *sensor_init_params;
+};
+
+enum msm_camera_i2c_cmd_type {
+ MSM_CAMERA_I2C_CMD_WRITE,
+ MSM_CAMERA_I2C_CMD_POLL,
+};
+
+struct msm_camera_i2c_reg_conf {
+ uint16_t reg_addr;
+ uint16_t reg_data;
+ enum msm_camera_i2c_data_type dt;
+ enum msm_camera_i2c_cmd_type cmd_type;
+ int16_t mask;
+};
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/iommu_perfmon.h b/arch/arm/mach-msm/include/mach/iommu_perfmon.h
new file mode 100644
index 0000000..b44523f
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/iommu_perfmon.h
@@ -0,0 +1,177 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+
+#ifndef MSM_IOMMU_PERFMON_H
+#define MSM_IOMMU_PERFMON_H
+
+
+/**
+ * struct iommu_access_ops - Callbacks for accessing IOMMU
+ * @iommu_power_on: Turn on clocks/power to unit
+ * @iommu_power_off: Turn off clocks/power to unit
+ * @iommu_lock_acquire: Acquire any locks needed
+ * @iommu_lock_release: Release locks needed
+ */
+struct iommu_access_ops {
+ int (*iommu_power_on)(void *);
+ int (*iommu_power_off)(void *);
+ void (*iommu_lock_acquire)(void);
+ void (*iommu_lock_release)(void);
+};
+
+/**
+ * struct iommu_pmon_counter - container for a performance counter.
+ * @counter_no: counter number within the group
+ * @absolute_counter_no: counter number within IOMMU PMU
+ * @value: cached counter value
+ * @overflow_count: no of times counter has overflowed
+ * @enabled: indicates whether counter is enabled or not
+ * @current_event_class: current selected event class, -1 if none
+ * @counter_dir: debugfs directory for this counter
+ * @cnt_group: group this counter belongs to
+ */
+struct iommu_pmon_counter {
+ unsigned int counter_no;
+ unsigned int absolute_counter_no;
+ unsigned long value;
+ unsigned long overflow_count;
+ unsigned int enabled;
+ int current_event_class;
+ struct dentry *counter_dir;
+ struct iommu_pmon_cnt_group *cnt_group;
+};
+
+/**
+ * struct iommu_pmon_cnt_group - container for a perf mon counter group.
+ * @grp_no: group number
+ * @num_counters: number of counters in this group
+ * @counters: list of counter in this group
+ * @group_dir: debugfs directory for this group
+ * @pmon: pointer to the iommu_pmon object this group belongs to
+ */
+struct iommu_pmon_cnt_group {
+ unsigned int grp_no;
+ unsigned int num_counters;
+ struct iommu_pmon_counter *counters;
+ struct dentry *group_dir;
+ struct iommu_pmon *pmon;
+};
+
+/**
+ * struct iommu_info - container for a perf mon iommu info.
+ * @iommu_name: name of the iommu from device tree
+ * @base: virtual base address for this iommu
+ * @evt_irq: irq number for event overflow interrupt
+ * @iommu_dev: pointer to iommu device
+ * @ops: iommu access operations pointer.
+ */
+struct iommu_info {
+ const char *iommu_name;
+ void *base;
+ int evt_irq;
+ struct device *iommu_dev;
+ struct iommu_access_ops *ops;
+};
+
+/**
+ * struct iommu_pmon - main container for a perf mon data.
+ * @iommu_dir: debugfs directory for this iommu
+ * @iommu: iommu_info instance
+ * @iommu_list: iommu_list head
+ * @cnt_grp: list of counter groups
+ * @num_groups: number of counter groups
+ * @event_cls_supp_value: event classes supported for this PMU
+ * @enabled: Indicates whether perf. mon is enabled or not
+ * @iommu_attached Indicates whether iommu is attached or not.
+ * @lock: mutex used to synchronize access to shared data
+ */
+struct iommu_pmon {
+ struct dentry *iommu_dir;
+ struct iommu_info iommu;
+ struct list_head iommu_list;
+ struct iommu_pmon_cnt_group *cnt_grp;
+ unsigned int num_groups;
+ unsigned int event_cls_supp_value;
+ unsigned int enabled;
+ unsigned int iommu_attach_count;
+ struct mutex lock;
+};
+
+extern struct iommu_access_ops iommu_access_ops;
+
+#ifdef CONFIG_MSM_IOMMU_PMON
+/**
+ * Allocate memory for performance monitor structure. Must
+ * be called befre iommu_pm_iommu_register
+ */
+struct iommu_info *msm_iommu_pm_alloc(struct device *iommu_dev);
+
+/**
+ * Free memory previously allocated with iommu_pm_alloc
+ */
+void msm_iommu_pm_free(struct device *iommu_dev);
+
+/**
+ * Register iommu with the performance monitor module.
+ */
+int msm_iommu_pm_iommu_register(struct iommu_info *info);
+
+/**
+ * Unregister iommu with the performance monitor module.
+ */
+void msm_iommu_pm_iommu_unregister(struct device *dev);
+
+/**
+ * Called by iommu driver when attaching is complete
+ * Must NOT be called with IOMMU mutexes held.
+ * @param iommu_dev IOMMU device that is attached
+ */
+void msm_iommu_attached(struct device *dev);
+
+/**
+ * Called by iommu driver before detaching.
+ * Must NOT be called with IOMMU mutexes held.
+ * @param iommu_dev IOMMU device that is going to be detached
+ */
+void msm_iommu_detached(struct device *dev);
+#else
+static inline struct iommu_info *msm_iommu_pm_alloc(struct device *iommu_dev)
+{
+ return NULL;
+}
+
+static inline void msm_iommu_pm_free(struct device *iommu_dev)
+{
+ return;
+}
+
+static inline int msm_iommu_pm_iommu_register(struct iommu_info *info)
+{
+ return -EIO;
+}
+
+static inline void msm_iommu_pm_iommu_unregister(struct device *dev)
+{
+}
+
+static inline void msm_iommu_attached(struct device *dev)
+{
+}
+
+static inline void msm_iommu_detached(struct device *dev)
+{
+}
+#endif
+#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_serial_hs.h b/arch/arm/mach-msm/include/mach/msm_serial_hs.h
index d2905d4..cc50955 100644
--- a/arch/arm/mach-msm/include/mach/msm_serial_hs.h
+++ b/arch/arm/mach-msm/include/mach/msm_serial_hs.h
@@ -17,15 +17,34 @@
#include<linux/serial_core.h>
-/* Optional platform device data for msm_serial_hs driver.
- * Used to configure low power wakeup */
+/**
+ * struct msm_serial_hs_platform_data - platform device data
+ * for msm hsuart device
+ * @wakeup_irq : IRQ line to be configured as Wakeup source.
+ * @inject_rx_on_wakeup : Set 1 if specific character to be inserted on wakeup
+ * @rx_to_inject : Character to be inserted on wakeup
+ * @gpio_config : Configure gpios that are used for uart communication
+ * @userid : User-defined number to be used to enumerate device as tty<userid>
+ * @uart_tx_gpio: GPIO number for UART Tx Line.
+ * @uart_rx_gpio: GPIO number for UART Rx Line.
+ * @uart_cts_gpio: GPIO number for UART CTS Line.
+ * @uart_rfr_gpio: GPIO number for UART RFR Line.
+ * @bam_tx_ep_pipe_index : BAM TX Endpoint Pipe Index for HSUART
+ * @bam_tx_ep_pipe_index : BAM RX Endpoint Pipe Index for HSUART
+ */
struct msm_serial_hs_platform_data {
int wakeup_irq; /* wakeup irq */
- /* bool: inject char into rx tty on wakeup */
unsigned char inject_rx_on_wakeup;
char rx_to_inject;
int (*gpio_config)(int);
int userid;
+
+ unsigned uart_tx_gpio;
+ unsigned uart_rx_gpio;
+ unsigned uart_cts_gpio;
+ unsigned uart_rfr_gpio;
+ unsigned bam_tx_ep_pipe_index;
+ unsigned bam_rx_ep_pipe_index;
};
unsigned int msm_hs_tx_empty(struct uart_port *uport);
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_b.h b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_b.h
new file mode 100644
index 0000000..11de6ef
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_b.h
@@ -0,0 +1,70 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __APR_US_B_H__
+#define __APR_US_B_H__
+
+#include "apr_us.h"
+
+/* ======================================================================= */
+/* Session Level commands */
+#define USM_CMD_SHARED_MEM_MAP_REGION 0x00012728
+struct usm_cmd_memory_map_region {
+ struct apr_hdr hdr;
+ u16 mempool_id;
+ u16 num_regions;
+ u32 flags;
+ u32 shm_addr_lsw;
+ u32 shm_addr_msw;
+ u32 mem_size_bytes;
+} __packed;
+
+#define USM_CMDRSP_SHARED_MEM_MAP_REGION 0x00012729
+struct usm_cmdrsp_memory_map_region {
+ u32 mem_map_handle;
+} __packed;
+
+#define USM_CMD_SHARED_MEM_UNMAP_REGION 0x0001272A
+struct usm_cmd_memory_unmap_region {
+ struct apr_hdr hdr;
+ u32 mem_map_handle;
+} __packed;
+
+#define USM_DATA_CMD_READ 0x00012724
+struct usm_stream_cmd_read {
+ struct apr_hdr hdr;
+ u32 buf_addr_lsw;
+ u32 buf_addr_msw;
+ u32 mem_map_handle;
+ u32 buf_size;
+ u32 seq_id;
+ u32 counter;
+} __packed;
+
+#define USM_DATA_EVENT_READ_DONE 0x00012725
+
+#define USM_DATA_CMD_WRITE 0x00012726
+struct usm_stream_cmd_write {
+ struct apr_hdr hdr;
+ u32 buf_addr_lsw;
+ u32 buf_addr_msw;
+ u32 mem_map_handle;
+ u32 buf_size;
+ u32 seq_id;
+ u32 res0;
+ u32 res1;
+ u32 res2;
+} __packed;
+
+#define USM_DATA_EVENT_WRITE_DONE 0x00012727
+
+#endif /* __APR_US_B_H__ */
diff --git a/arch/arm/mach-msm/ipc_router.c b/arch/arm/mach-msm/ipc_router.c
index fde43b0..cdacd87 100644
--- a/arch/arm/mach-msm/ipc_router.c
+++ b/arch/arm/mach-msm/ipc_router.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -2288,13 +2288,19 @@
RR("x REMOVE_SERVER Name=%d:%08x Id=%d:%08x\n",
msg.srv.service, msg.srv.instance,
msg.srv.node_id, msg.srv.port_id);
- } else if (port_ptr->type == CLIENT_PORT) {
- msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT;
- msg.cli.node_id = port_ptr->this_port.node_id;
- msg.cli.port_id = port_ptr->this_port.port_id;
- RR("x REMOVE_CLIENT id=%d:%08x\n",
- msg.cli.node_id, msg.cli.port_id);
+ broadcast_ctl_msg(&msg);
+ broadcast_ctl_msg_locally(&msg);
}
+
+ /*
+ * Server port could have been a client port earlier.
+ * Send REMOVE_CLIENT message in either case.
+ */
+ msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT;
+ msg.cli.node_id = port_ptr->this_port.node_id;
+ msg.cli.port_id = port_ptr->this_port.port_id;
+ RR("x REMOVE_CLIENT id=%d:%08x\n",
+ msg.cli.node_id, msg.cli.port_id);
broadcast_ctl_msg(&msg);
broadcast_ctl_msg_locally(&msg);
} else if (port_ptr->type == CONTROL_PORT) {
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c
index dbfa5ec..7b67157 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -539,7 +539,6 @@
static int qports_venus_p1[] = {5};
static int qports_vfe[] = {6};
static int qports_gemini_ocmem[] = {0};
-static int qports_mdp_ocmem[] = {1};
static int qports_venus_p0_ocmem[] = {2};
static int qports_venus_p1_ocmem[] = {3};
static int qports_vfe_ocmem[] = {4};
@@ -1154,7 +1153,6 @@
.num_tiers = ARRAY_SIZE(tier2),
.perm_mode = NOC_QOS_PERM_MODE_FIXED,
.mode = NOC_QOS_MODE_FIXED,
- .qport = qports_mdp_ocmem,
.mas_hw_id = MAS_MDP_OCMEM,
.hw_sel = MSM_BUS_NOC,
},
diff --git a/arch/arm/mach-msm/qdsp6v2/Makefile b/arch/arm/mach-msm/qdsp6v2/Makefile
index 08a6de6..323532c 100644
--- a/arch/arm/mach-msm/qdsp6v2/Makefile
+++ b/arch/arm/mach-msm/qdsp6v2/Makefile
@@ -27,3 +27,4 @@
obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_mp3.o audio_amrnb.o audio_amrwb.o audio_evrc.o audio_qcelp.o amrwb_in.o
obj-$(CONFIG_MSM_ADSP_LOADER) += adsp-loader.o
obj-$(CONFIG_MSM_ULTRASOUND_A) += ultrasound/version_a/
+obj-$(CONFIG_MSM_ULTRASOUND_B) += ultrasound/version_b/
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h b/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h
index c68ad68..bf47366 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -62,7 +62,7 @@
struct mutex lock;
spinlock_t dsp_lock;
/* extended parameters, related to q6 variants */
- void *ext;
+ void *ext;
};
struct us_client {
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/Makefile b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/Makefile
new file mode 100644
index 0000000..23de73f
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/Makefile
@@ -0,0 +1,2 @@
+obj-y += q6usm_b.o ../usf.o ../usfcdev.o
+ccflags-y := -I$(src)/.. -I$(src)/../..
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c
new file mode 100644
index 0000000..11b1405
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c
@@ -0,0 +1,1208 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/msm_audio.h>
+#include <sound/apr_audio.h>
+#include <mach/qdsp6v2/apr_us_b.h>
+#include "q6usm.h"
+
+#define ADSP_MEMORY_MAP_SHMEM8_4K_POOL 3
+
+#define MEM_4K_OFFSET 4095
+#define MEM_4K_MASK 0xfffff000
+
+#define SESSION_MAX 0x02 /* aDSP:USM limit */
+
+#define READDONE_IDX_STATUS 0
+
+#define WRITEDONE_IDX_STATUS 0
+
+/* Standard timeout in the asynchronous ops */
+#define Q6USM_TIMEOUT_JIFFIES (1*HZ) /* 1 sec */
+
+static DEFINE_MUTEX(session_lock);
+
+static struct us_client *session[SESSION_MAX];
+static int32_t q6usm_mmapcallback(struct apr_client_data *data, void *priv);
+static int32_t q6usm_callback(struct apr_client_data *data, void *priv);
+static void q6usm_add_hdr(struct us_client *usc, struct apr_hdr *hdr,
+ uint32_t pkt_size, bool cmd_flg);
+
+struct usm_mmap {
+ atomic_t ref_cnt;
+ atomic_t cmd_state;
+ wait_queue_head_t cmd_wait;
+ void *apr;
+ int mem_handle;
+};
+
+static struct usm_mmap this_mmap;
+
+static void q6usm_add_mmaphdr(struct us_client *usc, struct apr_hdr *hdr,
+ uint32_t pkt_size, bool cmd_flg, u32 token)
+{
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ hdr->src_port = 0;
+ hdr->dest_port = 0;
+ if (cmd_flg) {
+ hdr->token = token;
+ atomic_set(&this_mmap.cmd_state, 1);
+ }
+ hdr->pkt_size = pkt_size;
+ return;
+}
+
+static int q6usm_memory_map(struct us_client *usc, uint32_t buf_add, int dir,
+ uint32_t bufsz, uint32_t bufcnt)
+{
+ struct usm_cmd_memory_map_region mem_region_map;
+ int rc = 0;
+
+ if ((usc == NULL) || (usc->apr == NULL) || (this_mmap.apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6usm_add_mmaphdr(usc, &mem_region_map.hdr,
+ sizeof(struct usm_cmd_memory_map_region), true,
+ ((usc->session << 8) | dir));
+
+ mem_region_map.hdr.opcode = USM_CMD_SHARED_MEM_MAP_REGION;
+ mem_region_map.mempool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+
+ mem_region_map.num_regions = 1;
+ mem_region_map.flags = 0;
+
+ mem_region_map.shm_addr_lsw = buf_add;
+ mem_region_map.shm_addr_msw = 0;
+ mem_region_map.mem_size_bytes = bufsz * bufcnt;
+
+ rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_region_map);
+ if (rc < 0) {
+ pr_err("%s: mem_map op[0x%x]rc[%d]\n",
+ __func__, mem_region_map.hdr.opcode, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(this_mmap.cmd_wait,
+ (atomic_read(&this_mmap.cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: timeout. waited for memory_map\n", __func__);
+ } else {
+ struct us_port_data *port = &usc->port[dir];
+
+ *((uint32_t *)(port->ext)) = this_mmap.mem_handle;
+ rc = 0;
+ }
+fail_cmd:
+ return rc;
+}
+
+int q6usm_memory_unmap(struct us_client *usc, uint32_t buf_add, int dir)
+{
+ struct usm_cmd_memory_unmap_region mem_unmap;
+ struct us_port_data *port = &usc->port[dir];
+ int rc = 0;
+
+ if ((usc == NULL) || (usc->apr == NULL) || (this_mmap.apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ port = &usc->port[dir];
+ q6usm_add_mmaphdr(usc, &mem_unmap.hdr,
+ sizeof(struct usm_cmd_memory_unmap_region), true,
+ ((usc->session << 8) | dir));
+ mem_unmap.hdr.opcode = USM_CMD_SHARED_MEM_UNMAP_REGION;
+ mem_unmap.mem_map_handle = *((uint32_t *)(port->ext));
+
+ rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_unmap);
+ if (rc < 0) {
+ pr_err("%s: mem_unmap op[0x%x] rc[%d]\n",
+ __func__, mem_unmap.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(this_mmap.cmd_wait,
+ (atomic_read(&this_mmap.cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: timeout. waited for memory_unmap\n", __func__);
+ } else
+ rc = 0;
+fail_cmd:
+ return rc;
+}
+
+static int q6usm_session_alloc(struct us_client *usc)
+{
+ int ind = 0;
+
+ mutex_lock(&session_lock);
+ for (ind = 0; ind < SESSION_MAX; ++ind) {
+ if (!session[ind]) {
+ session[ind] = usc;
+ mutex_unlock(&session_lock);
+ ++ind; /* session id: 0 reserved */
+ pr_debug("%s: session[%d] was allocated\n",
+ __func__, ind);
+ return ind;
+ }
+ }
+ mutex_unlock(&session_lock);
+ return -ENOMEM;
+}
+
+static void q6usm_session_free(struct us_client *usc)
+{
+ /* Session index was incremented during allocation */
+ uint16_t ind = (uint16_t)usc->session - 1;
+
+ pr_debug("%s: to free session[%d]\n", __func__, ind);
+ if (ind < SESSION_MAX) {
+ mutex_lock(&session_lock);
+ session[ind] = 0;
+ mutex_unlock(&session_lock);
+ }
+}
+
+int q6usm_us_client_buf_free(unsigned int dir,
+ struct us_client *usc)
+{
+ struct us_port_data *port;
+ int rc = 0;
+ uint32_t size = 0;
+
+ if ((usc == NULL) ||
+ ((dir != IN) && (dir != OUT)))
+ return -EINVAL;
+
+ mutex_lock(&usc->cmd_lock);
+ port = &usc->port[dir];
+ if (port == NULL) {
+ mutex_unlock(&usc->cmd_lock);
+ return -EINVAL;
+ }
+
+ if (port->data == NULL) {
+ mutex_unlock(&usc->cmd_lock);
+ return 0;
+ }
+
+ rc = q6usm_memory_unmap(usc, port->phys, dir);
+ pr_debug("%s: data[%p]phys[%p][%p]\n", __func__,
+ (void *)port->data, (void *)port->phys, (void *)&port->phys);
+ /* 4K boundary is required by the API with QDSP6 */
+ size = (port->buf_size * port->buf_cnt + MEM_4K_OFFSET) & MEM_4K_MASK;
+ dma_free_coherent(NULL, size, port->data, port->phys);
+ port->data = NULL;
+ port->phys = 0;
+ port->buf_size = 0;
+ port->buf_cnt = 0;
+
+ mutex_unlock(&usc->cmd_lock);
+ return rc;
+}
+
+void q6usm_us_client_free(struct us_client *usc)
+{
+ int loopcnt = 0;
+ struct us_port_data *port;
+ uint32_t *p_mem_handle = NULL;
+
+ if ((usc == NULL) ||
+ !(usc->session))
+ return;
+
+ for (loopcnt = 0; loopcnt <= OUT; ++loopcnt) {
+ port = &usc->port[loopcnt];
+ if (port->data == NULL)
+ continue;
+ pr_debug("%s: loopcnt = %d\n", __func__, loopcnt);
+ q6usm_us_client_buf_free(loopcnt, usc);
+ }
+ q6usm_session_free(usc);
+ apr_deregister(usc->apr);
+
+ pr_debug("%s: APR De-Register\n", __func__);
+
+ if (atomic_read(&this_mmap.ref_cnt) <= 0) {
+ pr_err("%s: APR Common Port Already Closed\n", __func__);
+ goto done;
+ }
+
+ atomic_dec(&this_mmap.ref_cnt);
+ if (atomic_read(&this_mmap.ref_cnt) == 0) {
+ apr_deregister(this_mmap.apr);
+ pr_debug("%s: APR De-Register common port\n", __func__);
+ }
+
+done:
+ p_mem_handle = (uint32_t *)usc->port[IN].ext;
+ kfree(p_mem_handle);
+ kfree(usc);
+ pr_debug("%s:\n", __func__);
+ return;
+}
+
+struct us_client *q6usm_us_client_alloc(
+ void (*cb)(uint32_t, uint32_t, uint32_t *, void *),
+ void *priv)
+{
+ struct us_client *usc;
+ uint32_t *p_mem_handle = NULL;
+ int n;
+ int lcnt = 0;
+
+ usc = kzalloc(sizeof(struct us_client), GFP_KERNEL);
+ if (usc == NULL) {
+ pr_err("%s: us_client allocation failed\n", __func__);
+ return NULL;
+ }
+ p_mem_handle = kzalloc(sizeof(uint32_t) * 2, GFP_KERNEL);
+ if (p_mem_handle == NULL) {
+ pr_err("%s: p_mem_handle allocation failed\n", __func__);
+ kfree(usc);
+ return NULL;
+ }
+
+ n = q6usm_session_alloc(usc);
+ if (n <= 0)
+ goto fail_session;
+ usc->session = n;
+ usc->cb = cb;
+ usc->priv = priv;
+ usc->apr = apr_register("ADSP", "USM", \
+ (apr_fn)q6usm_callback,\
+ ((usc->session) << 8 | 0x0001),\
+ usc);
+
+ if (usc->apr == NULL) {
+ pr_err("%s: Registration with APR failed\n", __func__);
+ goto fail;
+ }
+ pr_debug("%s: Registering the common port with APR\n", __func__);
+ if (atomic_read(&this_mmap.ref_cnt) == 0) {
+ this_mmap.apr = apr_register("ADSP", "USM",
+ (apr_fn)q6usm_mmapcallback,
+ 0x0FFFFFFFF, &this_mmap);
+ if (this_mmap.apr == NULL) {
+ pr_err("%s: USM port registration failed\n",
+ __func__);
+ goto fail;
+ }
+ }
+
+ atomic_inc(&this_mmap.ref_cnt);
+ init_waitqueue_head(&usc->cmd_wait);
+ mutex_init(&usc->cmd_lock);
+ for (lcnt = 0; lcnt <= OUT; ++lcnt) {
+ mutex_init(&usc->port[lcnt].lock);
+ spin_lock_init(&usc->port[lcnt].dsp_lock);
+ usc->port[lcnt].ext = (void *)p_mem_handle++;
+ pr_err("%s: usc->port[%d].ext=%p;\n",
+ __func__, lcnt, usc->port[lcnt].ext);
+ }
+ atomic_set(&usc->cmd_state, 0);
+
+ return usc;
+fail:
+ q6usm_us_client_free(usc);
+ return NULL;
+fail_session:
+ kfree(p_mem_handle);
+ kfree(usc);
+ return NULL;
+}
+
+int q6usm_us_client_buf_alloc(unsigned int dir,
+ struct us_client *usc,
+ unsigned int bufsz,
+ unsigned int bufcnt)
+{
+ int rc = 0;
+ struct us_port_data *port = NULL;
+ unsigned int size = bufsz*bufcnt;
+
+ if ((usc == NULL) ||
+ ((dir != IN) && (dir != OUT)) || (size == 0) ||
+ (usc->session <= 0 || usc->session > SESSION_MAX)) {
+ pr_err("%s: wrong parameters: size=%d; bufcnt=%d\n",
+ __func__, size, bufcnt);
+ return -EINVAL;
+ }
+
+ mutex_lock(&usc->cmd_lock);
+
+ port = &usc->port[dir];
+
+ port->data = dma_alloc_coherent(NULL, size, &(port->phys), GFP_KERNEL);
+ if (port->data == NULL) {
+ pr_err("%s: US region allocation failed\n", __func__);
+ mutex_unlock(&usc->cmd_lock);
+ return -ENOMEM;
+ }
+
+ port->buf_cnt = bufcnt;
+ port->buf_size = bufsz;
+ pr_debug("%s: data[%p]; phys[%p]; [%p]\n", __func__,
+ (void *)port->data,
+ (void *)port->phys,
+ (void *)&port->phys);
+
+ size = (size + MEM_4K_OFFSET) & MEM_4K_MASK;
+ rc = q6usm_memory_map(usc, port->phys, dir, size, 1);
+ if (rc < 0) {
+ pr_err("%s: CMD Memory_map failed\n", __func__);
+ mutex_unlock(&usc->cmd_lock);
+ q6usm_us_client_buf_free(dir, usc);
+ } else {
+ mutex_unlock(&usc->cmd_lock);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+static int32_t q6usm_mmapcallback(struct apr_client_data *data, void *priv)
+{
+ uint32_t token;
+ uint32_t *payload = data->payload;
+
+ pr_debug("%s: ptr0[0x%x]; ptr1[0x%x]; opcode[0x%x]\n",
+ __func__, payload[0], payload[1], data->opcode);
+ pr_debug("%s: token[0x%x]; payload_size[%d]; src[%d]; dest[%d];\n",
+ __func__, data->token, data->payload_size,
+ data->src_port, data->dest_port);
+
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ /* status field check */
+ if (payload[1]) {
+ pr_err("%s: wrong response[%d] on cmd [%d]\n",
+ __func__, payload[1], payload[0]);
+ } else {
+ token = data->token;
+ switch (payload[0]) {
+ case USM_CMD_SHARED_MEM_UNMAP_REGION:
+ if (atomic_read(&this_mmap.cmd_state)) {
+ atomic_set(&this_mmap.cmd_state, 0);
+ wake_up(&this_mmap.cmd_wait);
+ }
+ case USM_CMD_SHARED_MEM_MAP_REGION:
+ /* For MEM_MAP, additional answer is waited, */
+ /* therfore, no wake-up here */
+ pr_debug("%s: cmd[0x%x]; result[0x%x]\n",
+ __func__, payload[0], payload[1]);
+ break;
+ default:
+ pr_debug("%s: wrong command[0x%x]\n",
+ __func__, payload[0]);
+ break;
+ }
+ }
+ } else {
+ if (data->opcode == USM_CMDRSP_SHARED_MEM_MAP_REGION) {
+ this_mmap.mem_handle = payload[0];
+ pr_debug("%s: memory map handle = 0x%x",
+ __func__, payload[0]);
+ if (atomic_read(&this_mmap.cmd_state)) {
+ atomic_set(&this_mmap.cmd_state, 0);
+ wake_up(&this_mmap.cmd_wait);
+ }
+ }
+ }
+ return 0;
+}
+
+
+static int32_t q6usm_callback(struct apr_client_data *data, void *priv)
+{
+ struct us_client *usc = (struct us_client *)priv;
+ unsigned long dsp_flags;
+ uint32_t *payload = data->payload;
+ uint32_t token = data->token;
+ uint32_t opcode = Q6USM_EVENT_UNDEF;
+
+ if (usc == NULL) {
+ pr_err("%s: client info is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ /* status field check */
+ if (payload[1]) {
+ pr_err("%s: wrong response[%d] on cmd [%d]\n",
+ __func__, payload[1], payload[0]);
+ if (usc->cb)
+ usc->cb(data->opcode, token,
+ (uint32_t *)data->payload, usc->priv);
+ } else {
+ switch (payload[0]) {
+ case USM_SESSION_CMD_RUN:
+ case USM_STREAM_CMD_CLOSE:
+ if (token != usc->session) {
+ pr_err("%s: wrong token[%d]",
+ __func__, token);
+ break;
+ }
+ case USM_STREAM_CMD_OPEN_READ:
+ case USM_STREAM_CMD_OPEN_WRITE:
+ case USM_STREAM_CMD_SET_ENC_PARAM:
+ case USM_DATA_CMD_MEDIA_FORMAT_UPDATE:
+ case USM_SESSION_CMD_SIGNAL_DETECT_MODE:
+ if (atomic_read(&usc->cmd_state)) {
+ atomic_set(&usc->cmd_state, 0);
+ wake_up(&usc->cmd_wait);
+ }
+ if (usc->cb)
+ usc->cb(data->opcode, token,
+ (uint32_t *)data->payload,
+ usc->priv);
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+ }
+
+ switch (data->opcode) {
+ case USM_DATA_EVENT_READ_DONE: {
+ struct us_port_data *port = &usc->port[OUT];
+
+ opcode = Q6USM_EVENT_READ_DONE;
+ spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+ if (payload[READDONE_IDX_STATUS]) {
+ pr_err("%s: wrong READDONE[%d]; token[%d]\n",
+ __func__,
+ payload[READDONE_IDX_STATUS],
+ token);
+ token = USM_WRONG_TOKEN;
+ spin_unlock_irqrestore(&port->dsp_lock,
+ dsp_flags);
+ break;
+ }
+
+ if (port->expected_token != token) {
+ u32 cpu_buf = port->cpu_buf;
+ pr_err("%s: expected[%d] != token[%d]\n",
+ __func__, port->expected_token, token);
+ pr_debug("%s: dsp_buf=%d; cpu_buf=%d;\n",
+ __func__, port->dsp_buf, cpu_buf);
+
+ token = USM_WRONG_TOKEN;
+ /* To prevent data handle continiue */
+ port->expected_token = USM_WRONG_TOKEN;
+ spin_unlock_irqrestore(&port->dsp_lock,
+ dsp_flags);
+ break;
+ } /* port->expected_token != data->token */
+
+ port->expected_token = token + 1;
+ if (port->expected_token == port->buf_cnt)
+ port->expected_token = 0;
+
+ /* gap support */
+ if (port->expected_token != port->cpu_buf) {
+ port->dsp_buf = port->expected_token;
+ token = port->dsp_buf; /* for callback */
+ } else
+ port->dsp_buf = token;
+
+ spin_unlock_irqrestore(&port->dsp_lock, dsp_flags);
+ break;
+ } /* case USM_DATA_EVENT_READ_DONE */
+
+ case USM_DATA_EVENT_WRITE_DONE: {
+ struct us_port_data *port = &usc->port[IN];
+
+ opcode = Q6USM_EVENT_WRITE_DONE;
+ if (payload[WRITEDONE_IDX_STATUS]) {
+ pr_err("%s: wrong WRITEDONE_IDX_STATUS[%d]\n",
+ __func__,
+ payload[WRITEDONE_IDX_STATUS]);
+ break;
+ }
+
+ spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+ port->dsp_buf = token + 1;
+ if (port->dsp_buf == port->buf_cnt)
+ port->dsp_buf = 0;
+ spin_unlock_irqrestore(&port->dsp_lock, dsp_flags);
+
+ break;
+ } /* case USM_DATA_EVENT_WRITE_DONE */
+
+ case USM_SESSION_EVENT_SIGNAL_DETECT_RESULT: {
+ pr_debug("%s: US detect result: result=%d",
+ __func__,
+ payload[0]);
+ opcode = Q6USM_EVENT_SIGNAL_DETECT_RESULT;
+
+ break;
+ } /* case USM_SESSION_EVENT_SIGNAL_DETECT_RESULT */
+
+ default:
+ return 0;
+
+ } /* switch */
+
+ if (usc->cb)
+ usc->cb(opcode, token,
+ data->payload, usc->priv);
+
+ return 0;
+}
+
+uint32_t q6usm_get_virtual_address(int dir,
+ struct us_client *usc,
+ struct vm_area_struct *vms)
+{
+ uint32_t ret = 0xffffffff;
+
+ if (vms && (usc != NULL) && ((dir == IN) || (dir == OUT))) {
+ struct us_port_data *port = &usc->port[dir];
+ int size = (port->buf_size * port->buf_cnt + MEM_4K_OFFSET)
+ & MEM_4K_MASK;
+
+ ret = dma_mmap_coherent(NULL, vms,
+ port->data, port->phys,
+ size);
+ }
+ return ret;
+}
+
+static void q6usm_add_hdr(struct us_client *usc, struct apr_hdr *hdr,
+ uint32_t pkt_size, bool cmd_flg)
+{
+ mutex_lock(&usc->cmd_lock);
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \
+ APR_HDR_LEN(sizeof(struct apr_hdr)),\
+ APR_PKT_VER);
+ hdr->src_svc = ((struct apr_svc *)usc->apr)->id;
+ hdr->src_domain = APR_DOMAIN_APPS;
+ hdr->dest_svc = APR_SVC_USM;
+ hdr->dest_domain = APR_DOMAIN_ADSP;
+ hdr->src_port = (usc->session << 8) | 0x0001;
+ hdr->dest_port = (usc->session << 8) | 0x0001;
+ if (cmd_flg) {
+ hdr->token = usc->session;
+ atomic_set(&usc->cmd_state, 1);
+ }
+ hdr->pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, pkt_size);
+ mutex_unlock(&usc->cmd_lock);
+ return;
+}
+
+static uint32_t q6usm_ext2int_format(uint32_t ext_format)
+{
+ uint32_t int_format = INVALID_FORMAT;
+ switch (ext_format) {
+ case FORMAT_USPS_EPOS:
+ int_format = US_POINT_EPOS_FORMAT;
+ break;
+ case FORMAT_USRAW:
+ int_format = US_RAW_FORMAT;
+ break;
+ case FORMAT_USPROX:
+ int_format = US_PROX_FORMAT;
+ break;
+ default:
+ pr_err("%s: Invalid format[%d]\n", __func__, ext_format);
+ break;
+ }
+
+ return int_format;
+}
+
+int q6usm_open_read(struct us_client *usc,
+ uint32_t format)
+{
+ uint32_t int_format = INVALID_FORMAT;
+ int rc = 0x00;
+ struct usm_stream_cmd_open_read open;
+
+ pr_debug("%s: session[%d]", __func__, usc->session);
+
+ if ((usc == NULL) || (usc->apr == NULL)) {
+ pr_err("%s: client or its apr is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6usm_add_hdr(usc, &open.hdr, sizeof(open), true);
+ open.hdr.opcode = USM_STREAM_CMD_OPEN_READ;
+ open.src_endpoint = 0; /* AFE */
+ open.pre_proc_top = 0; /* No preprocessing required */
+
+ int_format = q6usm_ext2int_format(format);
+ if (int_format == INVALID_FORMAT)
+ return -EINVAL;
+
+ open.uMode = STREAM_PRIORITY_NORMAL;
+ open.format = int_format;
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("%s: open failed op[0x%x]rc[%d]\n",
+ __func__, open.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(usc->cmd_wait,
+ (atomic_read(&usc->cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: timeout, waited for OPEN_READ rc[%d]\n",
+ __func__, rc);
+ goto fail_cmd;
+ } else
+ rc = 0;
+fail_cmd:
+ return rc;
+}
+
+
+int q6usm_enc_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg)
+{
+ uint32_t int_format = INVALID_FORMAT;
+ struct usm_stream_cmd_encdec_cfg_blk enc_cfg_obj;
+ struct usm_stream_cmd_encdec_cfg_blk *enc_cfg = &enc_cfg_obj;
+ int rc = 0;
+ uint32_t total_cfg_size =
+ sizeof(struct usm_stream_cmd_encdec_cfg_blk);
+ uint32_t round_params_size = 0;
+ uint8_t is_allocated = 0;
+
+
+ if ((usc == NULL) || (us_cfg == NULL)) {
+ pr_err("%s: wrong input", __func__);
+ return -EINVAL;
+ }
+
+ int_format = q6usm_ext2int_format(us_cfg->format_id);
+ if (int_format == INVALID_FORMAT) {
+ pr_err("%s: wrong input format[%d]",
+ __func__, us_cfg->format_id);
+ return -EINVAL;
+ }
+
+ /* Transparent configuration data is after enc_cfg */
+ /* Integer number of u32s is requred */
+ round_params_size = ((us_cfg->params_size + 3)/4) * 4;
+ if (round_params_size > USM_MAX_CFG_DATA_SIZE) {
+ /* Dynamic allocated encdec_cfg_blk is required */
+ /* static part use */
+ round_params_size -= USM_MAX_CFG_DATA_SIZE;
+ total_cfg_size += round_params_size;
+ enc_cfg = kzalloc(total_cfg_size, GFP_KERNEL);
+ if (enc_cfg == NULL) {
+ pr_err("%s: enc_cfg[%d] allocation failed\n",
+ __func__, total_cfg_size);
+ return -ENOMEM;
+ }
+ is_allocated = 1;
+ } else
+ round_params_size = 0;
+
+ q6usm_add_hdr(usc, &enc_cfg->hdr, total_cfg_size - APR_HDR_SIZE, true);
+
+ enc_cfg->hdr.opcode = USM_STREAM_CMD_SET_ENC_PARAM;
+ enc_cfg->param_id = USM_PARAM_ID_ENCDEC_ENC_CFG_BLK;
+ enc_cfg->param_size = sizeof(struct usm_encode_cfg_blk)+
+ round_params_size;
+ enc_cfg->enc_blk.frames_per_buf = 1;
+ enc_cfg->enc_blk.format_id = int_format;
+ enc_cfg->enc_blk.cfg_size = sizeof(struct usm_cfg_common)+
+ USM_MAX_CFG_DATA_SIZE +
+ round_params_size;
+ memcpy(&(enc_cfg->enc_blk.cfg_common), &(us_cfg->cfg_common),
+ sizeof(struct usm_cfg_common));
+
+ /* Transparent data copy */
+ memcpy(enc_cfg->enc_blk.transp_data, us_cfg->params,
+ us_cfg->params_size);
+ pr_debug("%s: cfg_size[%d], params_size[%d]\n",
+ __func__,
+ enc_cfg->enc_blk.cfg_size,
+ us_cfg->params_size);
+ pr_debug("%s: params[%d,%d,%d,%d, %d,%d,%d,%d]\n",
+ __func__,
+ enc_cfg->enc_blk.transp_data[0],
+ enc_cfg->enc_blk.transp_data[1],
+ enc_cfg->enc_blk.transp_data[2],
+ enc_cfg->enc_blk.transp_data[3],
+ enc_cfg->enc_blk.transp_data[4],
+ enc_cfg->enc_blk.transp_data[5],
+ enc_cfg->enc_blk.transp_data[6],
+ enc_cfg->enc_blk.transp_data[7]
+ );
+ pr_debug("%s: srate:%d, ch=%d, bps= %d; dmap:0x%x; dev_id=0x%x\n",
+ __func__, enc_cfg->enc_blk.cfg_common.sample_rate,
+ enc_cfg->enc_blk.cfg_common.ch_cfg,
+ enc_cfg->enc_blk.cfg_common.bits_per_sample,
+ enc_cfg->enc_blk.cfg_common.data_map,
+ enc_cfg->enc_blk.cfg_common.dev_id);
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) enc_cfg);
+ if (rc < 0) {
+ pr_err("%s:Comamnd open failed\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(usc->cmd_wait,
+ (atomic_read(&usc->cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: timeout opcode[0x%x]\n",
+ __func__, enc_cfg->hdr.opcode);
+ } else
+ rc = 0;
+
+fail_cmd:
+ if (is_allocated == 1)
+ kfree(enc_cfg);
+
+ return rc;
+}
+
+int q6usm_dec_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg)
+{
+
+ uint32_t int_format = INVALID_FORMAT;
+ struct usm_stream_media_format_update dec_cfg_obj;
+ struct usm_stream_media_format_update *dec_cfg = &dec_cfg_obj;
+
+ int rc = 0;
+ uint32_t total_cfg_size = sizeof(struct usm_stream_media_format_update);
+ uint32_t round_params_size = 0;
+ uint8_t is_allocated = 0;
+
+
+ if ((usc == NULL) || (us_cfg == NULL)) {
+ pr_err("%s: wrong input", __func__);
+ return -EINVAL;
+ }
+
+ int_format = q6usm_ext2int_format(us_cfg->format_id);
+ if (int_format == INVALID_FORMAT) {
+ pr_err("%s: wrong input format[%d]",
+ __func__, us_cfg->format_id);
+ return -EINVAL;
+ }
+
+ /* Transparent configuration data is after enc_cfg */
+ /* Integer number of u32s is requred */
+ round_params_size = ((us_cfg->params_size + 3)/4) * 4;
+ if (round_params_size > USM_MAX_CFG_DATA_SIZE) {
+ /* Dynamic allocated encdec_cfg_blk is required */
+ /* static part use */
+ round_params_size -= USM_MAX_CFG_DATA_SIZE;
+ total_cfg_size += round_params_size;
+ dec_cfg = kzalloc(total_cfg_size, GFP_KERNEL);
+ if (dec_cfg == NULL) {
+ pr_err("%s:dec_cfg[%d] allocation failed\n",
+ __func__, total_cfg_size);
+ return -ENOMEM;
+ }
+ is_allocated = 1;
+ } else { /* static transp_data is enough */
+ round_params_size = 0;
+ }
+
+ q6usm_add_hdr(usc, &dec_cfg->hdr, total_cfg_size - APR_HDR_SIZE, true);
+
+ dec_cfg->hdr.opcode = USM_DATA_CMD_MEDIA_FORMAT_UPDATE;
+ dec_cfg->format_id = int_format;
+ dec_cfg->cfg_size = sizeof(struct usm_cfg_common) +
+ USM_MAX_CFG_DATA_SIZE +
+ round_params_size;
+ memcpy(&(dec_cfg->cfg_common), &(us_cfg->cfg_common),
+ sizeof(struct usm_cfg_common));
+ /* Transparent data copy */
+ memcpy(dec_cfg->transp_data, us_cfg->params, us_cfg->params_size);
+ pr_debug("%s: cfg_size[%d], params_size[%d]; parambytes[%d,%d,%d,%d]\n",
+ __func__,
+ dec_cfg->cfg_size,
+ us_cfg->params_size,
+ dec_cfg->transp_data[0],
+ dec_cfg->transp_data[1],
+ dec_cfg->transp_data[2],
+ dec_cfg->transp_data[3]
+ );
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) dec_cfg);
+ if (rc < 0) {
+ pr_err("%s:Comamnd open failed\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(usc->cmd_wait,
+ (atomic_read(&usc->cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: timeout opcode[0x%x]\n",
+ __func__, dec_cfg->hdr.opcode);
+ } else
+ rc = 0;
+
+fail_cmd:
+ if (is_allocated == 1)
+ kfree(dec_cfg);
+
+ return rc;
+}
+
+int q6usm_open_write(struct us_client *usc,
+ uint32_t format)
+{
+ int rc = 0;
+ uint32_t int_format = INVALID_FORMAT;
+ struct usm_stream_cmd_open_write open;
+
+ pr_debug("%s: session[%d]", __func__, usc->session);
+
+ if ((usc == NULL) || (usc->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6usm_add_hdr(usc, &open.hdr, sizeof(open), true);
+ open.hdr.opcode = USM_STREAM_CMD_OPEN_WRITE;
+
+ int_format = q6usm_ext2int_format(format);
+ if (int_format == INVALID_FORMAT) {
+ pr_err("%s: wrong format[%d]", __func__, format);
+ return -EINVAL;
+ }
+
+ open.format = int_format;
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("%s:open failed op[0x%x]rc[%d]\n", \
+ __func__, open.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(usc->cmd_wait,
+ (atomic_read(&usc->cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s:timeout. waited for OPEN_WRITR rc[%d]\n",
+ __func__, rc);
+ goto fail_cmd;
+ } else
+ rc = 0;
+
+fail_cmd:
+ return rc;
+}
+
+int q6usm_run(struct us_client *usc, uint32_t flags,
+ uint32_t msw_ts, uint32_t lsw_ts)
+{
+ struct usm_stream_cmd_run run;
+ int rc = 0;
+
+ if ((usc == NULL) || (usc->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ q6usm_add_hdr(usc, &run.hdr, sizeof(run), true);
+
+ run.hdr.opcode = USM_SESSION_CMD_RUN;
+ run.flags = flags;
+ run.msw_ts = msw_ts;
+ run.lsw_ts = lsw_ts;
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) &run);
+ if (rc < 0) {
+ pr_err("%s: Commmand run failed[%d]\n", __func__, rc);
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(usc->cmd_wait,
+ (atomic_read(&usc->cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: timeout. waited for run success rc[%d]\n",
+ __func__, rc);
+ } else
+ rc = 0;
+
+fail_cmd:
+ return rc;
+}
+
+
+
+int q6usm_read(struct us_client *usc, uint32_t read_ind)
+{
+ struct usm_stream_cmd_read read;
+ struct us_port_data *port = NULL;
+ int rc = 0;
+ u32 read_counter = 0;
+ u32 loop_ind = 0;
+
+ if ((usc == NULL) || (usc->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ port = &usc->port[OUT];
+
+ if (read_ind > port->buf_cnt) {
+ pr_err("%s: wrong read_ind[%d]\n",
+ __func__, read_ind);
+ return -EINVAL;
+ }
+ if (read_ind == port->cpu_buf) {
+ pr_err("%s: no free region\n", __func__);
+ return 0;
+ }
+
+ if (read_ind > port->cpu_buf) { /* 1 range */
+ read_counter = read_ind - port->cpu_buf;
+ } else { /* 2 ranges */
+ read_counter = (port->buf_cnt - port->cpu_buf) + read_ind;
+ }
+
+ q6usm_add_hdr(usc, &read.hdr, (sizeof(read) - APR_HDR_SIZE), false);
+
+ read.hdr.opcode = USM_DATA_CMD_READ;
+ read.buf_size = port->buf_size;
+ read.buf_addr_msw = 0;
+ read.mem_map_handle = *((uint32_t *)(port->ext));
+
+ for (loop_ind = 0; loop_ind < read_counter; ++loop_ind) {
+ u32 temp_cpu_buf = port->cpu_buf;
+
+ read.buf_addr_lsw = (uint32_t)(port->phys) +
+ port->buf_size * (port->cpu_buf);
+ read.seq_id = port->cpu_buf;
+ read.hdr.token = port->cpu_buf;
+ read.counter = 1;
+
+ ++(port->cpu_buf);
+ if (port->cpu_buf == port->buf_cnt)
+ port->cpu_buf = 0;
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) &read);
+
+ if (rc < 0) {
+ port->cpu_buf = temp_cpu_buf;
+
+ pr_err("%s:read op[0x%x]rc[%d]\n",
+ __func__, read.hdr.opcode, rc);
+ break;
+ } else
+ rc = 0;
+ } /* bufs loop */
+
+ return rc;
+}
+
+int q6usm_write(struct us_client *usc, uint32_t write_ind)
+{
+ int rc = 0;
+ struct usm_stream_cmd_write cmd_write;
+ struct us_port_data *port = NULL;
+ u32 current_dsp_buf = 0;
+
+ if ((usc == NULL) || (usc->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ port = &usc->port[IN];
+
+ current_dsp_buf = port->dsp_buf;
+ /* free region, caused by new dsp_buf report from DSP, */
+ /* can be only extended */
+ if (port->cpu_buf >= current_dsp_buf) {
+ /* 2 -part free region, including empty buffer */
+ if ((write_ind <= port->cpu_buf) &&
+ (write_ind > current_dsp_buf)) {
+ pr_err("%s: wrong w_ind[%d]; d_buf=%d; c_buf=%d\n",
+ __func__, write_ind,
+ current_dsp_buf, port->cpu_buf);
+ return -EINVAL;
+ }
+ } else {
+ /* 1 -part free region */
+ if ((write_ind <= port->cpu_buf) ||
+ (write_ind > current_dsp_buf)) {
+ pr_err("%s: wrong w_ind[%d]; d_buf=%d; c_buf=%d\n",
+ __func__, write_ind,
+ current_dsp_buf, port->cpu_buf);
+ return -EINVAL;
+ }
+ }
+
+ q6usm_add_hdr(usc, &cmd_write.hdr,
+ (sizeof(cmd_write) - APR_HDR_SIZE), false);
+
+ cmd_write.hdr.opcode = USM_DATA_CMD_WRITE;
+ cmd_write.buf_size = port->buf_size;
+ cmd_write.buf_addr_msw = 0;
+ cmd_write.mem_map_handle = *((uint32_t *)(port->ext));
+ cmd_write.res0 = 0;
+ cmd_write.res1 = 0;
+ cmd_write.res2 = 0;
+
+ while (port->cpu_buf != write_ind) {
+ u32 temp_cpu_buf = port->cpu_buf;
+
+ cmd_write.buf_addr_lsw = (uint32_t)(port->phys) +
+ port->buf_size * (port->cpu_buf);
+ cmd_write.seq_id = port->cpu_buf;
+ cmd_write.hdr.token = port->cpu_buf;
+
+ ++(port->cpu_buf);
+ if (port->cpu_buf == port->buf_cnt)
+ port->cpu_buf = 0;
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) &cmd_write);
+
+ if (rc < 0) {
+ port->cpu_buf = temp_cpu_buf;
+ pr_err("%s:write op[0x%x];rc[%d];cpu_buf[%d]\n",
+ __func__, cmd_write.hdr.opcode,
+ rc, port->cpu_buf);
+ break;
+ }
+
+ rc = 0;
+ }
+
+ return rc;
+}
+
+bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t *free_region)
+{
+ struct us_port_data *port = NULL;
+ u32 cpu_buf = 0;
+
+ if ((usc == NULL) || !free_region) {
+ pr_err("%s: input data wrong\n", __func__);
+ return false;
+ }
+ port = &usc->port[IN];
+ cpu_buf = port->cpu_buf + 1;
+ if (cpu_buf == port->buf_cnt)
+ cpu_buf = 0;
+
+ *free_region = port->dsp_buf;
+
+ return cpu_buf == *free_region;
+}
+
+int q6usm_cmd(struct us_client *usc, int cmd)
+{
+ struct apr_hdr hdr;
+ int rc = 0;
+ atomic_t *state;
+
+ if ((usc == NULL) || (usc->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ q6usm_add_hdr(usc, &hdr, (sizeof(hdr) - APR_HDR_SIZE), true);
+ switch (cmd) {
+ case CMD_CLOSE:
+ hdr.opcode = USM_STREAM_CMD_CLOSE;
+ state = &usc->cmd_state;
+ break;
+
+ default:
+ pr_err("%s:Invalid format[%d]\n", __func__, cmd);
+ goto fail_cmd;
+ }
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) &hdr);
+ if (rc < 0) {
+ pr_err("%s: Command 0x%x failed\n", __func__, hdr.opcode);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(usc->cmd_wait, (atomic_read(state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s:timeout. waited for response opcode[0x%x]\n",
+ __func__, hdr.opcode);
+ } else
+ rc = 0;
+fail_cmd:
+ return rc;
+}
+
+int q6usm_set_us_detection(struct us_client *usc,
+ struct usm_session_cmd_detect_info *detect_info,
+ uint16_t detect_info_size)
+{
+ int rc = 0;
+
+ if ((usc == NULL) ||
+ (detect_info_size == 0) ||
+ (detect_info == NULL)) {
+ pr_err("%s: wrong input: usc=0x%p, inf_size=%d; info=0x%p",
+ __func__,
+ usc,
+ detect_info_size,
+ detect_info);
+ return -EINVAL;
+ }
+
+ q6usm_add_hdr(usc, &detect_info->hdr,
+ detect_info_size - APR_HDR_SIZE, true);
+
+ detect_info->hdr.opcode = USM_SESSION_CMD_SIGNAL_DETECT_MODE;
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *)detect_info);
+ if (rc < 0) {
+ pr_err("%s:Comamnd signal detect failed\n", __func__);
+ return -EINVAL;
+ }
+ rc = wait_event_timeout(usc->cmd_wait,
+ (atomic_read(&usc->cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: CMD_SIGNAL_DETECT_MODE: timeout=%d\n",
+ __func__, Q6USM_TIMEOUT_JIFFIES);
+ } else
+ rc = 0;
+
+ return rc;
+}
+
+static int __init q6usm_init(void)
+{
+ pr_debug("%s\n", __func__);
+ init_waitqueue_head(&this_mmap.cmd_wait);
+ memset(session, 0, sizeof(session));
+ return 0;
+}
+
+device_initcall(q6usm_init);
diff --git a/block/row-iosched.c b/block/row-iosched.c
index a4184da..b8c16e7 100644
--- a/block/row-iosched.c
+++ b/block/row-iosched.c
@@ -1,7 +1,7 @@
/*
* ROW (Read Over Write) I/O scheduler.
*
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -47,37 +47,38 @@
ROWQ_MAX_PRIO,
};
-/* Flags indicating whether idling is enabled on the queue */
-static const bool queue_idling_enabled[] = {
- true, /* ROWQ_PRIO_HIGH_READ */
- true, /* ROWQ_PRIO_REG_READ */
- false, /* ROWQ_PRIO_HIGH_SWRITE */
- false, /* ROWQ_PRIO_REG_SWRITE */
- false, /* ROWQ_PRIO_REG_WRITE */
- false, /* ROWQ_PRIO_LOW_READ */
- false, /* ROWQ_PRIO_LOW_SWRITE */
+/**
+ * struct row_queue_params - ROW queue parameters
+ * @idling_enabled: Flag indicating whether idling is enable on
+ * the queue
+ * @quantum: Number of requests to be dispatched from this queue
+ * in a dispatch cycle
+ * @is_urgent: Flags indicating whether the queue can notify on
+ * urgent requests
+ *
+ */
+struct row_queue_params {
+ bool idling_enabled;
+ int quantum;
+ bool is_urgent;
};
-/* Flags indicating whether the queue can notify on urgent requests */
-static const bool urgent_queues[] = {
- true, /* ROWQ_PRIO_HIGH_READ */
- true, /* ROWQ_PRIO_REG_READ */
- false, /* ROWQ_PRIO_HIGH_SWRITE */
- false, /* ROWQ_PRIO_REG_SWRITE */
- false, /* ROWQ_PRIO_REG_WRITE */
- false, /* ROWQ_PRIO_LOW_READ */
- false, /* ROWQ_PRIO_LOW_SWRITE */
-};
-
-/* Default values for row queues quantums in each dispatch cycle */
-static const int queue_quantum[] = {
- 100, /* ROWQ_PRIO_HIGH_READ */
- 100, /* ROWQ_PRIO_REG_READ */
- 2, /* ROWQ_PRIO_HIGH_SWRITE */
- 1, /* ROWQ_PRIO_REG_SWRITE */
- 1, /* ROWQ_PRIO_REG_WRITE */
- 1, /* ROWQ_PRIO_LOW_READ */
- 1 /* ROWQ_PRIO_LOW_SWRITE */
+/*
+ * This array holds the default values of the different configurables
+ * for each ROW queue. Each row of the array holds the following values:
+ * {idling_enabled, quantum, is_urgent}
+ * Each row corresponds to a queue with the same index (according to
+ * enum row_queue_prio)
+ */
+static const struct row_queue_params row_queues_def[] = {
+/* idling_enabled, quantum, is_urgent */
+ {true, 100, true}, /* ROWQ_PRIO_HIGH_READ */
+ {true, 100, true}, /* ROWQ_PRIO_REG_READ */
+ {false, 2, false}, /* ROWQ_PRIO_HIGH_SWRITE */
+ {false, 1, false}, /* ROWQ_PRIO_REG_SWRITE */
+ {false, 1, false}, /* ROWQ_PRIO_REG_WRITE */
+ {false, 1, false}, /* ROWQ_PRIO_LOW_READ */
+ {false, 1, false} /* ROWQ_PRIO_LOW_SWRITE */
};
/* Default values for idling on read queues (in msec) */
@@ -104,6 +105,9 @@
* @nr_dispatched: number of requests already dispatched in
* the current dispatch cycle
* @slice: number of requests to dispatch in a cycle
+ * @nr_req: number of requests in queue
+ * @dispatch quantum: number of requests this queue may
+ * dispatch in a dispatch cycle
* @idle_data: data for idling on queues
*
*/
@@ -115,6 +119,9 @@
unsigned int nr_dispatched;
unsigned int slice;
+ unsigned int nr_req;
+ int disp_quantum;
+
/* used only for READ queues */
struct rowq_idling_data idle_data;
};
@@ -138,8 +145,7 @@
/**
* struct row_queue - Per block device rqueue structure
* @dispatch_queue: dispatch rqueue
- * @row_queues: array of priority request queues with
- * dispatch quantum per rqueue
+ * @row_queues: array of priority request queues
* @curr_queue: index in the row_queues array of the
* currently serviced rqueue
* @read_idle: data for idling after READ request
@@ -152,10 +158,7 @@
struct row_data {
struct request_queue *dispatch_queue;
- struct {
- struct row_queue rqueue;
- int disp_quantum;
- } row_queues[ROWQ_MAX_PRIO];
+ struct row_queue row_queues[ROWQ_MAX_PRIO];
enum row_queue_prio curr_queue;
@@ -191,6 +194,18 @@
return rd->cycle_flags & (1 << qnum);
}
+static inline void __maybe_unused row_dump_queues_stat(struct row_data *rd)
+{
+ int i;
+
+ row_log(rd->dispatch_queue, " Queues status:");
+ for (i = 0; i < ROWQ_MAX_PRIO; i++)
+ row_log(rd->dispatch_queue,
+ "queue%d: dispatched= %d, nr_req=%d", i,
+ rd->row_queues[i].nr_dispatched,
+ rd->row_queues[i].nr_req);
+}
+
/******************** Static helper functions ***********************/
/*
* kick_queue() - Wake up device driver queue thread
@@ -210,7 +225,7 @@
row_log_rowq(rd, rd->curr_queue, "Performing delayed work");
/* Mark idling process as done */
- rd->row_queues[rd->curr_queue].rqueue.idle_data.begin_idling = false;
+ rd->row_queues[rd->curr_queue].idle_data.begin_idling = false;
if (!(rd->nr_reqs[0] + rd->nr_reqs[1]))
row_log(rd->dispatch_queue, "No requests in scheduler");
@@ -235,7 +250,7 @@
int i;
for (i = 0; i < ROWQ_MAX_PRIO; i++)
- rd->row_queues[i].rqueue.nr_dispatched = 0;
+ rd->row_queues[i].nr_dispatched = 0;
rd->curr_queue = ROWQ_PRIO_HIGH_READ;
row_log(rd->dispatch_queue, "Restarting cycle");
@@ -264,9 +279,10 @@
list_add_tail(&rq->queuelist, &rqueue->fifo);
rd->nr_reqs[rq_data_dir(rq)]++;
+ rqueue->nr_req++;
rq_set_fifo_time(rq, jiffies); /* for statistics*/
- if (queue_idling_enabled[rqueue->prio]) {
+ if (row_queues_def[rqueue->prio].idling_enabled) {
if (delayed_work_pending(&rd->read_idle.idle_work))
(void)cancel_delayed_work(
&rd->read_idle.idle_work);
@@ -282,13 +298,14 @@
rqueue->idle_data.last_insert_time = ktime_get();
}
- if (urgent_queues[rqueue->prio] &&
+ if (row_queues_def[rqueue->prio].is_urgent &&
row_rowq_unserved(rd, rqueue->prio)) {
row_log_rowq(rd, rqueue->prio,
- "added urgent req curr_queue = %d",
- rd->curr_queue);
+ "added urgent request (total on queue=%d)",
+ rqueue->nr_req);
} else
- row_log_rowq(rd, rqueue->prio, "added request");
+ row_log_rowq(rd, rqueue->prio,
+ "added request (total on queue=%d)", rqueue->nr_req);
}
/**
@@ -317,8 +334,10 @@
list_add(&rq->queuelist, &rqueue->fifo);
rd->nr_reqs[rq_data_dir(rq)]++;
+ rqueue->nr_req++;
- row_log_rowq(rd, rqueue->prio, "request reinserted");
+ row_log_rowq(rd, rqueue->prio,
+ "request reinserted (total on queue=%d)", rqueue->nr_req);
return 0;
}
@@ -334,8 +353,8 @@
int i;
for (i = 0; i < ROWQ_MAX_PRIO; i++)
- if (urgent_queues[i] && row_rowq_unserved(rd, i) &&
- !list_empty(&rd->row_queues[i].rqueue.fifo)) {
+ if (row_queues_def[i].is_urgent && row_rowq_unserved(rd, i) &&
+ !list_empty(&rd->row_queues[i].fifo)) {
row_log_rowq(rd, i,
"Urgent request pending (curr=%i)",
rd->curr_queue);
@@ -355,8 +374,10 @@
struct request *rq)
{
struct row_data *rd = (struct row_data *)q->elevator->elevator_data;
+ struct row_queue *rqueue = RQ_ROWQ(rq);
rq_fifo_clear(rq);
+ rqueue->nr_req--;
rd->nr_reqs[rq_data_dir(rq)]--;
}
@@ -372,13 +393,13 @@
{
struct request *rq;
- rq = rq_entry_fifo(rd->row_queues[rd->curr_queue].rqueue.fifo.next);
+ rq = rq_entry_fifo(rd->row_queues[rd->curr_queue].fifo.next);
row_remove_request(rd->dispatch_queue, rq);
elv_dispatch_add_tail(rd->dispatch_queue, rq);
- rd->row_queues[rd->curr_queue].rqueue.nr_dispatched++;
+ rd->row_queues[rd->curr_queue].nr_dispatched++;
row_clear_rowq_unserved(rd, rd->curr_queue);
row_log_rowq(rd, rd->curr_queue, " Dispatched request nr_disp = %d",
- rd->row_queues[rd->curr_queue].rqueue.nr_dispatched);
+ rd->row_queues[rd->curr_queue].nr_dispatched);
}
/*
@@ -404,7 +425,7 @@
* Loop over all queues to find the next queue that is not empty.
* Stop when you get back to curr_queue
*/
- while (list_empty(&rd->row_queues[rd->curr_queue].rqueue.fifo)
+ while (list_empty(&rd->row_queues[rd->curr_queue].fifo)
&& rd->curr_queue != prev_curr_queue) {
/* Mark rqueue as unserved */
row_mark_rowq_unserved(rd, rd->curr_queue);
@@ -436,9 +457,10 @@
*/
for (i = 0; i < currq; i++) {
if (row_rowq_unserved(rd, i) &&
- !list_empty(&rd->row_queues[i].rqueue.fifo)) {
+ !list_empty(&rd->row_queues[i].fifo)) {
row_log_rowq(rd, currq,
- " Preemting for unserved rowq%d", i);
+ " Preemting for unserved rowq%d. (nr_req=%u)",
+ i, rd->row_queues[currq].nr_req);
rd->curr_queue = i;
row_dispatch_insert(rd);
ret = 1;
@@ -446,9 +468,9 @@
}
}
- if (rd->row_queues[currq].rqueue.nr_dispatched >=
+ if (rd->row_queues[currq].nr_dispatched >=
rd->row_queues[currq].disp_quantum) {
- rd->row_queues[currq].rqueue.nr_dispatched = 0;
+ rd->row_queues[currq].nr_dispatched = 0;
row_log_rowq(rd, currq, "Expiring rqueue");
ret = row_choose_queue(rd);
if (ret)
@@ -457,7 +479,7 @@
}
/* Dispatch from curr_queue */
- if (list_empty(&rd->row_queues[currq].rqueue.fifo)) {
+ if (list_empty(&rd->row_queues[currq].fifo)) {
/* check idling */
if (delayed_work_pending(&rd->read_idle.idle_work)) {
if (force) {
@@ -472,8 +494,8 @@
}
}
- if (!force && queue_idling_enabled[currq] &&
- rd->row_queues[currq].rqueue.idle_data.begin_idling) {
+ if (!force && row_queues_def[currq].idling_enabled &&
+ rd->row_queues[currq].idle_data.begin_idling) {
if (!queue_delayed_work(rd->read_idle.idle_workqueue,
&rd->read_idle.idle_work,
rd->read_idle.idle_time)) {
@@ -520,12 +542,12 @@
return NULL;
for (i = 0; i < ROWQ_MAX_PRIO; i++) {
- INIT_LIST_HEAD(&rdata->row_queues[i].rqueue.fifo);
- rdata->row_queues[i].disp_quantum = queue_quantum[i];
- rdata->row_queues[i].rqueue.rdata = rdata;
- rdata->row_queues[i].rqueue.prio = i;
- rdata->row_queues[i].rqueue.idle_data.begin_idling = false;
- rdata->row_queues[i].rqueue.idle_data.last_insert_time =
+ INIT_LIST_HEAD(&rdata->row_queues[i].fifo);
+ rdata->row_queues[i].disp_quantum = row_queues_def[i].quantum;
+ rdata->row_queues[i].rdata = rdata;
+ rdata->row_queues[i].prio = i;
+ rdata->row_queues[i].idle_data.begin_idling = false;
+ rdata->row_queues[i].idle_data.last_insert_time =
ktime_set(0, 0);
}
@@ -564,7 +586,7 @@
int i;
for (i = 0; i < ROWQ_MAX_PRIO; i++)
- BUG_ON(!list_empty(&rd->row_queues[i].rqueue.fifo));
+ BUG_ON(!list_empty(&rd->row_queues[i].fifo));
(void)cancel_delayed_work_sync(&rd->read_idle.idle_work);
BUG_ON(delayed_work_pending(&rd->read_idle.idle_work));
destroy_workqueue(rd->read_idle.idle_workqueue);
@@ -583,6 +605,7 @@
struct row_queue *rqueue = RQ_ROWQ(next);
list_del_init(&next->queuelist);
+ rqueue->nr_req--;
rqueue->rdata->nr_reqs[rq_data_dir(rq)]--;
}
@@ -668,7 +691,7 @@
rowd->row_queues[ROWQ_PRIO_LOW_READ].disp_quantum, 0);
SHOW_FUNCTION(row_lp_swrite_quantum_show,
rowd->row_queues[ROWQ_PRIO_LOW_SWRITE].disp_quantum, 0);
-SHOW_FUNCTION(row_read_idle_show, rowd->read_idle.idle_time, 1);
+SHOW_FUNCTION(row_read_idle_show, rowd->read_idle.idle_time, 0);
SHOW_FUNCTION(row_read_idle_freq_show, rowd->read_idle.freq, 0);
#undef SHOW_FUNCTION
@@ -708,7 +731,7 @@
STORE_FUNCTION(row_lp_swrite_quantum_store,
&rowd->row_queues[ROWQ_PRIO_LOW_SWRITE].disp_quantum,
1, INT_MAX, 1);
-STORE_FUNCTION(row_read_idle_store, &rowd->read_idle.idle_time, 1, INT_MAX, 1);
+STORE_FUNCTION(row_read_idle_store, &rowd->read_idle.idle_time, 1, INT_MAX, 0);
STORE_FUNCTION(row_read_idle_freq_store, &rowd->read_idle.freq, 1, INT_MAX, 0);
#undef STORE_FUNCTION
diff --git a/block/test-iosched.c b/block/test-iosched.c
index 12f56bd..3b3d485 100644
--- a/block/test-iosched.c
+++ b/block/test-iosched.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012-2013, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -44,7 +44,6 @@
static struct test_data *ptd;
-
/**
* test_iosched_get_req_queue() - returns the request queue
* served by the scheduler
@@ -76,17 +75,20 @@
}
EXPORT_SYMBOL(test_iosched_mark_test_completion);
-/* Check if all the queued test requests were completed */
-static void check_test_completion(void)
+/**
+ * check_test_completion() - Check if all the queued test
+ * requests were completed
+ */
+void check_test_completion(void)
{
struct test_request *test_rq;
if (!ptd)
- return;
+ goto exit;
list_for_each_entry(test_rq, &ptd->dispatched_queue, queuelist)
if (!test_rq->req_completed)
- return;
+ goto exit;
if (!list_empty(&ptd->test_queue)
|| !list_empty(&ptd->reinsert_queue)
@@ -96,7 +98,7 @@
__func__, ptd->test_count, ptd->reinsert_count);
test_pr_info("%s: dispatched_count=%d, urgent_count=%d",
__func__, ptd->dispatched_count, ptd->urgent_count);
- return;
+ goto exit;
}
ptd->test_info.test_duration = jiffies -
@@ -108,7 +110,11 @@
__func__, ptd->dispatched_count);
test_iosched_mark_test_completion();
+
+exit:
+ return;
}
+EXPORT_SYMBOL(check_test_completion);
/*
* A callback to be called per bio completion.
@@ -348,7 +354,7 @@
rq->end_io = end_test_req;
rq->__sector = start_sec;
rq->cmd_type |= REQ_TYPE_FS;
- rq->cmd_flags |= REQ_SORTED; /* do we need this?*/
+ rq->cmd_flags |= REQ_SORTED;
if (rq->bio) {
rq->bio->bi_sector = start_sec;
@@ -684,6 +690,8 @@
/**
* test_iosched_start_test() - Prepares and runs the test.
+ * The members test_duration and test_byte_count of the input
+ * parameter t_info are modified by this function.
* @t_info: the current test testcase and callbacks
* functions
*
@@ -772,6 +780,7 @@
wait_event(ptd->wait_q, ptd->test_state == TEST_COMPLETED);
t_info->test_duration = ptd->test_info.test_duration;
+ t_info->test_byte_count = ptd->test_info.test_byte_count;
del_timer_sync(&ptd->timeout_timer);
ret = check_test_result(ptd);
@@ -1006,6 +1015,7 @@
print_req(rq);
elv_dispatch_sort(q, rq);
+ ptd->test_info.test_byte_count += test_rq->buf_size;
ret = 1;
goto err;
}
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 6bdedd7..83ef121 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -83,6 +83,17 @@
If unsure, say N.
+config SATA_AHCI_MSM
+ tristate "Qualcomm MSM AHCI SATA support"
+ depends on ARCH_MSM
+ select SATA_AHCI_PLATFORM
+ help
+ This option enables support for AHCI SATA controller
+ integrated into Qualcomm MSM chipsets. For more
+ information please refer to http://www.qualcomm.com/chipsets.
+
+ If unsure, say N.
+
config SATA_FSL
tristate "Freescale 3.0Gbps SATA support"
depends on FSL_SOC
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 6ece5b7..bc40152 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -9,6 +9,7 @@
obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o
obj-$(CONFIG_SATA_SIL24) += sata_sil24.o
obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o
+obj-$(CONFIG_SATA_AHCI_MSM) += ahci_msm.o
# SFF w/ custom DMA
obj-$(CONFIG_PDC_ADMA) += pdc_adma.o
diff --git a/drivers/ata/ahci_msm.c b/drivers/ata/ahci_msm.c
new file mode 100644
index 0000000..8536040
--- /dev/null
+++ b/drivers/ata/ahci_msm.c
@@ -0,0 +1,778 @@
+/*
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * SATA init module.
+ * To be used with SATA interface on MSM targets.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/regulator/consumer.h>
+#include <linux/ahci_platform.h>
+#include <mach/clk.h>
+
+/* PHY registers */
+#define UNIPHY_PLL_REFCLK_CFG 0x000
+#define UNIPHY_PLL_POSTDIV1_CFG 0x004
+#define UNIPHY_PLL_CHGPUMP_CFG 0x008
+#define UNIPHY_PLL_VCOLPF_CFG 0x00C
+#define UNIPHY_PLL_VREG_CFG 0x010
+#define UNIPHY_PLL_PWRGEN_CFG 0x014
+#define UNIPHY_PLL_DMUX_CFG 0x018
+#define UNIPHY_PLL_AMUX_CFG 0x01C
+#define UNIPHY_PLL_GLB_CFG 0x020
+#define UNIPHY_PLL_POSTDIV2_CFG 0x024
+#define UNIPHY_PLL_POSTDIV3_CFG 0x028
+#define UNIPHY_PLL_LPFR_CFG 0x02C
+#define UNIPHY_PLL_LPFC1_CFG 0x030
+#define UNIPHY_PLL_LPFC2_CFG 0x034
+#define UNIPHY_PLL_SDM_CFG0 0x038
+#define UNIPHY_PLL_SDM_CFG1 0x03C
+#define UNIPHY_PLL_SDM_CFG2 0x040
+#define UNIPHY_PLL_SDM_CFG3 0x044
+#define UNIPHY_PLL_SDM_CFG4 0x048
+#define UNIPHY_PLL_SSC_CFG0 0x04C
+#define UNIPHY_PLL_SSC_CFG1 0x050
+#define UNIPHY_PLL_SSC_CFG2 0x054
+#define UNIPHY_PLL_SSC_CFG3 0x058
+#define UNIPHY_PLL_LKDET_CFG0 0x05C
+#define UNIPHY_PLL_LKDET_CFG1 0x060
+#define UNIPHY_PLL_LKDET_CFG2 0x064
+#define UNIPHY_PLL_TEST_CFG 0x068
+#define UNIPHY_PLL_CAL_CFG0 0x06C
+#define UNIPHY_PLL_CAL_CFG1 0x070
+#define UNIPHY_PLL_CAL_CFG2 0x074
+#define UNIPHY_PLL_CAL_CFG3 0x078
+#define UNIPHY_PLL_CAL_CFG4 0x07C
+#define UNIPHY_PLL_CAL_CFG5 0x080
+#define UNIPHY_PLL_CAL_CFG6 0x084
+#define UNIPHY_PLL_CAL_CFG7 0x088
+#define UNIPHY_PLL_CAL_CFG8 0x08C
+#define UNIPHY_PLL_CAL_CFG9 0x090
+#define UNIPHY_PLL_CAL_CFG10 0x094
+#define UNIPHY_PLL_CAL_CFG11 0x098
+#define UNIPHY_PLL_EFUSE_CFG 0x09C
+#define UNIPHY_PLL_DEBUG_BUS_SEL 0x0A0
+#define UNIPHY_PLL_CTRL_42 0x0A4
+#define UNIPHY_PLL_CTRL_43 0x0A8
+#define UNIPHY_PLL_CTRL_44 0x0AC
+#define UNIPHY_PLL_CTRL_45 0x0B0
+#define UNIPHY_PLL_CTRL_46 0x0B4
+#define UNIPHY_PLL_CTRL_47 0x0B8
+#define UNIPHY_PLL_CTRL_48 0x0BC
+#define UNIPHY_PLL_STATUS 0x0C0
+#define UNIPHY_PLL_DEBUG_BUS0 0x0C4
+#define UNIPHY_PLL_DEBUG_BUS1 0x0C8
+#define UNIPHY_PLL_DEBUG_BUS2 0x0CC
+#define UNIPHY_PLL_DEBUG_BUS3 0x0D0
+#define UNIPHY_PLL_CTRL_54 0x0D4
+
+#define SATA_PHY_SER_CTRL 0x100
+#define SATA_PHY_TX_DRIV_CTRL0 0x104
+#define SATA_PHY_TX_DRIV_CTRL1 0x108
+#define SATA_PHY_TX_DRIV_CTRL2 0x10C
+#define SATA_PHY_TX_DRIV_CTRL3 0x110
+#define SATA_PHY_TX_RESV0 0x114
+#define SATA_PHY_TX_RESV1 0x118
+#define SATA_PHY_TX_IMCAL0 0x11C
+#define SATA_PHY_TX_IMCAL1 0x120
+#define SATA_PHY_TX_IMCAL2 0x124
+#define SATA_PHY_RX_IMCAL0 0x128
+#define SATA_PHY_RX_IMCAL1 0x12C
+#define SATA_PHY_RX_IMCAL2 0x130
+#define SATA_PHY_RX_TERM 0x134
+#define SATA_PHY_RX_TERM_RESV 0x138
+#define SATA_PHY_EQUAL 0x13C
+#define SATA_PHY_EQUAL_RESV 0x140
+#define SATA_PHY_OOB_TERM 0x144
+#define SATA_PHY_CDR_CTRL0 0x148
+#define SATA_PHY_CDR_CTRL1 0x14C
+#define SATA_PHY_CDR_CTRL2 0x150
+#define SATA_PHY_CDR_CTRL3 0x154
+#define SATA_PHY_CDR_CTRL4 0x158
+#define SATA_PHY_FA_LOAD0 0x15C
+#define SATA_PHY_FA_LOAD1 0x160
+#define SATA_PHY_CDR_CTRL_RESV 0x164
+#define SATA_PHY_PI_CTRL0 0x168
+#define SATA_PHY_PI_CTRL1 0x16C
+#define SATA_PHY_DESER_RESV 0x170
+#define SATA_PHY_RX_RESV0 0x174
+#define SATA_PHY_AD_TPA_CTRL 0x178
+#define SATA_PHY_REFCLK_CTRL 0x17C
+#define SATA_PHY_POW_DWN_CTRL0 0x180
+#define SATA_PHY_POW_DWN_CTRL1 0x184
+#define SATA_PHY_TX_DATA_CTRL 0x188
+#define SATA_PHY_BIST_GEN0 0x18C
+#define SATA_PHY_BIST_GEN1 0x190
+#define SATA_PHY_BIST_GEN2 0x194
+#define SATA_PHY_BIST_GEN3 0x198
+#define SATA_PHY_LBK_CTRL 0x19C
+#define SATA_PHY_TEST_DEBUG_CTRL 0x1A0
+#define SATA_PHY_ALIGNP 0x1A4
+#define SATA_PHY_PRBS_CFG0 0x1A8
+#define SATA_PHY_PRBS_CFG1 0x1AC
+#define SATA_PHY_PRBS_CFG2 0x1B0
+#define SATA_PHY_PRBS_CFG3 0x1B4
+#define SATA_PHY_CHAN_COMP_CHK_CNT 0x1B8
+#define SATA_PHY_RESET_CTRL 0x1BC
+#define SATA_PHY_RX_CLR 0x1C0
+#define SATA_PHY_RX_EBUF_CTRL 0x1C4
+#define SATA_PHY_ID0 0x1C8
+#define SATA_PHY_ID1 0x1CC
+#define SATA_PHY_ID2 0x1D0
+#define SATA_PHY_ID3 0x1D4
+#define SATA_PHY_RX_CHK_ERR_CNT0 0x1D8
+#define SATA_PHY_RX_CHK_ERR_CNT1 0x1DC
+#define SATA_PHY_RX_CHK_STAT 0x1E0
+#define SATA_PHY_TX_IMCAL_STAT 0x1E4
+#define SATA_PHY_RX_IMCAL_STAT 0x1E8
+#define SATA_PHY_RX_EBUF_STAT 0x1EC
+#define SATA_PHY_DEBUG_BUS_STAT0 0x1F0
+#define SATA_PHY_DEBUG_BUS_STAT1 0x1F4
+#define SATA_PHY_DEBUG_BUS_STAT2 0x1F8
+#define SATA_PHY_DEBUG_BUS_STAT3 0x1FC
+
+#define AHCI_HOST_CAP 0x00
+#define AHCI_HOST_CAP_MASK 0x1F
+#define AHCI_HOST_CAP_PMP (1 << 17)
+
+struct msm_sata_hba {
+ struct platform_device *ahci_pdev;
+ struct clk *slave_iface_clk;
+ struct clk *bus_clk;
+ struct clk *iface_clk;
+ struct clk *src_clk;
+ struct clk *rxoob_clk;
+ struct clk *pmalive_clk;
+ struct clk *cfg_clk;
+ struct regulator *clk_pwr;
+ struct regulator *pmp_pwr;
+ void __iomem *phy_base;
+ void __iomem *ahci_base;
+};
+
+static inline void msm_sata_delay_us(unsigned int delay)
+{
+ /* sleep for max. 50us more to combine processor wakeups */
+ usleep_range(delay, delay + 50);
+}
+
+static int msm_sata_clk_get_prepare_enable_set_rate(struct device *dev,
+ const char *name, struct clk **out_clk, int rate)
+{
+ int ret = 0;
+ struct clk *clk;
+
+ clk = devm_clk_get(dev, name);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ dev_err(dev, "failed to get clk: %s err = %d\n", name, ret);
+ goto out;
+ }
+
+ if (rate >= 0) {
+ ret = clk_set_rate(clk, rate);
+ if (ret) {
+ dev_err(dev, "failed to set rate: %d clk: %s err = %d\n",
+ rate, name, ret);
+ goto out;
+ }
+ }
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ dev_err(dev, "failed to enable clk: %s err = %d\n", name, ret);
+out:
+ if (!ret)
+ *out_clk = clk;
+
+ return ret;
+}
+
+static int msm_sata_clk_get_prepare_enable(struct device *dev,
+ const char *name, struct clk **out_clk)
+{
+ return msm_sata_clk_get_prepare_enable_set_rate(dev, name, out_clk, -1);
+}
+
+static void msm_sata_clk_put_unprepare_disable(struct clk **clk)
+{
+ if (*clk) {
+ clk_disable_unprepare(*clk);
+ clk_put(*clk);
+ *clk = NULL;
+ }
+}
+
+static int msm_sata_hard_reset(struct device *dev)
+{
+ int ret;
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ ret = clk_reset(hba->iface_clk, CLK_RESET_ASSERT);
+ if (ret) {
+ dev_err(dev, "iface_clk assert failed %d\n", ret);
+ goto out;
+ }
+
+ ret = clk_reset(hba->iface_clk, CLK_RESET_DEASSERT);
+ if (ret) {
+ dev_err(dev, "iface_clk de-assert failed %d\n", ret);
+ goto out;
+ }
+out:
+ return ret;
+}
+
+static int msm_sata_clk_init(struct device *dev)
+{
+ int ret = 0;
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ /* Enable AHB clock for system fabric slave port connected to SATA */
+ ret = msm_sata_clk_get_prepare_enable(dev,
+ "slave_iface_clk", &hba->slave_iface_clk);
+ if (ret)
+ goto out;
+
+ /* Enable AHB clock for system fabric and SATA core interface */
+ ret = msm_sata_clk_get_prepare_enable(dev,
+ "iface_clk", &hba->iface_clk);
+ if (ret)
+ goto put_dis_slave_iface_clk;
+
+ /* Enable AXI clock for SATA AXI master and slave interfaces */
+ ret = msm_sata_clk_get_prepare_enable(dev,
+ "bus_clk", &hba->bus_clk);
+ if (ret)
+ goto put_dis_iface_clk;
+
+ /* Enable the source clock for pmalive, rxoob and phy ref clocks */
+ ret = msm_sata_clk_get_prepare_enable_set_rate(dev,
+ "src_clk", &hba->src_clk, 100000000);
+ if (ret)
+ goto put_dis_bus_clk;
+
+ /*
+ * Enable RX OOB detection clock. The clock rate is
+ * same as PHY reference clock (100MHz).
+ */
+ ret = msm_sata_clk_get_prepare_enable(dev,
+ "core_rxoob_clk", &hba->rxoob_clk);
+ if (ret)
+ goto put_dis_src_clk;
+
+ /*
+ * Enable power management always-on clock. The clock rate
+ * is same as PHY reference clock (100MHz).
+ */
+ ret = msm_sata_clk_get_prepare_enable(dev,
+ "core_pmalive_clk", &hba->pmalive_clk);
+ if (ret)
+ goto put_dis_rxoob_clk;
+
+ /* Enable PHY configuration AHB clock, fixed 64MHz clock */
+ ret = msm_sata_clk_get_prepare_enable(dev,
+ "cfg_clk", &hba->cfg_clk);
+ if (ret)
+ goto put_dis_pmalive_clk;
+
+ return ret;
+
+put_dis_pmalive_clk:
+ msm_sata_clk_put_unprepare_disable(&hba->pmalive_clk);
+put_dis_rxoob_clk:
+ msm_sata_clk_put_unprepare_disable(&hba->rxoob_clk);
+put_dis_src_clk:
+ msm_sata_clk_put_unprepare_disable(&hba->src_clk);
+put_dis_bus_clk:
+ msm_sata_clk_put_unprepare_disable(&hba->bus_clk);
+put_dis_iface_clk:
+ msm_sata_clk_put_unprepare_disable(&hba->iface_clk);
+put_dis_slave_iface_clk:
+ msm_sata_clk_put_unprepare_disable(&hba->slave_iface_clk);
+out:
+ return ret;
+}
+
+static void msm_sata_clk_deinit(struct device *dev)
+{
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ msm_sata_clk_put_unprepare_disable(&hba->cfg_clk);
+ msm_sata_clk_put_unprepare_disable(&hba->pmalive_clk);
+ msm_sata_clk_put_unprepare_disable(&hba->rxoob_clk);
+ msm_sata_clk_put_unprepare_disable(&hba->src_clk);
+ msm_sata_clk_put_unprepare_disable(&hba->bus_clk);
+ msm_sata_clk_put_unprepare_disable(&hba->iface_clk);
+ msm_sata_clk_put_unprepare_disable(&hba->slave_iface_clk);
+}
+
+static int msm_sata_vreg_get_enable_set_vdd(struct device *dev,
+ const char *name, struct regulator **out_vreg,
+ int min_uV, int max_uV, int hpm_uA)
+{
+ int ret = 0;
+ struct regulator *vreg;
+
+ vreg = devm_regulator_get(dev, name);
+ if (IS_ERR(vreg)) {
+ ret = PTR_ERR(vreg);
+ dev_err(dev, "Regulator: %s get failed, err=%d\n", name, ret);
+ goto out;
+ }
+
+ if (regulator_count_voltages(vreg) > 0) {
+ ret = regulator_set_voltage(vreg, min_uV, max_uV);
+ if (ret) {
+ dev_err(dev, "Regulator: %s set voltage failed, err=%d\n",
+ name, ret);
+ goto err;
+ }
+
+ ret = regulator_set_optimum_mode(vreg, hpm_uA);
+ if (ret < 0) {
+ dev_err(dev, "Regulator: %s set optimum mode(uA_load=%d) failed, err=%d\n",
+ name, hpm_uA, ret);
+ goto err;
+ } else {
+ /*
+ * regulator_set_optimum_mode() can return non zero
+ * value even for success case.
+ */
+ ret = 0;
+ }
+ }
+
+ ret = regulator_enable(vreg);
+ if (ret)
+ dev_err(dev, "Regulator: %s enable failed, err=%d\n",
+ name, ret);
+err:
+ if (!ret)
+ *out_vreg = vreg;
+ else
+ devm_regulator_put(vreg);
+out:
+ return ret;
+}
+
+static int msm_sata_vreg_put_disable(struct device *dev,
+ struct regulator *reg, const char *name, int max_uV)
+{
+ int ret;
+
+ if (!reg)
+ return 0;
+
+ ret = regulator_disable(reg);
+ if (ret) {
+ dev_err(dev, "Regulator: %s disable failed err=%d\n",
+ name, ret);
+ goto err;
+ }
+
+ if (regulator_count_voltages(reg) > 0) {
+ ret = regulator_set_voltage(reg, 0, max_uV);
+ if (ret < 0) {
+ dev_err(dev, "Regulator: %s set voltage to 0 failed, err=%d\n",
+ name, ret);
+ goto err;
+ }
+
+ ret = regulator_set_optimum_mode(reg, 0);
+ if (ret < 0) {
+ dev_err(dev, "Regulator: %s set optimum mode(uA_load = 0) failed, err=%d\n",
+ name, ret);
+ goto err;
+ } else {
+ /*
+ * regulator_set_optimum_mode() can return non zero
+ * value even for success case.
+ */
+ ret = 0;
+ }
+ }
+
+err:
+ devm_regulator_put(reg);
+ return ret;
+}
+
+static int msm_sata_vreg_init(struct device *dev)
+{
+ int ret = 0;
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ /*
+ * The SATA clock generator needs 3.3V supply and can consume
+ * max. 850mA during functional mode.
+ */
+ ret = msm_sata_vreg_get_enable_set_vdd(dev, "sata_ext_3p3v",
+ &hba->clk_pwr, 3300000, 3300000, 850000);
+ if (ret)
+ goto out;
+
+ /* Add 1ms regulator ramp-up delay */
+ msm_sata_delay_us(1000);
+
+ /* Read AHCI capability register to check if PMP is supported.*/
+ if (readl_relaxed(hba->ahci_base +
+ AHCI_HOST_CAP) & AHCI_HOST_CAP_PMP) {
+ /* Power up port-multiplier */
+ ret = msm_sata_vreg_get_enable_set_vdd(dev, "sata_pmp_pwr",
+ &hba->pmp_pwr, 1800000, 1800000, 200000);
+ if (ret) {
+ msm_sata_vreg_put_disable(dev, hba->clk_pwr,
+ "sata_ext_3p3v", 3300000);
+ goto out;
+ }
+
+ /* Add 1ms regulator ramp-up delay */
+ msm_sata_delay_us(1000);
+ }
+
+out:
+ return ret;
+}
+
+static void msm_sata_vreg_deinit(struct device *dev)
+{
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ msm_sata_vreg_put_disable(dev, hba->clk_pwr,
+ "sata_ext_3p3v", 3300000);
+
+ if (hba->pmp_pwr)
+ msm_sata_vreg_put_disable(dev, hba->pmp_pwr,
+ "sata_pmp_pwr", 1800000);
+}
+
+static void msm_sata_phy_deinit(struct device *dev)
+{
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ /* Power down PHY */
+ writel_relaxed(0xF8, hba->phy_base + SATA_PHY_POW_DWN_CTRL0);
+ writel_relaxed(0xFE, hba->phy_base + SATA_PHY_POW_DWN_CTRL1);
+
+ /* Power down PLL block */
+ writel_relaxed(0x00, hba->phy_base + UNIPHY_PLL_GLB_CFG);
+ mb();
+
+ devm_iounmap(dev, hba->phy_base);
+}
+
+static int msm_sata_phy_init(struct device *dev)
+{
+ int ret = 0;
+ u32 reg = 0;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+ struct resource *mem;
+
+ mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem");
+ if (!mem) {
+ dev_err(dev, "no mmio space\n");
+ return -EINVAL;
+ }
+
+ hba->phy_base = devm_ioremap(dev, mem->start, resource_size(mem));
+ if (!hba->phy_base) {
+ dev_err(dev, "failed to allocate memory for SATA PHY\n");
+ return -ENOMEM;
+ }
+
+ /* SATA phy initialization */
+
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_SER_CTRL);
+
+ writel_relaxed(0xB1, hba->phy_base + SATA_PHY_POW_DWN_CTRL0);
+ mb();
+ msm_sata_delay_us(10);
+
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_POW_DWN_CTRL0);
+ writel_relaxed(0x3E, hba->phy_base + SATA_PHY_POW_DWN_CTRL1);
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_RX_IMCAL0);
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_TX_IMCAL0);
+ writel_relaxed(0x02, hba->phy_base + SATA_PHY_TX_IMCAL2);
+
+ /* Write UNIPHYPLL registers to configure PLL */
+ writel_relaxed(0x04, hba->phy_base + UNIPHY_PLL_REFCLK_CFG);
+ writel_relaxed(0x00, hba->phy_base + UNIPHY_PLL_PWRGEN_CFG);
+
+ writel_relaxed(0x0A, hba->phy_base + UNIPHY_PLL_CAL_CFG0);
+ writel_relaxed(0xF3, hba->phy_base + UNIPHY_PLL_CAL_CFG8);
+ writel_relaxed(0x01, hba->phy_base + UNIPHY_PLL_CAL_CFG9);
+ writel_relaxed(0xED, hba->phy_base + UNIPHY_PLL_CAL_CFG10);
+ writel_relaxed(0x02, hba->phy_base + UNIPHY_PLL_CAL_CFG11);
+
+ writel_relaxed(0x36, hba->phy_base + UNIPHY_PLL_SDM_CFG0);
+ writel_relaxed(0x0D, hba->phy_base + UNIPHY_PLL_SDM_CFG1);
+ writel_relaxed(0xA3, hba->phy_base + UNIPHY_PLL_SDM_CFG2);
+ writel_relaxed(0xF0, hba->phy_base + UNIPHY_PLL_SDM_CFG3);
+ writel_relaxed(0x00, hba->phy_base + UNIPHY_PLL_SDM_CFG4);
+
+ writel_relaxed(0x19, hba->phy_base + UNIPHY_PLL_SSC_CFG0);
+ writel_relaxed(0xE1, hba->phy_base + UNIPHY_PLL_SSC_CFG1);
+ writel_relaxed(0x00, hba->phy_base + UNIPHY_PLL_SSC_CFG2);
+ writel_relaxed(0x11, hba->phy_base + UNIPHY_PLL_SSC_CFG3);
+
+ writel_relaxed(0x04, hba->phy_base + UNIPHY_PLL_LKDET_CFG0);
+ writel_relaxed(0xFF, hba->phy_base + UNIPHY_PLL_LKDET_CFG1);
+
+ writel_relaxed(0x02, hba->phy_base + UNIPHY_PLL_GLB_CFG);
+ mb();
+ msm_sata_delay_us(40);
+
+ writel_relaxed(0x03, hba->phy_base + UNIPHY_PLL_GLB_CFG);
+ mb();
+ msm_sata_delay_us(400);
+
+ writel_relaxed(0x05, hba->phy_base + UNIPHY_PLL_LKDET_CFG2);
+ mb();
+
+ /* poll for ready status, timeout after 1 sec */
+ ret = readl_poll_timeout(hba->phy_base + UNIPHY_PLL_STATUS, reg,
+ (reg & 1 << 0), 100, 1000000);
+ if (ret) {
+ dev_err(dev, "poll timeout UNIPHY_PLL_STATUS\n");
+ goto out;
+ }
+
+ ret = readl_poll_timeout(hba->phy_base + SATA_PHY_TX_IMCAL_STAT, reg,
+ (reg & 1 << 0), 100, 1000000);
+ if (ret) {
+ dev_err(dev, "poll timeout SATA_PHY_TX_IMCAL_STAT\n");
+ goto out;
+ }
+
+ ret = readl_poll_timeout(hba->phy_base + SATA_PHY_RX_IMCAL_STAT, reg,
+ (reg & 1 << 0), 100, 1000000);
+ if (ret) {
+ dev_err(dev, "poll timeout SATA_PHY_RX_IMCAL_STAT\n");
+ goto out;
+ }
+
+ /* SATA phy calibrated succesfully, power up to functional mode */
+ writel_relaxed(0x3E, hba->phy_base + SATA_PHY_POW_DWN_CTRL1);
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_RX_IMCAL0);
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_TX_IMCAL0);
+
+ writel_relaxed(0x00, hba->phy_base + SATA_PHY_POW_DWN_CTRL1);
+ writel_relaxed(0x59, hba->phy_base + SATA_PHY_CDR_CTRL0);
+ writel_relaxed(0x04, hba->phy_base + SATA_PHY_CDR_CTRL1);
+ writel_relaxed(0x00, hba->phy_base + SATA_PHY_CDR_CTRL2);
+ writel_relaxed(0x00, hba->phy_base + SATA_PHY_PI_CTRL0);
+ writel_relaxed(0x00, hba->phy_base + SATA_PHY_CDR_CTRL3);
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_POW_DWN_CTRL0);
+
+ writel_relaxed(0x11, hba->phy_base + SATA_PHY_TX_DATA_CTRL);
+ writel_relaxed(0x43, hba->phy_base + SATA_PHY_ALIGNP);
+ writel_relaxed(0x04, hba->phy_base + SATA_PHY_OOB_TERM);
+
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_EQUAL);
+ writel_relaxed(0x09, hba->phy_base + SATA_PHY_TX_DRIV_CTRL0);
+ writel_relaxed(0x09, hba->phy_base + SATA_PHY_TX_DRIV_CTRL1);
+ mb();
+
+ dev_dbg(dev, "SATA PHY powered up in functional mode\n");
+
+out:
+ /* power down PHY in case of failure */
+ if (ret)
+ msm_sata_phy_deinit(dev);
+
+ return ret;
+}
+
+int msm_sata_init(struct device *ahci_dev, void __iomem *mmio)
+{
+ int ret;
+ struct device *dev = ahci_dev->parent;
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ /* Save ahci mmio to access vendor specific registers */
+ hba->ahci_base = mmio;
+
+ ret = msm_sata_clk_init(dev);
+ if (ret) {
+ dev_err(dev, "SATA clk init failed with err=%d\n", ret);
+ goto out;
+ }
+
+ ret = msm_sata_vreg_init(dev);
+ if (ret) {
+ dev_err(dev, "SATA vreg init failed with err=%d\n", ret);
+ msm_sata_clk_deinit(dev);
+ goto out;
+ }
+
+ ret = msm_sata_phy_init(dev);
+ if (ret) {
+ dev_err(dev, "SATA PHY init failed with err=%d\n", ret);
+ msm_sata_vreg_deinit(dev);
+ msm_sata_clk_deinit(dev);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+void msm_sata_deinit(struct device *ahci_dev)
+{
+ struct device *dev = ahci_dev->parent;
+
+ msm_sata_phy_deinit(dev);
+ msm_sata_vreg_deinit(dev);
+ msm_sata_clk_deinit(dev);
+}
+
+static int msm_sata_suspend(struct device *ahci_dev)
+{
+ msm_sata_deinit(ahci_dev);
+
+ return 0;
+}
+
+static int msm_sata_resume(struct device *ahci_dev)
+{
+ int ret;
+ struct device *dev = ahci_dev->parent;
+
+ ret = msm_sata_clk_init(dev);
+ if (ret) {
+ dev_err(dev, "SATA clk init failed with err=%d\n", ret);
+ /*
+ * If clock initialization failed, that means ahci driver
+ * cannot access any register going further. Since there is
+ * no check within ahci driver to check for clock failures,
+ * panic here instead of making an unclocked register access.
+ */
+ BUG();
+ }
+
+ /* Issue asynchronous reset to reset PHY */
+ ret = msm_sata_hard_reset(dev);
+ if (ret)
+ goto out;
+
+ ret = msm_sata_vreg_init(dev);
+ if (ret) {
+ dev_err(dev, "SATA vreg init failed with err=%d\n", ret);
+ /* Do not turn off clks, AHCI driver might do register access */
+ goto out;
+ }
+
+ ret = msm_sata_phy_init(dev);
+ if (ret) {
+ dev_err(dev, "SATA PHY init failed with err=%d\n", ret);
+ /* Do not turn off clks, AHCI driver might do register access */
+ msm_sata_vreg_deinit(dev);
+ goto out;
+ }
+out:
+ return ret;
+}
+
+static struct ahci_platform_data msm_ahci_pdata = {
+ .init = msm_sata_init,
+ .exit = msm_sata_deinit,
+ .suspend = msm_sata_suspend,
+ .resume = msm_sata_resume,
+};
+
+static int __devinit msm_sata_probe(struct platform_device *pdev)
+{
+ struct platform_device *ahci;
+ struct msm_sata_hba *hba;
+ int ret = 0;
+
+ hba = devm_kzalloc(&pdev->dev, sizeof(struct msm_sata_hba), GFP_KERNEL);
+ if (!hba) {
+ dev_err(&pdev->dev, "no memory\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, hba);
+
+ ahci = platform_device_alloc("ahci", pdev->id);
+ if (!ahci) {
+ dev_err(&pdev->dev, "couldn't allocate ahci device\n");
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ dma_set_coherent_mask(&ahci->dev, pdev->dev.coherent_dma_mask);
+
+ ahci->dev.parent = &pdev->dev;
+ ahci->dev.dma_mask = pdev->dev.dma_mask;
+ ahci->dev.dma_parms = pdev->dev.dma_parms;
+ hba->ahci_pdev = ahci;
+
+ ret = platform_device_add_resources(ahci, pdev->resource,
+ pdev->num_resources);
+ if (ret) {
+ dev_err(&pdev->dev, "couldn't add resources to ahci device\n");
+ goto err_put_device;
+ }
+
+ ahci->dev.platform_data = &msm_ahci_pdata;
+ ret = platform_device_add(ahci);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register ahci device\n");
+ goto err_put_device;
+ }
+
+ return 0;
+
+err_put_device:
+ platform_device_put(ahci);
+err_free:
+ devm_kfree(&pdev->dev, hba);
+err:
+ return ret;
+}
+
+static int __devexit msm_sata_remove(struct platform_device *pdev)
+{
+ struct msm_sata_hba *hba = platform_get_drvdata(pdev);
+
+ platform_device_unregister(hba->ahci_pdev);
+
+ return 0;
+}
+
+static struct platform_driver msm_sata_driver = {
+ .probe = msm_sata_probe,
+ .remove = __devexit_p(msm_sata_remove),
+ .driver = {
+ .name = "msm_sata",
+ },
+};
+
+module_platform_driver(msm_sata_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("AHCI platform MSM Glue Layer");
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index ced7a80..0f82142 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -21,6 +21,7 @@
#include <linux/platform_device.h>
#include <linux/libata.h>
#include <linux/ahci_platform.h>
+#include <linux/pm_runtime.h>
#include "ahci.h"
enum ahci_type {
@@ -192,6 +193,14 @@
if (rc)
goto err0;
+ rc = pm_runtime_set_active(dev);
+ if (rc) {
+ dev_warn(dev, "Unable to set runtime pm active err=%d\n", rc);
+ } else {
+ pm_runtime_enable(dev);
+ pm_runtime_forbid(dev);
+ }
+
return 0;
err0:
if (pdata && pdata->exit)
@@ -275,6 +284,8 @@
static struct dev_pm_ops ahci_pm_ops = {
.suspend = &ahci_suspend,
.resume = &ahci_resume,
+ .runtime_suspend = &ahci_suspend,
+ .runtime_resume = &ahci_resume,
};
#endif
diff --git a/drivers/base/sync.c b/drivers/base/sync.c
index c0690c8..202c40d 100644
--- a/drivers/base/sync.c
+++ b/drivers/base/sync.c
@@ -28,8 +28,13 @@
#include <linux/anon_inodes.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/sync.h>
+
static void sync_fence_signal_pt(struct sync_pt *pt);
static int _sync_pt_has_signaled(struct sync_pt *pt);
+static void sync_fence_free(struct kref *kref);
+static void sync_dump(void);
static LIST_HEAD(sync_timeline_list_head);
static DEFINE_SPINLOCK(sync_timeline_list_lock);
@@ -50,6 +55,7 @@
if (obj == NULL)
return NULL;
+ kref_init(&obj->kref);
obj->ops = ops;
strlcpy(obj->name, name, sizeof(obj->name));
@@ -67,8 +73,10 @@
}
EXPORT_SYMBOL(sync_timeline_create);
-static void sync_timeline_free(struct sync_timeline *obj)
+static void sync_timeline_free(struct kref *kref)
{
+ struct sync_timeline *obj =
+ container_of(kref, struct sync_timeline, kref);
unsigned long flags;
if (obj->ops->release_obj)
@@ -83,17 +91,14 @@
void sync_timeline_destroy(struct sync_timeline *obj)
{
- unsigned long flags;
- bool needs_freeing;
-
- spin_lock_irqsave(&obj->child_list_lock, flags);
obj->destroyed = true;
- needs_freeing = list_empty(&obj->child_list_head);
- spin_unlock_irqrestore(&obj->child_list_lock, flags);
- if (needs_freeing)
- sync_timeline_free(obj);
- else
+ /*
+ * If this is not the last reference, signal any children
+ * that their parent is going away.
+ */
+
+ if (!kref_put(&obj->kref, sync_timeline_free))
sync_timeline_signal(obj);
}
EXPORT_SYMBOL(sync_timeline_destroy);
@@ -113,7 +118,6 @@
{
struct sync_timeline *obj = pt->parent;
unsigned long flags;
- bool needs_freeing;
spin_lock_irqsave(&obj->active_list_lock, flags);
if (!list_empty(&pt->active_list))
@@ -121,12 +125,10 @@
spin_unlock_irqrestore(&obj->active_list_lock, flags);
spin_lock_irqsave(&obj->child_list_lock, flags);
- list_del(&pt->child_list);
- needs_freeing = obj->destroyed && list_empty(&obj->child_list_head);
+ if (!list_empty(&pt->child_list)) {
+ list_del_init(&pt->child_list);
+ }
spin_unlock_irqrestore(&obj->child_list_lock, flags);
-
- if (needs_freeing)
- sync_timeline_free(obj);
}
void sync_timeline_signal(struct sync_timeline *obj)
@@ -135,24 +137,30 @@
LIST_HEAD(signaled_pts);
struct list_head *pos, *n;
+ trace_sync_timeline(obj);
+
spin_lock_irqsave(&obj->active_list_lock, flags);
list_for_each_safe(pos, n, &obj->active_list_head) {
struct sync_pt *pt =
container_of(pos, struct sync_pt, active_list);
- if (_sync_pt_has_signaled(pt))
- list_move(pos, &signaled_pts);
+ if (_sync_pt_has_signaled(pt)) {
+ list_del_init(pos);
+ list_add(&pt->signaled_list, &signaled_pts);
+ kref_get(&pt->fence->kref);
+ }
}
spin_unlock_irqrestore(&obj->active_list_lock, flags);
list_for_each_safe(pos, n, &signaled_pts) {
struct sync_pt *pt =
- container_of(pos, struct sync_pt, active_list);
+ container_of(pos, struct sync_pt, signaled_list);
list_del_init(pos);
sync_fence_signal_pt(pt);
+ kref_put(&pt->fence->kref, sync_fence_free);
}
}
EXPORT_SYMBOL(sync_timeline_signal);
@@ -169,6 +177,7 @@
return NULL;
INIT_LIST_HEAD(&pt->active_list);
+ kref_get(&parent->kref);
sync_timeline_add_pt(parent, pt);
return pt;
@@ -182,6 +191,8 @@
sync_timeline_remove_pt(pt);
+ kref_put(&pt->parent->kref, sync_timeline_free);
+
kfree(pt);
}
EXPORT_SYMBOL(sync_pt_free);
@@ -255,6 +266,7 @@
if (fence->file == NULL)
goto err;
+ kref_init(&fence->kref);
strlcpy(fence->name, name, sizeof(fence->name));
INIT_LIST_HEAD(&fence->pt_list_head);
@@ -290,6 +302,12 @@
list_add(&pt->pt_list, &fence->pt_list_head);
sync_pt_activate(pt);
+ /*
+ * signal the fence in case pt was activated before
+ * sync_pt_activate(pt) was called
+ */
+ sync_fence_signal_pt(pt);
+
return fence;
}
EXPORT_SYMBOL(sync_fence_create);
@@ -314,6 +332,65 @@
return 0;
}
+static int sync_fence_merge_pts(struct sync_fence *dst, struct sync_fence *src)
+{
+ struct list_head *src_pos, *dst_pos, *n;
+
+ list_for_each(src_pos, &src->pt_list_head) {
+ struct sync_pt *src_pt =
+ container_of(src_pos, struct sync_pt, pt_list);
+ bool collapsed = false;
+
+ list_for_each_safe(dst_pos, n, &dst->pt_list_head) {
+ struct sync_pt *dst_pt =
+ container_of(dst_pos, struct sync_pt, pt_list);
+ /* collapse two sync_pts on the same timeline
+ * to a single sync_pt that will signal at
+ * the later of the two
+ */
+ if (dst_pt->parent == src_pt->parent) {
+ if (dst_pt->parent->ops->compare(dst_pt, src_pt) == -1) {
+ struct sync_pt *new_pt =
+ sync_pt_dup(src_pt);
+ if (new_pt == NULL)
+ return -ENOMEM;
+
+ new_pt->fence = dst;
+ list_replace(&dst_pt->pt_list,
+ &new_pt->pt_list);
+ sync_pt_activate(new_pt);
+ sync_pt_free(dst_pt);
+ }
+ collapsed = true;
+ break;
+ }
+ }
+
+ if (!collapsed) {
+ struct sync_pt *new_pt = sync_pt_dup(src_pt);
+
+ if (new_pt == NULL)
+ return -ENOMEM;
+
+ new_pt->fence = dst;
+ list_add(&new_pt->pt_list, &dst->pt_list_head);
+ sync_pt_activate(new_pt);
+ }
+ }
+
+ return 0;
+}
+
+static void sync_fence_detach_pts(struct sync_fence *fence)
+{
+ struct list_head *pos, *n;
+
+ list_for_each_safe(pos, n, &fence->pt_list_head) {
+ struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list);
+ sync_timeline_remove_pt(pt);
+ }
+}
+
static void sync_fence_free_pts(struct sync_fence *fence)
{
struct list_head *pos, *n;
@@ -388,11 +465,17 @@
if (err < 0)
goto err;
- err = sync_fence_copy_pts(fence, b);
+ err = sync_fence_merge_pts(fence, b);
if (err < 0)
goto err;
- fence->status = sync_fence_get_status(fence);
+ /*
+ * signal the fence in case one of it's pts were activated before
+ * they were activated
+ */
+ sync_fence_signal_pt(list_first_entry(&fence->pt_list_head,
+ struct sync_pt,
+ pt_list));
return fence;
err:
@@ -491,44 +574,87 @@
}
EXPORT_SYMBOL(sync_fence_cancel_async);
+static bool sync_fence_check(struct sync_fence *fence)
+{
+ /*
+ * Make sure that reads to fence->status are ordered with the
+ * wait queue event triggering
+ */
+ smp_rmb();
+ return fence->status != 0;
+}
+
int sync_fence_wait(struct sync_fence *fence, long timeout)
{
- int err;
+ int err = 0;
+ struct sync_pt *pt;
- if (timeout) {
+ trace_sync_wait(fence, 1);
+ list_for_each_entry(pt, &fence->pt_list_head, pt_list)
+ trace_sync_pt(pt);
+
+ if (timeout > 0) {
timeout = msecs_to_jiffies(timeout);
err = wait_event_interruptible_timeout(fence->wq,
- fence->status != 0,
+ sync_fence_check(fence),
timeout);
- } else {
- err = wait_event_interruptible(fence->wq, fence->status != 0);
+ } else if (timeout < 0) {
+ err = wait_event_interruptible(fence->wq,
+ sync_fence_check(fence));
}
+ trace_sync_wait(fence, 0);
if (err < 0)
return err;
- if (fence->status < 0)
+ if (fence->status < 0) {
+ pr_info("fence error %d on [%p]\n", fence->status, fence);
+ sync_dump();
return fence->status;
+ }
- if (fence->status == 0)
+ if (fence->status == 0) {
+ pr_info("fence timeout on [%p] after %dms\n", fence,
+ jiffies_to_msecs(timeout));
+ sync_dump();
return -ETIME;
+ }
return 0;
}
EXPORT_SYMBOL(sync_fence_wait);
+static void sync_fence_free(struct kref *kref)
+{
+ struct sync_fence *fence = container_of(kref, struct sync_fence, kref);
+
+ sync_fence_free_pts(fence);
+
+ kfree(fence);
+}
+
static int sync_fence_release(struct inode *inode, struct file *file)
{
struct sync_fence *fence = file->private_data;
unsigned long flags;
- sync_fence_free_pts(fence);
-
+ /*
+ * We need to remove all ways to access this fence before droping
+ * our ref.
+ *
+ * start with its membership in the global fence list
+ */
spin_lock_irqsave(&sync_fence_list_lock, flags);
list_del(&fence->sync_fence_list);
spin_unlock_irqrestore(&sync_fence_list_lock, flags);
- kfree(fence);
+ /*
+ * remove its pts from their parents so that sync_timeline_signal()
+ * can't reference the fence.
+ */
+ sync_fence_detach_pts(fence);
+
+ kref_put(&fence->kref, sync_fence_free);
return 0;
}
@@ -539,6 +665,12 @@
poll_wait(file, &fence->wq, wait);
+ /*
+ * Make sure that reads to fence->status are ordered with the
+ * wait queue event triggering
+ */
+ smp_rmb();
+
if (fence->status == 1)
return POLLIN;
else if (fence->status < 0)
@@ -549,7 +681,7 @@
static long sync_fence_ioctl_wait(struct sync_fence *fence, unsigned long arg)
{
- __u32 value;
+ __s32 value;
if (copy_from_user(&value, (void __user *)arg, sizeof(value)))
return -EFAULT;
@@ -564,8 +696,13 @@
struct sync_fence *fence2, *fence3;
struct sync_merge_data data;
- if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
- return -EFAULT;
+ if (fd < 0)
+ return fd;
+
+ if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {
+ err = -EFAULT;
+ goto err_put_fd;
+ }
fence2 = sync_fence_fdget(data.fd2);
if (fence2 == NULL) {
@@ -722,7 +859,17 @@
seq_printf(s, "@%ld.%06ld", tv.tv_sec, tv.tv_usec);
}
- if (pt->parent->ops->print_pt) {
+ if (pt->parent->ops->timeline_value_str &&
+ pt->parent->ops->pt_value_str) {
+ char value[64];
+ pt->parent->ops->pt_value_str(pt, value, sizeof(value));
+ seq_printf(s, ": %s", value);
+ if (fence) {
+ pt->parent->ops->timeline_value_str(pt->parent, value,
+ sizeof(value));
+ seq_printf(s, " / %s", value);
+ }
+ } else if (pt->parent->ops->print_pt) {
seq_printf(s, ": ");
pt->parent->ops->print_pt(s, pt);
}
@@ -737,7 +884,11 @@
seq_printf(s, "%s %s", obj->name, obj->ops->driver_name);
- if (obj->ops->print_obj) {
+ if (obj->ops->timeline_value_str) {
+ char value[64];
+ obj->ops->timeline_value_str(obj, value, sizeof(value));
+ seq_printf(s, ": %s", value);
+ } else if (obj->ops->print_obj) {
seq_printf(s, ": ");
obj->ops->print_obj(s, obj);
}
@@ -758,7 +909,8 @@
struct list_head *pos;
unsigned long flags;
- seq_printf(s, "%s: %s\n", fence->name, sync_status_str(fence->status));
+ seq_printf(s, "[%p] %s: %s\n", fence, fence->name,
+ sync_status_str(fence->status));
list_for_each(pos, &fence->pt_list_head) {
struct sync_pt *pt =
@@ -826,7 +978,34 @@
debugfs_create_file("sync", S_IRUGO, NULL, NULL, &sync_debugfs_fops);
return 0;
}
-
late_initcall(sync_debugfs_init);
+#define DUMP_CHUNK 256
+static char sync_dump_buf[64 * 1024];
+void sync_dump(void)
+{
+ struct seq_file s = {
+ .buf = sync_dump_buf,
+ .size = sizeof(sync_dump_buf) - 1,
+ };
+ int i;
+
+ sync_debugfs_show(&s, NULL);
+
+ for (i = 0; i < s.count; i += DUMP_CHUNK) {
+ if ((s.count - i) > DUMP_CHUNK) {
+ char c = s.buf[i + DUMP_CHUNK];
+ s.buf[i + DUMP_CHUNK] = 0;
+ pr_cont("%s", s.buf + i);
+ s.buf[i + DUMP_CHUNK] = c;
+ } else {
+ s.buf[s.count] = 0;
+ pr_cont("%s", s.buf + i);
+ }
+ }
+}
+#else
+static void sync_dump(void)
+{
+}
#endif
diff --git a/drivers/coresight/coresight-tmc.c b/drivers/coresight/coresight-tmc.c
index 3bb9ec7..09cda5d 100644
--- a/drivers/coresight/coresight-tmc.c
+++ b/drivers/coresight/coresight-tmc.c
@@ -50,41 +50,45 @@
mb(); \
} while (0)
-#define TMC_RSZ (0x004)
-#define TMC_STS (0x00C)
-#define TMC_RRD (0x010)
-#define TMC_RRP (0x014)
-#define TMC_RWP (0x018)
-#define TMC_TRG (0x01C)
-#define TMC_CTL (0x020)
-#define TMC_RWD (0x024)
-#define TMC_MODE (0x028)
-#define TMC_LBUFLEVEL (0x02C)
-#define TMC_CBUFLEVEL (0x030)
-#define TMC_BUFWM (0x034)
-#define TMC_RRPHI (0x038)
-#define TMC_RWPHI (0x03C)
-#define TMC_AXICTL (0x110)
-#define TMC_DBALO (0x118)
-#define TMC_DBAHI (0x11C)
-#define TMC_FFSR (0x300)
-#define TMC_FFCR (0x304)
-#define TMC_PSCR (0x308)
-#define TMC_ITMISCOP0 (0xEE0)
-#define TMC_ITTRFLIN (0xEE8)
-#define TMC_ITATBDATA0 (0xEEC)
-#define TMC_ITATBCTR2 (0xEF0)
-#define TMC_ITATBCTR1 (0xEF4)
-#define TMC_ITATBCTR0 (0xEF8)
+#define TMC_RSZ (0x004)
+#define TMC_STS (0x00C)
+#define TMC_RRD (0x010)
+#define TMC_RRP (0x014)
+#define TMC_RWP (0x018)
+#define TMC_TRG (0x01C)
+#define TMC_CTL (0x020)
+#define TMC_RWD (0x024)
+#define TMC_MODE (0x028)
+#define TMC_LBUFLEVEL (0x02C)
+#define TMC_CBUFLEVEL (0x030)
+#define TMC_BUFWM (0x034)
+#define TMC_RRPHI (0x038)
+#define TMC_RWPHI (0x03C)
+#define TMC_AXICTL (0x110)
+#define TMC_DBALO (0x118)
+#define TMC_DBAHI (0x11C)
+#define TMC_FFSR (0x300)
+#define TMC_FFCR (0x304)
+#define TMC_PSCR (0x308)
+#define TMC_ITMISCOP0 (0xEE0)
+#define TMC_ITTRFLIN (0xEE8)
+#define TMC_ITATBDATA0 (0xEEC)
+#define TMC_ITATBCTR2 (0xEF0)
+#define TMC_ITATBCTR1 (0xEF4)
+#define TMC_ITATBCTR0 (0xEF8)
-#define BYTES_PER_WORD 4
-#define TMC_ETR_BAM_PIPE_INDEX 0
-#define TMC_ETR_BAM_NR_PIPES 2
+#define BYTES_PER_WORD 4
+#define TMC_ETR_BAM_PIPE_INDEX 0
+#define TMC_ETR_BAM_NR_PIPES 2
-#define TMC_ETFETB_DUMP_VER_OFF (4)
-#define TMC_ETFETB_DUMP_VER (1)
-#define TMC_REG_DUMP_VER_OFF (4)
-#define TMC_REG_DUMP_VER (1)
+#define TMC_ETFETB_DUMP_MAGIC_OFF (0)
+#define TMC_ETFETB_DUMP_MAGIC (0x5D1DB1BF)
+#define TMC_ETFETB_DUMP_VER_OFF (4)
+#define TMC_ETFETB_DUMP_VER (1)
+#define TMC_REG_DUMP_MAGIC_OFF (0)
+#define TMC_REG_DUMP_MAGIC (0x5D1DB1BF)
+#define TMC_REG_DUMP_VER_OFF (4)
+#define TMC_REG_DUMP_VER (1)
enum tmc_config_type {
TMC_CONFIG_TYPE_ETB,
@@ -134,6 +138,8 @@
struct mutex read_lock;
int read_count;
bool reading;
+ bool aborting;
+ char *reg_buf;
char *buf;
unsigned long paddr;
void __iomem *vaddr;
@@ -462,10 +468,64 @@
return tmc_enable(drvdata, TMC_MODE_HARDWARE_FIFO);
}
+static void __tmc_reg_dump(struct tmc_drvdata *drvdata)
+{
+ char *reg_hdr;
+ uint32_t *reg_buf;
+
+ if (!drvdata->reg_buf || !drvdata->aborting)
+ return;
+
+ reg_hdr = drvdata->reg_buf - PAGE_SIZE;
+ reg_buf = (uint32_t *)drvdata->reg_buf;
+
+ reg_buf[1] = tmc_readl(drvdata, TMC_RSZ);
+ reg_buf[3] = tmc_readl(drvdata, TMC_STS);
+ reg_buf[5] = tmc_readl(drvdata, TMC_RRP);
+ reg_buf[6] = tmc_readl(drvdata, TMC_RWP);
+ reg_buf[7] = tmc_readl(drvdata, TMC_TRG);
+ reg_buf[8] = tmc_readl(drvdata, TMC_CTL);
+ reg_buf[10] = tmc_readl(drvdata, TMC_MODE);
+ reg_buf[11] = tmc_readl(drvdata, TMC_LBUFLEVEL);
+ reg_buf[12] = tmc_readl(drvdata, TMC_CBUFLEVEL);
+ reg_buf[13] = tmc_readl(drvdata, TMC_BUFWM);
+ if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
+ reg_buf[14] = tmc_readl(drvdata, TMC_RRPHI);
+ reg_buf[15] = tmc_readl(drvdata, TMC_RWPHI);
+ reg_buf[68] = tmc_readl(drvdata, TMC_AXICTL);
+ reg_buf[70] = tmc_readl(drvdata, TMC_DBALO);
+ reg_buf[71] = tmc_readl(drvdata, TMC_DBAHI);
+ }
+ reg_buf[192] = tmc_readl(drvdata, TMC_FFSR);
+ reg_buf[193] = tmc_readl(drvdata, TMC_FFCR);
+ reg_buf[194] = tmc_readl(drvdata, TMC_PSCR);
+ reg_buf[1000] = tmc_readl(drvdata, CORESIGHT_CLAIMSET);
+ reg_buf[1001] = tmc_readl(drvdata, CORESIGHT_CLAIMCLR);
+ reg_buf[1005] = tmc_readl(drvdata, CORESIGHT_LSR);
+ reg_buf[1006] = tmc_readl(drvdata, CORESIGHT_AUTHSTATUS);
+ reg_buf[1010] = tmc_readl(drvdata, CORESIGHT_DEVID);
+ reg_buf[1011] = tmc_readl(drvdata, CORESIGHT_DEVTYPE);
+ reg_buf[1012] = tmc_readl(drvdata, CORESIGHT_PERIPHIDR4);
+ reg_buf[1013] = tmc_readl(drvdata, CORESIGHT_PERIPHIDR5);
+ reg_buf[1014] = tmc_readl(drvdata, CORESIGHT_PERIPHIDR6);
+ reg_buf[1015] = tmc_readl(drvdata, CORESIGHT_PERIPHIDR7);
+ reg_buf[1016] = tmc_readl(drvdata, CORESIGHT_PERIPHIDR0);
+ reg_buf[1017] = tmc_readl(drvdata, CORESIGHT_PERIPHIDR1);
+ reg_buf[1018] = tmc_readl(drvdata, CORESIGHT_PERIPHIDR2);
+ reg_buf[1019] = tmc_readl(drvdata, CORESIGHT_PERIPHIDR3);
+ reg_buf[1020] = tmc_readl(drvdata, CORESIGHT_COMPIDR0);
+ reg_buf[1021] = tmc_readl(drvdata, CORESIGHT_COMPIDR1);
+ reg_buf[1022] = tmc_readl(drvdata, CORESIGHT_COMPIDR2);
+ reg_buf[1023] = tmc_readl(drvdata, CORESIGHT_COMPIDR3);
+
+ *(uint32_t *)(reg_hdr + TMC_REG_DUMP_MAGIC_OFF) = TMC_REG_DUMP_MAGIC;
+}
+
static void __tmc_etb_dump(struct tmc_drvdata *drvdata)
{
enum tmc_mem_intf_width memwidth;
uint8_t memwords;
+ char *hdr;
char *bufp;
uint32_t read_data;
int i;
@@ -485,11 +545,18 @@
for (i = 0; i < memwords; i++) {
read_data = tmc_readl(drvdata, TMC_RRD);
if (read_data == 0xFFFFFFFF)
- return;
+ goto out;
memcpy(bufp, &read_data, BYTES_PER_WORD);
bufp += BYTES_PER_WORD;
}
}
+
+out:
+ if (drvdata->aborting) {
+ hdr = drvdata->buf - PAGE_SIZE;
+ *(uint32_t *)(hdr + TMC_ETFETB_DUMP_MAGIC_OFF) =
+ TMC_ETFETB_DUMP_MAGIC;
+ }
}
static void __tmc_etb_disable(struct tmc_drvdata *drvdata)
@@ -498,6 +565,7 @@
tmc_flush_and_stop(drvdata);
__tmc_etb_dump(drvdata);
+ __tmc_reg_dump(drvdata);
__tmc_disable(drvdata);
TMC_LOCK(drvdata);
@@ -522,6 +590,7 @@
tmc_flush_and_stop(drvdata);
__tmc_etr_dump(drvdata);
+ __tmc_reg_dump(drvdata);
__tmc_disable(drvdata);
TMC_LOCK(drvdata);
@@ -604,6 +673,8 @@
unsigned long flags;
enum tmc_mode mode;
+ drvdata->aborting = true;
+
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading)
goto out0;
@@ -1077,8 +1148,10 @@
dump.start_addr = virt_to_phys(baddr);
dump.end_addr = dump.start_addr + PAGE_SIZE + drvdata->size;
ret = msm_dump_table_register(&dump);
- /* Don't free the buffer in case of error since it can still
- * be used to provide dump collection via the device node
+ /*
+ * Don't free the buffer in case of error since it can still
+ * be used to provide dump collection via the device node or
+ * as part of abort.
*/
if (ret)
dev_info(dev, "TMC ETF-ETB dump setup failed\n");
@@ -1087,15 +1160,19 @@
baddr = devm_kzalloc(dev, PAGE_SIZE + reg_size, GFP_KERNEL);
if (baddr) {
+ drvdata->reg_buf = baddr + PAGE_SIZE;
*(uint32_t *)(baddr + TMC_REG_DUMP_VER_OFF) = TMC_REG_DUMP_VER;
dump.id = MSM_TMC0_REG + count;
dump.start_addr = virt_to_phys(baddr);
dump.end_addr = dump.start_addr + PAGE_SIZE + reg_size;
ret = msm_dump_table_register(&dump);
- if (ret) {
- devm_kfree(dev, baddr);
+ /*
+ * Don't free the buffer in case of error since it can still
+ * be used to dump registers as part of abort to aid post crash
+ * parsing.
+ */
+ if (ret)
dev_info(dev, "TMC REG dump setup failed\n");
- }
} else {
dev_info(dev, "TMC REG dump space allocation failed\n");
}
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 6fb11e1..4f54a0c 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -36,6 +36,16 @@
If unsure, say N here.
+config MSM_IOMMU_PMON
+ bool "MSM IOMMU Perfomance Monitoring Support"
+ depends on ARCH_MSM8974 && MSM_IOMMU
+ help
+ Support for monitoring IOMMUs performance on certain Qualcomm SOCs.
+ It captures TLB statistics per context bank of the IOMMU as an
+ indication of its performance metric.
+
+ If unsure, say N here.
+
config IOMMU_PGTABLES_L2
bool "Allow SMMU page tables in the L2 cache (Experimental)"
depends on MSM_IOMMU && MMU && SMP && CPU_DCACHE_DISABLE=n
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 11d50c1..5db4258 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -3,6 +3,7 @@
ifdef CONFIG_OF
obj-$(CONFIG_MSM_IOMMU) += msm_iommu-v2.o msm_iommu_dev-v2.o msm_iommu_pagetable.o msm_iommu_sec.o
endif
+obj-$(CONFIG_MSM_IOMMU_PMON) += msm_iommu_perfmon.o
obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
obj-$(CONFIG_DMAR_TABLE) += dmar.o
diff --git a/drivers/iommu/msm_iommu-v2.c b/drivers/iommu/msm_iommu-v2.c
index aab8d24..79bd15c 100644
--- a/drivers/iommu/msm_iommu-v2.c
+++ b/drivers/iommu/msm_iommu-v2.c
@@ -30,7 +30,7 @@
#include <mach/iommu_hw-v2.h>
#include <mach/iommu.h>
-
+#include <mach/iommu_perfmon.h>
#include "msm_iommu_pagetable.h"
/* bitmap of the page sizes currently supported */
@@ -105,6 +105,53 @@
clk_disable_unprepare(drvdata->pclk);
}
+static int _iommu_power_off(void *data)
+{
+ struct msm_iommu_drvdata *drvdata;
+
+ drvdata = (struct msm_iommu_drvdata *)data;
+ __disable_clocks(drvdata);
+ __disable_regulators(drvdata);
+ return 0;
+}
+
+static int _iommu_power_on(void *data)
+{
+ int ret;
+ struct msm_iommu_drvdata *drvdata;
+
+ drvdata = (struct msm_iommu_drvdata *)data;
+ ret = __enable_regulators(drvdata);
+ if (ret)
+ goto fail;
+
+ ret = __enable_clocks(drvdata);
+ if (ret) {
+ __disable_regulators(drvdata);
+ goto fail;
+ }
+ return 0;
+fail:
+ return -EIO;
+}
+
+static void _iommu_lock_acquire(void)
+{
+ mutex_lock(&msm_iommu_lock);
+}
+
+static void _iommu_lock_release(void)
+{
+ mutex_unlock(&msm_iommu_lock);
+}
+
+struct iommu_access_ops iommu_access_ops = {
+ .iommu_power_on = _iommu_power_on,
+ .iommu_power_off = _iommu_power_off,
+ .iommu_lock_acquire = _iommu_lock_acquire,
+ .iommu_lock_release = _iommu_lock_release,
+};
+
static void __sync_tlb(void __iomem *base, int ctx)
{
SET_TLBSYNC(base, ctx, 0);
@@ -191,7 +238,6 @@
SET_GFAR(base, 0);
SET_GFSRRESTORE(base, 0);
SET_TLBIALLNSNH(base, 0);
- SET_PMCR(base, 0);
SET_SCR1(base, 0);
SET_SSDR_N(base, 0, 0);
smt_size = GET_IDR0_NUMSMRG(base);
@@ -542,6 +588,10 @@
list_add(&(ctx_drvdata->attached_elm), &priv->list_attached);
ctx_drvdata->attached_domain = domain;
+ mutex_unlock(&msm_iommu_lock);
+
+ msm_iommu_attached(dev->parent);
+ return ret;
fail:
mutex_unlock(&msm_iommu_lock);
return ret;
@@ -556,6 +606,8 @@
int ret;
int is_secure;
+ msm_iommu_detached(dev->parent);
+
mutex_lock(&msm_iommu_lock);
priv = domain->priv;
if (!priv || !dev)
diff --git a/drivers/iommu/msm_iommu_dev-v2.c b/drivers/iommu/msm_iommu_dev-v2.c
index 9b6dc0e..4571595 100644
--- a/drivers/iommu/msm_iommu_dev-v2.c
+++ b/drivers/iommu/msm_iommu_dev-v2.c
@@ -27,6 +27,7 @@
#include <mach/iommu_hw-v2.h>
#include <mach/iommu.h>
+#include <mach/iommu_perfmon.h>
static int msm_iommu_parse_bfb_settings(struct platform_device *pdev,
struct msm_iommu_drvdata *drvdata)
@@ -108,7 +109,11 @@
pr_err("Failed to create %s device\n", child->name);
}
- drvdata->name = dev_name(&pdev->dev);
+ ret = of_property_read_string(pdev->dev.of_node, "label",
+ &drvdata->name);
+ if (ret)
+ goto fail;
+
drvdata->sec_id = -1;
of_property_read_u32(pdev->dev.of_node, "qcom,iommu-secure-id",
&drvdata->sec_id);
@@ -130,8 +135,24 @@
return ret;
}
+static int msm_iommu_pmon_parse_dt(struct platform_device *pdev,
+ struct iommu_info *pmon_info)
+{
+ int ret = 0;
+ int irq = platform_get_irq(pdev, 0);
+
+ if (irq > 0) {
+ pmon_info->evt_irq = platform_get_irq(pdev, 0);
+ } else {
+ pmon_info->evt_irq = -1;
+ ret = irq;
+ }
+ return ret;
+}
+
static int __devinit msm_iommu_probe(struct platform_device *pdev)
{
+ struct iommu_info *pmon_info;
struct msm_iommu_drvdata *drvdata;
struct resource *r;
int ret, needs_alt_core_clk;
@@ -188,11 +209,32 @@
if (ret)
return ret;
- pr_info("device %s mapped at %p, with %d ctx banks\n",
+ dev_info(&pdev->dev, "device %s mapped at %p, with %d ctx banks\n",
drvdata->name, drvdata->base, drvdata->ncb);
platform_set_drvdata(pdev, drvdata);
+ pmon_info = msm_iommu_pm_alloc(&pdev->dev);
+ if (pmon_info != NULL) {
+ ret = msm_iommu_pmon_parse_dt(pdev, pmon_info);
+ if (ret) {
+ msm_iommu_pm_free(&pdev->dev);
+ pr_info("%s: pmon not available.\n", drvdata->name);
+ } else {
+ pmon_info->base = drvdata->base;
+ pmon_info->ops = &iommu_access_ops;
+ pmon_info->iommu_name = drvdata->name;
+ ret = msm_iommu_pm_iommu_register(pmon_info);
+ if (ret) {
+ pr_err("%s iommu register fail\n",
+ drvdata->name);
+ msm_iommu_pm_free(&pdev->dev);
+ } else {
+ pr_debug("%s iommu registered for pmon\n",
+ pmon_info->iommu_name);
+ }
+ }
+ }
return 0;
}
@@ -200,6 +242,9 @@
{
struct msm_iommu_drvdata *drv = NULL;
+ msm_iommu_pm_iommu_unregister(&pdev->dev);
+ msm_iommu_pm_free(&pdev->dev);
+
drv = platform_get_drvdata(pdev);
if (drv) {
msm_iommu_remove_drv(drv);
diff --git a/drivers/iommu/msm_iommu_perfmon.c b/drivers/iommu/msm_iommu_perfmon.c
new file mode 100644
index 0000000..15302b1
--- /dev/null
+++ b/drivers/iommu/msm_iommu_perfmon.c
@@ -0,0 +1,1104 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/iommu.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <mach/iommu_hw-v2.h>
+#include <mach/iommu.h>
+#include <mach/iommu_perfmon.h>
+
+#define PMCR_P_MASK (0x1)
+#define PMCR_P_SHIFT (1)
+#define PMCR_P (PMCR_P_MASK << PMCR_P_SHIFT)
+#define PMCFGR_NCG_MASK (0xFF)
+#define PMCFGR_NCG_SHIFT (24)
+#define PMCFGR_NCG (PMCFGR_NCG_MASK << PMCFGR_NCG_SHIFT)
+#define PMCFGR_N_MASK (0xFF)
+#define PMCFGR_N_SHIFT (0)
+#define PMCFGR_N (PMCFGR_N_MASK << PMCFGR_N_SHIFT)
+#define CR_E 0x1
+#define CGCR_CEN 0x800
+#define CGCR_CEN_SHFT (1 << 11)
+#define PMCGCR_CGNC_MASK (0x0F)
+#define PMCGCR_CGNC_SHIFT (24)
+#define PMCGCR_CGNC (PMCGCR_CGNC_MASK << PMCGCR_CGNC_SHIFT)
+#define PMCGCR_(group) (PMCGCR_N + group*4)
+
+#define PMOVSCLR_(n) (PMOVSCLR_N + n*4)
+#define PMCNTENSET_(n) (PMCNTENSET_N + n*4)
+#define PMCNTENCLR_(n) (PMCNTENCLR_N + n*4)
+#define PMINTENSET_(n) (PMINTENSET_N + n*4)
+#define PMINTENCLR_(n) (PMINTENCLR_N + n*4)
+
+#define PMEVCNTR_(n) (PMEVCNTR_N + n*4)
+#define PMEVTYPER_(n) (PMEVTYPER_N + n*4)
+
+static LIST_HEAD(iommu_list);
+static struct dentry *msm_iommu_root_debugfs_dir;
+static const char *NO_EVENT_CLASS_NAME = "none";
+static int NO_EVENT_CLASS = -1;
+static const unsigned int MAX_EVEN_CLASS_NAME_LEN = 36;
+
+struct event_class {
+ unsigned int event_number;
+ const char *desc;
+};
+
+static struct event_class pmu_event_classes[] = {
+ { 0x00, "cycle_count" },
+ { 0x01, "cycle_count64" },
+ { 0x08, "tlb_refill" },
+ { 0x09, "tlb_refill_read" },
+ { 0x0A, "tlb_refill_write" },
+ { 0x10, "access" },
+ { 0x11, "access_read" },
+ { 0x12, "access_write" },
+};
+
+static struct event_class pmu_event_classes_impl_defined[] = {
+ { 0x80, "full_misses" },
+ { 0x81, "partial_miss_1lbfb_hit" },
+ { 0x82, "partial_miss_2lbfb_hit" },
+ { 0x83, "full_hit" },
+ { 0x90, "pred_req_full_miss" },
+ { 0x91, "pred_req_partial_miss_1lbfb_hit" },
+ { 0x92, "pred_req_partial_miss_2lbfb_hit" },
+ { 0xb0, "tot_num_miss_axi_htw_read_req" },
+ { 0xb1, "tot_num_pred_axi_htw_read_req" },
+};
+
+static unsigned int iommu_pm_is_hw_access_OK(const struct iommu_pmon *pmon)
+{
+ return pmon->enabled && (pmon->iommu_attach_count > 0);
+}
+
+static unsigned int iommu_pm_create_sup_cls_str(char **buf,
+ unsigned int event_cls)
+{
+ unsigned long buf_size = (ARRAY_SIZE(pmu_event_classes) +
+ ARRAY_SIZE(pmu_event_classes_impl_defined)) *
+ MAX_EVEN_CLASS_NAME_LEN;
+ unsigned int pos = 0;
+
+ *buf = kzalloc(buf_size, GFP_KERNEL);
+ if (*buf) {
+ int i;
+ struct event_class *ptr;
+ size_t array_len = ARRAY_SIZE(pmu_event_classes);
+ ptr = pmu_event_classes;
+
+ for (i = 0; i < array_len; ++i) {
+ if ((1 << ptr[i].event_number) & event_cls) {
+ if (pos < buf_size) {
+ pos += snprintf(&(*buf)[pos],
+ buf_size-pos,
+ "[%u] %s\n",
+ ptr[i].event_number,
+ ptr[i].desc);
+ }
+
+ }
+ }
+
+ /*
+ * No way to read a register to check if impl. defined
+ * classes are supported or not so we just assume all of them
+ * are
+ */
+ array_len = ARRAY_SIZE(pmu_event_classes_impl_defined);
+ ptr = pmu_event_classes_impl_defined;
+
+ for (i = 0; i < array_len; ++i) {
+ if (buf_size > pos) {
+ pos += snprintf(&(*buf)[pos],
+ buf_size-pos,
+ "[%u] %s\n",
+ ptr[i].event_number,
+ ptr[i].desc);
+ }
+ }
+ }
+ return pos;
+}
+
+static const char *iommu_pm_find_event_class_name(int event_class)
+{
+ size_t array_len;
+ struct event_class *ptr;
+ int i;
+ const char *event_class_name = NO_EVENT_CLASS_NAME;
+ if (event_class < 0) {
+ goto out;
+ } else if (event_class < 0x80) {
+ array_len = ARRAY_SIZE(pmu_event_classes);
+ ptr = pmu_event_classes;
+ } else {
+ /* All implementation defined classes are above 0x7F */
+ array_len = ARRAY_SIZE(pmu_event_classes_impl_defined);
+ ptr = pmu_event_classes_impl_defined;
+ }
+
+ for (i = 0; i < array_len; ++i) {
+ if (ptr[i].event_number == event_class) {
+ event_class_name = ptr[i].desc;
+ break;
+ }
+ }
+
+out:
+ return event_class_name;
+}
+
+static int iommu_pm_find_event_class(const char *event_class_name)
+{
+ size_t array_len;
+ struct event_class *ptr;
+ int i;
+ int event_class = NO_EVENT_CLASS;
+
+ if (strcmp(event_class_name, NO_EVENT_CLASS_NAME) == 0)
+ goto out;
+
+ array_len = ARRAY_SIZE(pmu_event_classes);
+ ptr = pmu_event_classes;
+
+ for (i = 0; i < array_len; ++i) {
+ if (strcmp(ptr[i].desc, event_class_name) == 0) {
+ event_class = ptr[i].event_number;
+ goto out;
+ }
+ }
+
+ array_len = ARRAY_SIZE(pmu_event_classes_impl_defined);
+ ptr = pmu_event_classes_impl_defined;
+
+ for (i = 0; i < array_len; ++i) {
+ if (strcmp(ptr[i].desc, event_class_name) == 0) {
+ event_class = ptr[i].event_number;
+ goto out;
+ }
+ }
+
+out:
+ return event_class;
+}
+
+static inline void iommu_pm_add_to_iommu_list(struct iommu_pmon *iommu_pmon)
+{
+ list_add(&iommu_pmon->iommu_list, &iommu_list);
+}
+
+static inline void iommu_pm_del_from_iommu_list(struct iommu_pmon *iommu_pmon)
+{
+ list_del(&iommu_pmon->iommu_list);
+}
+
+static struct iommu_pmon *iommu_pm_get_pm_by_dev(struct device *dev)
+{
+ struct iommu_pmon *pmon;
+ struct iommu_info *info;
+ struct list_head *ent;
+ list_for_each(ent, &iommu_list) {
+ pmon = list_entry(ent, struct iommu_pmon, iommu_list);
+ info = &pmon->iommu;
+ if (dev == info->iommu_dev)
+ return pmon;
+ }
+ return NULL;
+}
+
+static void iommu_pm_grp_enable(struct iommu_info *iommu, unsigned int grp_no)
+{
+ unsigned int pmcgcr;
+ pmcgcr = readl_relaxed(iommu->base + PMCGCR_(grp_no));
+ pmcgcr |= CGCR_CEN;
+ writel_relaxed(pmcgcr, iommu->base + PMCGCR_(grp_no));
+}
+
+static void iommu_pm_grp_disable(struct iommu_info *iommu, unsigned int grp_no)
+{
+ unsigned int pmcgcr;
+ pmcgcr = readl_relaxed(iommu->base + PMCGCR_(grp_no));
+ pmcgcr &= ~CGCR_CEN;
+ writel_relaxed(pmcgcr, iommu->base + PMCGCR_(grp_no));
+}
+
+static void iommu_pm_enable(struct iommu_info *iommu)
+{
+ unsigned int pmcr;
+ pmcr = readl_relaxed(iommu->base + PMCR);
+ pmcr |= CR_E;
+ writel_relaxed(pmcr, iommu->base + PMCR);
+}
+
+void iommu_pm_disable(struct iommu_info *iommu)
+{
+ unsigned int pmcr;
+ pmcr = readl_relaxed(iommu->base + PMCR);
+ pmcr &= ~CR_E;
+ writel_relaxed(pmcr, iommu->base + PMCR);
+}
+
+static void iommu_pm_reset_counters(const struct iommu_info *iommu)
+{
+ unsigned int pmcr;
+ pmcr = readl_relaxed(iommu->base + PMCR);
+ pmcr |= PMCR_P;
+ writel_relaxed(pmcr, iommu->base + PMCR);
+}
+
+static void iommu_pm_check_for_overflow(struct iommu_pmon *pmon)
+{
+ struct iommu_pmon_counter *counter;
+ struct iommu_info *iommu = &pmon->iommu;
+ unsigned int reg_no = 0;
+ unsigned int bit_no;
+ unsigned int reg_value;
+ unsigned int i;
+ unsigned int j;
+ unsigned int curr_reg = 0;
+
+ reg_value = readl_relaxed(iommu->base + PMOVSCLR_(curr_reg));
+
+ for (i = 0; i < pmon->num_groups; ++i) {
+ struct iommu_pmon_cnt_group *cnt_grp = &pmon->cnt_grp[i];
+ for (j = 0; j < cnt_grp->num_counters; ++j) {
+ counter = &cnt_grp->counters[j];
+ reg_no = counter->absolute_counter_no / 32;
+ bit_no = counter->absolute_counter_no % 32;
+ if (reg_no != curr_reg) {
+ /* Clear overflow bits */
+ writel_relaxed(reg_value, iommu->base +
+ PMOVSCLR_(reg_no));
+ curr_reg = reg_no;
+ reg_value = readl_relaxed(iommu->base +
+ PMOVSCLR_(curr_reg));
+ }
+
+ if (counter->enabled) {
+ if (reg_value & (1 << bit_no))
+ counter->overflow_count++;
+ }
+ }
+ }
+
+ /* Clear overflow */
+ writel_relaxed(reg_value, iommu->base + PMOVSCLR_(reg_no));
+}
+
+irqreturn_t iommu_pm_evt_ovfl_int_handler(int irq, void *dev_id)
+{
+ struct iommu_pmon *pmon = dev_id;
+ struct iommu_info *iommu = &pmon->iommu;
+
+ mutex_lock(&pmon->lock);
+
+ if (!iommu_pm_is_hw_access_OK(pmon)) {
+ mutex_unlock(&pmon->lock);
+ goto out;
+ }
+
+ iommu->ops->iommu_lock_acquire();
+ iommu_pm_check_for_overflow(pmon);
+ iommu->ops->iommu_lock_release();
+
+ mutex_unlock(&pmon->lock);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static void iommu_pm_counter_enable(struct iommu_info *iommu,
+ struct iommu_pmon_counter *counter)
+{
+ unsigned int reg_no = counter->absolute_counter_no / 32;
+ unsigned int bit_no = counter->absolute_counter_no % 32;
+ unsigned int reg_value;
+
+ /* Clear overflow of counter */
+ reg_value = 1 << bit_no;
+ writel_relaxed(reg_value, iommu->base + PMOVSCLR_(reg_no));
+
+ /* Enable counter */
+ writel_relaxed(reg_value, iommu->base + PMCNTENSET_(reg_no));
+ counter->enabled = 1;
+}
+
+static void iommu_pm_counter_disable(struct iommu_info *iommu,
+ struct iommu_pmon_counter *counter)
+{
+ unsigned int reg_no = counter->absolute_counter_no / 32;
+ unsigned int bit_no = counter->absolute_counter_no % 32;
+ unsigned int reg_value;
+
+ counter->enabled = 0;
+
+ /* Disable counter */
+ reg_value = 1 << bit_no;
+ writel_relaxed(reg_value, iommu->base + PMCNTENCLR_(reg_no));
+
+ /* Clear overflow of counter */
+ writel_relaxed(reg_value, iommu->base + PMOVSCLR_(reg_no));
+}
+
+/*
+ * Must be called after iommu_start_access() is called
+ */
+static void iommu_pm_ovfl_int_enable(struct iommu_info *iommu,
+ const struct iommu_pmon_counter *counter)
+{
+ unsigned int reg_no = counter->absolute_counter_no / 32;
+ unsigned int bit_no = counter->absolute_counter_no % 32;
+ unsigned int reg_value;
+
+ /* Enable overflow interrupt for counter */
+ reg_value = (1 << bit_no);
+ writel_relaxed(reg_value, iommu->base + PMINTENSET_(reg_no));
+}
+
+/*
+ * Must be called after iommu_start_access() is called
+ */
+static void iommu_pm_ovfl_int_disable(struct iommu_info *iommu,
+ const struct iommu_pmon_counter *counter)
+{
+ unsigned int reg_no = counter->absolute_counter_no / 32;
+ unsigned int bit_no = counter->absolute_counter_no % 32;
+ unsigned int reg_value;
+
+ /* Disable overflow interrupt for counter */
+ reg_value = 1 << bit_no;
+ writel_relaxed(reg_value, iommu->base + PMINTENCLR_(reg_no));
+}
+
+static void iommu_pm_set_event_type(struct iommu_pmon *pmon,
+ struct iommu_pmon_counter *counter)
+{
+ int event_class;
+ unsigned int count_no;
+ struct iommu_info *iommu = &pmon->iommu;
+
+ event_class = counter->current_event_class;
+ count_no = counter->absolute_counter_no;
+
+ if (event_class == NO_EVENT_CLASS) {
+ if (iommu_pm_is_hw_access_OK(pmon)) {
+ iommu->ops->iommu_lock_acquire();
+ iommu_pm_counter_disable(iommu, counter);
+ iommu_pm_ovfl_int_disable(iommu, counter);
+ writel_relaxed(0, iommu->base + PMEVTYPER_(count_no));
+ iommu->ops->iommu_lock_release();
+ }
+ counter->overflow_count = 0;
+ counter->value = 0;
+ } else {
+ counter->overflow_count = 0;
+ counter->value = 0;
+ if (iommu_pm_is_hw_access_OK(pmon)) {
+ iommu->ops->iommu_lock_acquire();
+ writel_relaxed(event_class,
+ iommu->base + PMEVTYPER_(count_no));
+ iommu_pm_ovfl_int_enable(iommu, counter);
+ iommu_pm_counter_enable(iommu, counter);
+ iommu->ops->iommu_lock_release();
+ }
+ }
+}
+
+static void iommu_pm_reset_counts(struct iommu_pmon *pmon)
+{
+ unsigned int i;
+ unsigned int j;
+ for (i = 0; i < pmon->num_groups; ++i) {
+ struct iommu_pmon_cnt_group *cnt_grp = &pmon->cnt_grp[i];
+ for (j = 0; j < cnt_grp->num_counters; ++j) {
+ cnt_grp->counters[j].value = 0;
+ cnt_grp->counters[j].overflow_count = 0;
+ }
+ }
+}
+
+static unsigned int iommu_pm_read_counter(struct iommu_pmon_counter *counter)
+{
+ struct iommu_pmon *pmon = counter->cnt_group->pmon;
+ struct iommu_info *info = &pmon->iommu;
+ unsigned int cnt_no = counter->absolute_counter_no;
+ unsigned int pmevcntr;
+
+ pmevcntr = readl_relaxed(info->base + PMEVCNTR_(cnt_no));
+
+ return pmevcntr;
+
+}
+
+static void iommu_pm_set_all_counters(struct iommu_pmon *pmon)
+{
+ unsigned int i;
+ unsigned int j;
+ for (i = 0; i < pmon->num_groups; ++i) {
+ struct iommu_pmon_cnt_group *cnt_grp = &pmon->cnt_grp[i];
+ for (j = 0; j < cnt_grp->num_counters; ++j)
+ iommu_pm_set_event_type(pmon, &cnt_grp->counters[j]);
+ }
+}
+
+static void iommu_pm_read_all_counters(struct iommu_pmon *pmon)
+{
+ unsigned int i;
+ unsigned int j;
+ for (i = 0; i < pmon->num_groups; ++i) {
+ struct iommu_pmon_cnt_group *cnt_grp = &pmon->cnt_grp[i];
+ for (j = 0; j < cnt_grp->num_counters; ++j) {
+ struct iommu_pmon_counter *counter;
+ counter = &cnt_grp->counters[j];
+ counter->value = iommu_pm_read_counter(counter);
+ }
+ }
+}
+
+static void iommu_pm_on(struct iommu_pmon *pmon)
+{
+ unsigned int i;
+ struct iommu_info *iommu = &pmon->iommu;
+ struct msm_iommu_drvdata *iommu_drvdata =
+ dev_get_drvdata(iommu->iommu_dev);
+
+ iommu->ops->iommu_power_on(iommu_drvdata);
+
+ iommu_pm_reset_counts(pmon);
+
+ pmon->enabled = 1;
+
+ iommu_pm_set_all_counters(pmon);
+
+ iommu->ops->iommu_lock_acquire();
+
+ /* enable all counter group */
+ for (i = 0; i < pmon->num_groups; ++i)
+ iommu_pm_grp_enable(iommu, i);
+
+ /* enable global counters */
+ iommu_pm_enable(iommu);
+ iommu->ops->iommu_lock_release();
+
+ pr_info("%s: TLB performance monitoring turned ON\n",
+ pmon->iommu.iommu_name);
+}
+
+static void iommu_pm_off(struct iommu_pmon *pmon)
+{
+ unsigned int i;
+ struct iommu_info *iommu = &pmon->iommu;
+ struct msm_iommu_drvdata *iommu_drvdata =
+ dev_get_drvdata(iommu->iommu_dev);
+
+ pmon->enabled = 0;
+
+ iommu->ops->iommu_lock_acquire();
+
+ /* disable global counters */
+ iommu_pm_disable(iommu);
+
+ /* Check if we overflowed just before turning off pmon */
+ iommu_pm_check_for_overflow(pmon);
+
+ /* disable all counter group */
+ for (i = 0; i < pmon->num_groups; ++i)
+ iommu_pm_grp_disable(iommu, i);
+
+ /* Update cached copy of counters before turning off power */
+ iommu_pm_read_all_counters(pmon);
+
+ iommu->ops->iommu_lock_release();
+ iommu->ops->iommu_power_off(iommu_drvdata);
+
+ pr_info("%s: TLB performance monitoring turned OFF\n",
+ pmon->iommu.iommu_name);
+}
+
+static unsigned int iommu_pm_get_num_groups(struct iommu_pmon *iommu_pmon)
+{
+ unsigned int pmcfgr;
+ unsigned int num_cntgrp;
+ struct iommu_info *iommu = &iommu_pmon->iommu;
+
+ pmcfgr = readl_relaxed(iommu->base + PMCFGR);
+
+ /* Due to a bug in IOMMU hardware the counter register is returning
+ * the wrong information. num_cntgrp should return "total number
+ * of counter groups - 1". However, it returns "total number
+ * of counter groups". Thus we do not add 1 to the total number of
+ * counter groups. If we find that the number of counter group is 0
+ * then we assume the bug has been fixed and add 1 to the count.
+ */
+ num_cntgrp = ((pmcfgr & PMCFGR_NCG) >> PMCFGR_NCG_SHIFT);
+ if (num_cntgrp == 0)
+ num_cntgrp++;
+
+ return num_cntgrp;
+
+}
+
+static unsigned int iommu_pm_get_num_counters(struct iommu_pmon *iommu_pmon,
+ unsigned int group_no)
+{
+ unsigned int num_counters;
+ unsigned int tmp;
+ struct iommu_info *iommu = &iommu_pmon->iommu;
+
+ tmp = readl_relaxed(iommu->base + PMCGCR_(group_no));
+ num_counters = ((tmp & PMCGCR_CGNC) >> PMCGCR_CGNC_SHIFT);
+
+ return num_counters;
+
+}
+
+static unsigned int iommu_pm_get_sup_ev_cls(struct iommu_info *iommu)
+{
+ return readl_relaxed(iommu->base + PMCEID0);
+}
+
+static int iommu_pm_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t iommu_pm_count_value_read(struct file *fp,
+ char __user *user_buff,
+ size_t count, loff_t *pos)
+{
+ size_t rd_cnt;
+ unsigned long long full_count;
+
+ struct iommu_pmon_counter *counter = fp->private_data;
+ struct iommu_pmon *pmon = counter->cnt_group->pmon;
+ struct iommu_info *iommu = &pmon->iommu;
+ char buf[50];
+ size_t len;
+
+ mutex_lock(&pmon->lock);
+
+ if (iommu_pm_is_hw_access_OK(pmon)) {
+ iommu->ops->iommu_lock_acquire();
+ counter->value = iommu_pm_read_counter(counter);
+ iommu->ops->iommu_lock_release();
+ }
+ full_count = (unsigned long long) counter->value +
+ ((unsigned long long)counter->overflow_count *
+ 0x100000000ULL);
+
+ len = snprintf(buf, 50, "%llu\n", full_count);
+ rd_cnt = simple_read_from_buffer(user_buff, count, pos, buf, len);
+ mutex_unlock(&pmon->lock);
+
+ return rd_cnt;
+}
+
+static const struct file_operations cnt_value_file_ops = {
+ .open = iommu_pm_debug_open,
+ .read = iommu_pm_count_value_read,
+};
+
+static ssize_t iommu_pm_event_class_read(struct file *fp,
+ char __user *user_buff,
+ size_t count, loff_t *pos)
+{
+ size_t rd_cnt;
+ struct iommu_pmon_counter *counter = fp->private_data;
+ struct iommu_pmon *pmon = counter->cnt_group->pmon;
+ char buf[50];
+ const char *event_class_name;
+ size_t len;
+
+ mutex_lock(&pmon->lock);
+ event_class_name = iommu_pm_find_event_class_name(
+ counter->current_event_class);
+ len = snprintf(buf, 50, "%s\n", event_class_name);
+
+ rd_cnt = simple_read_from_buffer(user_buff, count, pos, buf, len);
+ mutex_unlock(&pmon->lock);
+ return rd_cnt;
+}
+
+static ssize_t iommu_pm_event_class_write(struct file *fp,
+ const char __user *user_buff,
+ size_t count, loff_t *pos)
+{
+ size_t wr_cnt;
+ char buf[50];
+ size_t buf_size = sizeof(buf);
+ struct iommu_pmon_counter *counter = fp->private_data;
+ struct iommu_pmon *pmon = counter->cnt_group->pmon;
+ int current_event_class;
+
+ if ((count + *pos) >= buf_size)
+ return -EINVAL;
+
+ mutex_lock(&pmon->lock);
+ current_event_class = counter->current_event_class;
+ wr_cnt = simple_write_to_buffer(buf, buf_size, pos, user_buff, count);
+ if (wr_cnt >= 1) {
+ int rv;
+ long value;
+ buf[wr_cnt-1] = '\0';
+ rv = kstrtol(buf, 50, &value);
+ if (!rv) {
+ counter->current_event_class =
+ iommu_pm_find_event_class(
+ iommu_pm_find_event_class_name(value));
+ } else {
+ counter->current_event_class =
+ iommu_pm_find_event_class(buf);
+ } }
+
+ if (current_event_class != counter->current_event_class)
+ iommu_pm_set_event_type(pmon, counter);
+
+ mutex_unlock(&pmon->lock);
+ return wr_cnt;
+}
+
+static const struct file_operations event_class_file_ops = {
+ .open = iommu_pm_debug_open,
+ .read = iommu_pm_event_class_read,
+ .write = iommu_pm_event_class_write,
+};
+
+static ssize_t iommu_reset_counters_write(struct file *fp,
+ const char __user *user_buff,
+ size_t count, loff_t *pos)
+{
+ size_t wr_cnt;
+ char buf[10];
+ size_t buf_size = sizeof(buf);
+ struct iommu_pmon *pmon = fp->private_data;
+ struct iommu_info *iommu = &pmon->iommu;
+
+ if ((count + *pos) >= buf_size)
+ return -EINVAL;
+
+ mutex_lock(&pmon->lock);
+ wr_cnt = simple_write_to_buffer(buf, buf_size, pos, user_buff, count);
+ if (wr_cnt >= 1) {
+ unsigned long cmd = 0;
+ int rv;
+ buf[wr_cnt-1] = '\0';
+ rv = kstrtoul(buf, 10, &cmd);
+ if (!rv && (cmd == 1)) {
+ if (iommu_pm_is_hw_access_OK(pmon)) {
+ iommu->ops->iommu_lock_acquire();
+ iommu_pm_reset_counters(&pmon->iommu);
+ iommu->ops->iommu_lock_release();
+ }
+ iommu_pm_reset_counts(pmon);
+ pr_info("TLB performance counters reset\n");
+ } else {
+ pr_err("Unknown performance monitor command: %lu\n",
+ cmd);
+ }
+ }
+ mutex_unlock(&pmon->lock);
+ return wr_cnt;
+}
+
+static const struct file_operations reset_file_ops = {
+ .open = iommu_pm_debug_open,
+ .write = iommu_reset_counters_write,
+};
+
+static ssize_t iommu_pm_enable_counters_read(struct file *fp,
+ char __user *user_buff,
+ size_t count, loff_t *pos)
+{
+ size_t rd_cnt;
+ char buf[5];
+ size_t len;
+ struct iommu_pmon *pmon = fp->private_data;
+
+ mutex_lock(&pmon->lock);
+ len = snprintf(buf, 5, "%u\n", pmon->enabled);
+ rd_cnt = simple_read_from_buffer(user_buff, count, pos, buf, len);
+ mutex_unlock(&pmon->lock);
+ return rd_cnt;
+}
+
+static ssize_t iommu_pm_enable_counters_write(struct file *fp,
+ const char __user *user_buff,
+ size_t count, loff_t *pos)
+{
+ size_t wr_cnt;
+ char buf[10];
+ size_t buf_size = sizeof(buf);
+ struct iommu_pmon *pmon = fp->private_data;
+
+ if ((count + *pos) >= buf_size)
+ return -EINVAL;
+
+ mutex_lock(&pmon->lock);
+ wr_cnt = simple_write_to_buffer(buf, buf_size, pos, user_buff, count);
+ if (wr_cnt >= 1) {
+ unsigned long cmd;
+ int rv;
+ buf[wr_cnt-1] = '\0';
+ rv = kstrtoul(buf, 10, &cmd);
+ if (!rv && (cmd < 2)) {
+ if (pmon->enabled == 1 && cmd == 0) {
+ if (pmon->iommu_attach_count > 0)
+ iommu_pm_off(pmon);
+ } else if (pmon->enabled == 0 && cmd == 1) {
+ /* We can only turn on perf. monitoring if
+ * iommu is attached. Delay turning on perf.
+ * monitoring until we are attached.
+ */
+ if (pmon->iommu_attach_count > 0)
+ iommu_pm_on(pmon);
+ else
+ pmon->enabled = 1;
+ }
+ } else {
+ pr_err("Unknown performance monitor command: %lu\n",
+ cmd);
+ }
+ }
+ mutex_unlock(&pmon->lock);
+ return wr_cnt;
+}
+
+static const struct file_operations event_enable_file_ops = {
+ .open = iommu_pm_debug_open,
+ .read = iommu_pm_enable_counters_read,
+ .write = iommu_pm_enable_counters_write,
+};
+
+static ssize_t iommu_pm_avail_event_cls_read(struct file *fp,
+ char __user *user_buff,
+ size_t count, loff_t *pos)
+{
+ size_t rd_cnt = 0;
+ struct iommu_pmon *pmon = fp->private_data;
+ char *buf;
+ size_t len;
+
+ mutex_lock(&pmon->lock);
+
+ len = iommu_pm_create_sup_cls_str(&buf, pmon->event_cls_supp_value);
+ if (buf) {
+ rd_cnt = simple_read_from_buffer(user_buff, count, pos,
+ buf, len);
+ kfree(buf);
+ }
+ mutex_unlock(&pmon->lock);
+ return rd_cnt;
+}
+
+static const struct file_operations available_event_cls_file_ops = {
+ .open = iommu_pm_debug_open,
+ .read = iommu_pm_avail_event_cls_read,
+};
+
+
+
+static int iommu_pm_create_grp_debugfs_counters_hierarchy(
+ struct iommu_pmon_cnt_group *cnt_grp,
+ unsigned int *abs_counter_no)
+{
+ int ret = 0;
+ int j;
+ char name[20];
+
+ for (j = 0; j < cnt_grp->num_counters; ++j) {
+ struct dentry *grp_dir = cnt_grp->group_dir;
+ struct dentry *counter_dir;
+ cnt_grp->counters[j].cnt_group = cnt_grp;
+ cnt_grp->counters[j].counter_no = j;
+ cnt_grp->counters[j].absolute_counter_no = *abs_counter_no;
+ (*abs_counter_no)++;
+ cnt_grp->counters[j].value = 0;
+ cnt_grp->counters[j].overflow_count = 0;
+ cnt_grp->counters[j].current_event_class = NO_EVENT_CLASS;
+
+ snprintf(name, 20, "counter%u", j);
+
+ counter_dir = debugfs_create_dir(name, grp_dir);
+
+ if (IS_ERR_OR_NULL(counter_dir)) {
+ pr_err("unable to create counter debugfs dir %s\n",
+ name);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cnt_grp->counters[j].counter_dir = counter_dir;
+
+ if (!debugfs_create_file("value", 0644, counter_dir,
+ &cnt_grp->counters[j],
+ &cnt_value_file_ops)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (!debugfs_create_file("current_event_class", 0644,
+ counter_dir, &cnt_grp->counters[j],
+ &event_class_file_ops)) {
+ ret = -EIO;
+ goto out;
+ }
+ }
+out:
+ return ret;
+}
+
+static int iommu_pm_create_group_debugfs_hierarchy(struct iommu_info *iommu,
+ struct iommu_pmon *pmon_entry)
+{
+ int i;
+ int ret = 0;
+ char name[20];
+ unsigned int abs_counter_no = 0;
+
+ for (i = 0; i < pmon_entry->num_groups; ++i) {
+ pmon_entry->cnt_grp[i].pmon = pmon_entry;
+ pmon_entry->cnt_grp[i].grp_no = i;
+ pmon_entry->cnt_grp[i].num_counters =
+ iommu_pm_get_num_counters(pmon_entry, i);
+ pmon_entry->cnt_grp[i].counters =
+ kzalloc(sizeof(*pmon_entry->cnt_grp[i].counters)
+ * pmon_entry->cnt_grp[i].num_counters, GFP_KERNEL);
+
+ if (!pmon_entry->cnt_grp[i].counters) {
+ pr_err("Unable to allocate memory for counters\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ snprintf(name, 20, "group%u", i);
+ pmon_entry->cnt_grp[i].group_dir = debugfs_create_dir(name,
+ pmon_entry->iommu_dir);
+ if (IS_ERR_OR_NULL(pmon_entry->cnt_grp[i].group_dir)) {
+ pr_err("unable to create group debugfs dir %s\n", name);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = iommu_pm_create_grp_debugfs_counters_hierarchy(
+ &pmon_entry->cnt_grp[i],
+ &abs_counter_no);
+ if (ret)
+ goto out;
+ }
+out:
+ return ret;
+}
+
+int msm_iommu_pm_iommu_register(struct iommu_info *iommu)
+{
+ struct iommu_pmon *pmon_entry;
+ int ret = 0;
+ struct msm_iommu_drvdata *iommu_drvdata;
+ int i;
+
+ if (!iommu->ops || !iommu->iommu_name || !iommu->base
+ || !iommu->iommu_dev) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!msm_iommu_root_debugfs_dir) {
+ msm_iommu_root_debugfs_dir = debugfs_create_dir("iommu", NULL);
+ if (IS_ERR_OR_NULL(msm_iommu_root_debugfs_dir)) {
+ pr_err("Failed creating iommu debugfs dir \"iommu\"\n");
+ ret = -EIO;
+ goto out;
+ }
+ }
+ pmon_entry = (struct iommu_pmon *)container_of(iommu,
+ struct iommu_pmon, iommu);
+ iommu_drvdata = dev_get_drvdata(iommu->iommu_dev);
+
+ iommu->ops->iommu_power_on(iommu_drvdata);
+ iommu->ops->iommu_lock_acquire();
+
+ pmon_entry->num_groups = iommu_pm_get_num_groups(pmon_entry);
+ pmon_entry->cnt_grp = kzalloc(sizeof(*pmon_entry->cnt_grp)
+ * pmon_entry->num_groups, GFP_KERNEL);
+ if (!pmon_entry->cnt_grp) {
+ pr_err("Unable to allocate memory for counter groups\n");
+ ret = -ENOMEM;
+ goto file_err;
+ }
+ pmon_entry->event_cls_supp_value = iommu_pm_get_sup_ev_cls(iommu);
+
+ pmon_entry->iommu_dir = debugfs_create_dir(iommu->iommu_name,
+ msm_iommu_root_debugfs_dir);
+ if (IS_ERR_OR_NULL(pmon_entry->iommu_dir)) {
+ pr_err("unable to create iommu debugfs dir %s\n",
+ iommu->iommu_name);
+ ret = -ENOMEM;
+ goto free_mem;
+ }
+
+ if (!debugfs_create_file("reset_counters", 0644,
+ pmon_entry->iommu_dir, pmon_entry, &reset_file_ops)) {
+ ret = -EIO;
+ goto free_mem;
+ }
+
+ if (!debugfs_create_file("enable_counters", 0644,
+ pmon_entry->iommu_dir, pmon_entry, &event_enable_file_ops)) {
+ ret = -EIO;
+ goto free_mem;
+ }
+
+ if (!debugfs_create_file("available_event_classes", 0644,
+ pmon_entry->iommu_dir, pmon_entry,
+ &available_event_cls_file_ops)) {
+ ret = -EIO;
+ goto free_mem;
+ }
+
+ ret = iommu_pm_create_group_debugfs_hierarchy(iommu, pmon_entry);
+ if (ret)
+ goto free_mem;
+
+ iommu->ops->iommu_lock_release();
+ iommu->ops->iommu_power_off(iommu_drvdata);
+
+ if (iommu->evt_irq > 0) {
+ ret = request_threaded_irq(iommu->evt_irq, NULL,
+ iommu_pm_evt_ovfl_int_handler,
+ IRQF_ONESHOT | IRQF_SHARED,
+ "msm_iommu_nonsecure_irq", pmon_entry);
+ if (ret) {
+ pr_err("Request IRQ %d failed with ret=%d\n",
+ iommu->evt_irq,
+ ret);
+ goto free_mem;
+ }
+ } else {
+ pr_info("%s: Overflow interrupt not available\n", __func__);
+ }
+
+ dev_dbg(iommu->iommu_dev, "%s iommu registered\n",
+ iommu->iommu_name);
+
+ goto out;
+free_mem:
+ if (pmon_entry->cnt_grp) {
+ for (i = 0; i < pmon_entry->num_groups; ++i) {
+ kfree(pmon_entry->cnt_grp[i].counters);
+ pmon_entry->cnt_grp[i].counters = 0;
+ }
+ }
+ kfree(pmon_entry->cnt_grp);
+ pmon_entry->cnt_grp = 0;
+file_err:
+ debugfs_remove_recursive(msm_iommu_root_debugfs_dir);
+out:
+ return ret;
+}
+EXPORT_SYMBOL(msm_iommu_pm_iommu_register);
+
+void msm_iommu_pm_iommu_unregister(struct device *dev)
+{
+ int i;
+ struct iommu_pmon *pmon_entry = iommu_pm_get_pm_by_dev(dev);
+
+ if (!pmon_entry)
+ return;
+
+ free_irq(pmon_entry->iommu.evt_irq, pmon_entry->iommu.iommu_dev);
+
+ if (!pmon_entry)
+ goto remove_debugfs;
+
+ if (pmon_entry->cnt_grp) {
+ for (i = 0; i < pmon_entry->num_groups; ++i)
+ kfree(pmon_entry->cnt_grp[i].counters);
+ }
+
+ kfree(pmon_entry->cnt_grp);
+
+remove_debugfs:
+ debugfs_remove_recursive(msm_iommu_root_debugfs_dir);
+
+ return;
+}
+EXPORT_SYMBOL(msm_iommu_pm_iommu_unregister);
+
+struct iommu_info *msm_iommu_pm_alloc(struct device *dev)
+{
+ struct iommu_pmon *pmon_entry;
+ struct iommu_info *info;
+ pmon_entry = devm_kzalloc(dev, sizeof(*pmon_entry), GFP_KERNEL);
+ if (!pmon_entry)
+ return NULL;
+ info = &pmon_entry->iommu;
+ info->iommu_dev = dev;
+ mutex_init(&pmon_entry->lock);
+ iommu_pm_add_to_iommu_list(pmon_entry);
+ return &pmon_entry->iommu;
+}
+EXPORT_SYMBOL(msm_iommu_pm_alloc);
+
+void msm_iommu_pm_free(struct device *dev)
+{
+ struct iommu_pmon *pmon = iommu_pm_get_pm_by_dev(dev);
+ if (pmon)
+ iommu_pm_del_from_iommu_list(pmon);
+}
+EXPORT_SYMBOL(msm_iommu_pm_free);
+
+void msm_iommu_attached(struct device *dev)
+{
+ struct iommu_pmon *pmon = iommu_pm_get_pm_by_dev(dev);
+ if (pmon) {
+ mutex_lock(&pmon->lock);
+ ++pmon->iommu_attach_count;
+ if (pmon->iommu_attach_count == 1) {
+ /* If perf. mon was enabled before we attached we do
+ * the actual after we attach.
+ */
+ if (pmon->enabled)
+ iommu_pm_on(pmon);
+ }
+ mutex_unlock(&pmon->lock);
+ }
+}
+EXPORT_SYMBOL(msm_iommu_attached);
+
+void msm_iommu_detached(struct device *dev)
+{
+ struct iommu_pmon *pmon = iommu_pm_get_pm_by_dev(dev);
+ if (pmon) {
+ mutex_lock(&pmon->lock);
+ if (pmon->iommu_attach_count == 1) {
+ /* If perf. mon is still enabled we have to disable
+ * before we do the detach.
+ */
+ if (pmon->enabled)
+ iommu_pm_off(pmon);
+ }
+ BUG_ON(pmon->iommu_attach_count == 0);
+ --pmon->iommu_attach_count;
+ mutex_unlock(&pmon->lock);
+ }
+}
+EXPORT_SYMBOL(msm_iommu_detached);
+
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 0d2d91c..830ba81 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -1195,8 +1195,28 @@
help
Enable printk() debug for msm camera
-
+if MSM_CAMERA
source "drivers/media/video/msm/Kconfig"
+endif # MSM_CAMERA
+
+menuconfig MSMB_CAMERA
+ bool "Qualcomm MSM camera and video capture 2.0 support"
+ depends on ARCH_MSM && VIDEO_V4L2 && I2C
+ ---help---
+ Say Y here to enable selecting the video adapters for
+ Qualcomm msm camera and video capture 2.0, enabling this
+ adds support for the camera driver stack including sensor, isp
+ and postprocessing drivers.
+
+config MSMB_CAMERA_DEBUG
+ bool "Qualcomm MSM camera 2.0 debugging with printk"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable printk() debug for msm camera 2.0
+
+if MSMB_CAMERA
+source "drivers/media/video/msmb/Kconfig"
+endif # MSMB_CAMERA
endif # V4L_PLATFORM_DRIVERS
endif # VIDEO_CAPTURE_DRIVERS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index fd736c3..e732773 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -213,6 +213,7 @@
obj-y += davinci/
obj-$(CONFIG_MSM_CAMERA) += msm/
+obj-$(CONFIG_MSMB_CAMERA) += msmb/
obj-$(CONFIG_ARCH_OMAP) += omap/
obj-$(CONFIG_MSM_VIDC_V4L2) += msm_vidc/
obj-$(CONFIG_MSM_WFD) += msm_wfd/
diff --git a/drivers/media/video/msm/Makefile b/drivers/media/video/msm/Makefile
index 56d3e0c..67ac906 100644
--- a/drivers/media/video/msm/Makefile
+++ b/drivers/media/video/msm/Makefile
@@ -22,7 +22,8 @@
obj-$(CONFIG_MSM_CAMERA) += msm_camera.o
endif
obj-$(CONFIG_MSM_CAMERA) += vfe/
-obj-$(CONFIG_MSM_CAMERA) += msm_axi_qos.o gemini/ mercury/ jpeg_10/
+obj-$(CONFIG_MSM_CAMERA) += msm_axi_qos.o gemini/ mercury/
+obj-$(CONFIG_MSM_JPEG) += jpeg_10/
ifeq ($(CONFIG_MSM_CAMERA_V4L2),y)
obj-$(CONFIG_ARCH_MSM8X60) += msm_vpe.o
obj-$(CONFIG_ARCH_MSM7X30) += msm_vpe.o msm_axi_qos.o
diff --git a/drivers/media/video/msmb/Kconfig b/drivers/media/video/msmb/Kconfig
new file mode 100644
index 0000000..c128b0d
--- /dev/null
+++ b/drivers/media/video/msmb/Kconfig
@@ -0,0 +1,93 @@
+config MSM_CAMERA_SENSOR
+ bool "Qualcomm MSM camera sensor support"
+ depends on MSMB_CAMERA
+ ---help---
+ This flag enables support for Camera Sensor.
+ The sensor driver is capable of providing real time
+ data for camera support. The driver support V4L2
+ subdev APIs.
+
+config MSM_CPP
+ bool "Qualcomm MSM Camera Post Processing Engine support"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable support for Camera Post-processing Engine
+ The Post processing engine is capable of scaling
+ and cropping image. The driver support V4L2 subdev
+ APIs.
+
+config MSM_CCI
+ bool "Qualcomm MSM Camera Control Interface support"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable support for Camera Control Interface driver only
+ for those platforms that have hardware support. This driver
+ is responsible for handling I2C read and write on the I2C
+ bus. It is also responsible for synchronization with
+ GPIO and data frames.
+
+config MSM_CSI20_HEADER
+ bool "Qualcomm MSM CSI 2.0 Header"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable support for CSI drivers to include 2.0
+ header. This header has register macros and its
+ values and bit mask for register configuration bits
+ This config macro is required targets based on 8960,
+ 8930 and 8064 platforms.
+
+config MSM_CSI30_HEADER
+ bool "Qualcomm MSM CSI 3.0 Header"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable support for CSI drivers to include 3.0
+ header. This header has register macros and its
+ values and bit mask for register configuration bits
+ This config macro is required for targets based on
+ 8064 platforms.
+
+config MSM_CSIPHY
+ bool "Qualcomm MSM Camera Serial Interface Physical receiver support"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable support for Camera Serial Interface
+ Physical receiver. It deserializes packets and
+ supports detection of packet start and stop
+ signalling.
+
+config MSM_CSID
+ bool "Qualcomm MSM Camera Serial Interface decoder support"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable support for Camera Serial Interface decoder.
+ It supports lane merging and decoding of packets
+ based on cid which is mapped to a virtual channel
+ and datatype.
+
+config MSM_ISPIF
+ bool "Qualcomm MSM Image Signal Processing interface support"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable support for Image Signal Processing interface module.
+ This module acts as a crossbar between CSID and VFE. Output
+ of any CID of CSID can be routed to of of pixel or raw
+ data interface in VFE.
+
+config S5K3L1YX
+ bool "Sensor S5K3L1YX (BAYER 12M)"
+ depends on MSMB_CAMERA
+ ---help---
+ Samsung 12 MP Bayer Sensor with auto focus, uses
+ 4 mipi lanes, preview config = 1984 * 1508 at 30 fps,
+ snapshot config = 4000 * 3000 at 20 fps,
+ hfr video at 60, 90 and 120 fps.
+
+config MSM_V4L2_VIDEO_OVERLAY_DEVICE
+ tristate "Qualcomm MSM V4l2 video overlay device"
+ ---help---
+ Enables support for the MSM V4L2 video
+ overlay driver. This allows video rendering
+ apps to render overlaid video using Video4Linux2
+ APIs, by using /dev/videoX device
+
+
diff --git a/drivers/media/video/msmb/Makefile b/drivers/media/video/msmb/Makefile
new file mode 100644
index 0000000..3986128
--- /dev/null
+++ b/drivers/media/video/msmb/Makefile
@@ -0,0 +1,14 @@
+ccflags-y += -Idrivers/media/video/msmb
+ccflags-y += -Idrivers/media/video/msmb/sensor
+ccflags-y += -Idrivers/media/video/msmb/codecs
+ccflags-y += -Idrivers/media/video/msmb/isps
+ccflags-y += -Idrivers/media/video/msmb/pps
+ccflags-y += -Idrivers/media/video/msmb/msm_vb2
+ccflags-y += -Idrivers/media/video/msmb/camera
+
+obj-$(CONFIG_MSMB_CAMERA) += msm.o
+obj-$(CONFIG_MSMB_CAMERA) += camera/
+obj-$(CONFIG_MSMB_CAMERA) += msm_vb2/
+obj-$(CONFIG_MSMB_CAMERA) += sensor/
+obj-$(CONFIG_MSMB_CAMERA) += isp/
+obj-$(CONFIG_MSMB_CAMERA) += ispif/
diff --git a/drivers/media/video/msmb/camera/Makefile b/drivers/media/video/msmb/camera/Makefile
new file mode 100644
index 0000000..89ff167
--- /dev/null
+++ b/drivers/media/video/msmb/camera/Makefile
@@ -0,0 +1,3 @@
+ccflags-y += -Idrivers/media/video/msmb
+ccflags-y += -Idrivers/media/video/msmb/msm_vb2
+obj-$(CONFIG_MSMB_CAMERA) += camera.o
diff --git a/drivers/media/video/msmb/camera/camera.c b/drivers/media/video/msmb/camera/camera.c
new file mode 100644
index 0000000..c726958
--- /dev/null
+++ b/drivers/media/video/msmb/camera/camera.c
@@ -0,0 +1,706 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include <linux/atomic.h>
+#include <linux/wait.h>
+#include <linux/videodev2.h>
+#include <linux/msm_ion.h>
+#include <linux/iommu.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-fh.h>
+
+#include "camera.h"
+#include "msm.h"
+#include "msm_vb2.h"
+
+#define fh_to_private(__fh) \
+ container_of(__fh, struct camera_v4l2_private, fh)
+
+struct camera_v4l2_private {
+ struct v4l2_fh fh;
+ unsigned int stream_id;
+ struct vb2_queue vb2_q;
+};
+
+static void camera_pack_event(struct file *filep, int evt_id,
+ int command, struct v4l2_event *event)
+{
+ struct msm_v4l2_event_data *event_data =
+ (struct msm_v4l2_event_data *)&event->u.data[0];
+ struct msm_video_device *pvdev = video_drvdata(filep);
+ struct camera_v4l2_private *sp = fh_to_private(filep->private_data);
+
+ /* always MSM_CAMERA_V4L2_EVENT_TYPE */
+ event->type = MSM_CAMERA_V4L2_EVENT_TYPE;
+ event->id = evt_id;
+ event_data->command = command;
+ event_data->session_id = pvdev->vdev->num;
+ event_data->stream_id = sp->stream_id;
+}
+
+static int camera_check_event_status(struct v4l2_event *event)
+{
+ struct msm_v4l2_event_data *event_data =
+ (struct msm_v4l2_event_data *)&event->u.data[0];
+
+ if (event_data->status > MSM_CAMERA_ERR_EVT_BASE)
+ return -EFAULT;
+
+ return 0;
+}
+
+static int camera_v4l2_querycap(struct file *filep, void *fh,
+ struct v4l2_capability *cap)
+{
+ int rc;
+ struct v4l2_event event;
+
+ /* can use cap->driver to make differentiation */
+ camera_pack_event(filep, MSM_CAMERA_GET_PARM,
+ MSM_CAMERA_PRIV_QUERY_CAP, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+
+ rc = camera_check_event_status(&event);
+
+ return rc;
+}
+
+static int camera_v4l2_s_crop(struct file *filep, void *fh,
+ struct v4l2_crop *crop)
+{
+ int rc = 0;
+ struct v4l2_event event;
+
+ if (crop->type == V4L2_BUF_TYPE_PRIVATE) {
+
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM,
+ MSM_CAMERA_PRIV_S_CROP, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+
+ rc = camera_check_event_status(&event);
+ }
+
+ return rc;
+}
+
+static int camera_v4l2_g_crop(struct file *filep, void *fh,
+ struct v4l2_crop *crop)
+{
+ int rc = 0;
+ struct v4l2_event event;
+
+ if (crop->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ camera_pack_event(filep, MSM_CAMERA_GET_PARM,
+ MSM_CAMERA_PRIV_G_CROP, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+
+ rc = camera_check_event_status(&event);
+ }
+
+ return rc;
+}
+
+static int camera_v4l2_queryctrl(struct file *filep, void *fh,
+ struct v4l2_queryctrl *ctrl)
+{
+ int rc = 0;
+ struct v4l2_event event;
+
+ if (ctrl->type == V4L2_CTRL_TYPE_MENU) {
+
+ camera_pack_event(filep, MSM_CAMERA_GET_PARM,
+ ctrl->id, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+
+ rc = camera_check_event_status(&event);
+ }
+
+ return rc;
+}
+
+static int camera_v4l2_g_ctrl(struct file *filep, void *fh,
+ struct v4l2_control *ctrl)
+{
+ int rc = 0;
+ struct v4l2_event event;
+
+ if (ctrl->id >= V4L2_CID_PRIVATE_BASE) {
+ camera_pack_event(filep, MSM_CAMERA_GET_PARM, ctrl->id, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+
+ rc = camera_check_event_status(&event);
+ }
+
+ return rc;
+}
+
+static int camera_v4l2_s_ctrl(struct file *filep, void *fh,
+ struct v4l2_control *ctrl)
+{
+ int rc = 0;
+ struct v4l2_event event;
+ if (ctrl->id >= V4L2_CID_PRIVATE_BASE) {
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM, ctrl->id, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+
+ rc = camera_check_event_status(&event);
+ }
+
+ return rc;
+}
+
+static int camera_v4l2_reqbufs(struct file *filep, void *fh,
+ struct v4l2_requestbuffers *req)
+{
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+
+ return vb2_reqbufs(&sp->vb2_q, req);
+}
+
+static int camera_v4l2_querybuf(struct file *filep, void *fh,
+ struct v4l2_buffer *pb)
+{
+ return 0;
+}
+
+static int camera_v4l2_qbuf(struct file *filep, void *fh,
+ struct v4l2_buffer *pb)
+{
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+
+ return vb2_qbuf(&sp->vb2_q, pb);
+}
+
+static int camera_v4l2_dqbuf(struct file *filep, void *fh,
+ struct v4l2_buffer *pb)
+{
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+
+ return vb2_dqbuf(&sp->vb2_q, pb, filep->f_flags & O_NONBLOCK);
+}
+
+static int camera_v4l2_streamon(struct file *filep, void *fh,
+ enum v4l2_buf_type buf_type)
+{
+ struct v4l2_event event;
+ int rc;
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+
+ rc = vb2_streamon(&sp->vb2_q, buf_type);
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM,
+ MSM_CAMERA_PRIV_STREAM_ON, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+
+ rc = camera_check_event_status(&event);
+ return rc;
+}
+
+static int camera_v4l2_streamoff(struct file *filep, void *fh,
+ enum v4l2_buf_type buf_type)
+{
+ struct v4l2_event event;
+ int rc;
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM,
+ MSM_CAMERA_PRIV_STREAM_OFF, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+
+ rc = camera_check_event_status(&event);
+ vb2_streamoff(&sp->vb2_q, buf_type);
+ return rc;
+}
+
+static int camera_v4l2_g_fmt_cap_private(struct file *filep, void *fh,
+ struct v4l2_format *pfmt)
+{
+ int rc = -EINVAL;
+
+ if (pfmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ struct v4l2_event event;
+
+ camera_pack_event(filep, MSM_CAMERA_GET_PARM,
+ MSM_CAMERA_PRIV_G_FMT, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+
+ rc = camera_check_event_status(&event);
+ }
+
+ return rc;
+}
+
+static int camera_v4l2_s_fmt_cap_private(struct file *filep, void *fh,
+ struct v4l2_format *pfmt)
+{
+ int rc = 0;
+ int i = 0;
+ struct v4l2_event event;
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+ struct msm_v4l2_format_data *user_fmt;
+
+ if (pfmt->type == V4L2_BUF_TYPE_PRIVATE) {
+
+ if (WARN_ON(!sp->vb2_q.drv_priv))
+ return -ENOMEM;
+
+ memcpy(sp->vb2_q.drv_priv, pfmt->fmt.raw_data,
+ sizeof(struct msm_v4l2_format_data));
+ user_fmt = (struct msm_v4l2_format_data *)sp->vb2_q.drv_priv;
+
+ pr_debug("%s: num planes :%c\n", __func__,
+ user_fmt->num_planes);
+ for (i = 0; i < user_fmt->num_planes; i++)
+ pr_debug("%s: plane size[%d]\n", __func__,
+ user_fmt->plane_sizes[i]);
+
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM,
+ MSM_CAMERA_PRIV_S_FMT, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ goto set_fmt_fail;
+
+ rc = camera_check_event_status(&event);
+ if (rc < 0)
+ goto set_fmt_fail;
+ }
+
+ return rc;
+
+set_fmt_fail:
+ kfree(sp->vb2_q.drv_priv);
+ return rc;
+}
+
+static int camera_v4l2_try_fmt_cap_private(struct file *filep, void *fh,
+ struct v4l2_format *pfmt)
+{
+ return 0;
+}
+
+int camera_v4l2_g_fmt_vid_cap_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ return 0;
+}
+
+static int camera_v4l2_g_parm(struct file *filep, void *fh,
+ struct v4l2_streamparm *a)
+{
+ /* TODO */
+ return 0;
+}
+
+static int camera_v4l2_s_parm(struct file *filep, void *fh,
+ struct v4l2_streamparm *parm)
+{
+ int rc = 0;
+ struct v4l2_event event;
+ struct msm_v4l2_event_data *event_data =
+ (struct msm_v4l2_event_data *)&event.u.data[0];
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM,
+ MSM_CAMERA_PRIV_NEW_STREAM, &event);
+
+ rc = msm_create_stream(event_data->session_id,
+ event_data->stream_id, &sp->vb2_q);
+ if (rc < 0)
+ return rc;
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ goto error;
+
+ rc = camera_check_event_status(&event);
+ if (rc < 0)
+ goto error;
+
+ /* use stream_id as stream index */
+ parm->parm.capture.extendedmode = sp->stream_id;
+
+ return rc;
+
+error:
+ msm_delete_stream(event_data->session_id,
+ event_data->stream_id);
+ return rc;
+}
+
+static int camera_v4l2_subscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ int rc = 0;
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+
+ rc = v4l2_event_subscribe(&sp->fh, sub, 5);
+
+ return rc;
+}
+
+static int camera_v4l2_unsubscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ int rc = 0;
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+
+ rc = v4l2_event_unsubscribe(&sp->fh, sub);
+
+ return rc;
+}
+
+static const struct v4l2_ioctl_ops camera_v4l2_ioctl_ops = {
+ .vidioc_querycap = camera_v4l2_querycap,
+ .vidioc_s_crop = camera_v4l2_s_crop,
+ .vidioc_g_crop = camera_v4l2_g_crop,
+ .vidioc_queryctrl = camera_v4l2_queryctrl,
+ .vidioc_g_ctrl = camera_v4l2_g_ctrl,
+ .vidioc_s_ctrl = camera_v4l2_s_ctrl,
+ .vidioc_reqbufs = camera_v4l2_reqbufs,
+ .vidioc_querybuf = camera_v4l2_querybuf,
+ .vidioc_qbuf = camera_v4l2_qbuf,
+ .vidioc_dqbuf = camera_v4l2_dqbuf,
+ .vidioc_streamon = camera_v4l2_streamon,
+ .vidioc_streamoff = camera_v4l2_streamoff,
+ .vidioc_g_fmt_type_private = camera_v4l2_g_fmt_cap_private,
+ .vidioc_s_fmt_type_private = camera_v4l2_s_fmt_cap_private,
+ .vidioc_try_fmt_type_private = camera_v4l2_try_fmt_cap_private,
+ .vidioc_g_fmt_vid_cap_mplane = camera_v4l2_g_fmt_vid_cap_mplane,
+
+ /* Stream type-dependent parameter ioctls */
+ .vidioc_g_parm = camera_v4l2_g_parm,
+ .vidioc_s_parm = camera_v4l2_s_parm,
+
+ /* event subscribe/unsubscribe */
+ .vidioc_subscribe_event = camera_v4l2_subscribe_event,
+ .vidioc_unsubscribe_event = camera_v4l2_unsubscribe_event,
+};
+
+static int camera_v4l2_fh_open(struct file *filep)
+{
+ struct msm_video_device *pvdev = video_drvdata(filep);
+ struct camera_v4l2_private *sp;
+
+ sp = kzalloc(sizeof(*sp), GFP_KERNEL);
+ if (!sp)
+ return -ENOMEM;
+
+ filep->private_data = &sp->fh;
+
+ /* stream_id = open id */
+ sp->stream_id = atomic_read(&pvdev->opened);
+
+ v4l2_fh_init(&sp->fh, pvdev->vdev);
+ v4l2_fh_add(&sp->fh);
+
+ return 0;
+}
+
+static int camera_v4l2_fh_release(struct file *filep)
+{
+ struct camera_v4l2_private *sp = fh_to_private(filep->private_data);
+
+ if (sp) {
+ v4l2_fh_del(&sp->fh);
+ v4l2_fh_exit(&sp->fh);
+ }
+
+ kfree(sp);
+ return 0;
+}
+
+static int camera_v4l2_vb2_q_init(struct file *filep)
+{
+ struct camera_v4l2_private *sp = fh_to_private(filep->private_data);
+ struct vb2_queue *q = &sp->vb2_q;
+
+ memset(q, 0, sizeof(struct vb2_queue));
+
+ /* free up this buffer when stream is done */
+ q->drv_priv =
+ kzalloc(sizeof(struct msm_v4l2_format_data), GFP_KERNEL);
+ if (!q->drv_priv)
+ return -ENOMEM;
+
+ q->mem_ops = msm_vb2_get_q_mem_ops();
+ q->ops = msm_vb2_get_q_ops();
+
+ /* default queue type */
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ q->io_modes = VB2_USERPTR;
+ q->io_flags = 0;
+ q->buf_struct_size = sizeof(struct msm_vb2_buffer);
+ vb2_queue_init(q);
+
+ return 0;
+}
+
+static void camera_v4l2_vb2_q_release(struct file *filep)
+{
+ struct camera_v4l2_private *sp = filep->private_data;
+
+ kfree(sp->vb2_q.drv_priv);
+ vb2_queue_release(&sp->vb2_q);
+}
+
+static int camera_v4l2_open(struct file *filep)
+{
+ int rc = 0;
+ struct v4l2_event event;
+ struct msm_video_device *pvdev = video_drvdata(filep);
+ BUG_ON(!pvdev);
+
+ rc = camera_v4l2_fh_open(filep);
+ if (rc < 0)
+ goto fh_open_fail;
+
+ /* every stream has a vb2 queue */
+ rc = camera_v4l2_vb2_q_init(filep);
+ if (rc < 0)
+ goto vb2_q_fail;
+
+ if (!atomic_read(&pvdev->opened)) {
+
+ /* create a new session when first opened */
+ rc = msm_create_session(pvdev->vdev->num, pvdev->vdev);
+ if (rc < 0)
+ goto session_fail;
+
+ rc = msm_create_command_ack_q(pvdev->vdev->num, 0);
+ if (rc < 0)
+ goto command_ack_q_fail;
+
+ camera_pack_event(filep, MSM_CAMERA_NEW_SESSION, 0, &event);
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ goto post_fail;
+
+ rc = camera_check_event_status(&event);
+ if (rc < 0)
+ goto post_fail;
+ } else {
+ rc = msm_create_command_ack_q(pvdev->vdev->num,
+ atomic_read(&pvdev->opened));
+ if (rc < 0)
+ goto session_fail;
+ }
+
+ atomic_add(1, &pvdev->opened);
+ return rc;
+
+post_fail:
+ msm_delete_command_ack_q(pvdev->vdev->num, 0);
+command_ack_q_fail:
+ msm_destroy_session(pvdev->vdev->num);
+session_fail:
+ camera_v4l2_vb2_q_release(filep);
+vb2_q_fail:
+ camera_v4l2_fh_release(filep);
+fh_open_fail:
+ return rc;
+}
+
+static unsigned int camera_v4l2_poll(struct file *filep,
+ struct poll_table_struct *wait)
+{
+ int rc = 0;
+ struct camera_v4l2_private *sp = fh_to_private(filep->private_data);
+
+ rc = vb2_poll(&sp->vb2_q, filep, wait);
+
+ poll_wait(filep, &sp->fh.wait, wait);
+ if (v4l2_event_pending(&sp->fh))
+ rc |= POLLPRI;
+
+ return rc;
+}
+
+static int camera_v4l2_close(struct file *filep)
+{
+ int rc = 0;
+ struct v4l2_event event;
+ struct msm_video_device *pvdev = video_drvdata(filep);
+ struct camera_v4l2_private *sp = fh_to_private(filep->private_data);
+
+ BUG_ON(!pvdev);
+
+ atomic_sub_return(1, &pvdev->opened);
+
+ if (atomic_read(&pvdev->opened) == 0) {
+
+ camera_pack_event(filep, MSM_CAMERA_DEL_SESSION, 0, &event);
+
+ /* Donot wait, imaging server may have crashed */
+ msm_post_event(&event, -1);
+
+ /* This should take care of both normal close
+ * and application crashes */
+ msm_destroy_session(pvdev->vdev->num);
+
+ } else {
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM,
+ MSM_CAMERA_PRIV_DEL_STREAM, &event);
+
+ /* Donot wait, imaging server may have crashed */
+ msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+
+ msm_delete_command_ack_q(pvdev->vdev->num,
+ sp->stream_id);
+
+ msm_delete_stream(pvdev->vdev->num, sp->stream_id);
+ }
+
+ camera_v4l2_vb2_q_release(filep);
+ camera_v4l2_fh_release(filep);
+
+ return rc;
+}
+
+static struct v4l2_file_operations camera_v4l2_fops = {
+ .owner = THIS_MODULE,
+ .open = camera_v4l2_open,
+ .poll = camera_v4l2_poll,
+ .release = camera_v4l2_close,
+ .ioctl = video_ioctl2,
+};
+
+int camera_init_v4l2(struct device *dev, unsigned int *session)
+{
+ struct msm_video_device *pvdev;
+ struct v4l2_device *v4l2_dev;
+ int rc = 0;
+
+ pvdev = kzalloc(sizeof(struct msm_video_device),
+ GFP_KERNEL);
+ if (WARN_ON(!pvdev)) {
+ rc = -ENOMEM;
+ goto init_end;
+ }
+
+ pvdev->vdev = video_device_alloc();
+ if (WARN_ON(!pvdev->vdev)) {
+ rc = -ENOMEM;
+ goto video_fail;
+ }
+
+ v4l2_dev = kzalloc(sizeof(struct v4l2_device), GFP_KERNEL);
+ if (WARN_ON(!v4l2_dev)) {
+ rc = -ENOMEM;
+ goto v4l2_fail;
+ }
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ v4l2_dev->mdev = kzalloc(sizeof(struct media_device),
+ GFP_KERNEL);
+ if (!v4l2_dev->mdev) {
+ rc = -ENOMEM;
+ goto mdev_fail;
+ }
+ strlcpy(v4l2_dev->mdev->model, MSM_CAMERA_NAME,
+ sizeof(v4l2_dev->mdev->model));
+
+ v4l2_dev->mdev->dev = dev;
+
+ rc = media_device_register(v4l2_dev->mdev);
+ if (WARN_ON(rc < 0))
+ goto media_fail;
+
+ rc = media_entity_init(&pvdev->vdev->entity, 0, NULL, 0);
+ if (WARN_ON(rc < 0))
+ goto entity_fail;
+ pvdev->vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
+ pvdev->vdev->entity.group_id = QCAMERA_VNODE_GROUP_ID;
+#endif
+
+ v4l2_dev->notify = NULL;
+ pvdev->vdev->v4l2_dev = v4l2_dev;
+
+ rc = v4l2_device_register(dev, pvdev->vdev->v4l2_dev);
+ if (WARN_ON(rc < 0))
+ goto register_fail;
+
+ strlcpy(pvdev->vdev->name, "msm-sensor", sizeof(pvdev->vdev->name));
+ pvdev->vdev->release = video_device_release;
+ pvdev->vdev->fops = &camera_v4l2_fops;
+ pvdev->vdev->ioctl_ops = &camera_v4l2_ioctl_ops;
+ pvdev->vdev->minor = -1;
+ pvdev->vdev->vfl_type = VFL_TYPE_GRABBER;
+ rc = video_register_device(pvdev->vdev,
+ VFL_TYPE_GRABBER, -1);
+ if (WARN_ON(rc < 0))
+ goto video_register_fail;
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ /* FIXME: How to get rid of this messy? */
+ pvdev->vdev->entity.name = video_device_node_name(pvdev->vdev);
+#endif
+
+ *session = pvdev->vdev->num;
+ atomic_set(&pvdev->opened, 0);
+ video_set_drvdata(pvdev->vdev, pvdev);
+ goto init_end;
+
+video_register_fail:
+ v4l2_device_unregister(pvdev->vdev->v4l2_dev);
+register_fail:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ media_entity_cleanup(&pvdev->vdev->entity);
+entity_fail:
+ media_device_unregister(v4l2_dev->mdev);
+media_fail:
+ kfree(v4l2_dev->mdev);
+mdev_fail:
+#endif
+ kfree(v4l2_dev);
+v4l2_fail:
+ video_device_release(pvdev->vdev);
+video_fail:
+ kfree(pvdev);
+init_end:
+ return rc;
+}
diff --git a/drivers/media/video/msmb/camera/camera.h b/drivers/media/video/msmb/camera/camera.h
new file mode 100644
index 0000000..ac860a4
--- /dev/null
+++ b/drivers/media/video/msmb/camera/camera.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAMERA_H
+#define _CAMERA_H
+
+enum stream_state {
+ START_STREAM = 0,
+ STOP_STREAM,
+};
+
+int camera_init_v4l2(struct device *dev, unsigned int *session);
+
+#endif /*_CAMERA_H */
diff --git a/drivers/media/video/msmb/isp/Makefile b/drivers/media/video/msmb/isp/Makefile
new file mode 100644
index 0000000..e517798
--- /dev/null
+++ b/drivers/media/video/msmb/isp/Makefile
@@ -0,0 +1,4 @@
+ccflags-y += -Idrivers/media/video/msmb
+ccflags-y += -Idrivers/media/video/msmb/sensor/io
+obj-$(CONFIG_MSMB_CAMERA) += msm_isp.o msm_buf_mgr.o msm_isp_util.o msm_isp_axi_util.o msm_isp_stats_util.o
+obj-$(CONFIG_MSMB_CAMERA) += msm_isp40.o
diff --git a/drivers/media/video/msmb/isp/msm_buf_mgr.c b/drivers/media/video/msmb/isp/msm_buf_mgr.c
new file mode 100644
index 0000000..aab97d7
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_buf_mgr.c
@@ -0,0 +1,604 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+#include <linux/proc_fs.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/android_pmem.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-core.h>
+#include <media/msm_camera.h>
+#include <media/msm_isp.h>
+
+#include <mach/iommu.h>
+
+#include "msm.h"
+#include "msm_buf_mgr.h"
+
+/*#define CONFIG_MSM_ISP_DBG*/
+#undef CDBG
+#ifdef CONFIG_MSM_ISP_DBG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+static struct msm_isp_bufq *msm_isp_get_bufq(
+ struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle)
+{
+ struct msm_isp_bufq *bufq = NULL;
+ uint32_t bufq_index = bufq_handle & 0xFF;
+ if (bufq_index > buf_mgr->num_buf_q)
+ return bufq;
+
+ bufq = &buf_mgr->bufq[bufq_index];
+ if (bufq->bufq_handle == bufq_handle)
+ return bufq;
+
+ return NULL;
+}
+
+static struct msm_isp_buffer *msm_isp_get_buf_ptr(
+ struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index)
+{
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq\n", __func__);
+ return buf_info;
+ }
+
+ if (bufq->num_bufs <= buf_index) {
+ pr_err("%s: Invalid buf index\n", __func__);
+ return buf_info;
+ }
+ buf_info = &bufq->bufs[buf_index];
+ return buf_info;
+}
+
+static uint32_t msm_isp_get_buf_handle(
+ struct msm_isp_buf_mgr *buf_mgr)
+{
+ int i;
+ if ((buf_mgr->buf_handle_cnt << 8) == 0)
+ buf_mgr->buf_handle_cnt++;
+
+ for (i = 0; i < buf_mgr->num_buf_q; i++) {
+ if (buf_mgr->bufq[i].bufq_handle == 0) {
+ memset(&buf_mgr->bufq[i],
+ 0, sizeof(struct msm_isp_bufq));
+ buf_mgr->bufq[i].bufq_handle =
+ (++buf_mgr->buf_handle_cnt) << 8 | i;
+ return buf_mgr->bufq[i].bufq_handle;
+ }
+ }
+ return 0;
+}
+
+static int msm_isp_free_buf_handle(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle)
+{
+ struct msm_isp_bufq *bufq =
+ msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq)
+ return -EINVAL;
+ memset(bufq, 0, sizeof(struct msm_isp_bufq));
+ return 0;
+}
+
+static int msm_isp_prepare_v4l2_buf(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_buffer *buf_info,
+ struct v4l2_buffer *v4l2_buf)
+{
+ int i, rc = -1;
+ struct msm_isp_buffer_mapped_info *mapped_info;
+ for (i = 0; i < v4l2_buf->length; i++) {
+ mapped_info = &buf_info->mapped_info[i];
+ mapped_info->handle =
+ ion_import_dma_buf(buf_mgr->client,
+ v4l2_buf->m.planes[i].m.userptr);
+ if (IS_ERR_OR_NULL(mapped_info->handle)) {
+ pr_err("%s: buf has null/error ION handle %p\n",
+ __func__, mapped_info->handle);
+ goto ion_map_error;
+ }
+ if (ion_map_iommu(buf_mgr->client, mapped_info->handle,
+ buf_mgr->iommu_domain_num, 0, SZ_4K,
+ 0, &(mapped_info->paddr),
+ &(mapped_info->len), 0, 0) < 0) {
+ rc = -EINVAL;
+ pr_err("%s: cannot map address", __func__);
+ ion_free(buf_mgr->client, mapped_info->handle);
+ goto ion_map_error;
+ }
+ mapped_info->paddr += v4l2_buf->m.planes[i].data_offset;
+ CDBG("%s: plane: %d addr:%lu\n",
+ __func__, i, mapped_info->paddr);
+ }
+ buf_info->num_planes = v4l2_buf->length;
+ return 0;
+ion_map_error:
+ for (--i; i >= 0; i--) {
+ mapped_info = &buf_info->mapped_info[i];
+ ion_unmap_iommu(buf_mgr->client, mapped_info->handle,
+ buf_mgr->iommu_domain_num, 0);
+ ion_free(buf_mgr->client, mapped_info->handle);
+ }
+ return rc;
+}
+
+static void msm_isp_unprepare_v4l2_buf(
+ struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_buffer *buf_info)
+{
+ int i;
+ struct msm_isp_buffer_mapped_info *mapped_info;
+ for (i = 0; i < buf_info->num_planes; i++) {
+ mapped_info = &buf_info->mapped_info[i];
+ ion_unmap_iommu(buf_mgr->client, mapped_info->handle,
+ buf_mgr->iommu_domain_num, 0);
+ ion_free(buf_mgr->client, mapped_info->handle);
+ }
+ return;
+}
+
+static int msm_isp_buf_prepare(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_qbuf_info *info, struct vb2_buffer *vb2_buf)
+{
+ int rc = -1;
+ struct msm_isp_buffer *buf_info = NULL;
+ struct v4l2_buffer *buf = NULL;
+ struct v4l2_plane *plane = NULL;
+
+ buf_info = msm_isp_get_buf_ptr(buf_mgr,
+ info->handle, info->buf_idx);
+ if (!buf_info) {
+ pr_err("Invalid buffer prepare\n");
+ return rc;
+ }
+
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_UNUSED ||
+ buf_info->state != MSM_ISP_BUFFER_STATE_INITIALIZED) {
+ pr_err("%s: Invalid buffer state: %d\n",
+ __func__, buf_info->state);
+ return rc;
+ }
+
+ if (vb2_buf) {
+ buf = &vb2_buf->v4l2_buf;
+ buf_info->vb2_buf = vb2_buf;
+ } else {
+ buf = &info->buffer;
+ plane =
+ kzalloc(sizeof(struct v4l2_plane) * buf->length,
+ GFP_KERNEL);
+ if (!plane) {
+ pr_err("%s: Cannot alloc plane: %d\n",
+ __func__, buf_info->state);
+ return rc;
+ }
+ if (copy_from_user(plane,
+ (void __user *)(buf->m.planes),
+ sizeof(struct v4l2_plane) * buf->length)) {
+ kfree(plane);
+ return rc;
+ }
+ buf->m.planes = plane;
+ }
+
+ rc = msm_isp_prepare_v4l2_buf(buf_mgr, buf_info, buf);
+ if (rc < 0) {
+ pr_err("%s: Prepare buffer error\n", __func__);
+ kfree(plane);
+ return rc;
+ }
+ buf_info->state = MSM_ISP_BUFFER_STATE_PREPARED;
+ kfree(plane);
+ return rc;
+}
+
+static int msm_isp_buf_unprepare(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t buf_handle)
+{
+ int rc = -1, i;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+ bufq = msm_isp_get_bufq(buf_mgr, buf_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq\n", __func__);
+ return rc;
+ }
+
+ for (i = 0; i < bufq->num_bufs; i++) {
+ buf_info = msm_isp_get_buf_ptr(buf_mgr, buf_handle, i);
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_UNUSED ||
+ buf_info->state ==
+ MSM_ISP_BUFFER_STATE_INITIALIZED)
+ continue;
+ msm_isp_unprepare_v4l2_buf(buf_mgr, buf_info);
+ }
+ return 0;
+}
+
+static int msm_isp_get_buf(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, struct msm_isp_buffer **buf_info)
+{
+ int rc = -1;
+ struct msm_isp_buffer *temp_buf_info;
+ struct msm_isp_bufq *bufq = NULL;
+ struct vb2_buffer *vb2_buf = NULL;
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq\n", __func__);
+ return rc;
+ }
+
+ *buf_info = NULL;
+ if (BUF_SRC(bufq->stream_id)) {
+ list_for_each_entry(temp_buf_info, &bufq->head, list) {
+ if (temp_buf_info->state ==
+ MSM_ISP_BUFFER_STATE_QUEUED) {
+ /* found one buf */
+ list_del_init(&temp_buf_info->list);
+ *buf_info = temp_buf_info;
+ break;
+ }
+ }
+ } else {
+ vb2_buf = buf_mgr->vb2_ops->get_buf(
+ bufq->session_id, bufq->stream_id);
+ if (vb2_buf) {
+ if (vb2_buf->v4l2_buf.index < bufq->num_bufs) {
+ *buf_info =
+ &bufq->bufs[vb2_buf->v4l2_buf.index];
+ (*buf_info)->vb2_buf = vb2_buf;
+ } else {
+ pr_err("%s: Incorrect buf index %d\n",
+ __func__, vb2_buf->v4l2_buf.index);
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (!(*buf_info)) {
+ pr_err("%s: No free buffer\n", __func__);
+ return rc;
+ }
+
+ (*buf_info)->state = MSM_ISP_BUFFER_STATE_DEQUEUED;
+ return 0;
+}
+
+static int msm_isp_put_buf(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index)
+{
+ int rc = -1;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq\n", __func__);
+ return rc;
+ }
+
+ buf_info = msm_isp_get_buf_ptr(buf_mgr, bufq_handle, buf_index);
+ if (!buf_info) {
+ pr_err("%s: buf not found\n", __func__);
+ return rc;
+ }
+
+ switch (buf_info->state) {
+ case MSM_ISP_BUFFER_STATE_PREPARED:
+ case MSM_ISP_BUFFER_STATE_DEQUEUED:
+ case MSM_ISP_BUFFER_STATE_DISPATCHED:
+ if (BUF_SRC(bufq->stream_id))
+ list_add_tail(&buf_info->list, &bufq->head);
+ else
+ buf_mgr->vb2_ops->put_buf(buf_info->vb2_buf);
+ buf_info->state = MSM_ISP_BUFFER_STATE_QUEUED;
+ rc = 0;
+ break;
+ default:
+ pr_err("%s: incorrect state = %d",
+ __func__, buf_info->state);
+ break;
+ }
+
+ return rc;
+}
+
+static int msm_isp_buf_done(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index,
+ struct timeval *tv, uint32_t frame_id)
+{
+ int rc = -1;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("Invalid bufq\n");
+ return rc;
+ }
+
+ buf_info = msm_isp_get_buf_ptr(buf_mgr, bufq_handle, buf_index);
+ if (!buf_info) {
+ pr_err("%s: buf not found\n", __func__);
+ return rc;
+ }
+
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_DEQUEUED) {
+ buf_info->state = MSM_ISP_BUFFER_STATE_DISPATCHED;
+ if (!(BUF_SRC(bufq->stream_id))) {
+ buf_info->vb2_buf->v4l2_buf.timestamp = *tv;
+ buf_info->vb2_buf->v4l2_buf.sequence = frame_id;
+ buf_mgr->vb2_ops->buf_done(buf_info->vb2_buf);
+ }
+ }
+
+ return 0;
+}
+
+static int msm_isp_buf_enqueue(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_qbuf_info *info)
+{
+ int rc;
+ struct msm_isp_bufq *bufq = NULL;
+ rc = msm_isp_buf_prepare(buf_mgr, info, NULL);
+ if (rc < 0) {
+ pr_err("%s: Buf prepare failed\n", __func__);
+ return rc;
+ }
+
+ bufq = msm_isp_get_bufq(buf_mgr, info->handle);
+ if (BUF_SRC(bufq->stream_id)) {
+ rc = msm_isp_put_buf(buf_mgr, info->handle, info->buf_idx);
+ if (rc < 0) {
+ pr_err("%s: Buf put failed\n", __func__);
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int msm_isp_get_bufq_handle(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t session_id, uint32_t stream_id)
+{
+ int i;
+ for (i = 0; i < buf_mgr->num_buf_q; i++) {
+ if (buf_mgr->bufq[i].session_id == session_id &&
+ buf_mgr->bufq[i].stream_id == stream_id) {
+ return buf_mgr->bufq[i].bufq_handle;
+ }
+ }
+ return 0;
+}
+
+static int msm_isp_request_bufq(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_buf_request *buf_request)
+{
+ int rc = -1, i;
+ struct msm_isp_bufq *bufq = NULL;
+ CDBG("%s: E\n", __func__);
+
+ if (!buf_request->num_buf) {
+ pr_err("Invalid buffer request\n");
+ return rc;
+ }
+
+ buf_request->handle = msm_isp_get_buf_handle(buf_mgr);
+ if (!buf_request->handle) {
+ pr_err("Invalid buffer handle\n");
+ return rc;
+ }
+
+ bufq = msm_isp_get_bufq(buf_mgr, buf_request->handle);
+ if (!bufq) {
+ pr_err("Invalid buffer queue\n");
+ return rc;
+ }
+
+ bufq->bufs = kzalloc(sizeof(struct msm_isp_buffer) *
+ buf_request->num_buf, GFP_KERNEL);
+ if (!bufq->bufs) {
+ pr_err("No free memory for buf info\n");
+ msm_isp_free_buf_handle(buf_mgr, buf_request->handle);
+ return rc;
+ }
+
+ bufq->bufq_handle = buf_request->handle;
+ bufq->session_id = buf_request->session_id;
+ bufq->stream_id = buf_request->stream_id;
+ bufq->num_bufs = buf_request->num_buf;
+ INIT_LIST_HEAD(&bufq->head);
+ for (i = 0; i < buf_request->num_buf; i++) {
+ bufq->bufs[i].state = MSM_ISP_BUFFER_STATE_INITIALIZED;
+ bufq->bufs[i].bufq_handle = bufq->bufq_handle;
+ bufq->bufs[i].buf_idx = i;
+ }
+
+ return 0;
+}
+
+static int msm_isp_release_bufq(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle)
+{
+ struct msm_isp_bufq *bufq = NULL;
+ int rc = -1;
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("Invalid bufq release\n");
+ return rc;
+ }
+
+ msm_isp_buf_unprepare(buf_mgr, bufq_handle);
+
+ kfree(bufq->bufs);
+ msm_isp_free_buf_handle(buf_mgr, bufq_handle);
+ return 0;
+}
+
+static void msm_isp_release_all_bufq(
+ struct msm_isp_buf_mgr *buf_mgr)
+{
+ struct msm_isp_bufq *bufq = NULL;
+ int i;
+ for (i = 0; i < buf_mgr->num_buf_q; i++) {
+ bufq = &buf_mgr->bufq[i];
+ if (!bufq->bufq_handle)
+ continue;
+ msm_isp_buf_unprepare(buf_mgr, bufq->bufq_handle);
+ kfree(bufq->bufs);
+ msm_isp_free_buf_handle(buf_mgr, bufq->bufq_handle);
+ }
+}
+
+static int msm_isp_attach_ctx(struct msm_isp_buf_mgr *buf_mgr,
+ struct device *iommu_ctx)
+{
+ int rc;
+ rc = iommu_attach_device(buf_mgr->iommu_domain, iommu_ctx);
+ if (rc) {
+ pr_err("%s: Iommu attach error\n", __func__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void msm_isp_detach_ctx(struct msm_isp_buf_mgr *buf_mgr,
+ struct device *iommu_ctx)
+{
+ iommu_detach_device(buf_mgr->iommu_domain, iommu_ctx);
+}
+
+static int msm_isp_init_isp_buf_mgr(
+ struct msm_isp_buf_mgr *buf_mgr,
+ const char *ctx_name, uint16_t num_buf_q)
+{
+ int rc = -1;
+ if (!num_buf_q) {
+ pr_err("Invalid buffer queue number\n");
+ return rc;
+ }
+
+ CDBG("%s: E\n", __func__);
+ buf_mgr->num_buf_q = num_buf_q;
+ buf_mgr->bufq =
+ kzalloc(sizeof(struct msm_isp_bufq) * num_buf_q,
+ GFP_KERNEL);
+ if (!buf_mgr->bufq) {
+ pr_err("Bufq malloc error\n");
+ goto bufq_error;
+ }
+ buf_mgr->client = msm_ion_client_create(-1, ctx_name);
+ buf_mgr->buf_handle_cnt = 0;
+
+ return 0;
+bufq_error:
+ return rc;
+}
+
+static int msm_isp_deinit_isp_buf_mgr(
+ struct msm_isp_buf_mgr *buf_mgr)
+{
+ msm_isp_release_all_bufq(buf_mgr);
+ ion_client_destroy(buf_mgr->client);
+ kfree(buf_mgr->bufq);
+ buf_mgr->num_buf_q = 0;
+ return 0;
+}
+
+int msm_isp_proc_buf_cmd(struct msm_isp_buf_mgr *buf_mgr,
+ unsigned int cmd, void *arg)
+{
+ switch (cmd) {
+ case VIDIOC_MSM_ISP_REQUEST_BUF: {
+ struct msm_isp_buf_request *buf_req = arg;
+ buf_mgr->ops->request_buf(buf_mgr, buf_req);
+ break;
+ }
+ case VIDIOC_MSM_ISP_ENQUEUE_BUF: {
+ struct msm_isp_qbuf_info *qbuf_info = arg;
+ buf_mgr->ops->enqueue_buf(buf_mgr, qbuf_info);
+ break;
+ }
+ case VIDIOC_MSM_ISP_RELEASE_BUF: {
+ struct msm_isp_buf_request *buf_req = arg;
+ buf_mgr->ops->release_buf(buf_mgr, buf_req->handle);
+ break;
+ }
+ }
+ return 0;
+}
+
+static struct msm_isp_buf_ops isp_buf_ops = {
+ .request_buf = msm_isp_request_bufq,
+ .enqueue_buf = msm_isp_buf_enqueue,
+ .release_buf = msm_isp_release_bufq,
+ .get_bufq_handle = msm_isp_get_bufq_handle,
+ .get_buf = msm_isp_get_buf,
+ .put_buf = msm_isp_put_buf,
+ .buf_done = msm_isp_buf_done,
+ .attach_ctx = msm_isp_attach_ctx,
+ .detach_ctx = msm_isp_detach_ctx,
+ .buf_mgr_init = msm_isp_init_isp_buf_mgr,
+ .buf_mgr_deinit = msm_isp_deinit_isp_buf_mgr,
+};
+
+int msm_isp_create_isp_buf_mgr(
+ struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_sd_req_vb2_q *vb2_ops,
+ struct msm_iova_layout *iova_layout)
+{
+ int rc = 0;
+ if (buf_mgr->init_done)
+ return rc;
+
+ buf_mgr->iommu_domain_num = msm_register_domain(iova_layout);
+ if (buf_mgr->iommu_domain_num < 0) {
+ pr_err("%s: Invalid iommu domain number\n", __func__);
+ rc = -1;
+ goto iommu_domain_error;
+ }
+
+ buf_mgr->iommu_domain = msm_get_iommu_domain(
+ buf_mgr->iommu_domain_num);
+ if (!buf_mgr->iommu_domain) {
+ pr_err("%s: Invalid iommu domain\n", __func__);
+ rc = -1;
+ goto iommu_domain_error;
+ }
+
+ buf_mgr->ops = &isp_buf_ops;
+ buf_mgr->vb2_ops = vb2_ops;
+ buf_mgr->init_done = 1;
+ buf_mgr->ref_count = 0;
+ return 0;
+iommu_domain_error:
+ return rc;
+}
diff --git a/drivers/media/video/msmb/isp/msm_buf_mgr.h b/drivers/media/video/msmb/isp/msm_buf_mgr.h
new file mode 100644
index 0000000..a44b5ec
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_buf_mgr.h
@@ -0,0 +1,123 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_ISP_BUF_H_
+#define _MSM_ISP_BUF_H_
+
+#include <media/msmb_isp.h>
+#include <mach/iommu_domains.h>
+#include "msm_sd.h"
+
+#define BUF_SRC_SHIFT 16
+/*Buffer source can be from userspace / HAL*/
+#define BUF_SRC(id) (id >> BUF_SRC_SHIFT)
+
+struct msm_isp_buf_mgr;
+
+enum msm_isp_buffer_state {
+ MSM_ISP_BUFFER_STATE_UNUSED, /* not used */
+ MSM_ISP_BUFFER_STATE_INITIALIZED, /* REQBUF done */
+ MSM_ISP_BUFFER_STATE_PREPARED, /* BUF mapped */
+ MSM_ISP_BUFFER_STATE_QUEUED, /* buf queued */
+ MSM_ISP_BUFFER_STATE_DEQUEUED, /* in use in VFE */
+ MSM_ISP_BUFFER_STATE_DISPATCHED, /* sent to userspace */
+};
+
+struct msm_isp_buffer_mapped_info {
+ unsigned long len;
+ unsigned long paddr;
+ struct ion_handle *handle;
+};
+
+struct msm_isp_buffer {
+ /*Common Data structure*/
+ int num_planes;
+ struct msm_isp_buffer_mapped_info mapped_info[VIDEO_MAX_PLANES];
+ int buf_idx;
+ uint32_t bufq_handle;
+
+ /*Native buffer*/
+ struct list_head list;
+ enum msm_isp_buffer_state state;
+
+ /*Vb2 buffer data*/
+ struct vb2_buffer *vb2_buf;
+
+};
+
+struct msm_isp_bufq {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t num_bufs;
+ uint32_t bufq_handle;
+ struct msm_isp_buffer *bufs;
+
+ /*Native buffer queue*/
+ struct list_head head;
+};
+
+struct msm_isp_buf_ops {
+ int (*request_buf) (struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_buf_request *buf_request);
+
+ int (*enqueue_buf) (struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_qbuf_info *info);
+
+ int (*release_buf) (struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle);
+
+ int (*get_bufq_handle) (struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t session_id, uint32_t stream_id);
+
+ int (*get_buf) (struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, struct msm_isp_buffer **buf_info);
+
+ int (*put_buf) (struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index);
+
+ int (*buf_done) (struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index,
+ struct timeval *tv, uint32_t frame_id);
+ int (*attach_ctx) (struct msm_isp_buf_mgr *buf_mgr,
+ struct device *iommu_ctx);
+ void (*detach_ctx) (struct msm_isp_buf_mgr *buf_mgr,
+ struct device *iommu_ctx);
+ int (*buf_mgr_init) (struct msm_isp_buf_mgr *buf_mgr,
+ const char *ctx_name, uint16_t num_buf_q);
+ int (*buf_mgr_deinit) (struct msm_isp_buf_mgr *buf_mgr);
+};
+
+struct msm_isp_buf_mgr {
+ int init_done;
+ uint32_t ref_count;
+ spinlock_t lock;
+ uint16_t num_buf_q;
+ struct msm_isp_bufq *bufq;
+
+ struct ion_client *client;
+ struct msm_isp_buf_ops *ops;
+ uint32_t buf_handle_cnt;
+
+ struct msm_sd_req_vb2_q *vb2_ops;
+
+ /*IOMMU specific*/
+ int iommu_domain_num;
+ struct iommu_domain *iommu_domain;
+};
+
+int msm_isp_create_isp_buf_mgr(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_sd_req_vb2_q *vb2_ops, struct msm_iova_layout *iova_layout);
+
+int msm_isp_proc_buf_cmd(struct msm_isp_buf_mgr *buf_mgr,
+ unsigned int cmd, void *arg);
+
+#endif /* _MSM_ISP_BUF_H_ */
diff --git a/drivers/media/video/msmb/isp/msm_isp.c b/drivers/media/video/msmb/isp/msm_isp.c
new file mode 100644
index 0000000..960144e
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp.c
@@ -0,0 +1,166 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/debugfs.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-device.h>
+#include <mach/board.h>
+#include <mach/vreg.h>
+#include <mach/iommu.h>
+
+#include "msm_isp.h"
+#include "msm_isp_util.h"
+#include "msm_isp_axi_util.h"
+#include "msm_isp_stats_util.h"
+#include "msm_sd.h"
+#include "msm_isp40.h"
+
+static struct msm_sd_req_vb2_q vfe_vb2_ops;
+
+static const struct of_device_id msm_vfe_dt_match[] = {
+ {
+ .compatible = "qcom,vfe40",
+ .data = &vfe40_hw_info,
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_vfe_dt_match);
+
+static const struct platform_device_id msm_vfe_dev_id[] = {
+ {"msm_vfe32"},
+ {}
+};
+
+static struct msm_isp_buf_mgr vfe_buf_mgr;
+
+static int __devinit vfe_probe(struct platform_device *pdev)
+{
+ struct vfe_device *vfe_dev;
+ /*struct msm_cam_subdev_info sd_info;*/
+ const struct of_device_id *match_dev;
+ int rc = 0;
+
+ struct msm_iova_partition vfe_partition = {
+ .start = SZ_128K,
+ .size = SZ_2G - SZ_128K,
+ };
+ struct msm_iova_layout vfe_layout = {
+ .partitions = &vfe_partition,
+ .npartitions = 1,
+ .client_name = "vfe",
+ .domain_flags = 0,
+ };
+
+ vfe_dev = kzalloc(sizeof(struct vfe_device), GFP_KERNEL);
+ if (!vfe_dev) {
+ pr_err("%s: no enough memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ if (pdev->dev.of_node) {
+ of_property_read_u32((&pdev->dev)->of_node,
+ "cell-index", &pdev->id);
+ match_dev = of_match_device(msm_vfe_dt_match, &pdev->dev);
+ vfe_dev->hw_info =
+ (struct msm_vfe_hardware_info *) match_dev->data;
+ } else {
+ vfe_dev->hw_info = platform_get_drvdata(pdev);
+ }
+
+ if (!vfe_dev->hw_info) {
+ pr_err("%s: No vfe hardware info\n", __func__);
+ return -EINVAL;
+ }
+ ISP_DBG("%s: device id = %d\n", __func__, pdev->id);
+
+ vfe_dev->pdev = pdev;
+ rc = vfe_dev->hw_info->vfe_ops.core_ops.get_platform_data(vfe_dev);
+ if (rc < 0) {
+ pr_err("%s: failed to get platform resources\n", __func__);
+ kfree(vfe_dev);
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&vfe_dev->tasklet_q);
+ tasklet_init(&vfe_dev->vfe_tasklet,
+ msm_isp_do_tasklet, (unsigned long)vfe_dev);
+
+ v4l2_subdev_init(&vfe_dev->subdev.sd, vfe_dev->hw_info->subdev_ops);
+ vfe_dev->subdev.sd.internal_ops =
+ vfe_dev->hw_info->subdev_internal_ops;
+ snprintf(vfe_dev->subdev.sd.name,
+ ARRAY_SIZE(vfe_dev->subdev.sd.name),
+ "vfe");
+ vfe_dev->subdev.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ vfe_dev->subdev.sd.flags |= V4L2_SUBDEV_FL_HAS_EVENTS;
+ v4l2_set_subdevdata(&vfe_dev->subdev.sd, vfe_dev);
+ platform_set_drvdata(pdev, &vfe_dev->subdev.sd);
+ mutex_init(&vfe_dev->mutex);
+ spin_lock_init(&vfe_dev->tasklet_lock);
+ spin_lock_init(&vfe_dev->shared_data_lock);
+ media_entity_init(&vfe_dev->subdev.sd.entity, 0, NULL, 0);
+ vfe_dev->subdev.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ vfe_dev->subdev.sd.entity.group_id = MSM_CAMERA_SUBDEV_VFE;
+ vfe_dev->subdev.sd.entity.name = pdev->name;
+ rc = msm_sd_register(&vfe_dev->subdev);
+ if (rc != 0) {
+ pr_err("%s: msm_sd_register error = %d\n", __func__, rc);
+ kfree(vfe_dev);
+ goto end;
+ }
+
+ vfe_dev->buf_mgr = &vfe_buf_mgr;
+ v4l2_subdev_notify(&vfe_dev->subdev.sd,
+ MSM_SD_NOTIFY_REQ_CB, &vfe_vb2_ops);
+ rc = msm_isp_create_isp_buf_mgr(vfe_dev->buf_mgr,
+ &vfe_vb2_ops, &vfe_layout);
+ if (rc < 0) {
+ pr_err("%s: Unable to create buffer manager\n", __func__);
+ kfree(vfe_dev);
+ return -EINVAL;
+ }
+ vfe_dev->vfe_open_cnt = 0;
+end:
+ return rc;
+}
+
+static struct platform_driver vfe_driver = {
+ .probe = vfe_probe,
+ .driver = {
+ .name = "msm_vfe",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_vfe_dt_match,
+ },
+ .id_table = msm_vfe_dev_id,
+};
+
+static int __init msm_vfe_init_module(void)
+{
+ return platform_driver_register(&vfe_driver);
+}
+
+static void __exit msm_vfe_exit_module(void)
+{
+ platform_driver_unregister(&vfe_driver);
+}
+
+module_init(msm_vfe_init_module);
+module_exit(msm_vfe_exit_module);
+MODULE_DESCRIPTION("MSM VFE driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msmb/isp/msm_isp.h b/drivers/media/video/msmb/isp/msm_isp.h
new file mode 100644
index 0000000..c320a1a
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp.h
@@ -0,0 +1,347 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_VFE_H__
+#define __MSM_VFE_H__
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <media/v4l2-subdev.h>
+#include <media/msmb_isp.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+
+#include "msm_buf_mgr.h"
+
+#define MAX_NUM_WM 7
+#define MAX_NUM_RDI 3
+#define MAX_NUM_RDI_MASTER 3
+#define MAX_NUM_COMPOSITE_MASK 4
+#define MAX_NUM_STATS_COMP_MASK 2
+#define MAX_INIT_FRAME_DROP 31
+#define ISP_SUB(a) ((a > 0) ? a-1 : 0)
+
+struct vfe_device;
+struct msm_vfe_axi_stream;
+struct msm_vfe_stats_stream;
+
+struct vfe_subscribe_info {
+ struct v4l2_fh *vfh;
+ uint32_t active;
+};
+
+enum msm_isp_camif_update_state {
+ NO_UPDATE,
+ ENABLE_CAMIF,
+ DISABLE_CAMIF,
+ DISABLE_CAMIF_IMMEDIATELY
+};
+
+struct msm_vfe_irq_ops {
+ void (*read_irq_status) (struct vfe_device *vfe_dev,
+ uint32_t *irq_status0, uint32_t *irq_status1);
+ void (*process_reg_update) (struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1);
+ void (*process_reset_irq) (struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1);
+ void (*process_halt_irq) (struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1);
+ void (*process_camif_irq) (struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1);
+ void (*process_axi_irq) (struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct timeval *tv);
+ void (*process_error_irq) (struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1);
+ void (*process_stats_irq) (struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct timeval *tv);
+};
+
+struct msm_vfe_axi_ops {
+ void (*reload_wm) (struct vfe_device *vfe_dev,
+ uint32_t reload_mask);
+ void (*enable_wm) (struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint8_t enable);
+
+ void (*cfg_framedrop) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+ void (*clear_framedrop) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+ void (*cfg_comp_mask) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+ void (*clear_comp_mask) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+ void (*cfg_wm_irq_mask) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+ void (*clear_wm_irq_mask) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+
+ void (*cfg_wm_reg) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ uint8_t plane_idx);
+ void (*clear_wm_reg) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx);
+
+ void (*cfg_wm_xbar_reg) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ uint8_t plane_idx);
+ void (*clear_wm_xbar_reg) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx);
+
+ void (*cfg_rdi_reg) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ uint8_t plane_idx);
+
+ void (*cfg_ub) (struct vfe_device *vfe_dev);
+
+ void (*update_ping_pong_addr) (struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint32_t pingpong_status, unsigned long paddr);
+
+ uint32_t (*get_wm_mask) (uint32_t irq_status0, uint32_t irq_status1);
+ uint32_t (*get_comp_mask) (uint32_t irq_status0, uint32_t irq_status1);
+ uint32_t (*get_pingpong_status) (struct vfe_device *vfe_dev);
+ long (*halt) (struct vfe_device *vfe_dev);
+};
+
+struct msm_vfe_core_ops {
+ void (*epoch_irq) (struct vfe_device *vfe_dev,
+ uint32_t epoch_line0, uint32_t epoch_line1);
+ void (*reg_update) (struct vfe_device *vfe_dev, uint32_t update_mask);
+ long (*reset_hw) (struct vfe_device *vfe_dev);
+ int (*init_hw) (struct vfe_device *vfe_dev);
+ void (*init_hw_reg) (struct vfe_device *vfe_dev);
+ void (*release_hw) (struct vfe_device *vfe_dev);
+ void (*cfg_camif) (struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg);
+ void (*update_camif_state) (struct vfe_device *vfe_dev,
+ enum msm_isp_camif_update_state update_state);
+ int (*get_platform_data) (struct vfe_device *vfe_dev);
+};
+struct msm_vfe_stats_ops {
+ void (*cfg_comp_mask) (struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+ void (*clear_comp_mask) (struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+ void (*cfg_wm_irq_mask) (struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+ void (*clear_wm_irq_mask) (struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+
+ void (*cfg_wm_reg) (struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream_request_cmd *stream_cfg_cmd);
+ void (*clear_wm_reg) (struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+
+ void (*cfg_ub) (struct vfe_device *vfe_dev);
+
+ void (*stats_enable) (struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable);
+
+ void (*update_ping_pong_addr) (struct vfe_device *vfe_dev,
+ enum msm_isp_stats_type stats_type, uint32_t pingpong_status,
+ unsigned long paddr);
+
+ uint32_t (*get_frame_id) (struct vfe_device *vfe_dev);
+ uint32_t (*get_wm_mask) (uint32_t irq_status0, uint32_t irq_status1);
+ uint32_t (*get_comp_mask) (uint32_t irq_status0, uint32_t irq_status1);
+};
+
+struct msm_vfe_ops {
+ struct msm_vfe_irq_ops irq_ops;
+ struct msm_vfe_axi_ops axi_ops;
+ struct msm_vfe_core_ops core_ops;
+ struct msm_vfe_stats_ops stats_ops;
+};
+
+struct msm_vfe_hardware_info {
+ struct msm_vfe_ops vfe_ops;
+ struct msm_vfe_axi_hardware_info *axi_hw_info;
+ struct v4l2_subdev_internal_ops *subdev_internal_ops;
+ struct v4l2_subdev_ops *subdev_ops;
+};
+
+struct msm_vfe_axi_hardware_info {
+ uint8_t num_wm;
+ uint8_t num_rdi;
+ uint8_t num_rdi_master;
+ uint8_t num_comp_mask;
+ uint32_t min_wm_ub;
+};
+
+enum msm_vfe_axi_state {
+ AVALIABLE,
+ INACTIVE,
+ ACTIVE,
+ PAUSE,
+ START_PENDING,
+ STOP_PENDING,
+ STOPPING,
+ PAUSE_PENDING,
+};
+
+#define VFE_NO_DROP 0xFFFFFFFF
+#define VFE_DROP_EVERY_2FRAME 0x55555555
+#define VFE_DROP_EVERY_4FRAME 0x11111111
+#define VFE_DROP_EVERY_8FRAME 0x01010101
+#define VFE_DROP_EVERY_16FRAME 0x00010001
+#define VFE_DROP_EVERY_32FRAME 0x00000001
+
+enum msm_vfe_axi_stream_type {
+ CONTINUOUS_STREAM,
+ BURST_STREAM,
+};
+
+struct msm_vfe_axi_stream {
+ uint32_t frame_id;
+ enum msm_vfe_axi_state state;
+ enum msm_vfe_axi_stream_src stream_src;
+ uint8_t num_planes;
+ uint8_t wm[MAX_PLANES_PER_STREAM];
+ uint8_t rdi[MAX_PLANES_PER_STREAM];
+ uint8_t rdi_master[MAX_PLANES_PER_STREAM];
+ uint8_t comp_mask_index;
+ struct msm_isp_buffer *buf[2];
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t bufq_handle;
+ uint32_t stream_handle;
+ uint8_t buf_divert;
+ enum msm_vfe_axi_stream_type stream_type;
+
+ uint32_t framedrop_pattern;
+ uint32_t init_frame_drop;
+ uint32_t burst_frame_count;/*number of sof before burst stop*/
+ uint8_t auto_trigger_stop;
+ uint8_t framedrop_update;
+};
+
+struct msm_vfe_axi_composite_info {
+ uint32_t stream_handle;
+ uint32_t stream_composite_mask;
+};
+struct msm_vfe_src_info {
+ unsigned long frame_id;
+ uint8_t active;
+ uint8_t pix_stream_count;
+ uint8_t raw_stream_count;
+ enum msm_vfe_inputmux input_mux;
+};
+
+
+struct msm_vfe_stats_stream {
+ uint32_t frame_id;
+ uint8_t enable;
+ enum msm_isp_stats_type stats_type;
+ uint8_t comp_mask_index;
+ struct msm_isp_buffer *buf[2];
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t bufq_handle;
+ uint32_t stream_handle;
+ uint32_t framedrop_pattern;
+};
+
+struct msm_vfe_stats_composite_info {
+ uint32_t stream_handle;
+ uint32_t stream_composite_mask;
+};
+
+enum msm_wm_ub_cfg_type {
+ MSM_WM_UB_CFG_DEFAULT,
+ MSM_WM_UB_EQUAL_SLICING,
+ MSM_WM_UB_CFG_MAX_NUM
+};
+struct msm_vfe_axi_shared_data {
+ struct msm_vfe_axi_hardware_info *hw_info;
+ struct msm_vfe_axi_stream stream_info[MAX_NUM_STREAM];
+ uint32_t free_wm[MAX_NUM_WM];
+ uint32_t wm_image_size[MAX_NUM_WM];
+ enum msm_wm_ub_cfg_type wm_ub_cfg_policy;
+ uint8_t num_used_wm;
+ uint8_t free_rdi[MAX_NUM_RDI];
+ uint8_t free_rdi_master[MAX_NUM_RDI][MAX_NUM_RDI_MASTER];
+ uint8_t num_used_rdi;
+ uint8_t num_active_stream;
+ struct msm_vfe_axi_composite_info
+ composite_info[MAX_NUM_COMPOSITE_MASK];
+ uint8_t num_used_composite_mask;
+ uint32_t stream_update;
+ struct msm_vfe_src_info src_info[VFE_SRC_MAX];
+ uint16_t stream_handle_cnt;
+ unsigned long event_mask;
+};
+
+struct msm_vfe_stats_shared_data {
+ struct msm_vfe_stats_stream stream_info[MSM_ISP_STATS_MAX];
+ struct msm_vfe_stats_composite_info
+ comp_info[MAX_NUM_STATS_COMP_MASK];
+ uint8_t num_active_stream;
+ uint8_t num_used_composite_mask;
+ uint16_t stream_handle_cnt;
+};
+struct msm_vfe_tasklet_queue_cmd {
+ struct list_head list;
+ uint32_t vfeInterruptStatus0;
+ uint32_t vfeInterruptStatus1;
+ struct timeval tv;
+ uint8_t cmd_used;
+};
+
+#define MSM_VFE_TASKLETQ_SIZE 200
+
+struct vfe_device {
+ struct platform_device *pdev;
+ struct msm_sd_subdev subdev;
+ struct resource *vfe_irq;
+ struct resource *vfe_mem;
+ struct resource *vfe_vbif_mem;
+ struct resource *vfe_io;
+ struct resource *vfe_vbif_io;
+ void __iomem *vfe_base;
+ void __iomem *vfe_vbif_base;
+
+ struct device *iommu_ctx;
+
+ struct regulator *fs_vfe;
+ struct clk *vfe_clk[7];
+
+ uint32_t bus_perf_client;
+
+ struct completion reset_complete;
+ struct completion halt_complete;
+ struct completion stream_config_complete;
+ struct mutex mutex;
+
+ atomic_t irq_cnt;
+ uint8_t taskletq_idx;
+ spinlock_t tasklet_lock;
+ spinlock_t shared_data_lock;
+ struct list_head tasklet_q;
+ struct tasklet_struct vfe_tasklet;
+ struct msm_vfe_tasklet_queue_cmd
+ tasklet_queue_cmd[MSM_VFE_TASKLETQ_SIZE];
+
+ struct msm_vfe_hardware_info *hw_info;
+
+ struct msm_vfe_axi_shared_data axi_data;
+ struct msm_vfe_stats_shared_data stats_data;
+ struct msm_isp_buf_mgr *buf_mgr;
+ int dump_reg;
+ uint32_t vfe_open_cnt;
+};
+
+#endif
diff --git a/drivers/media/video/msmb/isp/msm_isp32.c b/drivers/media/video/msmb/isp/msm_isp32.c
new file mode 100644
index 0000000..d7b62d1
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp32.c
@@ -0,0 +1,822 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+
+#include "msm_isp32.h"
+#include "msm_isp_util.h"
+#include "msm_isp_axi_util.h"
+#include "msm_isp.h"
+#include "msm.h"
+
+#define VFE32_BURST_LEN 4
+#define VFE32_EQUAL_SLICE_UB 117
+#define VFE32_WM_BASE(idx) (0x4C + 0x18 * idx)
+#define VFE32_RDI_BASE(idx) (0x734 + 0x4 * idx)
+#define VFE32_RDI_MN_BASE(m) (0x734 + 0x4 * m/3)
+#define VFE32_RDI_MN_SEL_SHIFT(m) (4*(m%3) + 4)
+#define VFE32_RDI_MN_FB_SHIFT(m) ((m%3) + 16)
+#define VFE32_XBAR_BASE(idx) (0x40 + 0x4 * (idx / 4))
+#define VFE32_XBAR_SHIFT(idx) ((idx % 4) * 8)
+#define VFE32_PING_PONG_BASE(wm, ping_pong) \
+ (VFE32_WM_BASE(wm) + 0x4 * (1 + (~(ping_pong >> wm) & 0x1)))
+
+/*Temporary use fixed bus vectors in VFE */
+static struct msm_bus_vectors msm_vfe32_init_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VFE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = 0,
+ },
+};
+
+static struct msm_bus_vectors msm_vfe32_preview_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VFE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 1027648000,
+ .ib = 1105920000,
+ },
+};
+
+static struct msm_bus_paths msm_vfe32_bus_client_config[] = {
+ {
+ ARRAY_SIZE(msm_vfe32_init_vectors),
+ msm_vfe32_init_vectors,
+ },
+ {
+ ARRAY_SIZE(msm_vfe32_preview_vectors),
+ msm_vfe32_preview_vectors,
+ },
+};
+
+static struct msm_bus_scale_pdata msm_vfe32_bus_client_pdata = {
+ msm_vfe32_bus_client_config,
+ ARRAY_SIZE(msm_vfe32_bus_client_config),
+ .name = "msm_camera_vfe",
+};
+
+static struct msm_cam_clk_info msm_vfe32_clk_info[] = {
+ {"vfe_clk", 228570000},
+ {"vfe_pclk", -1},
+ {"csi_vfe_clk", -1},
+};
+
+static int msm_vfe32_init_hardware(struct vfe_device *vfe_dev)
+{
+ int rc = -1;
+
+ vfe_dev->bus_perf_client =
+ msm_bus_scale_register_client(&msm_vfe32_bus_client_pdata);
+ if (!vfe_dev->bus_perf_client) {
+ pr_err("%s: Registration Failed!\n", __func__);
+ vfe_dev->bus_perf_client = 0;
+ goto bus_scale_register_failed;
+ }
+ msm_bus_scale_client_update_request(vfe_dev->bus_perf_client, 1);
+
+ if (vfe_dev->fs_vfe) {
+ rc = regulator_enable(vfe_dev->fs_vfe);
+ if (rc) {
+ pr_err("%s: Regulator enable failed\n", __func__);
+ goto fs_failed;
+ }
+ }
+
+ rc = msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe32_clk_info,
+ vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe32_clk_info), 1);
+ if (rc < 0)
+ goto clk_enable_failed;
+
+ vfe_dev->vfe_base = ioremap(vfe_dev->vfe_mem->start,
+ resource_size(vfe_dev->vfe_mem));
+ if (!vfe_dev->vfe_base) {
+ rc = -ENOMEM;
+ pr_err("%s: vfe ioremap failed\n", __func__);
+ goto vfe_remap_failed;
+ }
+
+ rc = request_irq(vfe_dev->vfe_irq->start, msm_isp_process_irq,
+ IRQF_TRIGGER_RISING, "vfe", vfe_dev);
+ if (rc < 0) {
+ pr_err("%s: irq request failed\n", __func__);
+ goto irq_req_failed;
+ }
+
+ return rc;
+irq_req_failed:
+ iounmap(vfe_dev->vfe_base);
+vfe_remap_failed:
+ msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe32_clk_info,
+ vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe32_clk_info), 0);
+clk_enable_failed:
+ regulator_disable(vfe_dev->fs_vfe);
+fs_failed:
+ msm_bus_scale_client_update_request(vfe_dev->bus_perf_client, 0);
+ msm_bus_scale_unregister_client(vfe_dev->bus_perf_client);
+bus_scale_register_failed:
+ return rc;
+}
+
+static void msm_vfe32_release_hardware(struct vfe_device *vfe_dev)
+{
+ free_irq(vfe_dev->vfe_irq->start, vfe_dev);
+ tasklet_kill(&vfe_dev->vfe_tasklet);
+ iounmap(vfe_dev->vfe_base);
+ msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe32_clk_info,
+ vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe32_clk_info), 0);
+ regulator_disable(vfe_dev->fs_vfe);
+ msm_bus_scale_client_update_request(vfe_dev->bus_perf_client, 0);
+ msm_bus_scale_unregister_client(vfe_dev->bus_perf_client);
+}
+
+static void msm_vfe32_init_hardware_reg(struct vfe_device *vfe_dev)
+{
+ /* CGC_OVERRIDE */
+ msm_camera_io_w(0x07FFFFFF, vfe_dev->vfe_base + 0xC);
+ /* BUS_CFG */
+ msm_camera_io_w(0x00000001, vfe_dev->vfe_base + 0x3C);
+ msm_camera_io_w(0x00000025, vfe_dev->vfe_base + 0x1C);
+ msm_camera_io_w_mb(0x1DFFFFFF, vfe_dev->vfe_base + 0x20);
+ msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x24);
+ msm_camera_io_w_mb(0x1FFFFFFF, vfe_dev->vfe_base + 0x28);
+}
+
+static void msm_vfe32_process_reset_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ if (irq_status1 & (1 << 23))
+ complete(&vfe_dev->reset_complete);
+}
+
+static void msm_vfe32_process_halt_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ if (irq_status1 & (1 << 24))
+ complete(&vfe_dev->halt_complete);
+}
+
+static void msm_vfe32_process_camif_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ if (!(irq_status0 & 0x1F))
+ return;
+
+ if (irq_status0 & (1 << 0)) {
+ ISP_DBG("%s: PIX0 frame id: %lu\n", __func__,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
+ msm_isp_update_framedrop_count(vfe_dev);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id++;
+ }
+}
+
+static void msm_vfe32_process_violation_irq(struct vfe_device *vfe_dev)
+{
+ uint32_t violation_status;
+ violation_status = msm_camera_io_r(vfe_dev->vfe_base + 0x48);
+ if (!violation_status)
+ return;
+
+ if (violation_status & (1 << 0))
+ pr_err("%s: black violation\n", __func__);
+ if (violation_status & (1 << 1))
+ pr_err("%s: rolloff violation\n", __func__);
+ if (violation_status & (1 << 2))
+ pr_err("%s: demux violation\n", __func__);
+ if (violation_status & (1 << 3))
+ pr_err("%s: demosaic violation\n", __func__);
+ if (violation_status & (1 << 4))
+ pr_err("%s: crop violation\n", __func__);
+ if (violation_status & (1 << 5))
+ pr_err("%s: scale violation\n", __func__);
+ if (violation_status & (1 << 6))
+ pr_err("%s: wb violation\n", __func__);
+ if (violation_status & (1 << 7))
+ pr_err("%s: clf violation\n", __func__);
+ if (violation_status & (1 << 8))
+ pr_err("%s: matrix violation\n", __func__);
+ if (violation_status & (1 << 9))
+ pr_err("%s: rgb lut violation\n", __func__);
+ if (violation_status & (1 << 10))
+ pr_err("%s: la violation\n", __func__);
+ if (violation_status & (1 << 11))
+ pr_err("%s: chroma enhance violation\n", __func__);
+ if (violation_status & (1 << 12))
+ pr_err("%s: chroma supress mce violation\n", __func__);
+ if (violation_status & (1 << 13))
+ pr_err("%s: skin enhance violation\n", __func__);
+ if (violation_status & (1 << 14))
+ pr_err("%s: asf violation\n", __func__);
+ if (violation_status & (1 << 15))
+ pr_err("%s: scale y violation\n", __func__);
+ if (violation_status & (1 << 16))
+ pr_err("%s: scale cbcr violation\n", __func__);
+ if (violation_status & (1 << 17))
+ pr_err("%s: chroma subsample violation\n", __func__);
+ if (violation_status & (1 << 18))
+ pr_err("%s: framedrop enc y violation\n", __func__);
+ if (violation_status & (1 << 19))
+ pr_err("%s: framedrop enc cbcr violation\n", __func__);
+ if (violation_status & (1 << 20))
+ pr_err("%s: framedrop view y violation\n", __func__);
+ if (violation_status & (1 << 21))
+ pr_err("%s: framedrop view cbcr violation\n", __func__);
+ if (violation_status & (1 << 22))
+ pr_err("%s: realign buf y violation\n", __func__);
+ if (violation_status & (1 << 23))
+ pr_err("%s: realign buf cb violation\n", __func__);
+ if (violation_status & (1 << 24))
+ pr_err("%s: realign buf cr violation\n", __func__);
+}
+
+static void msm_vfe32_process_error_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ uint32_t camif_status;
+ if (!(irq_status1 & 0x7FFFFF))
+ return;
+
+ if (irq_status1 & (1 << 0)) {
+ camif_status = msm_camera_io_r(vfe_dev->vfe_base + 0x204);
+ pr_err("%s: camif error status: 0x%x\n",
+ __func__, camif_status);
+ }
+ if (irq_status1 & (1 << 1))
+ pr_err("%s: stats bhist overwrite\n", __func__);
+ if (irq_status1 & (1 << 2))
+ pr_err("%s: stats cs overwrite\n", __func__);
+ if (irq_status1 & (1 << 3))
+ pr_err("%s: stats ihist overwrite\n", __func__);
+ if (irq_status1 & (1 << 4))
+ pr_err("%s: realign buf y overflow\n", __func__);
+ if (irq_status1 & (1 << 5))
+ pr_err("%s: realign buf cb overflow\n", __func__);
+ if (irq_status1 & (1 << 6))
+ pr_err("%s: realign buf cr overflow\n", __func__);
+ if (irq_status1 & (1 << 7)) {
+ pr_err("%s: violation\n", __func__);
+ msm_vfe32_process_violation_irq(vfe_dev);
+ }
+ if (irq_status1 & (1 << 8))
+ pr_err("%s: image master 0 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 9))
+ pr_err("%s: image master 1 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 10))
+ pr_err("%s: image master 2 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 11))
+ pr_err("%s: image master 3 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 12))
+ pr_err("%s: image master 4 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 13))
+ pr_err("%s: image master 5 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 14))
+ pr_err("%s: image master 6 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 15))
+ pr_err("%s: status ae/bg bus overflow\n", __func__);
+ if (irq_status1 & (1 << 16))
+ pr_err("%s: status af/bf bus overflow\n", __func__);
+ if (irq_status1 & (1 << 17))
+ pr_err("%s: status awb bus overflow\n", __func__);
+ if (irq_status1 & (1 << 18))
+ pr_err("%s: status rs bus overflow\n", __func__);
+ if (irq_status1 & (1 << 19))
+ pr_err("%s: status cs bus overflow\n", __func__);
+ if (irq_status1 & (1 << 20))
+ pr_err("%s: status ihist bus overflow\n", __func__);
+ if (irq_status1 & (1 << 21))
+ pr_err("%s: status skin bhist bus overflow\n", __func__);
+ if (irq_status1 & (1 << 22))
+ pr_err("%s: axi error\n", __func__);
+}
+
+static void msm_vfe32_read_irq_status(struct vfe_device *vfe_dev,
+ uint32_t *irq_status0, uint32_t *irq_status1)
+{
+ *irq_status0 = msm_camera_io_r(vfe_dev->vfe_base + 0x2C);
+ *irq_status1 = msm_camera_io_r(vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w(*irq_status0, vfe_dev->vfe_base + 0x24);
+ msm_camera_io_w(*irq_status1, vfe_dev->vfe_base + 0x28);
+ msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x18);
+}
+
+static void msm_vfe32_process_reg_update(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ if (!(irq_status0 & 0x20) && !(irq_status1 & 0x1C000000))
+ return;
+
+ if (vfe_dev->axi_data.stream_update)
+ msm_isp_axi_stream_update(vfe_dev);
+
+ msm_isp_update_framedrop_reg(vfe_dev);
+
+ return;
+}
+
+static void msm_vfe32_reg_update(
+ struct vfe_device *vfe_dev, uint32_t update_mask)
+{
+ msm_camera_io_w_mb(update_mask, vfe_dev->vfe_base + 0x260);
+}
+
+static long msm_vfe32_reset_hardware(struct vfe_device *vfe_dev)
+{
+ init_completion(&vfe_dev->reset_complete);
+ msm_camera_io_w_mb(0x3FF, vfe_dev->vfe_base + 0x4);
+ return wait_for_completion_interruptible_timeout(
+ &vfe_dev->reset_complete, msecs_to_jiffies(50));
+}
+
+static void msm_vfe32_axi_reload_wm(
+ struct vfe_device *vfe_dev, uint32_t reload_mask)
+{
+ msm_camera_io_w_mb(reload_mask, vfe_dev->vfe_base + 0x38);
+}
+
+static void msm_vfe32_axi_enable_wm(struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint8_t enable)
+{
+ if (enable)
+ msm_camera_io_w_mb(0x1,
+ vfe_dev->vfe_base + VFE32_WM_BASE(wm_idx));
+ else
+ msm_camera_io_w_mb(0x0,
+ vfe_dev->vfe_base + VFE32_WM_BASE(wm_idx));
+}
+
+static void msm_vfe32_axi_cfg_comp_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ uint32_t comp_mask, comp_mask_index =
+ stream_info->comp_mask_index;
+ uint32_t irq_mask;
+
+ comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x34);
+ comp_mask &= ~(0x7F << (comp_mask_index * 8));
+ comp_mask |= (axi_data->composite_info[comp_mask_index].
+ stream_composite_mask << (comp_mask_index * 8));
+ msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x34);
+
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
+ irq_mask |= 1 << (comp_mask_index + 21);
+ msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C);
+}
+
+static void msm_vfe32_axi_clear_comp_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t comp_mask, comp_mask_index = stream_info->comp_mask_index;
+ uint32_t irq_mask;
+
+ comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x34);
+ comp_mask &= ~(0x7F << (comp_mask_index * 8));
+ msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x34);
+
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
+ irq_mask &= ~(1 << (comp_mask_index + 21));
+ msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C);
+}
+
+static void msm_vfe32_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t irq_mask;
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
+ irq_mask |= 1 << (stream_info->wm[0] + 6);
+ msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C);
+}
+
+static void msm_vfe32_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t irq_mask;
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
+ irq_mask &= ~(1 << (stream_info->wm[0] + 6));
+ msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C);
+}
+
+static void msm_vfe32_cfg_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t framedrop_pattern = 0;
+
+ if (stream_info->init_frame_drop == 0)
+ framedrop_pattern = stream_info->framedrop_pattern;
+
+ if (stream_info->stream_type == BURST_STREAM &&
+ stream_info->burst_frame_count == 0)
+ framedrop_pattern = 0;
+
+ if (stream_info->stream_src == PIX_ENCODER) {
+ msm_camera_io_w(0x1F, vfe_dev->vfe_base + 0x504);
+ msm_camera_io_w(0x1F, vfe_dev->vfe_base + 0x508);
+ msm_camera_io_w(framedrop_pattern, vfe_dev->vfe_base + 0x50C);
+ msm_camera_io_w(framedrop_pattern, vfe_dev->vfe_base + 0x510);
+ } else if (stream_info->stream_src == PIX_VIEWFINDER) {
+ msm_camera_io_w(0x1F, vfe_dev->vfe_base + 0x514);
+ msm_camera_io_w(0x1F, vfe_dev->vfe_base + 0x518);
+ msm_camera_io_w(framedrop_pattern, vfe_dev->vfe_base + 0x51C);
+ msm_camera_io_w(framedrop_pattern, vfe_dev->vfe_base + 0x520);
+ }
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x260);
+}
+
+static void msm_vfe32_clear_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ if (stream_info->stream_src == PIX_ENCODER) {
+ msm_camera_io_w(0, vfe_dev->vfe_base + 0x50C);
+ msm_camera_io_w(0, vfe_dev->vfe_base + 0x510);
+ } else if (stream_info->stream_src == PIX_VIEWFINDER) {
+ msm_camera_io_w(0, vfe_dev->vfe_base + 0x51C);
+ msm_camera_io_w(0, vfe_dev->vfe_base + 0x520);
+ }
+}
+
+static void msm_vfe32_cfg_camif(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg)
+{
+ uint16_t first_pixel, last_pixel, first_line, last_line;
+ struct msm_vfe_camif_cfg *camif_cfg = &pix_cfg->camif_cfg;
+ uint32_t val;
+
+ first_pixel = camif_cfg->left_crop;
+ last_pixel = camif_cfg->pixels_per_line -
+ camif_cfg->left_crop -
+ camif_cfg->right_crop;
+ first_line = camif_cfg->left_crop;
+ last_line = camif_cfg->lines_per_frame -
+ camif_cfg->top_crop -
+ camif_cfg->bottom_crop;
+
+ msm_camera_io_w(pix_cfg->input_mux << 16 | pix_cfg->pixel_pattern,
+ vfe_dev->vfe_base + 0x14);
+
+ msm_camera_io_w(camif_cfg->lines_per_frame << 16 |
+ camif_cfg->pixels_per_line,
+ vfe_dev->vfe_base + 0x1EC);
+
+ msm_camera_io_w(ISP_SUB(first_pixel) << 16 | ISP_SUB(last_pixel),
+ vfe_dev->vfe_base + 0x1F0);
+
+ msm_camera_io_w(ISP_SUB(first_line) << 16 | ISP_SUB(last_line),
+ vfe_dev->vfe_base + 0x1F4);
+
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x6FC);
+ val &= 0xFFFFFFFC;
+ val |= camif_cfg->camif_input;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x6FC);
+}
+
+static void msm_vfe32_update_camif_state(
+ struct vfe_device *vfe_dev,
+ enum msm_isp_camif_update_state update_state)
+{
+ uint32_t val;
+ bool bus_en, vfe_en;
+ if (update_state == NO_UPDATE)
+ return;
+
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x1E4);
+ if (update_state == ENABLE_CAMIF) {
+ bus_en =
+ ((vfe_dev->axi_data.src_info[
+ VFE_PIX_0].raw_stream_count > 0) ? 1 : 0);
+ vfe_en =
+ ((vfe_dev->axi_data.src_info[
+ VFE_PIX_0].pix_stream_count > 0) ? 1 : 0);
+ val &= 0xFFFFFF3F;
+ val = val | bus_en << 7 | vfe_en << 6;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x1E4);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x1E0);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].active = 1;
+ } else if (update_state == DISABLE_CAMIF) {
+ msm_camera_io_w_mb(0x0, vfe_dev->vfe_base + 0x1E0);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0;
+ }
+}
+
+static void msm_vfe32_axi_cfg_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ uint8_t plane_idx)
+{
+ uint32_t val;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->axi_stream_handle & 0xFF)];
+ uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[plane_idx]);
+
+ /*WR_IMAGE_SIZE*/
+ val =
+ ((msm_isp_cal_word_per_line(stream_cfg_cmd->output_format,
+ stream_cfg_cmd->plane_cfg[
+ plane_idx].output_width)+1)/2 - 1) << 16 |
+ (stream_cfg_cmd->plane_cfg[plane_idx].output_height - 1);
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x10);
+
+ /*WR_BUFFER_CFG*/
+ val =
+ msm_isp_cal_word_per_line(stream_cfg_cmd->output_format,
+ stream_cfg_cmd->plane_cfg[plane_idx].output_width) << 16 |
+ (stream_cfg_cmd->plane_cfg[
+ plane_idx].output_scan_lines - 1) << 4 |
+ VFE32_BURST_LEN >> 2;
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
+ return;
+}
+
+static void msm_vfe32_axi_clear_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
+{
+ uint32_t val = 0;
+ uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[plane_idx]);
+ /*WR_IMAGE_SIZE*/
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x10);
+ /*WR_BUFFER_CFG*/
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
+ return;
+}
+
+static void msm_vfe32_axi_cfg_rdi_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ uint8_t plane_idx)
+{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[(stream_cfg_cmd->axi_stream_handle & 0xFF)];
+ struct msm_vfe_axi_plane_cfg *plane_cfg =
+ &stream_cfg_cmd->plane_cfg[plane_idx];
+ uint8_t rdi = stream_info->rdi[plane_idx];
+ uint8_t rdi_master = stream_info->rdi_master[plane_idx];
+ uint32_t rdi_reg_cfg;
+
+ rdi_reg_cfg = msm_camera_io_r(vfe_dev->vfe_base + VFE32_RDI_BASE(rdi));
+ rdi_reg_cfg = (rdi_reg_cfg & 0xFFFFFFF) | rdi_master << 28;
+ msm_camera_io_w(rdi_reg_cfg, vfe_dev->vfe_base + VFE32_RDI_BASE(rdi));
+
+ rdi_reg_cfg = msm_camera_io_r(
+ vfe_dev->vfe_base + VFE32_RDI_MN_BASE(rdi_master));
+ rdi_reg_cfg &= ~((0xF << VFE32_RDI_MN_SEL_SHIFT(rdi_master)) |
+ (0x1 << VFE32_RDI_MN_FB_SHIFT(rdi_master)));
+ rdi_reg_cfg |= (plane_cfg->rdi_cid <<
+ VFE32_RDI_MN_SEL_SHIFT(rdi_master) |
+ (stream_cfg_cmd->frame_base <<
+ VFE32_RDI_MN_FB_SHIFT(rdi_master)));
+ msm_camera_io_w(rdi_reg_cfg,
+ vfe_dev->vfe_base +
+ VFE32_RDI_MN_BASE(rdi_master));
+}
+
+static void msm_vfe32_axi_cfg_wm_xbar_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ uint8_t plane_idx)
+{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[(stream_cfg_cmd->axi_stream_handle & 0xFF)];
+ struct msm_vfe_axi_plane_cfg *plane_cfg =
+ &stream_cfg_cmd->plane_cfg[plane_idx];
+ uint8_t wm = stream_info->wm[plane_idx];
+ uint32_t xbar_cfg = 0;
+ uint32_t xbar_reg_cfg = 0;
+
+ switch (stream_cfg_cmd->stream_src) {
+ case PIX_ENCODER:
+ case PIX_VIEWFINDER: {
+ if (plane_cfg->output_plane_format
+ != CRCB_PLANE) {
+ /*SINGLE_STREAM_SEL*/
+ xbar_cfg |= plane_cfg->output_plane_format << 5;
+ } else {
+ switch (stream_cfg_cmd->output_format) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV16:
+ xbar_cfg |= 0x3 << 3; /*PAIR_STREAM_SWAP_CTRL*/
+ break;
+ }
+ xbar_cfg |= 0x1 << 1; /*PAIR_STREAM_EN*/
+ }
+ if (stream_cfg_cmd->stream_src == PIX_VIEWFINDER)
+ xbar_cfg |= 0x1; /*VIEW_STREAM_EN*/
+ break;
+ }
+ case CAMIF_RAW:
+ xbar_cfg = 0x60;
+ break;
+ case IDEAL_RAW:
+ xbar_cfg = 0x80;
+ break;
+ case RDI:
+ if (stream_info->rdi[plane_idx] == 0)
+ xbar_cfg = 0xA0;
+ else if (stream_info->rdi[plane_idx] == 1)
+ xbar_cfg = 0xC0;
+ else if (stream_info->rdi[plane_idx] == 2)
+ xbar_cfg = 0xE0;
+ break;
+ default:
+ pr_err("%s: Invalid stream src\n", __func__);
+ }
+ xbar_reg_cfg = msm_camera_io_r(vfe_dev->vfe_base + VFE32_XBAR_BASE(wm));
+ xbar_reg_cfg &= ~(0xFF << VFE32_XBAR_SHIFT(wm));
+ xbar_reg_cfg |= (xbar_cfg << VFE32_XBAR_SHIFT(wm));
+ msm_camera_io_w(xbar_reg_cfg, vfe_dev->vfe_base + VFE32_XBAR_BASE(wm));
+ return;
+}
+
+static void msm_vfe32_axi_clear_wm_xbar_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
+{
+ uint8_t wm = stream_info->wm[plane_idx];
+ uint32_t xbar_reg_cfg = 0;
+
+ xbar_reg_cfg = msm_camera_io_r(vfe_dev->vfe_base + VFE32_XBAR_BASE(wm));
+ xbar_reg_cfg &= ~(0xFF << VFE32_XBAR_SHIFT(wm));
+ msm_camera_io_w(xbar_reg_cfg, vfe_dev->vfe_base + VFE32_XBAR_BASE(wm));
+}
+
+static void msm_vfe32_cfg_axi_ub(struct vfe_device *vfe_dev)
+{
+ int i;
+ uint32_t ub_offset = 0;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ msm_camera_io_w(ub_offset << 16 | (VFE32_EQUAL_SLICE_UB - 1),
+ vfe_dev->vfe_base + VFE32_WM_BASE(i) + 0xC);
+ ub_offset += VFE32_EQUAL_SLICE_UB;
+ }
+}
+
+static void msm_vfe32_update_ping_pong_addr(struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint32_t pingpong_status, unsigned long paddr)
+{
+ msm_camera_io_w(paddr, vfe_dev->vfe_base +
+ VFE32_PING_PONG_BASE(wm_idx, pingpong_status));
+}
+
+static long msm_vfe32_axi_halt(struct vfe_device *vfe_dev)
+{
+ uint32_t halt_mask;
+ halt_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x20);
+ halt_mask |= (1 << 24);
+ msm_camera_io_w_mb(halt_mask, vfe_dev->vfe_base + 0x20);
+ init_completion(&vfe_dev->halt_complete);
+ /*TD: Need to fix crashes with this*/
+ /*msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x1D8);*/
+ return wait_for_completion_interruptible_timeout(
+ &vfe_dev->halt_complete, msecs_to_jiffies(500));
+}
+
+static uint32_t msm_vfe32_get_wm_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 6) & 0x7F;
+}
+
+static uint32_t msm_vfe32_get_comp_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 21) & 0x7;
+}
+
+static uint32_t msm_vfe32_get_pingpong_status(struct vfe_device *vfe_dev)
+{
+ return msm_camera_io_r(vfe_dev->vfe_base + 0x180);
+}
+
+static int msm_vfe32_get_platform_data(struct vfe_device *vfe_dev)
+{
+ int rc = 0;
+ vfe_dev->vfe_mem = platform_get_resource_byname(vfe_dev->pdev,
+ IORESOURCE_MEM, "vfe");
+ if (!vfe_dev->vfe_mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+
+ vfe_dev->vfe_irq = platform_get_resource_byname(vfe_dev->pdev,
+ IORESOURCE_IRQ, "vfe");
+ if (!vfe_dev->vfe_irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+
+ vfe_dev->fs_vfe = regulator_get(&vfe_dev->pdev->dev, "vdd");
+ if (IS_ERR(vfe_dev->fs_vfe)) {
+ pr_err("%s: Regulator get failed %ld\n", __func__,
+ PTR_ERR(vfe_dev->fs_vfe));
+ vfe_dev->fs_vfe = NULL;
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+
+ vfe_dev->iommu_ctx[0] = msm_iommu_get_ctx("vfe_imgwr");
+ if (!vfe_dev->iommu_ctx[0]) {
+ pr_err("%s: no iommux ctx resource?\n", __func__);
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+
+ vfe_dev->iommu_ctx[1] = msm_iommu_get_ctx("vfe_misc");
+ if (!vfe_dev->iommu_ctx[1]) {
+ pr_err("%s: no iommux ctx resource?\n", __func__);
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+ vfe_dev->num_ctx = 2;
+
+vfe_no_resource:
+ return rc;
+}
+
+struct msm_vfe_axi_hardware_info msm_vfe32_axi_hw_info = {
+ .num_wm = 7,
+ .num_comp_mask = 3,
+ .num_rdi = 3,
+ .num_rdi_master = 3,
+};
+
+static struct v4l2_subdev_core_ops msm_vfe32_subdev_core_ops = {
+ .ioctl = msm_isp_ioctl,
+ .subscribe_event = msm_isp_subscribe_event,
+ .unsubscribe_event = msm_isp_unsubscribe_event,
+};
+
+static struct v4l2_subdev_ops msm_vfe32_subdev_ops = {
+ .core = &msm_vfe32_subdev_core_ops,
+};
+
+static struct v4l2_subdev_internal_ops msm_vfe32_internal_ops = {
+ .open = msm_isp_open_node,
+ .close = msm_isp_close_node,
+};
+
+struct msm_vfe_hardware_info vfe32_hw_info = {
+ .vfe_ops = {
+ .irq_ops = {
+ .read_irq_status = msm_vfe32_read_irq_status,
+ .process_camif_irq = msm_vfe32_process_camif_irq,
+ .process_reset_irq = msm_vfe32_process_reset_irq,
+ .process_halt_irq = msm_vfe32_process_halt_irq,
+ .process_reset_irq = msm_vfe32_process_reset_irq,
+ .process_error_irq = msm_vfe32_process_error_irq,
+ .process_reg_update = msm_vfe32_process_reg_update,
+ .process_axi_irq = msm_isp_process_axi_irq,
+ },
+ .axi_ops = {
+ .reload_wm = msm_vfe32_axi_reload_wm,
+ .enable_wm = msm_vfe32_axi_enable_wm,
+ .cfg_comp_mask = msm_vfe32_axi_cfg_comp_mask,
+ .clear_comp_mask = msm_vfe32_axi_clear_comp_mask,
+ .cfg_wm_irq_mask = msm_vfe32_axi_cfg_wm_irq_mask,
+ .clear_wm_irq_mask = msm_vfe32_axi_clear_wm_irq_mask,
+ .cfg_framedrop = msm_vfe32_cfg_framedrop,
+ .clear_framedrop = msm_vfe32_clear_framedrop,
+ .cfg_wm_reg = msm_vfe32_axi_cfg_wm_reg,
+ .clear_wm_reg = msm_vfe32_axi_clear_wm_reg,
+ .cfg_wm_xbar_reg = msm_vfe32_axi_cfg_wm_xbar_reg,
+ .clear_wm_xbar_reg = msm_vfe32_axi_clear_wm_xbar_reg,
+ .cfg_rdi_reg = msm_vfe32_axi_cfg_rdi_reg,
+ .cfg_ub = msm_vfe32_cfg_axi_ub,
+ .update_ping_pong_addr =
+ msm_vfe32_update_ping_pong_addr,
+ .get_comp_mask = msm_vfe32_get_comp_mask,
+ .get_wm_mask = msm_vfe32_get_wm_mask,
+ .get_pingpong_status = msm_vfe32_get_pingpong_status,
+ .halt = msm_vfe32_axi_halt,
+ },
+ .core_ops = {
+ .reg_update = msm_vfe32_reg_update,
+ .cfg_camif = msm_vfe32_cfg_camif,
+ .update_camif_state = msm_vfe32_update_camif_state,
+ .reset_hw = msm_vfe32_reset_hardware,
+ .init_hw = msm_vfe32_init_hardware,
+ .init_hw_reg = msm_vfe32_init_hardware_reg,
+ .release_hw = msm_vfe32_release_hardware,
+ .get_platform_data = msm_vfe32_get_platform_data,
+ },
+ },
+ .axi_hw_info = &msm_vfe32_axi_hw_info,
+ .subdev_ops = &msm_vfe32_subdev_ops,
+ .subdev_internal_ops = &msm_vfe32_internal_ops,
+};
+EXPORT_SYMBOL(vfe32_hw_info);
diff --git a/drivers/media/video/msmb/isp/msm_isp32.h b/drivers/media/video/msmb/isp/msm_isp32.h
new file mode 100644
index 0000000..0535048
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp32.h
@@ -0,0 +1,17 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_ISP32_H__
+#define __MSM_ISP32_H__
+
+extern struct msm_vfe_hardware_info vfe32_hw_info;
+#endif /* __MSM_ISP32_H__ */
diff --git a/drivers/media/video/msmb/isp/msm_isp40.c b/drivers/media/video/msmb/isp/msm_isp40.c
new file mode 100644
index 0000000..47abcef
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp40.c
@@ -0,0 +1,1127 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <mach/iommu.h>
+
+#include "msm_isp40.h"
+#include "msm_isp_util.h"
+#include "msm_isp_axi_util.h"
+#include "msm_isp_stats_util.h"
+#include "msm_isp.h"
+#include "msm.h"
+#include "msm_camera_io_util.h"
+
+/*#define CONFIG_MSM_ISP_DBG*/
+#undef CDBG
+#ifdef CONFIG_MSM_ISP_DBG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+#define VFE40_BURST_LEN 4
+#define VFE40_EQUAL_SLICE_UB 117
+#define VFE40_WM_BASE(idx) (0x6C + 0x24 * idx)
+#define VFE40_RDI_BASE(idx) (0x2E8 + 0x4 * idx)
+#define VFE40_RDI_MN_BASE(m) (0x2E8 + 0x4 * m/3)
+#define VFE40_RDI_MN_SEL_SHIFT(m) (4*(m%3) + 4)
+#define VFE40_RDI_MN_FB_SHIFT(m) ((m%3) + 16)
+#define VFE40_XBAR_BASE(idx) (0x58 + 0x4 * (idx / 2))
+#define VFE40_XBAR_SHIFT(idx) ((idx%2) ? 16 : 0)
+#define VFE40_PING_PONG_BASE(wm, ping_pong) \
+ (VFE40_WM_BASE(wm) + 0x4 * (1 + (~(ping_pong >> wm) & 0x1)))
+
+#define VFE40_VBIF_CLKON 0x4
+#define VFE40_VBIF_IN_RD_LIM_CONF0 0xB0
+#define VFE40_VBIF_IN_RD_LIM_CONF1 0xB4
+#define VFE40_VBIF_IN_RD_LIM_CONF2 0xB8
+#define VFE40_VBIF_IN_WR_LIM_CONF0 0xC0
+#define VFE40_VBIF_IN_WR_LIM_CONF1 0xC4
+#define VFE40_VBIF_IN_WR_LIM_CONF2 0xC8
+#define VFE40_VBIF_OUT_RD_LIM_CONF0 0xD0
+#define VFE40_VBIF_OUT_WR_LIM_CONF0 0xD4
+#define VFE40_VBIF_DDR_OUT_MAX_BURST 0xD8
+#define VFE40_VBIF_ARB_CTL 0xF0
+#define VFE40_VBIF_DDR_ARB_CONF0 0xF4
+#define VFE40_VBIF_DDR_ARB_CONF1 0xF8
+#define VFE40_VBIF_ROUND_ROBIN_QOS_ARB 0x124
+#define VFE40_VBIF_OUT_AXI_AOOO_EN 0x178
+#define VFE40_VBIF_OUT_AXI_AOOO 0x17C
+
+/*Temporary use fixed bus vectors in VFE */
+static struct msm_bus_vectors msm_vfe40_init_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VFE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = 0,
+ },
+};
+
+static struct msm_bus_vectors msm_vfe40_preview_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VFE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 1027648000,
+ .ib = 1105920000,
+ },
+};
+
+static struct msm_bus_paths msm_vfe40_bus_client_config[] = {
+ {
+ ARRAY_SIZE(msm_vfe40_init_vectors),
+ msm_vfe40_init_vectors,
+ },
+ {
+ ARRAY_SIZE(msm_vfe40_preview_vectors),
+ msm_vfe40_preview_vectors,
+ },
+};
+
+static struct msm_bus_scale_pdata msm_vfe40_bus_client_pdata = {
+ msm_vfe40_bus_client_config,
+ ARRAY_SIZE(msm_vfe40_bus_client_config),
+ .name = "msm_camera_vfe",
+};
+
+static struct msm_cam_clk_info msm_vfe40_clk_info[] = {
+ {"camss_top_ahb_clk", -1},
+ {"vfe_clk_src", 266670000},
+ {"camss_vfe_vfe_clk", -1},
+ {"camss_csi_vfe_clk", -1},
+ {"iface_clk", -1},
+ {"bus_clk", -1},
+ {"alt_bus_clk", -1},
+};
+
+static void msm_vfe40_init_vbif_parms(
+ void __iomem *vfe_vbif_base)
+{
+ msm_camera_io_w_mb(0x1,
+ vfe_vbif_base + VFE40_VBIF_CLKON);
+ msm_camera_io_w_mb(0x1,
+ vfe_vbif_base + VFE40_VBIF_ROUND_ROBIN_QOS_ARB);
+ msm_camera_io_w_mb(0xFFFF,
+ vfe_vbif_base + VFE40_VBIF_OUT_AXI_AOOO_EN);
+ msm_camera_io_w_mb(0xFFFFFFFF,
+ vfe_vbif_base + VFE40_VBIF_OUT_AXI_AOOO);
+
+ msm_camera_io_w_mb(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF0);
+ msm_camera_io_w_mb(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF1);
+ msm_camera_io_w_mb(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF2);
+ msm_camera_io_w_mb(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF0);
+ msm_camera_io_w_mb(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF1);
+ msm_camera_io_w_mb(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF2);
+ msm_camera_io_w_mb(0x00001010,
+ vfe_vbif_base + VFE40_VBIF_OUT_RD_LIM_CONF0);
+ msm_camera_io_w_mb(0x00001010,
+ vfe_vbif_base + VFE40_VBIF_OUT_WR_LIM_CONF0);
+ msm_camera_io_w_mb(0x00000707,
+ vfe_vbif_base + VFE40_VBIF_DDR_OUT_MAX_BURST);
+ msm_camera_io_w_mb(0x00000030,
+ vfe_vbif_base + VFE40_VBIF_ARB_CTL);
+ msm_camera_io_w_mb(0x04210842,
+ vfe_vbif_base + VFE40_VBIF_DDR_ARB_CONF0);
+ msm_camera_io_w_mb(0x04210842,
+ vfe_vbif_base + VFE40_VBIF_DDR_ARB_CONF1);
+}
+
+static int msm_vfe40_init_hardware(struct vfe_device *vfe_dev)
+{
+ int rc = -1;
+
+ vfe_dev->bus_perf_client =
+ msm_bus_scale_register_client(&msm_vfe40_bus_client_pdata);
+ if (!vfe_dev->bus_perf_client) {
+ pr_err("%s: Registration Failed!\n", __func__);
+ vfe_dev->bus_perf_client = 0;
+ goto bus_scale_register_failed;
+ }
+ msm_bus_scale_client_update_request(
+ vfe_dev->bus_perf_client, 1);
+
+ if (vfe_dev->fs_vfe) {
+ rc = regulator_enable(vfe_dev->fs_vfe);
+ if (rc) {
+ pr_err("%s: Regulator enable failed\n", __func__);
+ goto fs_failed;
+ }
+ }
+
+ rc = msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe40_clk_info,
+ vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe40_clk_info), 1);
+ if (rc < 0)
+ goto clk_enable_failed;
+
+ vfe_dev->vfe_base = ioremap(vfe_dev->vfe_mem->start,
+ resource_size(vfe_dev->vfe_mem));
+ if (!vfe_dev->vfe_base) {
+ rc = -ENOMEM;
+ pr_err("%s: vfe ioremap failed\n", __func__);
+ goto vfe_remap_failed;
+ }
+
+ vfe_dev->vfe_vbif_base = ioremap(vfe_dev->vfe_vbif_mem->start,
+ resource_size(vfe_dev->vfe_vbif_mem));
+ if (!vfe_dev->vfe_vbif_base) {
+ rc = -ENOMEM;
+ pr_err("%s: vfe ioremap failed\n", __func__);
+ goto vbif_remap_failed;
+ }
+
+ rc = request_irq(vfe_dev->vfe_irq->start, msm_isp_process_irq,
+ IRQF_TRIGGER_RISING, "vfe", vfe_dev);
+ if (rc < 0) {
+ pr_err("%s: irq request failed\n", __func__);
+ goto irq_req_failed;
+ }
+
+ msm_vfe40_init_vbif_parms(vfe_dev->vfe_vbif_base);
+ return rc;
+irq_req_failed:
+ iounmap(vfe_dev->vfe_vbif_base);
+vbif_remap_failed:
+ iounmap(vfe_dev->vfe_base);
+vfe_remap_failed:
+ msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe40_clk_info,
+ vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe40_clk_info), 0);
+clk_enable_failed:
+ regulator_disable(vfe_dev->fs_vfe);
+fs_failed:
+ msm_bus_scale_client_update_request(vfe_dev->bus_perf_client, 0);
+ msm_bus_scale_unregister_client(vfe_dev->bus_perf_client);
+bus_scale_register_failed:
+ return rc;
+}
+
+static void msm_vfe40_release_hardware(struct vfe_device *vfe_dev)
+{
+ free_irq(vfe_dev->vfe_irq->start, vfe_dev);
+ tasklet_kill(&vfe_dev->vfe_tasklet);
+ iounmap(vfe_dev->vfe_vbif_base);
+ iounmap(vfe_dev->vfe_base);
+ msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe40_clk_info,
+ vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe40_clk_info), 0);
+ regulator_disable(vfe_dev->fs_vfe);
+ msm_bus_scale_client_update_request(vfe_dev->bus_perf_client, 0);
+ msm_bus_scale_unregister_client(vfe_dev->bus_perf_client);
+}
+
+static void msm_vfe40_init_hardware_reg(struct vfe_device *vfe_dev)
+{
+ /* CGC_OVERRIDE */
+ msm_camera_io_w(0x3FFFFFFF, vfe_dev->vfe_base + 0x14);
+ /* BUS_CFG */
+ msm_camera_io_w(0x00000001, vfe_dev->vfe_base + 0x50);
+ msm_camera_io_w(0x800000F3, vfe_dev->vfe_base + 0x28);
+ msm_camera_io_w_mb(0xFEFFFFFF, vfe_dev->vfe_base + 0x2C);
+ msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w_mb(0xFEFFFFFF, vfe_dev->vfe_base + 0x34);
+}
+
+static void msm_vfe40_process_reset_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ if (irq_status0 & (1 << 31))
+ complete(&vfe_dev->reset_complete);
+}
+
+static void msm_vfe40_process_halt_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ if (irq_status1 & (1 << 8))
+ complete(&vfe_dev->halt_complete);
+}
+
+static void msm_vfe40_process_camif_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ if (!(irq_status0 & 0xF))
+ return;
+
+ if (vfe_dev->hw_info->vfe_ops.core_ops.epoch_irq) {
+ if (irq_status0 & (1 << 2)) {
+ ISP_DBG("%s: EPOCH0 IRQ, PIX0_frameid = 0x%lu\n",
+ __func__,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
+ msm_isp_new_frame_notify(vfe_dev, VFE_PIX_0);
+ }
+ } else {
+ if (irq_status0 & (1 << 0)) {
+ ISP_DBG("%s: SOF: PIX0 frame id: %lu\n", __func__,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
+ msm_isp_new_frame_notify(vfe_dev, VFE_PIX_0);
+ }
+ }
+}
+
+static void msm_vfe40_process_violation_irq(
+ struct vfe_device *vfe_dev)
+{
+ uint32_t violation_status;
+ violation_status = msm_camera_io_r(vfe_dev->vfe_base + 0x48);
+ if (!violation_status)
+ return;
+
+ if (violation_status & (1 << 0))
+ pr_err("%s: camif violation\n", __func__);
+ if (violation_status & (1 << 1))
+ pr_err("%s: black violation\n", __func__);
+ if (violation_status & (1 << 2))
+ pr_err("%s: rolloff violation\n", __func__);
+ if (violation_status & (1 << 3))
+ pr_err("%s: demux violation\n", __func__);
+ if (violation_status & (1 << 4))
+ pr_err("%s: demosaic violation\n", __func__);
+ if (violation_status & (1 << 5))
+ pr_err("%s: wb violation\n", __func__);
+ if (violation_status & (1 << 6))
+ pr_err("%s: clf violation\n", __func__);
+ if (violation_status & (1 << 7))
+ pr_err("%s: color correct violation\n", __func__);
+ if (violation_status & (1 << 8))
+ pr_err("%s: rgb lut violation\n", __func__);
+ if (violation_status & (1 << 9))
+ pr_err("%s: la violation\n", __func__);
+ if (violation_status & (1 << 10))
+ pr_err("%s: chroma enhance violation\n", __func__);
+ if (violation_status & (1 << 11))
+ pr_err("%s: chroma supress mce violation\n", __func__);
+ if (violation_status & (1 << 12))
+ pr_err("%s: skin enhance violation\n", __func__);
+ if (violation_status & (1 << 13))
+ pr_err("%s: color tranform enc violation\n", __func__);
+ if (violation_status & (1 << 14))
+ pr_err("%s: color tranform view violation\n", __func__);
+ if (violation_status & (1 << 15))
+ pr_err("%s: scale enc y violation\n", __func__);
+ if (violation_status & (1 << 16))
+ pr_err("%s: scale enc cbcr violation\n", __func__);
+ if (violation_status & (1 << 17))
+ pr_err("%s: scale view y violation\n", __func__);
+ if (violation_status & (1 << 18))
+ pr_err("%s: scale view cbcr violation\n", __func__);
+ if (violation_status & (1 << 19))
+ pr_err("%s: asf enc violation\n", __func__);
+ if (violation_status & (1 << 20))
+ pr_err("%s: asf view violation\n", __func__);
+ if (violation_status & (1 << 21))
+ pr_err("%s: crop enc y violation\n", __func__);
+ if (violation_status & (1 << 22))
+ pr_err("%s: crop enc cbcr violation\n", __func__);
+ if (violation_status & (1 << 23))
+ pr_err("%s: crop view y violation\n", __func__);
+ if (violation_status & (1 << 24))
+ pr_err("%s: crop view cbcr violation\n", __func__);
+ if (violation_status & (1 << 25))
+ pr_err("%s: realign buf y violation\n", __func__);
+ if (violation_status & (1 << 26))
+ pr_err("%s: realign buf cb violation\n", __func__);
+ if (violation_status & (1 << 27))
+ pr_err("%s: realign buf cr violation\n", __func__);
+}
+
+static void msm_vfe40_process_error_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ uint32_t camif_status;
+ if (!(irq_status1 & 0x00FFFEFF))
+ return;
+
+ if (irq_status1 & (1 << 0)) {
+ camif_status = msm_camera_io_r(vfe_dev->vfe_base + 0x31C);
+ pr_err("%s: camif error status: 0x%x\n",
+ __func__, camif_status);
+ }
+ if (irq_status1 & (1 << 1))
+ pr_err("%s: stats bhist overwrite\n", __func__);
+ if (irq_status1 & (1 << 2))
+ pr_err("%s: stats cs overwrite\n", __func__);
+ if (irq_status1 & (1 << 3))
+ pr_err("%s: stats ihist overwrite\n", __func__);
+ if (irq_status1 & (1 << 4))
+ pr_err("%s: realign buf y overflow\n", __func__);
+ if (irq_status1 & (1 << 5))
+ pr_err("%s: realign buf cb overflow\n", __func__);
+ if (irq_status1 & (1 << 6))
+ pr_err("%s: realign buf cr overflow\n", __func__);
+ if (irq_status1 & (1 << 7)) {
+ pr_err("%s: violation\n", __func__);
+ msm_vfe40_process_violation_irq(vfe_dev);
+ }
+ if (irq_status1 & (1 << 9))
+ pr_err("%s: image master 0 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 10))
+ pr_err("%s: image master 1 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 11))
+ pr_err("%s: image master 2 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 12))
+ pr_err("%s: image master 3 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 13))
+ pr_err("%s: image master 4 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 14))
+ pr_err("%s: image master 5 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 15))
+ pr_err("%s: image master 6 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 16))
+ pr_err("%s: status be bus overflow\n", __func__);
+ if (irq_status1 & (1 << 17))
+ pr_err("%s: status bg bus overflow\n", __func__);
+ if (irq_status1 & (1 << 18))
+ pr_err("%s: status bf bus overflow\n", __func__);
+ if (irq_status1 & (1 << 19))
+ pr_err("%s: status awb bus overflow\n", __func__);
+ if (irq_status1 & (1 << 20))
+ pr_err("%s: status rs bus overflow\n", __func__);
+ if (irq_status1 & (1 << 21))
+ pr_err("%s: status cs bus overflow\n", __func__);
+ if (irq_status1 & (1 << 22))
+ pr_err("%s: status ihist bus overflow\n", __func__);
+ if (irq_status1 & (1 << 23))
+ pr_err("%s: status skin bhist bus overflow\n", __func__);
+}
+
+static void msm_vfe40_read_irq_status(struct vfe_device *vfe_dev,
+ uint32_t *irq_status0, uint32_t *irq_status1)
+{
+ *irq_status0 = msm_camera_io_r(vfe_dev->vfe_base + 0x38);
+ *irq_status1 = msm_camera_io_r(vfe_dev->vfe_base + 0x3C);
+ msm_camera_io_w(*irq_status0, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w(*irq_status1, vfe_dev->vfe_base + 0x34);
+ msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x24);
+}
+
+static void msm_vfe40_process_reg_update(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ uint32_t update_mask = 0xF;
+
+ if (!(irq_status0 & 0xF0))
+ return;
+
+ if (vfe_dev->axi_data.stream_update)
+ msm_isp_axi_stream_update(vfe_dev);
+ msm_isp_update_framedrop_reg(vfe_dev);
+
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, update_mask);
+ return;
+}
+
+static void msm_vfe40_epoch_irq_enb(struct vfe_device *vfe_dev,
+ uint32_t epoch_line0, uint32_t epoch_line1)
+{
+ uint32_t irq_mask = 0;
+ uint32_t epoch_val = 0;
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28);
+ if (epoch_line0 > 0) {
+ irq_mask |= 0x4;
+ epoch_val |= (epoch_line0 - 1) << 16;
+ } else {
+ irq_mask &= ~0x4;
+ epoch_val &= 0xFFFF;
+ }
+ if (epoch_line1 > 0) {
+ irq_mask |= 0x8;
+ epoch_val |= epoch_line1 - 1;
+ } else {
+ irq_mask &= ~0x8;
+ epoch_val &= 0xFFFF0000;
+ }
+ msm_camera_io_w_mb(epoch_val, vfe_dev->vfe_base + 0x318);
+ msm_camera_io_w_mb(irq_mask, vfe_dev->vfe_base + 0x28);
+}
+
+static void msm_vfe40_reg_update(
+ struct vfe_device *vfe_dev, uint32_t update_mask)
+{
+ msm_camera_io_w_mb(update_mask, vfe_dev->vfe_base + 0x378);
+}
+
+static long msm_vfe40_reset_hardware(struct vfe_device *vfe_dev)
+{
+ init_completion(&vfe_dev->reset_complete);
+ msm_camera_io_w_mb(0x1FF, vfe_dev->vfe_base + 0xC);
+ return wait_for_completion_interruptible_timeout(
+ &vfe_dev->reset_complete, msecs_to_jiffies(50));
+}
+
+static void msm_vfe40_axi_reload_wm(
+ struct vfe_device *vfe_dev, uint32_t reload_mask)
+{
+ msm_camera_io_w_mb(reload_mask, vfe_dev->vfe_base + 0x4C);
+}
+
+static void msm_vfe40_axi_enable_wm(struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint8_t enable)
+{
+ if (enable)
+ msm_camera_io_w_mb(0x1,
+ vfe_dev->vfe_base + VFE40_WM_BASE(wm_idx));
+ else
+ msm_camera_io_w_mb(0x0,
+ vfe_dev->vfe_base + VFE40_WM_BASE(wm_idx));
+}
+
+static void msm_vfe40_axi_cfg_comp_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ uint32_t comp_mask, comp_mask_index =
+ stream_info->comp_mask_index;
+ uint32_t irq_mask;
+
+ comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x40);
+ comp_mask &= ~(0x7F << (comp_mask_index * 8));
+ comp_mask |= (axi_data->composite_info[comp_mask_index].
+ stream_composite_mask << (comp_mask_index * 8));
+ msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x40);
+
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28);
+ irq_mask |= 1 << (comp_mask_index + 25);
+ msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x28);
+}
+
+static void msm_vfe40_axi_clear_comp_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t comp_mask, comp_mask_index = stream_info->comp_mask_index;
+ uint32_t irq_mask;
+
+ comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x40);
+ comp_mask &= ~(0x7F << (comp_mask_index * 8));
+ msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x40);
+
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28);
+ irq_mask &= ~(1 << (comp_mask_index + 25));
+ msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x28);
+}
+
+static void msm_vfe40_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t irq_mask;
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28);
+ irq_mask |= 1 << (stream_info->wm[0] + 8);
+ msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x28);
+}
+
+static void msm_vfe40_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t irq_mask;
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28);
+ irq_mask &= ~(1 << (stream_info->wm[0] + 8));
+ msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x28);
+}
+
+static void msm_vfe40_cfg_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t i;
+ uint32_t framedrop_pattern = 0;
+
+ if (stream_info->init_frame_drop == 0)
+ framedrop_pattern = stream_info->framedrop_pattern;
+
+ if (stream_info->stream_type == BURST_STREAM &&
+ stream_info->burst_frame_count == 0)
+ framedrop_pattern = 0;
+
+ for (i = 0; i < stream_info->num_planes; i++)
+ msm_camera_io_w(framedrop_pattern, vfe_dev->vfe_base +
+ VFE40_WM_BASE(stream_info->wm[i]) + 0x1C);
+
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x378);
+}
+
+static void msm_vfe40_clear_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t i;
+ for (i = 0; i < stream_info->num_planes; i++)
+ msm_camera_io_w(0, vfe_dev->vfe_base +
+ VFE40_WM_BASE(stream_info->wm[i]) + 0x1C);
+}
+
+static void msm_vfe40_cfg_camif(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg)
+{
+ uint16_t first_pixel, last_pixel, first_line, last_line;
+ struct msm_vfe_camif_cfg *camif_cfg = &pix_cfg->camif_cfg;
+ uint32_t val;
+
+ first_pixel = camif_cfg->first_pixel;
+ last_pixel = camif_cfg->last_pixel;
+ first_line = camif_cfg->first_line;
+ last_line = camif_cfg->last_line;
+
+ msm_camera_io_w(pix_cfg->input_mux << 16 | pix_cfg->pixel_pattern,
+ vfe_dev->vfe_base + 0x1C);
+
+ msm_camera_io_w(camif_cfg->lines_per_frame << 16 |
+ camif_cfg->pixels_per_line, vfe_dev->vfe_base + 0x300);
+
+ msm_camera_io_w(first_pixel << 16 | last_pixel,
+ vfe_dev->vfe_base + 0x304);
+
+ msm_camera_io_w(first_line << 16 | last_line,
+ vfe_dev->vfe_base + 0x308);
+
+ msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x314);
+
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x2E8);
+ val |= camif_cfg->camif_input;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x2E8);
+
+ vfe_dev->hw_info->vfe_ops.core_ops.epoch_irq(vfe_dev,
+ camif_cfg->epoch_line0, camif_cfg->epoch_line1);
+
+ switch (pix_cfg->input_mux) {
+ case CAMIF:
+ val = 0x01;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x2F4);
+ break;
+ case TESTGEN:
+ val = 0x01;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x93C);
+ break;
+ case EXTERNAL_READ:
+ default:
+ pr_err("%s: not supported input_mux %d\n",
+ __func__, pix_cfg->input_mux);
+ break;
+ }
+}
+
+static void msm_vfe40_update_camif_state(struct vfe_device *vfe_dev,
+ enum msm_isp_camif_update_state update_state)
+{
+ uint32_t val;
+ bool bus_en, vfe_en;
+ if (update_state == NO_UPDATE)
+ return;
+
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x2F8);
+ if (update_state == ENABLE_CAMIF) {
+ bus_en =
+ ((vfe_dev->axi_data.
+ src_info[VFE_PIX_0].raw_stream_count > 0) ? 1 : 0);
+ vfe_en =
+ ((vfe_dev->axi_data.
+ src_info[VFE_PIX_0].pix_stream_count > 0) ? 1 : 0);
+ val &= 0xFFFFFF3F;
+ val = val | bus_en << 7 | vfe_en << 6;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x2F8);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x2F4);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].active = 1;
+ } else if (update_state == DISABLE_CAMIF) {
+ msm_camera_io_w_mb(0x0, vfe_dev->vfe_base + 0x2F4);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0;
+ } else if (update_state == DISABLE_CAMIF_IMMEDIATELY) {
+ msm_camera_io_w_mb(0x2, vfe_dev->vfe_base + 0x2F4);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0;
+ }
+}
+
+static void msm_vfe40_axi_cfg_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ uint8_t plane_idx)
+{
+ uint32_t val;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->axi_stream_handle & 0xFF)];
+ uint32_t wm_base = VFE40_WM_BASE(stream_info->wm[plane_idx]);
+
+ /*WR_ADDR_CFG*/
+ msm_camera_io_w(0x7C, vfe_dev->vfe_base + wm_base + 0xC);
+
+ /*WR_IMAGE_SIZE*/
+ val =
+ ((msm_isp_cal_word_per_line(stream_cfg_cmd->output_format,
+ stream_cfg_cmd->plane_cfg[plane_idx].
+ output_width)+1)/2 - 1) << 16 |
+ (stream_cfg_cmd->plane_cfg[plane_idx].
+ output_height - 1);
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
+
+ /*WR_BUFFER_CFG*/
+ val =
+ msm_isp_cal_word_per_line(stream_cfg_cmd->output_format,
+ stream_cfg_cmd->plane_cfg[
+ plane_idx].output_width) << 16 |
+ (stream_cfg_cmd->plane_cfg[
+ plane_idx].output_scan_lines - 1) << 4 |
+ VFE40_BURST_LEN >> 2;
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x18);
+
+ /*WR_IRQ_SUBSAMPLE_PATTERN*/
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe_dev->vfe_base + wm_base + 0x20);
+ /* TD: Add IRQ subsample pattern */
+ return;
+}
+
+static void msm_vfe40_axi_clear_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
+{
+ uint32_t val = 0;
+ uint32_t wm_base = VFE40_WM_BASE(stream_info->wm[plane_idx]);
+ /*WR_ADDR_CFG*/
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0xC);
+ /*WR_IMAGE_SIZE*/
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
+ /*WR_BUFFER_CFG*/
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x18);
+ /*WR_IRQ_SUBSAMPLE_PATTERN*/
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x20);
+ return;
+}
+
+static void msm_vfe40_axi_cfg_rdi_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ uint8_t plane_idx)
+{
+ struct msm_vfe_axi_shared_data *axi_data =
+ &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->axi_stream_handle & 0xFF)];
+ struct msm_vfe_axi_plane_cfg *plane_cfg =
+ &stream_cfg_cmd->plane_cfg[plane_idx];
+ uint8_t rdi = stream_info->rdi[plane_idx];
+ uint8_t rdi_master = stream_info->rdi_master[plane_idx];
+ uint32_t rdi_reg_cfg;
+
+ rdi_reg_cfg = msm_camera_io_r(
+ vfe_dev->vfe_base + VFE40_RDI_BASE(rdi));
+ rdi_reg_cfg = (rdi_reg_cfg & 0xFFFFFFF) | rdi_master << 28;
+ msm_camera_io_w(
+ rdi_reg_cfg, vfe_dev->vfe_base + VFE40_RDI_BASE(rdi));
+
+ rdi_reg_cfg = msm_camera_io_r(
+ vfe_dev->vfe_base + VFE40_RDI_MN_BASE(rdi_master));
+ rdi_reg_cfg &= ~((0xF << VFE40_RDI_MN_SEL_SHIFT(rdi_master)) |
+ (0x1 << VFE40_RDI_MN_FB_SHIFT(rdi_master)));
+ rdi_reg_cfg |=
+ (plane_cfg->rdi_cid << VFE40_RDI_MN_SEL_SHIFT(rdi_master) |
+ (stream_cfg_cmd->frame_base <<
+ VFE40_RDI_MN_FB_SHIFT(rdi_master)));
+ msm_camera_io_w(rdi_reg_cfg,
+ vfe_dev->vfe_base + VFE40_RDI_MN_BASE(rdi_master));
+}
+
+static void msm_vfe40_axi_cfg_wm_xbar_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ uint8_t plane_idx)
+{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->axi_stream_handle & 0xFF)];
+ struct msm_vfe_axi_plane_cfg *plane_cfg =
+ &stream_cfg_cmd->plane_cfg[plane_idx];
+ uint8_t wm = stream_info->wm[plane_idx];
+ uint32_t xbar_cfg = 0;
+ uint32_t xbar_reg_cfg = 0;
+
+ switch (stream_cfg_cmd->stream_src) {
+ case PIX_ENCODER:
+ case PIX_VIEWFINDER: {
+ if (plane_cfg->output_plane_format
+ != CRCB_PLANE) {
+ /*SINGLE_STREAM_SEL*/
+ xbar_cfg |= plane_cfg->output_plane_format << 8;
+ } else {
+ switch (stream_cfg_cmd->output_format) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV16:
+ xbar_cfg |= 0x3 << 4; /*PAIR_STREAM_SWAP_CTRL*/
+ break;
+ }
+ xbar_cfg |= 0x1 << 1; /*PAIR_STREAM_EN*/
+ }
+ if (stream_cfg_cmd->stream_src == PIX_VIEWFINDER)
+ xbar_cfg |= 0x1; /*VIEW_STREAM_EN*/
+ break;
+ }
+ case CAMIF_RAW:
+ xbar_cfg = 0x300;
+ break;
+ case IDEAL_RAW:
+ xbar_cfg = 0x400;
+ break;
+ case RDI:
+ if (stream_info->rdi[plane_idx] == 0)
+ xbar_cfg = 0x500;
+ else if (stream_info->rdi[plane_idx] == 1)
+ xbar_cfg = 0x600;
+ else if (stream_info->rdi[plane_idx] == 2)
+ xbar_cfg = 0x700;
+ break;
+ default:
+ pr_err("%s: Invalid stream src\n", __func__);
+ break;
+ }
+ xbar_reg_cfg =
+ msm_camera_io_r(vfe_dev->vfe_base + VFE40_XBAR_BASE(wm));
+ xbar_reg_cfg &= ~(0xFFFF << VFE40_XBAR_SHIFT(wm));
+ xbar_reg_cfg |= (xbar_cfg << VFE40_XBAR_SHIFT(wm));
+ msm_camera_io_w(xbar_reg_cfg,
+ vfe_dev->vfe_base + VFE40_XBAR_BASE(wm));
+ return;
+}
+
+static void msm_vfe40_axi_clear_wm_xbar_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
+{
+ uint8_t wm = stream_info->wm[plane_idx];
+ uint32_t xbar_reg_cfg = 0;
+
+ xbar_reg_cfg =
+ msm_camera_io_r(vfe_dev->vfe_base + VFE40_XBAR_BASE(wm));
+ xbar_reg_cfg &= ~(0xFFFF << VFE40_XBAR_SHIFT(wm));
+ msm_camera_io_w(xbar_reg_cfg,
+ vfe_dev->vfe_base + VFE40_XBAR_BASE(wm));
+}
+
+#define MSM_ISP40_TOTAL_WM_UB 819
+
+static void msm_vfe40_cfg_axi_ub_equal_default(
+ struct vfe_device *vfe_dev)
+{
+ int i;
+ uint32_t ub_offset = 0;
+ struct msm_vfe_axi_shared_data *axi_data =
+ &vfe_dev->axi_data;
+ uint32_t total_image_size = 0;
+ uint8_t num_used_wms = 0;
+ uint32_t prop_size = 0;
+ uint32_t wm_ub_size;
+ uint32_t delta;
+
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ if (axi_data->free_wm[i] > 0) {
+ num_used_wms++;
+ total_image_size += axi_data->wm_image_size[i];
+ }
+ }
+ prop_size = MSM_ISP40_TOTAL_WM_UB -
+ axi_data->hw_info->min_wm_ub * num_used_wms;
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ if (axi_data->free_wm[i]) {
+ delta =
+ (axi_data->wm_image_size[i] *
+ prop_size)/total_image_size;
+ wm_ub_size = axi_data->hw_info->min_wm_ub + delta;
+ msm_camera_io_w(ub_offset << 16 | (wm_ub_size - 1),
+ vfe_dev->vfe_base + VFE40_WM_BASE(i) + 0x10);
+ ub_offset += wm_ub_size;
+ } else
+ msm_camera_io_w(0,
+ vfe_dev->vfe_base + VFE40_WM_BASE(i) + 0x10);
+ }
+}
+
+static void msm_vfe40_cfg_axi_ub_equal_slicing(
+ struct vfe_device *vfe_dev)
+{
+ int i;
+ uint32_t ub_offset = 0;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ msm_camera_io_w(ub_offset << 16 | (VFE40_EQUAL_SLICE_UB - 1),
+ vfe_dev->vfe_base + VFE40_WM_BASE(i) + 0x10);
+ ub_offset += VFE40_EQUAL_SLICE_UB;
+ }
+}
+
+static void msm_vfe40_cfg_axi_ub(struct vfe_device *vfe_dev)
+{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+
+ if (axi_data->wm_ub_cfg_policy == MSM_WM_UB_EQUAL_SLICING)
+ msm_vfe40_cfg_axi_ub_equal_slicing(vfe_dev);
+ else
+ msm_vfe40_cfg_axi_ub_equal_default(vfe_dev);
+}
+
+static void msm_vfe40_update_ping_pong_addr(
+ struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint32_t pingpong_status, unsigned long paddr)
+{
+ msm_camera_io_w(paddr, vfe_dev->vfe_base +
+ VFE40_PING_PONG_BASE(wm_idx, pingpong_status));
+}
+
+static long msm_vfe40_axi_halt(struct vfe_device *vfe_dev)
+{
+ uint32_t halt_mask;
+ halt_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x2C);
+ halt_mask |= (1 << 8);
+ msm_camera_io_w_mb(halt_mask, vfe_dev->vfe_base + 0x2C);
+ init_completion(&vfe_dev->halt_complete);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x2C0);
+ return wait_for_completion_interruptible_timeout(
+ &vfe_dev->halt_complete, msecs_to_jiffies(500));
+}
+
+static uint32_t msm_vfe40_get_wm_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 8) & 0x7F;
+}
+
+static uint32_t msm_vfe40_get_comp_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 25) & 0xF;
+}
+
+static uint32_t msm_vfe40_get_pingpong_status(
+ struct vfe_device *vfe_dev)
+{
+ return msm_camera_io_r(vfe_dev->vfe_base + 0x268);
+}
+
+static void msm_vfe40_stats_cfg_comp_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+
+}
+
+static void msm_vfe40_stats_clear_comp_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+
+}
+
+static void msm_vfe40_stats_cfg_wm_irq_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+
+}
+
+static void msm_vfe40_stats_clear_wm_irq_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+
+}
+
+static void msm_vfe40_stats_cfg_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream_request_cmd *stream_cfg_cmd)
+{
+
+}
+
+static void msm_vfe40_stats_clear_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+
+}
+
+static void msm_vfe40_stats_cfg_ub(struct vfe_device *vfe_dev)
+{
+}
+
+static void msm_vfe40_stats_enable(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable)
+{
+
+}
+
+static void msm_vfe40_stats_update_ping_pong_addr(
+ struct vfe_device *vfe_dev,
+ enum msm_isp_stats_type stats_type,
+ uint32_t pingpong_status,
+ unsigned long paddr)
+{
+}
+
+static uint32_t msm_vfe40_stats_get_wm_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 16) & 0xFF;
+}
+
+static uint32_t msm_vfe40_stats_get_comp_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 29) & 0x2;
+}
+
+static uint32_t msm_vfe40_stats_get_frame_id(
+ struct vfe_device *vfe_dev)
+{
+ return vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+}
+static int msm_vfe40_get_platform_data(struct vfe_device *vfe_dev)
+{
+ int rc = 0;
+ vfe_dev->vfe_mem = platform_get_resource_byname(vfe_dev->pdev,
+ IORESOURCE_MEM, "vfe");
+ if (!vfe_dev->vfe_mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+
+ vfe_dev->vfe_vbif_mem = platform_get_resource_byname(
+ vfe_dev->pdev,
+ IORESOURCE_MEM, "vfe_vbif");
+ if (!vfe_dev->vfe_vbif_mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+
+ vfe_dev->vfe_irq = platform_get_resource_byname(vfe_dev->pdev,
+ IORESOURCE_IRQ, "vfe");
+ if (!vfe_dev->vfe_irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+
+ vfe_dev->fs_vfe = regulator_get(&vfe_dev->pdev->dev, "vdd");
+ if (IS_ERR(vfe_dev->fs_vfe)) {
+ pr_err("%s: Regulator get failed %ld\n", __func__,
+ PTR_ERR(vfe_dev->fs_vfe));
+ vfe_dev->fs_vfe = NULL;
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+
+ if (vfe_dev->pdev->id == 0)
+ vfe_dev->iommu_ctx = msm_iommu_get_ctx("vfe0");
+ else if (vfe_dev->pdev->id == 1)
+ vfe_dev->iommu_ctx = msm_iommu_get_ctx("vfe1");
+ if (!vfe_dev->iommu_ctx) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+
+vfe_no_resource:
+ return rc;
+}
+
+struct msm_vfe_axi_hardware_info msm_vfe40_axi_hw_info = {
+ .num_wm = 7,
+ .num_comp_mask = 4,
+ .num_rdi = 3,
+ .num_rdi_master = 3,
+ .min_wm_ub = 64,
+};
+
+static struct v4l2_subdev_core_ops msm_vfe40_subdev_core_ops = {
+ .ioctl = msm_isp_ioctl,
+ .subscribe_event = msm_isp_subscribe_event,
+ .unsubscribe_event = msm_isp_unsubscribe_event,
+};
+
+static struct v4l2_subdev_ops msm_vfe40_subdev_ops = {
+ .core = &msm_vfe40_subdev_core_ops,
+};
+
+static struct v4l2_subdev_internal_ops msm_vfe40_internal_ops = {
+ .open = msm_isp_open_node,
+ .close = msm_isp_close_node,
+};
+
+struct msm_vfe_hardware_info vfe40_hw_info = {
+ .vfe_ops = {
+ .irq_ops = {
+ .read_irq_status = msm_vfe40_read_irq_status,
+ .process_camif_irq = msm_vfe40_process_camif_irq,
+ .process_reset_irq = msm_vfe40_process_reset_irq,
+ .process_halt_irq = msm_vfe40_process_halt_irq,
+ .process_reset_irq = msm_vfe40_process_reset_irq,
+ .process_error_irq = msm_vfe40_process_error_irq,
+ .process_reg_update = msm_vfe40_process_reg_update,
+ .process_axi_irq = msm_isp_process_axi_irq,
+ .process_stats_irq = msm_isp_process_stats_irq,
+ },
+ .axi_ops = {
+ .reload_wm = msm_vfe40_axi_reload_wm,
+ .enable_wm = msm_vfe40_axi_enable_wm,
+ .cfg_comp_mask = msm_vfe40_axi_cfg_comp_mask,
+ .clear_comp_mask = msm_vfe40_axi_clear_comp_mask,
+ .cfg_wm_irq_mask = msm_vfe40_axi_cfg_wm_irq_mask,
+ .clear_wm_irq_mask = msm_vfe40_axi_clear_wm_irq_mask,
+ .cfg_framedrop = msm_vfe40_cfg_framedrop,
+ .clear_framedrop = msm_vfe40_clear_framedrop,
+ .cfg_wm_reg = msm_vfe40_axi_cfg_wm_reg,
+ .clear_wm_reg = msm_vfe40_axi_clear_wm_reg,
+ .cfg_wm_xbar_reg = msm_vfe40_axi_cfg_wm_xbar_reg,
+ .clear_wm_xbar_reg = msm_vfe40_axi_clear_wm_xbar_reg,
+ .cfg_rdi_reg = msm_vfe40_axi_cfg_rdi_reg,
+ .cfg_ub = msm_vfe40_cfg_axi_ub,
+ .update_ping_pong_addr =
+ msm_vfe40_update_ping_pong_addr,
+ .get_comp_mask = msm_vfe40_get_comp_mask,
+ .get_wm_mask = msm_vfe40_get_wm_mask,
+ .get_pingpong_status = msm_vfe40_get_pingpong_status,
+ .halt = msm_vfe40_axi_halt,
+ },
+ .core_ops = {
+ .epoch_irq = msm_vfe40_epoch_irq_enb,
+ .reg_update = msm_vfe40_reg_update,
+ .cfg_camif = msm_vfe40_cfg_camif,
+ .update_camif_state = msm_vfe40_update_camif_state,
+ .reset_hw = msm_vfe40_reset_hardware,
+ .init_hw = msm_vfe40_init_hardware,
+ .init_hw_reg = msm_vfe40_init_hardware_reg,
+ .release_hw = msm_vfe40_release_hardware,
+ .get_platform_data = msm_vfe40_get_platform_data,
+ },
+ .stats_ops = {
+ .cfg_comp_mask = msm_vfe40_stats_cfg_comp_mask,
+ .clear_comp_mask = msm_vfe40_stats_clear_comp_mask,
+ .cfg_wm_irq_mask = msm_vfe40_stats_cfg_wm_irq_mask,
+ .clear_wm_irq_mask = msm_vfe40_stats_clear_wm_irq_mask,
+ .cfg_wm_reg = msm_vfe40_stats_cfg_wm_reg,
+ .clear_wm_reg = msm_vfe40_stats_clear_wm_reg,
+ .cfg_ub = msm_vfe40_stats_cfg_ub,
+ .stats_enable = msm_vfe40_stats_enable,
+ .update_ping_pong_addr =
+ msm_vfe40_stats_update_ping_pong_addr,
+ .get_comp_mask = msm_vfe40_stats_get_comp_mask,
+ .get_wm_mask = msm_vfe40_stats_get_wm_mask,
+ .get_frame_id = msm_vfe40_stats_get_frame_id,
+ },
+ },
+ .axi_hw_info = &msm_vfe40_axi_hw_info,
+ .subdev_ops = &msm_vfe40_subdev_ops,
+ .subdev_internal_ops = &msm_vfe40_internal_ops,
+};
+EXPORT_SYMBOL(vfe40_hw_info);
diff --git a/drivers/media/video/msmb/isp/msm_isp40.h b/drivers/media/video/msmb/isp/msm_isp40.h
new file mode 100644
index 0000000..e9b1518
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp40.h
@@ -0,0 +1,17 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_ISP40_H__
+#define __MSM_ISP40_H__
+
+extern struct msm_vfe_hardware_info vfe40_hw_info;
+#endif /* __MSM_ISP40_H__ */
diff --git a/drivers/media/video/msmb/isp/msm_isp_axi_util.c b/drivers/media/video/msmb/isp/msm_isp_axi_util.c
new file mode 100644
index 0000000..fe55b40
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp_axi_util.c
@@ -0,0 +1,968 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/io.h>
+#include <media/v4l2-subdev.h>
+#include "msm_isp_util.h"
+#include "msm_isp_axi_util.h"
+
+int msm_isp_axi_create_stream(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
+{
+ int i, rc = -1;
+ for (i = 0; i < MAX_NUM_STREAM; i++) {
+ if (axi_data->stream_info[i].state == AVALIABLE)
+ break;
+ }
+
+ if (i == MAX_NUM_STREAM) {
+ pr_err("%s: No free stream\n", __func__);
+ return rc;
+ }
+
+ axi_data->stream_info[i].session_id = stream_cfg_cmd->session_id;
+ axi_data->stream_info[i].stream_id = stream_cfg_cmd->stream_id;
+ axi_data->stream_info[i].buf_divert = stream_cfg_cmd->buf_divert;
+ axi_data->stream_info[i].state = INACTIVE;
+
+ if ((axi_data->stream_handle_cnt << 8) == 0)
+ axi_data->stream_handle_cnt++;
+
+ stream_cfg_cmd->axi_stream_handle =
+ (++axi_data->stream_handle_cnt) << 8 | i;
+
+ return 0;
+}
+
+void msm_isp_axi_destroy_stream(
+ struct msm_vfe_axi_shared_data *axi_data, int stream_idx)
+{
+ if (axi_data->stream_info[stream_idx].state != AVALIABLE)
+ axi_data->stream_info[stream_idx].state = AVALIABLE;
+ else
+ pr_err("%s: stream does not exist\n", __func__);
+}
+
+int msm_isp_validate_axi_request(struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
+{
+ int rc = -1;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->axi_stream_handle & 0xFF)];
+
+ switch (stream_cfg_cmd->output_format) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ stream_info->num_planes = 1;
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ stream_info->num_planes = 2;
+ break;
+ /*TD: Add more image format*/
+ default:
+ pr_err("%s: Invalid output format\n", __func__);
+ return rc;
+ }
+
+ if (axi_data->hw_info->num_wm - axi_data->num_used_wm <
+ stream_info->num_planes) {
+ pr_err("%s: No free write masters\n", __func__);
+ return rc;
+ }
+
+ if ((stream_info->num_planes > 1) &&
+ (axi_data->hw_info->num_comp_mask -
+ axi_data->num_used_composite_mask < 1)) {
+ pr_err("%s: No free composite mask\n", __func__);
+ return rc;
+ }
+
+ if (stream_cfg_cmd->stream_src == RDI) {
+ if (axi_data->hw_info->num_rdi -
+ axi_data->num_used_rdi < stream_info->num_planes) {
+ pr_err("%s: No free RDI\n", __func__);
+ return rc;
+ }
+ }
+
+ if (stream_cfg_cmd->init_frame_drop >= MAX_INIT_FRAME_DROP) {
+ pr_err("%s: Invalid skip pattern\n", __func__);
+ return rc;
+ }
+
+ if (stream_cfg_cmd->frame_skip_pattern >= MAX_SKIP) {
+ pr_err("%s: Invalid skip pattern\n", __func__);
+ return rc;
+ }
+
+ return 0;
+}
+
+static uint32_t msm_isp_axi_get_plane_size(
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd, int plane_idx)
+{
+ uint32_t size = 0;
+ struct msm_vfe_axi_plane_cfg *plane_cfg = stream_cfg_cmd->plane_cfg;
+ switch (stream_cfg_cmd->output_format) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width;
+ break;
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ /* TODO: fix me */
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width;
+ break;
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ /* TODO: fix me */
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width;
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ if (plane_cfg[plane_idx].output_plane_format == Y_PLANE)
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width;
+ else
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width / 2;
+ break;
+ /*TD: Add more image format*/
+ default:
+ pr_err("%s: Invalid output format\n", __func__);
+ break;
+ }
+ return size;
+}
+
+void msm_isp_axi_reserve_wm(struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
+{
+ int i, j;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->axi_stream_handle & 0xFF)];
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ for (j = 0; j < axi_data->hw_info->num_wm; j++) {
+ if (!axi_data->free_wm[j]) {
+ axi_data->free_wm[j] =
+ stream_cfg_cmd->axi_stream_handle;
+ axi_data->wm_image_size[j] =
+ msm_isp_axi_get_plane_size(
+ stream_cfg_cmd, i);
+ axi_data->num_used_wm++;
+ break;
+ }
+ }
+ stream_info->wm[i] = j;
+ }
+}
+
+void msm_isp_axi_free_wm(struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int i;
+ for (i = 0; i < stream_info->num_planes; i++) {
+ axi_data->free_wm[stream_info->wm[i]] = 0;
+ axi_data->num_used_wm--;
+ }
+}
+
+void msm_isp_axi_reserve_rdi(struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
+{
+ int i, j;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[(stream_cfg_cmd->axi_stream_handle & 0xFF)];
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ uint8_t csid = stream_cfg_cmd->plane_cfg[i].csid_src;
+
+ for (j = 0; j < axi_data->hw_info->num_rdi; j++) {
+ if (!axi_data->free_rdi[j]) {
+ axi_data->free_rdi[j] = 1;
+ axi_data->num_used_rdi++;
+ break;
+ }
+ }
+ stream_info->rdi[i] = j;
+
+ for (j = 0; j < axi_data->hw_info->num_rdi; j++) {
+ if (!axi_data->free_rdi_master[csid][j]) {
+ axi_data->free_rdi_master[csid][j] = 1;
+ axi_data->num_used_rdi++;
+ break;
+ }
+ }
+ stream_info->rdi_master[i] =
+ csid * axi_data->hw_info->num_rdi_master + j;
+ }
+ return;
+}
+
+void msm_isp_axi_reserve_comp_mask(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
+{
+ int i;
+ uint8_t comp_mask = 0;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->axi_stream_handle & 0xFF)];
+ for (i = 0; i < stream_info->num_planes; i++)
+ comp_mask |= 1 << stream_info->wm[i];
+
+ for (i = 0; i < axi_data->hw_info->num_comp_mask; i++) {
+ if (!axi_data->composite_info[i].stream_handle) {
+ axi_data->composite_info[i].stream_handle =
+ stream_cfg_cmd->axi_stream_handle;
+ axi_data->composite_info[i].
+ stream_composite_mask = comp_mask;
+ axi_data->num_used_composite_mask++;
+ break;
+ }
+ }
+ stream_info->comp_mask_index = i;
+ return;
+}
+
+void msm_isp_axi_free_comp_mask(struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ axi_data->composite_info[stream_info->comp_mask_index].
+ stream_composite_mask = 0;
+ axi_data->composite_info[stream_info->comp_mask_index].
+ stream_handle = 0;
+ axi_data->num_used_composite_mask--;
+}
+
+int msm_isp_axi_check_stream_state(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int rc = 0, i, j;
+ uint8_t src_state;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info;
+ enum msm_vfe_axi_state valid_state =
+ (stream_cfg_cmd->cmd == START_STREAM) ? INACTIVE : ACTIVE;
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ stream_info = &axi_data->stream_info[
+ (stream_cfg_cmd->stream_handle[i] & 0xFF)];
+ if (stream_info->state != valid_state) {
+ pr_err("%s: Invalid stream state\n", __func__);
+ rc = -EINVAL;
+ break;
+ }
+ /*
+ * For RDI stream, if multiple RDIs are used
+ * check if all the RDI srcs are in the same state, on/off
+ */
+ if (stream_info->stream_src == RDI) {
+ src_state = axi_data->src_info[
+ stream_info->rdi[0]+1].active;
+ for (j = 0; j < stream_info->num_planes; j++) {
+ if (src_state !=
+ axi_data->src_info[
+ stream_info->rdi[j]+1].active) {
+ pr_err("%s: RDI stream has inconsistent state\n",
+ __func__);
+ rc = -EINVAL;
+ break;
+ }
+ }
+ }
+
+ if (stream_cfg_cmd->cmd == START_STREAM) {
+ stream_info->bufq_handle =
+ vfe_dev->buf_mgr->ops->get_bufq_handle(
+ vfe_dev->buf_mgr, stream_info->session_id,
+ stream_info->stream_id);
+ if (stream_info->bufq_handle == 0) {
+ pr_err("%s: Stream has no valid buffer queue\n",
+ __func__);
+ rc = -EINVAL;
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+void msm_isp_update_framedrop_reg(struct vfe_device *vfe_dev)
+{
+ int i;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info;
+ for (i = 0; i < MAX_NUM_STREAM; i++) {
+ stream_info = &axi_data->stream_info[i];
+ if (stream_info->framedrop_update) {
+ if (stream_info->init_frame_drop == 0) {
+ stream_info->framedrop_update = 0;
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_framedrop(vfe_dev, stream_info);
+ }
+ }
+ if (stream_info->stream_type == BURST_STREAM) {
+ if (stream_info->burst_frame_count == 0 &&
+ stream_info->state == ACTIVE) {
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_framedrop(vfe_dev, stream_info);
+ if (stream_info->stream_src == RDI) {
+ uint32_t wm_reload_mask = 0,
+ reg_update_mask = 0;
+ stream_info->state = STOP_PENDING;
+ msm_isp_axi_stream_enable_cfg(
+ vfe_dev, stream_info,
+ &wm_reload_mask,
+ ®_update_mask);
+ }
+ }
+ }
+ }
+}
+
+void msm_isp_update_framedrop_count(
+ struct vfe_device *vfe_dev)
+{
+ int i;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info;
+ for (i = 0; i < MAX_NUM_STREAM; i++) {
+ stream_info = &axi_data->stream_info[i];
+ if (stream_info->framedrop_update) {
+ stream_info->init_frame_drop--;
+ if (stream_info->init_frame_drop == 1) {
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, 0xF);
+ }
+ }
+ if (stream_info->stream_type == BURST_STREAM) {
+ stream_info->burst_frame_count--;
+ if (stream_info->burst_frame_count == 1) {
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, 0xF);
+ } else if (stream_info->burst_frame_count == 0) {
+ if (stream_info->stream_src != RDI) {
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev,
+ DISABLE_CAMIF);
+ pr_err("%s: pending burst_cnt = %d, disable camif\n",
+ __func__,
+ stream_info->burst_frame_count);
+ }
+ }
+ }
+ }
+}
+
+void msm_isp_new_frame_notify(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src) {
+ switch (frame_src) {
+ case VFE_PIX_0:
+ ISP_DBG("%s: PIX0 frame id: %lu\n", __func__,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
+ msm_isp_update_framedrop_count(vfe_dev);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id++;
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id == 0)
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id = 1;
+ break;
+ case VFE_RAW_0:
+ case VFE_RAW_1:
+ case VFE_RAW_2:
+ break;
+ default:
+ pr_err("%s: invalid frame src %d received\n",
+ __func__, frame_src);
+ break;
+ }
+}
+void msm_isp_calculate_framedrop(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
+{
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->axi_stream_handle & 0xFF)];
+ uint32_t framedrop_period = 1;
+
+ switch (stream_cfg_cmd->frame_skip_pattern) {
+ case NO_SKIP:
+ stream_info->framedrop_pattern = VFE_NO_DROP;
+ framedrop_period = 1;
+ break;
+ case EVERY_2FRAME:
+ stream_info->framedrop_pattern = VFE_DROP_EVERY_2FRAME;
+ framedrop_period = 2;
+ break;
+ case EVERY_4FRAME:
+ stream_info->framedrop_pattern = VFE_DROP_EVERY_4FRAME;
+ framedrop_period = 4;
+ break;
+ case EVERY_8FRAME:
+ stream_info->framedrop_pattern = VFE_DROP_EVERY_8FRAME;
+ framedrop_period = 8;
+ break;
+ case EVERY_16FRAME:
+ stream_info->framedrop_pattern = VFE_DROP_EVERY_16FRAME;
+ framedrop_period = 16;
+ break;
+ case EVERY_32FRAME:
+ stream_info->framedrop_pattern = VFE_DROP_EVERY_32FRAME;
+ framedrop_period = 32;
+ break;
+ default:
+ stream_info->framedrop_pattern = VFE_NO_DROP;
+ framedrop_period = 1;
+ break;
+ }
+
+ if (stream_cfg_cmd->init_frame_drop < framedrop_period) {
+ stream_info->framedrop_pattern <<=
+ stream_cfg_cmd->init_frame_drop;
+ stream_info->init_frame_drop = 0;
+ stream_info->framedrop_update = 0;
+ } else {
+ stream_info->init_frame_drop = stream_cfg_cmd->init_frame_drop;
+ stream_info->framedrop_update = 1;
+ }
+
+ if (stream_cfg_cmd->burst_count > 0) {
+ stream_info->stream_type = BURST_STREAM;
+ stream_info->burst_frame_count =
+ stream_cfg_cmd->init_frame_drop +
+ (stream_cfg_cmd->burst_count - 1) *
+ framedrop_period + 1;
+ } else {
+ stream_info->stream_type = CONTINUOUS_STREAM;
+ stream_info->burst_frame_count = 0;
+ }
+}
+
+int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0, i;
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd = arg;
+ struct msm_vfe_axi_stream *stream_info;
+
+ rc = msm_isp_axi_create_stream(
+ &vfe_dev->axi_data, stream_cfg_cmd);
+ if (rc) {
+ pr_err("%s: create stream failed\n", __func__);
+ return rc;
+ }
+
+ rc = msm_isp_validate_axi_request(
+ &vfe_dev->axi_data, stream_cfg_cmd);
+ if (rc) {
+ pr_err("%s: Request validation failed\n", __func__);
+ msm_isp_axi_destroy_stream(&vfe_dev->axi_data,
+ (stream_cfg_cmd->axi_stream_handle & 0xFF));
+ return rc;
+ }
+
+ msm_isp_axi_reserve_wm(&vfe_dev->axi_data, stream_cfg_cmd);
+ if (stream_cfg_cmd->stream_src == RDI)
+ msm_isp_axi_reserve_rdi(&vfe_dev->axi_data, stream_cfg_cmd);
+
+ stream_info =
+ &vfe_dev->axi_data.
+ stream_info[(stream_cfg_cmd->axi_stream_handle & 0xFF)];
+
+ msm_isp_calculate_framedrop(&vfe_dev->axi_data, stream_cfg_cmd);
+ vfe_dev->hw_info->vfe_ops.axi_ops.cfg_framedrop(vfe_dev, stream_info);
+
+ if (stream_info->num_planes > 1) {
+ msm_isp_axi_reserve_comp_mask(
+ &vfe_dev->axi_data, stream_cfg_cmd);
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_comp_mask(vfe_dev, stream_info);
+ } else {
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_wm_irq_mask(vfe_dev, stream_info);
+ }
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_wm_reg(vfe_dev, stream_cfg_cmd, i);
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_wm_xbar_reg(vfe_dev, stream_cfg_cmd, i);
+
+ if (stream_cfg_cmd->stream_src == RDI)
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_rdi_reg(vfe_dev, stream_cfg_cmd, i);
+ }
+ return rc;
+}
+
+int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0, i;
+ struct msm_vfe_axi_stream_release_cmd *stream_release_cmd = arg;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[
+ (stream_release_cmd->stream_handle & 0xFF)];
+ struct msm_vfe_axi_stream_cfg_cmd stream_cfg;
+
+ if (stream_info->state == AVALIABLE) {
+ pr_err("%s: Stream already released\n", __func__);
+ return -EINVAL;
+ } else if (stream_info->state != INACTIVE) {
+ stream_cfg.cmd = STOP_STREAM;
+ stream_cfg.num_streams = 1;
+ stream_cfg.stream_handle[0] = stream_release_cmd->stream_handle;
+ msm_isp_cfg_axi_stream(vfe_dev, (void *) &stream_cfg);
+ }
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ clear_wm_reg(vfe_dev, stream_info, i);
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ clear_wm_xbar_reg(vfe_dev, stream_info, i);
+ }
+
+ if (stream_info->num_planes > 1) {
+ msm_isp_axi_free_comp_mask(&vfe_dev->axi_data, stream_info);
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ clear_comp_mask(vfe_dev, stream_info);
+ } else {
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ clear_wm_irq_mask(vfe_dev, stream_info);
+ }
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.clear_framedrop(vfe_dev, stream_info);
+ msm_isp_axi_free_wm(axi_data, stream_info);
+
+ msm_isp_axi_destroy_stream(&vfe_dev->axi_data,
+ (stream_release_cmd->stream_handle & 0xFF));
+
+ return rc;
+}
+
+void msm_isp_axi_stream_enable_cfg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint32_t *wm_reload_mask, uint32_t *reg_update_mask)
+{
+ int i;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ if (stream_info->state == INACTIVE)
+ return;
+ for (i = 0; i < stream_info->num_planes; i++) {
+ /*TD: Frame base command*/
+ if (stream_info->state == START_PENDING)
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ enable_wm(vfe_dev, stream_info->wm[i], 1);
+ else
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ enable_wm(vfe_dev, stream_info->wm[i], 0);
+
+ *wm_reload_mask |= (1 << stream_info->wm[i]);
+ if (stream_info->stream_src == RDI)
+ *reg_update_mask |= (1 << stream_info->rdi[i]);
+ }
+ if (stream_info->state == START_PENDING) {
+ axi_data->num_active_stream++;
+ stream_info->state = ACTIVE;
+ } else {
+ axi_data->num_active_stream--;
+ stream_info->state = INACTIVE;
+ }
+}
+
+void msm_isp_axi_stream_update(struct vfe_device *vfe_dev)
+{
+ int i;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ uint32_t wm_reload_mask = 0x0, reg_update_mask = 0x1;
+ int send_update_complete = 0;
+ for (i = 0; i < MAX_NUM_STREAM; i++) {
+ if (axi_data->stream_info[i].state == START_PENDING ||
+ axi_data->stream_info[i].state ==
+ STOP_PENDING) {
+ msm_isp_axi_stream_enable_cfg(
+ vfe_dev, &axi_data->stream_info[i],
+ &wm_reload_mask, ®_update_mask);
+ if (axi_data->stream_info[i].state == STOP_PENDING)
+ axi_data->stream_info[i].state = STOPPING;
+ } else if (axi_data->stream_info[i].state == STOPPING) {
+ send_update_complete = 1;
+ axi_data->stream_info[i].state = INACTIVE;
+ }
+ }
+ /*Reload AXI*/
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ reload_wm(vfe_dev, wm_reload_mask);
+ /*Reg update per src*/
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, reg_update_mask);
+ if (send_update_complete) {
+ ISP_DBG("%s: send update complete\n", __func__);
+ vfe_dev->axi_data.stream_update = 0;
+ complete(&vfe_dev->stream_config_complete);
+ }
+}
+
+#define VFE_PING_FLAG 0xFFFFFFFF
+#define VFE_PONG_FLAG 0x0
+
+int msm_isp_cfg_ping_pong_address(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status,
+ struct timeval *tv)
+{
+ int i, rc = -1;
+ struct msm_isp_buffer *buf;
+ struct msm_isp_event_data buf_event;
+ uint32_t pingpong_bit = 0;
+ uint32_t bufq_handle = stream_info->bufq_handle;
+
+ pingpong_bit = (~(pingpong_status >> stream_info->wm[0]) & 0x1);
+ rc = vfe_dev->buf_mgr->ops->get_buf(
+ vfe_dev->buf_mgr, bufq_handle, &buf);
+ if (rc < 0) {
+ if (stream_info->stream_type == BURST_STREAM &&
+ stream_info->burst_frame_count <= 1) {
+ rc = 0;
+ if (pingpong_bit)
+ buf = stream_info->buf[0];
+ else
+ buf = stream_info->buf[1];
+ } else {
+ pr_err("%s: No free buffer, stream_type = %d, burst_cnt = %d\n",
+ __func__, stream_info->stream_type,
+ stream_info->burst_frame_count);
+ return rc;
+ }
+ }
+
+ if (buf->num_planes != stream_info->num_planes) {
+ pr_err("%s: Invalid buffer\n", __func__);
+ rc = -EINVAL;
+ goto buf_error;
+ }
+ for (i = 0; i < stream_info->num_planes; i++) {
+ if (pingpong_bit !=
+ (~(pingpong_status >> stream_info->wm[i]) & 0x1)) {
+ pr_warn("%s: Write master ping pong mismatch. Status: 0x%x\n",
+ __func__, pingpong_status);
+ }
+ }
+ for (i = 0; i < stream_info->num_planes; i++)
+ vfe_dev->hw_info->vfe_ops.axi_ops.update_ping_pong_addr(
+ vfe_dev, stream_info->wm[i],
+ pingpong_status, buf->mapped_info[i].paddr);
+
+ if (stream_info->buf[pingpong_bit]) {
+ if (stream_info->buf_divert) {
+ buf_event.frame_id = stream_info->frame_id;
+ buf_event.timestamp = *tv;
+ buf_event.u.buf_done.session_id =
+ stream_info->session_id;
+ buf_event.u.buf_done.stream_id =
+ stream_info->stream_id;
+ buf_event.u.buf_done.handle =
+ stream_info->bufq_handle;
+ buf_event.u.buf_done.buf_idx =
+ stream_info->buf[pingpong_bit]->buf_idx;
+ msm_isp_send_event(
+ vfe_dev, ISP_EVENT_BUF_DIVERT, &buf_event);
+ } else {
+ vfe_dev->buf_mgr->ops->buf_done(vfe_dev->buf_mgr,
+ stream_info->buf[pingpong_bit]->bufq_handle,
+ stream_info->buf[pingpong_bit]->buf_idx,
+ tv, stream_info->frame_id);
+ }
+ }
+
+ stream_info->buf[pingpong_bit] = buf;
+ return 0;
+buf_error:
+ vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx);
+ return rc;
+}
+
+enum msm_isp_camif_update_state
+ msm_isp_update_camif_output_count(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int i;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ uint8_t cur_pix_count = axi_data->src_info[VFE_PIX_0].
+ pix_stream_count;
+ uint8_t cur_raw_count = axi_data->src_info[VFE_PIX_0].
+ raw_stream_count;
+ uint8_t pix_stream_cnt = 0;
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->stream_handle[i] & 0xFF)];
+ if (stream_info->stream_src != RDI)
+ pix_stream_cnt++;
+ if (stream_info->stream_src == PIX_ENCODER ||
+ stream_info->stream_src == PIX_VIEWFINDER) {
+ if (stream_cfg_cmd->cmd == START_STREAM)
+ vfe_dev->axi_data.src_info[VFE_PIX_0].
+ pix_stream_count++;
+ else
+ vfe_dev->axi_data.src_info[VFE_PIX_0].
+ pix_stream_count--;
+ } else if (stream_info->stream_src == CAMIF_RAW ||
+ stream_info->stream_src == IDEAL_RAW) {
+ if (stream_cfg_cmd->cmd == START_STREAM)
+ vfe_dev->axi_data.src_info[VFE_PIX_0].
+ raw_stream_count++;
+ else
+ vfe_dev->axi_data.src_info[VFE_PIX_0].
+ raw_stream_count--;
+ }
+ }
+ if (pix_stream_cnt) {
+ if ((cur_pix_count + cur_raw_count == 0) &&
+ (axi_data->src_info[VFE_PIX_0].
+ pix_stream_count +
+ axi_data->src_info[VFE_PIX_0].
+ raw_stream_count != 0)) {
+ return ENABLE_CAMIF;
+ }
+
+ if ((cur_pix_count + cur_raw_count != 0) &&
+ (axi_data->src_info[VFE_PIX_0].
+ pix_stream_count +
+ axi_data->src_info[VFE_PIX_0].
+ raw_stream_count == 0)) {
+ return DISABLE_CAMIF;
+ }
+ }
+
+ return NO_UPDATE;
+}
+
+void msm_camera_io_dump_2(void __iomem *addr, int size)
+{
+ char line_str[128], *p_str;
+ int i;
+ u32 *p = (u32 *) addr;
+ u32 data;
+ ISP_DBG("%s: %p %d\n", __func__, addr, size);
+ line_str[0] = '\0';
+ p_str = line_str;
+ for (i = 0; i < size/4; i++) {
+ if (i % 4 == 0) {
+ snprintf(p_str, 12, "%08x: ", (u32) p);
+ p_str += 10;
+ }
+ data = readl_relaxed(p++);
+ snprintf(p_str, 12, "%08x ", data);
+ p_str += 9;
+ if ((i + 1) % 4 == 0) {
+ ISP_DBG("%s\n", line_str);
+ line_str[0] = '\0';
+ p_str = line_str;
+ }
+ }
+ if (line_str[0] != '\0')
+ ISP_DBG("%s\n", line_str);
+}
+
+int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0, i;
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd = arg;
+ uint32_t wm_reload_mask = 0x0, reg_update_mask = 0x1;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ uint8_t src_state;
+ enum msm_isp_camif_update_state camif_update;
+ uint8_t wait_for_complete = 0;
+ rc = msm_isp_axi_check_stream_state(vfe_dev, stream_cfg_cmd);
+ if (rc < 0) {
+ pr_err("%s: Invalid stream state\n", __func__);
+ return rc;
+ }
+
+ if (axi_data->num_active_stream == 0) {
+ /*Configure UB*/
+ vfe_dev->hw_info->vfe_ops.axi_ops.cfg_ub(vfe_dev);
+ }
+
+ camif_update =
+ msm_isp_update_camif_output_count(vfe_dev, stream_cfg_cmd);
+
+ if (camif_update == DISABLE_CAMIF)
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev, DISABLE_CAMIF_IMMEDIATELY);
+
+ /*
+ * Stream start either immediately or at reg update
+ * Depends on whether the stream src is active
+ * If source is on, start and stop have to be done during reg update
+ * If source is off, start can happen immediately or during reg update
+ * stop has to be done immediately.
+ */
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->stream_handle[i] & 0xFF)];
+
+
+ if (stream_info->stream_src == RDI)
+ src_state =
+ axi_data->src_info[
+ stream_info->rdi[0]+1].active;
+ else
+ src_state = axi_data->src_info[0].active;
+
+ stream_info->state = (stream_cfg_cmd->cmd == START_STREAM) ?
+ START_PENDING : STOP_PENDING;
+
+ if (stream_cfg_cmd->cmd == START_STREAM) {
+ /*Set address for both PING & PONG register */
+ rc = msm_isp_cfg_ping_pong_address(vfe_dev,
+ stream_info, VFE_PING_FLAG, NULL);
+ rc = msm_isp_cfg_ping_pong_address(vfe_dev,
+ stream_info, VFE_PONG_FLAG, NULL);
+ }
+ if (src_state && camif_update != DISABLE_CAMIF) {
+ /*On the fly stream start/stop */
+ wait_for_complete = 1;
+ reg_update_mask = 0xF; /*TD: Maybe set this per src*/
+ } else {
+ if (vfe_dev->dump_reg)
+ msm_camera_io_dump_2(vfe_dev->vfe_base, 0x900);
+ /*Configure AXI start bits to start immediately*/
+ msm_isp_axi_stream_enable_cfg(
+ vfe_dev, stream_info,
+ &wm_reload_mask, ®_update_mask);
+ }
+ }
+ if (!wait_for_complete) {
+ /*Reload AXI*/
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ reload_wm(vfe_dev, wm_reload_mask);
+ /*Reg update per src*/
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, reg_update_mask);
+
+ if (camif_update == ENABLE_CAMIF)
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev, camif_update);
+ } else {
+ unsigned long flags;
+ spin_lock_irqsave(&vfe_dev->shared_data_lock, flags);
+ init_completion(&vfe_dev->stream_config_complete);
+ axi_data->stream_update = 1;
+ spin_unlock_irqrestore(&vfe_dev->shared_data_lock, flags);
+ /*Reload AXI*/
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ reload_wm(vfe_dev, wm_reload_mask);
+ /*Reg update per src*/
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, reg_update_mask);
+ rc = wait_for_completion_interruptible_timeout(
+ &vfe_dev->stream_config_complete,
+ msecs_to_jiffies(500));
+ if (rc == 0) {
+ pr_err("%s: wait timeout\n", __func__);
+ rc = -1;
+ }
+ }
+ return rc;
+}
+
+void msm_isp_process_axi_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct timeval *tv)
+{
+ int i;
+ uint32_t comp_mask = 0, wm_mask = 0;
+ uint32_t pingpong_status, stream_idx;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_composite_info *comp_info;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+
+ ISP_DBG("%s: status: 0x%x\n", __func__, irq_status0);
+ comp_mask = vfe_dev->hw_info->vfe_ops.axi_ops.
+ get_comp_mask(irq_status0, irq_status1);
+ wm_mask = vfe_dev->hw_info->vfe_ops.axi_ops.
+ get_wm_mask(irq_status0, irq_status1);
+ if (!(comp_mask || wm_mask))
+ return;
+
+ pingpong_status =
+ vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status(vfe_dev);
+
+ for (i = 0; i < axi_data->hw_info->num_comp_mask; i++) {
+ comp_info = &axi_data->composite_info[i];
+ if (comp_mask & (1 << i)) {
+ if (!comp_info->stream_handle) {
+ pr_err("%s: Invalid handle for composite irq\n",
+ __func__);
+ } else {
+ stream_idx = comp_info->stream_handle & 0xFF;
+ stream_info =
+ &axi_data->stream_info[stream_idx];
+ ISP_DBG("%s: stream%d frame id: 0x%x\n",
+ __func__,
+ stream_idx, stream_info->frame_id);
+ stream_info->frame_id++;
+ msm_isp_cfg_ping_pong_address(vfe_dev,
+ stream_info, pingpong_status, tv);
+ }
+ }
+ wm_mask &= ~(comp_info->stream_composite_mask);
+ }
+
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ if (wm_mask & (1 << i)) {
+ if (!axi_data->free_wm[i]) {
+ pr_err("%s: Invalid handle for wm irq\n",
+ __func__);
+ continue;
+ }
+ stream_idx = axi_data->free_wm[i] & 0xFF;
+ stream_info = &axi_data->stream_info[stream_idx];
+ stream_info->frame_id++;
+ msm_isp_cfg_ping_pong_address(vfe_dev,
+ stream_info, pingpong_status, tv);
+ }
+ }
+ return;
+}
diff --git a/drivers/media/video/msmb/isp/msm_isp_axi_util.h b/drivers/media/video/msmb/isp/msm_isp_axi_util.h
new file mode 100644
index 0000000..4847c06
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp_axi_util.h
@@ -0,0 +1,65 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __MSM_ISP_AXI_UTIL_H__
+#define __MSM_ISP_AXI_UTIL_H__
+
+#include "msm_isp.h"
+
+int msm_isp_axi_create_stream(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd);
+
+void msm_isp_axi_destroy_stream(
+ struct msm_vfe_axi_shared_data *axi_data, int stream_idx);
+
+int msm_isp_validate_axi_request(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd);
+
+void msm_isp_axi_reserve_wm(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd);
+
+void msm_isp_axi_reserve_rdi(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd);
+
+void msm_isp_axi_reserve_comp_mask(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd);
+
+int msm_isp_axi_check_stream_state(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd);
+
+int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg);
+int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg);
+int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg);
+
+void msm_isp_axi_stream_enable_cfg(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint32_t *wm_reload_mask, uint32_t *reg_update_mask);
+
+void msm_isp_axi_stream_update(struct vfe_device *vfe_dev);
+
+int msm_isp_cfg_ping_pong_address(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status,
+ struct timeval *tv);
+
+void msm_isp_update_framedrop_reg(struct vfe_device *vfe_dev);
+void msm_isp_update_framedrop_count(struct vfe_device *vfe_dev);
+void msm_isp_new_frame_notify(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src);
+void msm_isp_process_axi_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct timeval *tv);
+#endif /* __MSM_ISP_AXI_UTIL_H__ */
diff --git a/drivers/media/video/msmb/isp/msm_isp_stats_util.c b/drivers/media/video/msmb/isp/msm_isp_stats_util.c
new file mode 100644
index 0000000..15f4c23
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp_stats_util.c
@@ -0,0 +1,69 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/io.h>
+#include <media/v4l2-subdev.h>
+#include "msm_isp_util.h"
+#include "msm_isp_stats_util.h"
+
+void msm_isp_process_stats_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct timeval *tv)
+{
+ uint32_t frame_id;
+ uint32_t stats_comp_mask = 0, stats_mask = 0;
+ ISP_DBG("%s: status: 0x%x\n", __func__, irq_status0);
+ stats_comp_mask = vfe_dev->hw_info->vfe_ops.stats_ops.
+ get_comp_mask(irq_status0, irq_status1);
+ stats_mask = vfe_dev->hw_info->vfe_ops.stats_ops.
+ get_wm_mask(irq_status0, irq_status1);
+ if (!(stats_comp_mask || stats_mask))
+ return;
+ frame_id = vfe_dev->hw_info->vfe_ops.stats_ops.get_frame_id(vfe_dev);
+ /* TD: process comp/non comp stats */
+}
+
+int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0;
+ /*To Do*/
+ return rc;
+}
+
+int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0;
+ struct msm_vfe_stats_stream_release_cmd *stream_release_cmd = arg;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ struct msm_vfe_stats_stream *stream_info =
+ &stats_data->stream_info[
+ (stream_release_cmd->stream_handle & 0xFF)];
+
+ if (stream_info == NULL)
+ rc = -1;
+ return rc;
+}
+
+int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0;
+ uint32_t stats_mask = 0;
+ uint8_t enable = 0;
+ uint32_t pingpong_status = 0;
+ struct msm_isp_buffer *buf = NULL;
+ enum msm_isp_stats_type stats_type = MSM_ISP_STATS_BE;
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ stats_enable(vfe_dev, stats_mask, enable);
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ update_ping_pong_addr(vfe_dev, stats_type,
+ pingpong_status, buf->mapped_info[0].paddr);
+ return rc;
+}
diff --git a/drivers/media/video/msmb/isp/msm_isp_stats_util.h b/drivers/media/video/msmb/isp/msm_isp_stats_util.h
new file mode 100644
index 0000000..4feb653
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp_stats_util.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __MSM_ISP_STATS_UTIL_H__
+#define __MSM_ISP_STATS_UTIL_H__
+
+#include "msm_isp.h"
+
+void msm_isp_process_stats_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct timeval *tv);
+int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg);
+int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg);
+int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg);
+#endif /* __MSM_ISP_STATS_UTIL_H__ */
diff --git a/drivers/media/video/msmb/isp/msm_isp_util.c b/drivers/media/video/msmb/isp/msm_isp_util.c
new file mode 100644
index 0000000..ee3e50e
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp_util.c
@@ -0,0 +1,462 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/io.h>
+#include <media/v4l2-subdev.h>
+
+#include "msm.h"
+#include "msm_isp_util.h"
+#include "msm_isp_axi_util.h"
+#include "msm_isp_stats_util.h"
+#include "msm_camera_io_util.h"
+
+#define MAX_ISP_V4l2_EVENTS 100
+
+void msm_isp_gettimeofday(struct timeval *tv)
+{
+ struct timespec ts;
+
+ ktime_get_ts(&ts);
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec/1000;
+}
+
+int msm_isp_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
+ int rc = 0;
+ rc = v4l2_event_subscribe(fh, sub, MAX_ISP_V4l2_EVENTS);
+ if (rc == 0) {
+ if (sub->type == V4L2_EVENT_ALL) {
+ int i;
+
+ vfe_dev->axi_data.event_mask = 0;
+ for (i = 0; i < ISP_EVENT_MAX; i++)
+ vfe_dev->axi_data.event_mask |= (1 << i);
+ } else {
+ int event_idx = sub->type - ISP_EVENT_BASE;
+
+ vfe_dev->axi_data.event_mask |= (1 << event_idx);
+ }
+ }
+ return rc;
+}
+
+int msm_isp_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
+ int rc = 0;
+
+ rc = v4l2_event_unsubscribe(fh, sub);
+ if (sub->type == V4L2_EVENT_ALL) {
+ vfe_dev->axi_data.event_mask = 0;
+ } else {
+ int event_idx = sub->type - ISP_EVENT_BASE;
+
+ vfe_dev->axi_data.event_mask &= ~(1 << event_idx);
+ }
+ return rc;
+}
+
+int msm_isp_cfg_pix(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg)
+{
+ int rc = 0;
+ /*TD Validate config info
+ * should check if all streams are off */
+
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux = pix_cfg->input_mux;
+
+ vfe_dev->hw_info->vfe_ops.core_ops.cfg_camif(vfe_dev, pix_cfg);
+ return rc;
+}
+
+int msm_isp_cfg_input(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0;
+ struct msm_vfe_input_cfg *input_cfg = arg;
+
+ switch (input_cfg->input_src) {
+ case VFE_PIX_0:
+ msm_isp_cfg_pix(vfe_dev, &input_cfg->d.pix_cfg);
+ break;
+ case VFE_RAW_0:
+ case VFE_RAW_1:
+ case VFE_RAW_2:
+ case VFE_SRC_MAX:
+ break;
+ }
+ return rc;
+}
+
+long msm_isp_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
+
+ mutex_lock(&vfe_dev->mutex);
+ ISP_DBG("%s cmd: %d\n", __func__, cmd);
+
+ switch (cmd) {
+ case VIDIOC_MSM_VFE_REG_CFG: {
+ msm_isp_proc_cmd(vfe_dev, arg);
+ break;
+ }
+ case VIDIOC_MSM_ISP_REQUEST_BUF:
+ case VIDIOC_MSM_ISP_ENQUEUE_BUF:
+ case VIDIOC_MSM_ISP_RELEASE_BUF: {
+ msm_isp_proc_buf_cmd(vfe_dev->buf_mgr, cmd, arg);
+ break;
+ }
+ case VIDIOC_MSM_ISP_REQUEST_STREAM:
+ msm_isp_request_axi_stream(vfe_dev, arg);
+ break;
+ case VIDIOC_MSM_ISP_RELEASE_STREAM:
+ msm_isp_release_axi_stream(vfe_dev, arg);
+ break;
+ case VIDIOC_MSM_ISP_CFG_STREAM:
+ msm_isp_cfg_axi_stream(vfe_dev, arg);
+ break;
+ case VIDIOC_MSM_ISP_INPUT_CFG:
+ msm_isp_cfg_input(vfe_dev, arg);
+ break;
+ case VIDIOC_MSM_ISP_SET_SRC_STATE:
+ msm_isp_set_src_state(vfe_dev, arg);
+ break;
+ case VIDIOC_MSM_ISP_REQUEST_STATS_STREAM:
+ msm_isp_request_stats_stream(vfe_dev, arg);
+ break;
+ case VIDIOC_MSM_ISP_RELEASE_STATS_STREAM:
+ msm_isp_release_stats_stream(vfe_dev, arg);
+ break;
+ case VIDIOC_MSM_ISP_CFG_STATS_STREAM:
+ msm_isp_cfg_stats_stream(vfe_dev, arg);
+ break;
+ }
+
+ mutex_unlock(&vfe_dev->mutex);
+ return 0;
+}
+
+static int msm_isp_send_hw_cmd(struct vfe_device *vfe_dev,
+ struct msm_vfe_reg_cfg_cmd *reg_cfg_cmd, uint32_t *cfg_data)
+{
+ switch (reg_cfg_cmd->cmd_type) {
+ case VFE_WRITE: {
+ if (resource_size(vfe_dev->vfe_mem) <
+ (reg_cfg_cmd->reg_offset + reg_cfg_cmd->len)) {
+ pr_err("%s: Invalid length\n", __func__);
+ return -EINVAL;
+ }
+ msm_camera_io_memcpy(vfe_dev->vfe_base +
+ reg_cfg_cmd->reg_offset,
+ cfg_data + reg_cfg_cmd->cmd_data/4, reg_cfg_cmd->len);
+ break;
+ }
+ case VFE_WRITE_MB: {
+ uint32_t *data_ptr = cfg_data + reg_cfg_cmd->cmd_data/4;
+ msm_camera_io_w_mb(*data_ptr, vfe_dev->vfe_base +
+ reg_cfg_cmd->reg_offset);
+ break;
+ }
+ case VFE_WRITE_MASK: {
+ uint32_t temp;
+ temp = msm_camera_io_r(vfe_dev->vfe_base +
+ reg_cfg_cmd->reg_offset);
+ temp |= reg_cfg_cmd->cmd_data;
+ msm_camera_io_w(temp, vfe_dev->vfe_base +
+ reg_cfg_cmd->reg_offset);
+ break;
+ }
+ case VFE_CLEAR_MASK: {
+ uint32_t temp;
+ temp = msm_camera_io_r(vfe_dev->vfe_base +
+ reg_cfg_cmd->reg_offset);
+ temp &= ~reg_cfg_cmd->cmd_data;
+ msm_camera_io_w(temp, vfe_dev->vfe_base +
+ reg_cfg_cmd->reg_offset);
+ break;
+ }
+ case VFE_WRITE_AUTO_INCREMENT: {
+ int i;
+ uint32_t *data_ptr = cfg_data + reg_cfg_cmd->cmd_data/4;
+ for (i = 0; i < reg_cfg_cmd->len/4; i++)
+ msm_camera_io_w(*data_ptr++,
+ vfe_dev->vfe_base + reg_cfg_cmd->reg_offset);
+ break;
+ }
+ case VFE_READ: {
+ int i;
+ uint32_t *data_ptr = cfg_data + reg_cfg_cmd->cmd_data/4;
+ for (i = 0; i < reg_cfg_cmd->len/4; i++)
+ *data_ptr++ = msm_camera_io_r(
+ vfe_dev->vfe_base + reg_cfg_cmd->reg_offset++);
+ break;
+ }
+ }
+ return 0;
+}
+
+int msm_isp_proc_cmd(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0, i;
+ struct msm_vfe_cfg_cmd2 *proc_cmd = arg;
+ struct msm_vfe_reg_cfg_cmd *reg_cfg_cmd;
+ uint32_t *cfg_data;
+
+ reg_cfg_cmd = kzalloc(sizeof(struct msm_vfe_reg_cfg_cmd)*
+ proc_cmd->num_cfg, GFP_KERNEL);
+ if (!reg_cfg_cmd) {
+ pr_err("%s: reg_cfg alloc failed\n", __func__);
+ rc = -ENOMEM;
+ goto reg_cfg_failed;
+ }
+
+ cfg_data = kzalloc(proc_cmd->cmd_len, GFP_KERNEL);
+ if (!cfg_data) {
+ pr_err("%s: cfg_data alloc failed\n", __func__);
+ rc = -ENOMEM;
+ goto cfg_data_failed;
+ }
+
+ if (copy_from_user(reg_cfg_cmd,
+ (void __user *)(proc_cmd->cfg_cmd),
+ sizeof(struct msm_vfe_reg_cfg_cmd) * proc_cmd->num_cfg)) {
+ rc = -EFAULT;
+ goto copy_cmd_failed;
+ }
+
+ if (copy_from_user(cfg_data,
+ (void __user *)(proc_cmd->cfg_data),
+ proc_cmd->cmd_len)) {
+ rc = -EFAULT;
+ goto copy_cmd_failed;
+ }
+
+ for (i = 0; i < proc_cmd->num_cfg; i++)
+ msm_isp_send_hw_cmd(vfe_dev, ®_cfg_cmd[i], cfg_data);
+
+ if (copy_to_user(proc_cmd->cfg_data,
+ cfg_data, proc_cmd->cmd_len)) {
+ rc = -EFAULT;
+ goto copy_cmd_failed;
+ }
+
+copy_cmd_failed:
+ kfree(cfg_data);
+cfg_data_failed:
+ kfree(reg_cfg_cmd);
+reg_cfg_failed:
+ return rc;
+}
+
+int msm_isp_send_event(struct vfe_device *vfe_dev,
+ uint32_t event_type,
+ struct msm_isp_event_data *event_data)
+{
+ struct v4l2_event isp_event;
+ memset(&isp_event, 0, sizeof(struct v4l2_event));
+ isp_event.id = 0;
+ isp_event.type = event_type;
+ memcpy(&isp_event.u.data[0], event_data,
+ sizeof(struct msm_isp_event_data));
+ v4l2_event_queue(vfe_dev->subdev.sd.devnode, &isp_event);
+ return 0;
+}
+
+#define CAL_WORD(width, M, N) ((width * M + N - 1) / N)
+
+int msm_isp_cal_word_per_line(uint32_t output_format,
+ uint32_t pixel_per_line)
+{
+ int val = -1;
+ switch (output_format) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ val = CAL_WORD(pixel_per_line, 1, 8);
+ break;
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ val = CAL_WORD(pixel_per_line, 1, 6);
+ break;
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ val = CAL_WORD(pixel_per_line, 1, 5);
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ val = CAL_WORD(pixel_per_line, 1, 8);
+ break;
+ /*TD: Add more image format*/
+ default:
+ pr_err("%s: Invalid output format\n", __func__);
+ break;
+ }
+ return val;
+}
+
+irqreturn_t msm_isp_process_irq(int irq_num, void *data)
+{
+ unsigned long flags;
+ struct msm_vfe_tasklet_queue_cmd *queue_cmd;
+ struct vfe_device *vfe_dev = (struct vfe_device *) data;
+ uint32_t irq_status0, irq_status1;
+
+ vfe_dev->hw_info->vfe_ops.irq_ops.
+ read_irq_status(vfe_dev, &irq_status0, &irq_status1);
+ if ((irq_status0 == 0) && (irq_status1 == 0)) {
+ ISP_DBG("%s: irq_status0 & 1 are both 0!\n", __func__);
+ return IRQ_HANDLED;
+ }
+
+ spin_lock_irqsave(&vfe_dev->tasklet_lock, flags);
+ queue_cmd = &vfe_dev->tasklet_queue_cmd[vfe_dev->taskletq_idx];
+ if (queue_cmd->cmd_used) {
+ pr_err("%s: Tasklet queue overflow\n", __func__);
+ list_del(&queue_cmd->list);
+ } else {
+ atomic_add(1, &vfe_dev->irq_cnt);
+ }
+ queue_cmd->vfeInterruptStatus0 = irq_status0;
+ queue_cmd->vfeInterruptStatus1 = irq_status1;
+ msm_isp_gettimeofday(&queue_cmd->tv);
+ queue_cmd->cmd_used = 1;
+ vfe_dev->taskletq_idx =
+ (vfe_dev->taskletq_idx + 1) % MSM_VFE_TASKLETQ_SIZE;
+ list_add_tail(&queue_cmd->list, &vfe_dev->tasklet_q);
+ spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags);
+ tasklet_schedule(&vfe_dev->vfe_tasklet);
+ return IRQ_HANDLED;
+}
+
+void msm_isp_do_tasklet(unsigned long data)
+{
+ unsigned long flags;
+ struct vfe_device *vfe_dev = (struct vfe_device *) data;
+ struct msm_vfe_irq_ops *irq_ops = &vfe_dev->hw_info->vfe_ops.irq_ops;
+ struct msm_vfe_tasklet_queue_cmd *queue_cmd;
+ struct timeval tv;
+ uint32_t irq_status0, irq_status1;
+ while (atomic_read(&vfe_dev->irq_cnt)) {
+ spin_lock_irqsave(&vfe_dev->tasklet_lock, flags);
+ queue_cmd = list_first_entry(&vfe_dev->tasklet_q,
+ struct msm_vfe_tasklet_queue_cmd, list);
+ if (!queue_cmd) {
+ atomic_set(&vfe_dev->irq_cnt, 0);
+ spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags);
+ return;
+ }
+ atomic_sub(1, &vfe_dev->irq_cnt);
+ list_del(&queue_cmd->list);
+ queue_cmd->cmd_used = 0;
+ irq_status0 = queue_cmd->vfeInterruptStatus0;
+ irq_status1 = queue_cmd->vfeInterruptStatus1;
+ tv = queue_cmd->tv;
+ spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags);
+ ISP_DBG("%s: status0: 0x%x status1: 0x%x\n",
+ __func__, irq_status0, irq_status1);
+ irq_ops->process_reset_irq(vfe_dev,
+ irq_status0, irq_status1);
+ irq_ops->process_halt_irq(vfe_dev,
+ irq_status0, irq_status1);
+ irq_ops->process_camif_irq(vfe_dev,
+ irq_status0, irq_status1);
+ irq_ops->process_error_irq(vfe_dev,
+ irq_status0, irq_status1);
+ irq_ops->process_axi_irq(vfe_dev,
+ irq_status0, irq_status1, &tv);
+ irq_ops->process_stats_irq(vfe_dev,
+ irq_status0, irq_status1, &tv);
+ irq_ops->process_reg_update(vfe_dev, irq_status0, irq_status1);
+ }
+}
+
+void msm_isp_set_src_state(struct vfe_device *vfe_dev, void *arg)
+{
+ struct msm_vfe_axi_src_state *src_state = arg;
+ vfe_dev->axi_data.src_info[src_state->input_src].active =
+ src_state->src_active;
+}
+
+int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
+ long rc;
+ ISP_DBG("%s\n", __func__);
+
+ mutex_lock(&vfe_dev->mutex);
+ if (vfe_dev->vfe_open_cnt == 1) {
+ pr_err("VFE already open\n");
+ mutex_unlock(&vfe_dev->mutex);
+ return -ENODEV;
+ }
+
+ if (vfe_dev->hw_info->vfe_ops.core_ops.init_hw(vfe_dev) < 0) {
+ pr_err("%s: init hardware failed\n", __func__);
+ mutex_unlock(&vfe_dev->mutex);
+ return -EBUSY;
+ }
+
+ rc = vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev);
+ if (rc <= 0) {
+ pr_err("%s: reset timeout\n", __func__);
+ mutex_unlock(&vfe_dev->mutex);
+ return -EINVAL;
+ }
+ vfe_dev->hw_info->vfe_ops.core_ops.init_hw_reg(vfe_dev);
+
+ vfe_dev->buf_mgr->ops->attach_ctx(vfe_dev->buf_mgr, vfe_dev->iommu_ctx);
+ vfe_dev->buf_mgr->ops->buf_mgr_init(vfe_dev->buf_mgr, "msm_isp", 14);
+
+ memset(&vfe_dev->axi_data, 0, sizeof(struct msm_vfe_axi_shared_data));
+ vfe_dev->axi_data.hw_info = vfe_dev->hw_info->axi_hw_info;
+
+ ISP_DBG("%s: HW Version: 0x%x\n",
+ __func__, msm_camera_io_r(vfe_dev->vfe_base));
+
+ vfe_dev->vfe_open_cnt++;
+ vfe_dev->taskletq_idx = 0;
+ mutex_unlock(&vfe_dev->mutex);
+ return 0;
+}
+
+int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ long rc;
+ struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
+ ISP_DBG("%s\n", __func__);
+ mutex_lock(&vfe_dev->mutex);
+ if (vfe_dev->vfe_open_cnt == 0) {
+ pr_err("%s: Invalid close\n", __func__);
+ mutex_unlock(&vfe_dev->mutex);
+ return -ENODEV;
+ }
+
+ rc = vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev);
+ if (rc <= 0)
+ pr_err("%s: halt timeout\n", __func__);
+
+ vfe_dev->buf_mgr->ops->buf_mgr_deinit(vfe_dev->buf_mgr);
+ vfe_dev->buf_mgr->ops->detach_ctx(vfe_dev->buf_mgr, vfe_dev->iommu_ctx);
+ vfe_dev->hw_info->vfe_ops.core_ops.release_hw(vfe_dev);
+
+ vfe_dev->vfe_open_cnt--;
+ mutex_unlock(&vfe_dev->mutex);
+ return 0;
+}
diff --git a/drivers/media/video/msmb/isp/msm_isp_util.h b/drivers/media/video/msmb/isp/msm_isp_util.h
new file mode 100644
index 0000000..729c8b5
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp_util.h
@@ -0,0 +1,45 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __MSM_ISP_UTIL_H__
+#define __MSM_ISP_UTIL_H__
+
+#include "msm_isp.h"
+
+/* #define CONFIG_MSM_ISP_DBG 1 */
+
+#ifdef CONFIG_MSM_ISP_DBG
+#define ISP_DBG(fmt, args...) printk(fmt, ##args)
+#else
+#define ISP_DBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+void msm_isp_gettimeofday(struct timeval *tv);
+
+int msm_isp_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub);
+
+int msm_isp_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub);
+
+int msm_isp_proc_cmd(struct vfe_device *vfe_dev, void *arg);
+int msm_isp_send_event(struct vfe_device *vfe_dev,
+ uint32_t type, struct msm_isp_event_data *event_data);
+int msm_isp_cal_word_per_line(uint32_t output_format,
+ uint32_t pixel_per_line);
+irqreturn_t msm_isp_process_irq(int irq_num, void *data);
+void msm_isp_set_src_state(struct vfe_device *vfe_dev, void *arg);
+void msm_isp_do_tasklet(unsigned long data);
+
+int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);
+int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);
+long msm_isp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
+#endif /* __MSM_ISP_UTIL_H__ */
diff --git a/drivers/media/video/msmb/ispif/Makefile b/drivers/media/video/msmb/ispif/Makefile
new file mode 100644
index 0000000..908cc28
--- /dev/null
+++ b/drivers/media/video/msmb/ispif/Makefile
@@ -0,0 +1,3 @@
+ccflags-y += -Idrivers/media/video/msmb
+ccflags-y += -Idrivers/media/video/msmb/sensor/io
+obj-$(CONFIG_MSM_CSID) += msm_ispif.o
diff --git a/drivers/media/video/msmb/ispif/msm_ispif.c b/drivers/media/video/msmb/ispif/msm_ispif.c
new file mode 100644
index 0000000..ffb9263
--- /dev/null
+++ b/drivers/media/video/msmb/ispif/msm_ispif.c
@@ -0,0 +1,954 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/of.h>
+#include <linux/videodev2.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+
+#include "msm_ispif.h"
+#include "msm.h"
+#include "msm_ispif_hwreg.h"
+#include "msm_sd.h"
+#include "msm_camera_io_util.h"
+
+#define V4L2_IDENT_ISPIF 50001
+#define MSM_ISPIF_DRV_NAME "msm_ispif"
+#define DUMP_BUFF_SIZE_128 128
+
+#define ISPIF_INTF_CMD_ENABLE_FRAME_BOUNDARY 0x01
+#define ISPIF_INTF_CMD_DISABLE_FRAME_BOUNDARY 0x00
+#define ISPIF_INTF_CMD_DISABLE_IMMEDIATELY 0x02
+
+#define CONFIG_MSMB_CAMERA_DEBUG
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+static void msm_ispif_io_dump(void __iomem *addr, int size)
+{
+ char line_str[DUMP_BUFF_SIZE_128], *p_str;
+ int i;
+ u32 *p = (u32 *) addr;
+ u32 data;
+
+
+ CDBG("%s: %p %d\n", __func__, addr, size);
+ line_str[0] = '\0';
+ p_str = line_str;
+ for (i = 0; i < size/4; i++) {
+ if (i % 4 == 0) {
+ snprintf(p_str, 12, "%08x: ", (u32) p);
+ p_str += 10;
+ }
+ data = readl_relaxed(p++);
+ snprintf(p_str, 12, "%08x ", data);
+ p_str += 9;
+ if ((i + 1) % 4 == 0) {
+ CDBG("%s\n", line_str);
+ line_str[0] = '\0';
+ p_str = line_str;
+ }
+ }
+ if (line_str[0] != '\0')
+ CDBG("%s\n", line_str);
+}
+
+static void msm_ispif_io_dump_reg(struct ispif_device *ispif)
+{
+ int size;
+
+ if (!ispif->enb_dump_reg)
+ return;
+ size = 0x250;
+ msm_ispif_io_dump(ispif->base+0x100, size);
+}
+
+static int msm_ispif_intf_reset(struct ispif_device *ispif,
+ struct msm_ispif_param_data *params)
+{
+
+ int i, rc = 0;
+ enum msm_ispif_intftype intf_type;
+ uint32_t data = (0x1 << STROBED_RST_EN);
+
+ for (i = 0; i < params->num; i++) {
+ intf_type = params->entries[i].intftype;
+ ispif->sof_count[params->vfe_intf].sof_cnt[intf_type] = 0;
+ switch (intf_type) {
+ case PIX0:
+ data |= (0x1 << PIX_0_VFE_RST_STB) |
+ (0x1 << PIX_0_CSID_RST_STB);
+ break;
+ case RDI0:
+ data |= (0x1 << RDI_0_VFE_RST_STB) |
+ (0x1 << RDI_0_CSID_RST_STB);
+ break;
+ case PIX1:
+ data |= (0x1 << PIX_1_VFE_RST_STB) |
+ (0x1 << PIX_1_CSID_RST_STB);
+ break;
+ case RDI1:
+ data |= (0x1 << RDI_1_VFE_RST_STB) |
+ (0x1 << RDI_1_CSID_RST_STB);
+ break;
+ case RDI2:
+ data |= (0x1 << RDI_2_VFE_RST_STB) |
+ (0x1 << RDI_2_CSID_RST_STB);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ }
+ if (data > 0x1) {
+ unsigned long jiffes = msecs_to_jiffies(500);
+ long lrc = 0;
+ if (params->vfe_intf == VFE0)
+ msm_camera_io_w(data, ispif->base + ISPIF_RST_CMD_ADDR);
+ else
+ msm_camera_io_w(data, ispif->base +
+ ISPIF_RST_CMD_1_ADDR);
+ lrc = wait_for_completion_interruptible_timeout(
+ &ispif->reset_complete, jiffes);
+ if (lrc < 0 || !lrc) {
+ pr_err("%s: wait timeout ret = %ld\n", __func__, lrc);
+ rc = -EIO;
+ }
+ }
+ return rc;
+}
+
+static int msm_ispif_reset(struct ispif_device *ispif)
+{
+ int rc = 0;
+ unsigned long jiffes = msecs_to_jiffies(500);
+ long lrc = 0;
+
+ memset(ispif->sof_count, 0, sizeof(ispif->sof_count));
+ msm_camera_io_w(ISPIF_RST_CMD_MASK, ispif->base + ISPIF_RST_CMD_ADDR);
+ if (ispif->csid_version == CSID_VERSION_V3)
+ msm_camera_io_w_mb(ISPIF_RST_CMD_1_MASK, ispif->base +
+ ISPIF_RST_CMD_1_ADDR);
+ CDBG("%s: Sending reset\n", __func__);
+ lrc = wait_for_completion_interruptible_timeout(
+ &ispif->reset_complete, jiffes);
+ if (lrc < 0 || !lrc) {
+ pr_err("%s: wait timeout ret = %ld\n", __func__, lrc);
+ rc = -EIO;
+ }
+ CDBG("%s: reset returned\n", __func__);
+ return rc;
+}
+
+static int msm_ispif_subdev_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ BUG_ON(!chip);
+ chip->ident = V4L2_IDENT_ISPIF;
+ chip->revision = 0;
+ return 0;
+}
+
+static void msm_ispif_sel_csid_core(struct ispif_device *ispif,
+ uint8_t intftype, uint8_t csid, uint8_t vfe_intf)
+{
+ int rc = 0;
+ uint32_t data = 0;
+
+ if (ispif->csid_version <= CSID_VERSION_V2) {
+ if (ispif->ispif_clk[intftype] == NULL) {
+ CDBG("%s: ispif NULL clk\n", __func__);
+ return;
+ }
+ rc = clk_set_rate(ispif->ispif_clk[intftype], csid);
+ if (rc < 0)
+ pr_err("%s: clk_set_rate failed %d\n", __func__, rc);
+ return;
+ }
+ data = msm_camera_io_r(ispif->base + ISPIF_INPUT_SEL_ADDR +
+ (0x200 * vfe_intf));
+ switch (intftype) {
+ case PIX0:
+ data &= ~(0x3); /* clear old setting */
+ data |= csid; /* add new setting */
+ break;
+ case RDI0:
+ data &= ~(0x3 << 4); /* clear old setting */
+ data |= (csid << 4); /* add new setting */
+ break;
+ case PIX1:
+ data &= ~(0x3 << 8); /* clear old setting */
+ data |= (csid << 8); /* add new setting */
+ break;
+ case RDI1:
+ data &= ~(0x3 << 12); /* clear old setting */
+ data |= (csid << 12); /* add new setting */
+ break;
+ case RDI2:
+ data &= ~(0x3 << 20); /* clear old setting */
+ data |= (csid << 20); /* add new setting */
+ break;
+ }
+ if (data) {
+ msm_camera_io_w_mb(data, ispif->base + ISPIF_INPUT_SEL_ADDR +
+ (0x200 * vfe_intf));
+ }
+}
+
+static void msm_ispif_enable_intf_cids(struct ispif_device *ispif,
+ uint8_t intftype, uint16_t cid_mask,
+ uint8_t vfe_intf, uint8_t enable)
+{
+ uint32_t data = 0;
+
+ switch (intftype) {
+ case PIX0:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_PIX_0_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
+ if (enable)
+ data |= cid_mask; /* add new config */
+ else
+ data &= ~cid_mask; /* remove CID bit */
+ msm_camera_io_w_mb(data, ispif->base +
+ ISPIF_PIX_0_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
+ break;
+ case RDI0:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_RDI_0_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
+ if (enable)
+ data |= cid_mask; /* add new config */
+ else
+ data &= ~cid_mask; /* remove CID bit */
+ msm_camera_io_w_mb(data, ispif->base +
+ ISPIF_RDI_0_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
+ break;
+ case PIX1:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_PIX_1_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
+ if (enable)
+ data |= cid_mask; /* add new config */
+ else
+ data &= ~cid_mask; /* remove CID bit */
+ msm_camera_io_w_mb(data, ispif->base +
+ ISPIF_PIX_1_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
+ break;
+ case RDI1:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_RDI_1_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
+ if (enable)
+ data |= cid_mask; /* add new config */
+ else
+ data &= ~cid_mask; /* remove CID bit */
+ msm_camera_io_w_mb(data, ispif->base +
+ ISPIF_RDI_1_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
+ break;
+ case RDI2:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_RDI_2_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
+ if (enable)
+ data |= cid_mask; /* add new config */
+ else
+ data &= ~cid_mask; /* remove CID bit */
+ msm_camera_io_w_mb(data, ispif->base +
+ ISPIF_RDI_2_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
+ break;
+ }
+}
+
+static int32_t msm_ispif_validate_intf_status(struct ispif_device *ispif,
+ uint8_t intftype, uint8_t vfe_intf)
+{
+ int32_t rc = 0;
+ uint32_t data = 0;
+ switch (intftype) {
+ case PIX0:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_PIX_0_STATUS_ADDR + (0x200 * vfe_intf));
+ break;
+ case RDI0:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_RDI_0_STATUS_ADDR + (0x200 * vfe_intf));
+ break;
+ case PIX1:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_PIX_1_STATUS_ADDR + (0x200 * vfe_intf));
+ break;
+ case RDI1:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_RDI_1_STATUS_ADDR + (0x200 * vfe_intf));
+ break;
+ case RDI2:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_RDI_2_STATUS_ADDR + (0x200 * vfe_intf));
+ break;
+ }
+ if ((data & 0xf) != 0xf)
+ rc = -EBUSY;
+ return rc;
+}
+
+static uint16_t msm_ispif_get_cids_mask_from_cfg(
+ struct msm_ispif_params_entry *entry)
+{
+ int i;
+ uint16_t cids_mask = 0;
+
+ for (i = 0; i < entry->num_cids; i++)
+ cids_mask |= (1 << entry->cids[i]);
+ return cids_mask;
+}
+
+static int msm_ispif_config(struct ispif_device *ispif,
+ struct msm_ispif_param_data *params)
+{
+ int rc = 0, i = 0;
+ enum msm_ispif_intftype intftype;
+ enum msm_ispif_vfe_intf vfe_intf = params->vfe_intf;
+ uint16_t cid_mask;
+
+ msm_camera_io_w(0x00000000, ispif->base + ISPIF_IRQ_MASK_ADDR);
+ msm_camera_io_w(0x00000000, ispif->base + ISPIF_IRQ_MASK_1_ADDR);
+ msm_camera_io_w_mb(0x00000000, ispif->base + ISPIF_IRQ_MASK_2_ADDR);
+ for (i = 0; i < params->num; i++) {
+ intftype = params->entries[i].intftype;
+ vfe_intf = params->vfe_intf;
+ CDBG("%s intftype %x, vfe_intf %d, csid %d\n", __func__,
+ intftype, vfe_intf, params->entries[i].csid);
+ if ((intftype >= INTF_MAX) ||
+ (ispif->csid_version <= CSID_VERSION_V2 &&
+ vfe_intf > VFE0) ||
+ (ispif->csid_version == CSID_VERSION_V3 &&
+ vfe_intf >= VFE_MAX)) {
+ pr_err("%s: VFEID %d and CSID version %d mismatch\n",
+ __func__, vfe_intf, ispif->csid_version);
+ return -EINVAL;
+ }
+ rc = msm_ispif_validate_intf_status(ispif, intftype, vfe_intf);
+ if (rc < 0) {
+ pr_err("%s:validate_intf_status failed, rc = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ msm_ispif_sel_csid_core(ispif, intftype,
+ params->entries[i].csid, vfe_intf);
+ cid_mask = msm_ispif_get_cids_mask_from_cfg(
+ ¶ms->entries[i]);
+ msm_ispif_enable_intf_cids(ispif, intftype,
+ cid_mask, vfe_intf, 1);
+ }
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_MASK, ispif->base +
+ ISPIF_IRQ_MASK_ADDR);
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_MASK, ispif->base +
+ ISPIF_IRQ_CLEAR_ADDR);
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_1_MASK, ispif->base +
+ ISPIF_IRQ_MASK_1_ADDR);
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_1_MASK, ispif->base +
+ ISPIF_IRQ_CLEAR_1_ADDR);
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_2_MASK, ispif->base +
+ ISPIF_IRQ_MASK_2_ADDR);
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_2_MASK, ispif->base +
+ ISPIF_IRQ_CLEAR_2_ADDR);
+
+ msm_camera_io_w_mb(ISPIF_IRQ_GLOBAL_CLEAR_CMD, ispif->base +
+ ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+ return rc;
+}
+
+static void msm_ispif_intf_cmd(struct ispif_device *ispif,
+ uint32_t cmd_bits,
+ struct msm_ispif_param_data *params)
+{
+ uint8_t vc = 0;
+ int i, k;
+ enum msm_ispif_intftype intf_type;
+ enum msm_ispif_cid cid;
+ enum msm_ispif_vfe_intf vfe_intf = params->vfe_intf;
+
+ for (i = 0; i < params->num; i++) {
+ intf_type = params->entries[i].intftype;
+ for (k = 0; k < params->entries[i].num_cids; k++) {
+ cid = params->entries[i].cids[k];
+ vc = cid % 4;
+ if (intf_type == RDI2) {
+ /* zero out two bits */
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd1 &=
+ ~(0x3 << (vc * 2 + 8));
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd1 |=
+ (cmd_bits << (vc * 2 + 8)); /* set cmd bits */
+ } else {
+ /* zero 2 bits */
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd &=
+ ~(0x3 << (vc * 2 + vfe_intf * 8));
+ /* set cmd bits */
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd |=
+ (cmd_bits << (vc * 2 + vfe_intf * 8));
+ }
+ }
+ }
+ /* cmd for PIX0, PIX1, RDI0, RDI1 */
+ if (ispif->applied_intf_cmd[vfe_intf].intf_cmd != 0xFFFFFFFF) {
+ msm_camera_io_w_mb(ispif->applied_intf_cmd[vfe_intf].intf_cmd,
+ ispif->base + ISPIF_INTF_CMD_ADDR +
+ (0x200 * vfe_intf));
+ }
+ /* cmd for RDI2 */
+ if (ispif->applied_intf_cmd[vfe_intf].intf_cmd1 != 0xFFFFFFFF)
+ msm_camera_io_w_mb(ispif->applied_intf_cmd[vfe_intf].intf_cmd1,
+ ispif->base + ISPIF_INTF_CMD_1_ADDR +
+ (0x200 * vfe_intf));
+}
+
+static int msm_ispif_stop_immediately(struct ispif_device *ispif,
+ struct msm_ispif_param_data *params)
+{
+ int i, rc = 0;
+ uint16_t cid_mask = 0;
+
+ msm_ispif_intf_cmd(ispif, ISPIF_INTF_CMD_DISABLE_IMMEDIATELY, params);
+
+ /* after stop the interface we need to unmask the CID enable bits */
+ for (i = 0; i < params->num; i++) {
+ cid_mask = msm_ispif_get_cids_mask_from_cfg(
+ ¶ms->entries[i]);
+ msm_ispif_enable_intf_cids(ispif, params->entries[i].intftype,
+ cid_mask, params->vfe_intf, 0);
+ }
+ return rc;
+}
+
+static int msm_ispif_start_frame_boundary(struct ispif_device *ispif,
+ struct msm_ispif_param_data *params)
+{
+ int rc = 0;
+
+ rc = msm_ispif_intf_reset(ispif, params);
+ msm_ispif_intf_cmd(ispif, ISPIF_INTF_CMD_ENABLE_FRAME_BOUNDARY, params);
+ return rc;
+}
+
+static int msm_ispif_stop_frame_boundary(struct ispif_device *ispif,
+ struct msm_ispif_param_data *params)
+{
+ int i, rc = 0;
+ uint16_t cid_mask = 0;
+
+ msm_ispif_intf_cmd(ispif,
+ ISPIF_INTF_CMD_DISABLE_FRAME_BOUNDARY, params);
+ for (i = 0; i < params->num; i++) {
+ cid_mask =
+ msm_ispif_get_cids_mask_from_cfg(
+ ¶ms->entries[i]);
+ switch (params->entries[i].intftype) {
+ case PIX0:
+ while ((msm_camera_io_r(ispif->base +
+ ISPIF_PIX_0_STATUS_ADDR +
+ (0x200 * params->vfe_intf)) & 0xf) != 0xf) {
+ CDBG("Wait for pix0 Idle\n");
+ }
+ break;
+ case RDI0:
+ while ((msm_camera_io_r(ispif->base +
+ ISPIF_RDI_0_STATUS_ADDR +
+ (0x200 * params->vfe_intf)) & 0xf) != 0xf) {
+ CDBG("Wait for rdi0 Idle\n");
+ }
+ break;
+ case PIX1:
+ while ((msm_camera_io_r(ispif->base +
+ ISPIF_PIX_1_STATUS_ADDR +
+ (0x200 * params->vfe_intf)) & 0xf) != 0xf) {
+ CDBG("Wait for pix1 Idle\n");
+ }
+ break;
+ case RDI1:
+ while ((msm_camera_io_r(ispif->base +
+ ISPIF_RDI_1_STATUS_ADDR +
+ (0x200 * params->vfe_intf)) & 0xf) != 0xf) {
+ CDBG("Wait for rdi1 Idle\n");
+ }
+ break;
+ case RDI2:
+ while ((msm_camera_io_r(ispif->base +
+ ISPIF_RDI_2_STATUS_ADDR +
+ (0x200 * params->vfe_intf)) & 0xf) != 0xf) {
+ CDBG("Wait for rdi2 Idle\n");
+ }
+ break;
+ default:
+ break;
+ }
+ /* disable CIDs in CID_MASK register */
+ msm_ispif_enable_intf_cids(ispif, params->entries[i].intftype,
+ cid_mask, params->vfe_intf, 0);
+ }
+ return rc;
+}
+
+static void ispif_process_irq(struct ispif_device *ispif,
+ struct ispif_irq_status *out, enum msm_ispif_vfe_intf vfe_id)
+{
+ if (out[vfe_id].ispifIrqStatus0 &
+ ISPIF_IRQ_STATUS_PIX_SOF_MASK) {
+ ispif->sof_count[vfe_id].sof_cnt[PIX0]++;
+ }
+ if (out[vfe_id].ispifIrqStatus0 &
+ ISPIF_IRQ_STATUS_RDI0_SOF_MASK) {
+ ispif->sof_count[vfe_id].sof_cnt[RDI0]++;
+ }
+ if (out[vfe_id].ispifIrqStatus1 &
+ ISPIF_IRQ_STATUS_RDI1_SOF_MASK) {
+ ispif->sof_count[vfe_id].sof_cnt[RDI1]++;
+ }
+ if (out[vfe_id].ispifIrqStatus2 &
+ ISPIF_IRQ_STATUS_RDI2_SOF_MASK) {
+ ispif->sof_count[vfe_id].sof_cnt[RDI2]++;
+ }
+ return;
+}
+
+static inline void msm_ispif_read_irq_status(struct ispif_irq_status *out,
+ void *data)
+{
+ struct ispif_device *ispif = (struct ispif_device *)data;
+
+ out[VFE0].ispifIrqStatus0 = msm_camera_io_r(ispif->base +
+ ISPIF_IRQ_STATUS_ADDR);
+ out[VFE0].ispifIrqStatus1 = msm_camera_io_r(ispif->base +
+ ISPIF_IRQ_STATUS_1_ADDR);
+ out[VFE0].ispifIrqStatus2 = msm_camera_io_r(ispif->base +
+ ISPIF_IRQ_STATUS_2_ADDR);
+ msm_camera_io_w(out[VFE0].ispifIrqStatus0,
+ ispif->base + ISPIF_IRQ_CLEAR_ADDR);
+ msm_camera_io_w(out[VFE0].ispifIrqStatus1,
+ ispif->base + ISPIF_IRQ_CLEAR_1_ADDR);
+ msm_camera_io_w_mb(out[VFE0].ispifIrqStatus2,
+ ispif->base + ISPIF_IRQ_CLEAR_2_ADDR);
+
+ if (out[VFE0].ispifIrqStatus0 & ISPIF_IRQ_STATUS_MASK) {
+ if (out[VFE0].ispifIrqStatus0 & (0x1 <<
+ RESET_DONE_IRQ))
+ complete(&ispif->reset_complete);
+ if (out[VFE0].ispifIrqStatus0 & (0x1 <<
+ PIX_INTF_0_OVERFLOW_IRQ))
+ pr_err("%s: VFE0 pix0 overflow.\n", __func__);
+ if (out[VFE0].ispifIrqStatus0 & (0x1 <<
+ RAW_INTF_0_OVERFLOW_IRQ))
+ pr_err("%s: VFE0 rdi0 overflow.\n", __func__);
+ if (out[VFE0].ispifIrqStatus1 & (0x1 <<
+ RAW_INTF_1_OVERFLOW_IRQ))
+ pr_err("%s: VFE0 rdi1 overflow.\n", __func__);
+ if (out[VFE0].ispifIrqStatus2 & (0x1 <<
+ RAW_INTF_2_OVERFLOW_IRQ))
+ pr_err("%s: VFE0 rdi2 overflow.\n", __func__);
+ if ((out[VFE0].ispifIrqStatus0 &
+ ISPIF_IRQ_STATUS_SOF_MASK) ||
+ (out[VFE0].ispifIrqStatus1 &
+ ISPIF_IRQ_STATUS_SOF_MASK) ||
+ (out[VFE0].ispifIrqStatus2 &
+ ISPIF_IRQ_STATUS_RDI2_SOF_MASK))
+ ispif_process_irq(ispif, out, VFE0);
+ }
+ if (ispif->csid_version == CSID_VERSION_V3) {
+ out[VFE1].ispifIrqStatus0 = msm_camera_io_r(ispif->base +
+ ISPIF_IRQ_STATUS_ADDR + 0x200);
+ msm_camera_io_w(out[VFE1].ispifIrqStatus0,
+ ispif->base + ISPIF_IRQ_CLEAR_ADDR + 0x200);
+ out[VFE1].ispifIrqStatus1 = msm_camera_io_r(ispif->base +
+ ISPIF_IRQ_STATUS_1_ADDR + 0x200);
+ msm_camera_io_w(out[VFE1].ispifIrqStatus1,
+ ispif->base + ISPIF_IRQ_CLEAR_1_ADDR + 0x200);
+ out[VFE1].ispifIrqStatus2 = msm_camera_io_r(ispif->base +
+ ISPIF_IRQ_STATUS_2_ADDR + 0x200);
+ msm_camera_io_w_mb(out[VFE1].ispifIrqStatus2,
+ ispif->base + ISPIF_IRQ_CLEAR_2_ADDR + 0x200);
+ if (out[VFE1].ispifIrqStatus0 & (0x1 <<
+ PIX_INTF_0_OVERFLOW_IRQ))
+ pr_err("%s: VFE1 pix0 overflow.\n", __func__);
+ if (out[VFE1].ispifIrqStatus0 & (0x1 <<
+ RAW_INTF_0_OVERFLOW_IRQ))
+ pr_err("%s: VFE1 rdi0 overflow.\n", __func__);
+ if (out[VFE1].ispifIrqStatus1 & (0x1 <<
+ RAW_INTF_1_OVERFLOW_IRQ))
+ pr_err("%s: VFE1 rdi1 overflow.\n", __func__);
+ if (out[VFE1].ispifIrqStatus2 & (0x1 <<
+ RAW_INTF_2_OVERFLOW_IRQ))
+ pr_err("%s: VFE1 rdi2 overflow.\n", __func__);
+ if ((out[VFE1].ispifIrqStatus0 & ISPIF_IRQ_STATUS_SOF_MASK) ||
+ (out[VFE1].ispifIrqStatus1 &
+ ISPIF_IRQ_STATUS_SOF_MASK) ||
+ (out[VFE1].ispifIrqStatus2 &
+ ISPIF_IRQ_STATUS_RDI2_SOF_MASK))
+ ispif_process_irq(ispif, out, VFE1);
+ }
+ msm_camera_io_w_mb(ISPIF_IRQ_GLOBAL_CLEAR_CMD, ispif->base +
+ ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+}
+
+static irqreturn_t msm_io_ispif_irq(int irq_num, void *data)
+{
+ struct ispif_irq_status irq[VFE_MAX];
+
+ msm_ispif_read_irq_status(irq, data);
+ return IRQ_HANDLED;
+}
+
+static struct msm_cam_clk_info ispif_8960_clk_info[] = {
+ {"csi_pix_clk", 0},
+ {"csi_rdi_clk", 0},
+ {"csi_pix1_clk", 0},
+ {"csi_rdi1_clk", 0},
+ {"csi_rdi2_clk", 0},
+};
+static struct msm_cam_clk_info ispif_8974_clk_info[] = {
+ {"camss_vfe_vfe_clk", -1},
+ {"camss_csi_vfe_clk", -1},
+ {"camss_vfe_vfe_clk1", -1},
+ {"camss_csi_vfe_clk1", -1},
+};
+
+static int msm_ispif_init(struct ispif_device *ispif,
+ uint32_t csid_version)
+{
+ int rc = 0;
+
+ if (ispif->ispif_state == ISPIF_POWER_UP) {
+ CDBG("%s: ispif already initted state = %d\n", __func__,
+ ispif->ispif_state);
+ rc = -EAGAIN;
+ return rc;
+ }
+
+ /* can we set to zero? */
+ ispif->applied_intf_cmd[VFE0].intf_cmd = 0xFFFFFFFF;
+ ispif->applied_intf_cmd[VFE0].intf_cmd1 = 0xFFFFFFFF;
+ ispif->applied_intf_cmd[VFE1].intf_cmd = 0xFFFFFFFF;
+ ispif->applied_intf_cmd[VFE1].intf_cmd1 = 0xFFFFFFFF;
+ memset(ispif->sof_count, 0, sizeof(ispif->sof_count));
+
+ ispif->csid_version = csid_version;
+ if (ispif->csid_version < CSID_VERSION_V2) {
+ rc = msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info,
+ ispif->ispif_clk, 2, 1);
+ if (rc < 0) {
+ pr_err("%s: cannot enable clock, error = %d\n",
+ __func__, rc);
+ goto end;
+ }
+ } else if (ispif->csid_version == CSID_VERSION_V2) {
+ rc = msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info,
+ ispif->ispif_clk, ARRAY_SIZE(ispif_8960_clk_info), 1);
+ if (rc < 0) {
+ pr_err("%s: cannot enable clock, error = %d\n",
+ __func__, rc);
+ goto end;
+ }
+ } else {
+ rc = msm_cam_clk_enable(&ispif->pdev->dev, ispif_8974_clk_info,
+ ispif->ispif_clk, ARRAY_SIZE(ispif_8974_clk_info), 1);
+ if (rc < 0) {
+ pr_err("%s: cannot enable clock, error = %d\n",
+ __func__, rc);
+ goto end;
+ }
+ }
+ ispif->base = ioremap(ispif->mem->start,
+ resource_size(ispif->mem));
+ if (!ispif->base) {
+ rc = -ENOMEM;
+ pr_err("%s: nomem\n", __func__);
+ goto error_clk;
+ }
+ rc = request_irq(ispif->irq->start, msm_io_ispif_irq,
+ IRQF_TRIGGER_RISING, "ispif", ispif);
+ init_completion(&ispif->reset_complete);
+ if (rc < 0) {
+ pr_err("%s: request_irq error = %d\n", __func__, rc);
+ goto error_irq;
+ }
+ rc = msm_ispif_reset(ispif);
+ if (rc == 0) {
+ ispif->ispif_state = ISPIF_POWER_UP;
+ CDBG("%s: power up done\n", __func__);
+ goto end;
+ }
+ free_irq(ispif->irq->start, ispif);
+error_irq:
+ iounmap(ispif->base);
+error_clk:
+ if (ispif->csid_version < CSID_VERSION_V2) {
+ msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info,
+ ispif->ispif_clk, 2, 0);
+ } else if (ispif->csid_version == CSID_VERSION_V2) {
+ msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info,
+ ispif->ispif_clk, ARRAY_SIZE(ispif_8960_clk_info), 0);
+ }
+end:
+ return rc;
+}
+
+static void msm_ispif_release(struct ispif_device *ispif)
+{
+ if (ispif->ispif_state != ISPIF_POWER_UP) {
+ pr_err("%s: ispif invalid state %d\n", __func__,
+ ispif->ispif_state);
+ return;
+ }
+ /* make sure no streaming going on */
+ msm_ispif_reset(ispif);
+ free_irq(ispif->irq->start, ispif);
+ iounmap(ispif->base);
+ if (ispif->csid_version < CSID_VERSION_V2) {
+ msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info,
+ ispif->ispif_clk, 2, 0);
+ } else if (ispif->csid_version == CSID_VERSION_V2) {
+ msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info,
+ ispif->ispif_clk, ARRAY_SIZE(ispif_8960_clk_info), 0);
+ }
+ ispif->ispif_state = ISPIF_POWER_DOWN;
+}
+
+static int msm_ispif_clk_enable(struct ispif_device *ispif,
+ uint32_t csid_version, int enable)
+{
+ int rc = 0;
+
+ if (csid_version != CSID_VERSION_V3)
+ goto end;
+ rc = msm_cam_clk_enable(&ispif->pdev->dev, ispif_8974_clk_info,
+ ispif->ispif_clk, ARRAY_SIZE(ispif_8974_clk_info), enable);
+ if (rc < 0)
+ pr_err("%s: cannot enable clock, error = %d\n", __func__, rc);
+end:
+ return rc;
+}
+
+static long msm_ispif_cmd(struct v4l2_subdev *sd, void *arg)
+{
+ long rc = 0;
+ struct ispif_cfg_data *pcdata = (struct ispif_cfg_data *)arg;
+ struct ispif_device *ispif =
+ (struct ispif_device *)v4l2_get_subdevdata(sd);
+ mutex_lock(&ispif->mutex);
+ switch (pcdata->cfg_type) {
+ case ISPIF_CLK_ENABLE:
+ rc = msm_ispif_clk_enable(ispif, pcdata->csid_version, 1);
+ break;
+ case ISPIF_CLK_DISABLE:
+ rc = msm_ispif_clk_enable(ispif, pcdata->csid_version, 0);
+ break;
+ case ISPIF_ENABLE_REG_DUMP:
+ ispif->enb_dump_reg = pcdata->reg_dump; /* save dump config */
+ break;
+ case ISPIF_INIT:
+ /* need to move back to CDBG */
+ rc = msm_ispif_init(ispif, pcdata->csid_version);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_CFG:
+ rc = msm_ispif_config(ispif, &pcdata->params);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_START_FRAME_BOUNDARY:
+ rc = msm_ispif_start_frame_boundary(ispif, &pcdata->params);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_STOP_FRAME_BOUNDARY:
+ rc = msm_ispif_stop_frame_boundary(ispif, &pcdata->params);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_STOP_IMMEDIATELY:
+ rc = msm_ispif_stop_immediately(ispif, &pcdata->params);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_RELEASE:
+ msm_ispif_release(ispif);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&ispif->mutex);
+ return rc;
+}
+
+static long msm_ispif_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ switch (cmd) {
+ case VIDIOC_MSM_ISPIF_CFG:
+ return msm_ispif_cmd(sd, arg);
+ default:
+ pr_err("%s: invalid cmd received\n", __func__);
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int ispif_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct ispif_device *ispif = v4l2_get_subdevdata(sd);
+ int rc = 0;
+
+ mutex_lock(&ispif->mutex);
+ if (ispif->open_cnt > 0) {
+ CDBG("%s: dev already open\n", __func__);
+ goto end;
+ }
+ /* mem remap is done in init when the clock is on */
+ ispif->open_cnt++;
+end:
+ mutex_unlock(&ispif->mutex);
+ return rc;
+}
+
+static int ispif_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct ispif_device *ispif = v4l2_get_subdevdata(sd);
+ int rc = 0;
+
+ CDBG("%s\n", __func__);
+ mutex_lock(&ispif->mutex);
+ if (ispif->open_cnt == 0) {
+ pr_err("Invalid close\n");
+ rc = -ENODEV;
+ goto end;
+ }
+ ispif->open_cnt--;
+ if (ispif->open_cnt == 0)
+ msm_ispif_release(ispif);
+end:
+ mutex_unlock(&ispif->mutex);
+ return rc;
+}
+
+static struct v4l2_subdev_core_ops msm_ispif_subdev_core_ops = {
+ .g_chip_ident = &msm_ispif_subdev_g_chip_ident,
+ .ioctl = &msm_ispif_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_ispif_subdev_ops = {
+ .core = &msm_ispif_subdev_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops msm_ispif_internal_ops = {
+ .open = ispif_open_node,
+ .close = ispif_close_node,
+};
+static int __devinit ispif_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct ispif_device *ispif;
+
+ CDBG("%s\n", __func__);
+ ispif = kzalloc(sizeof(struct ispif_device), GFP_KERNEL);
+ if (!ispif) {
+ pr_err("%s: no enough memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ v4l2_subdev_init(&ispif->msm_sd.sd, &msm_ispif_subdev_ops);
+ ispif->msm_sd.sd.internal_ops = &msm_ispif_internal_ops;
+ ispif->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ snprintf(ispif->msm_sd.sd.name,
+ ARRAY_SIZE(ispif->msm_sd.sd.name), "msm_ispif");
+ v4l2_set_subdevdata(&ispif->msm_sd.sd, ispif);
+ platform_set_drvdata(pdev, &ispif->msm_sd.sd);
+ mutex_init(&ispif->mutex);
+ ispif->pdev = pdev;
+ media_entity_init(&ispif->msm_sd.sd.entity, 0, NULL, 0);
+ ispif->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ ispif->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_ISPIF;
+ ispif->msm_sd.sd.entity.name = pdev->name;
+ rc = msm_sd_register(&ispif->msm_sd);
+ if (rc != 0) {
+ pr_err("%s: msm_sd_register error = %d\n", __func__, rc);
+ goto error;
+ }
+ if (pdev->dev.of_node)
+ of_property_read_u32((&pdev->dev)->of_node,
+ "cell-index", &pdev->id);
+
+ ispif->mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "ispif");
+ if (!ispif->mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto error;
+ }
+ ispif->irq = platform_get_resource_byname(pdev,
+ IORESOURCE_IRQ, "ispif");
+ if (!ispif->irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto error;
+ }
+ ispif->io = request_mem_region(ispif->mem->start,
+ resource_size(ispif->mem), pdev->name);
+ if (!ispif->io) {
+ pr_err("%s: no valid mem region\n", __func__);
+ rc = -EBUSY;
+ goto error;
+ }
+ ispif->pdev = pdev;
+ ispif->ispif_state = ISPIF_POWER_DOWN;
+ ispif->open_cnt = 0;
+ return 0;
+
+error:
+ mutex_destroy(&ispif->mutex);
+ kfree(ispif);
+ return rc;
+}
+
+static const struct of_device_id msm_ispif_dt_match[] = {
+ {.compatible = "qcom,ispif"},
+};
+
+MODULE_DEVICE_TABLE(of, msm_ispif_dt_match);
+
+static struct platform_driver ispif_driver = {
+ .probe = ispif_probe,
+ .driver = {
+ .name = MSM_ISPIF_DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_ispif_dt_match,
+ },
+};
+
+static int __init msm_ispif_init_module(void)
+{
+ return platform_driver_register(&ispif_driver);
+}
+
+static void __exit msm_ispif_exit_module(void)
+{
+ platform_driver_unregister(&ispif_driver);
+}
+
+module_init(msm_ispif_init_module);
+module_exit(msm_ispif_exit_module);
+MODULE_DESCRIPTION("MSM ISP Interface driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msmb/ispif/msm_ispif.h b/drivers/media/video/msmb/ispif/msm_ispif.h
new file mode 100644
index 0000000..c4418c1
--- /dev/null
+++ b/drivers/media/video/msmb/ispif/msm_ispif.h
@@ -0,0 +1,59 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_ISPIF_H
+#define MSM_ISPIF_H
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <media/v4l2-subdev.h>
+#include <media/msmb_ispif.h>
+#include "msm_sd.h"
+
+struct ispif_irq_status {
+ uint32_t ispifIrqStatus0;
+ uint32_t ispifIrqStatus1;
+ uint32_t ispifIrqStatus2;
+};
+
+enum msm_ispif_state_t {
+ ISPIF_POWER_UP,
+ ISPIF_POWER_DOWN,
+};
+struct ispif_sof_count {
+ uint32_t sof_cnt[INTF_MAX];
+};
+
+struct ispif_intf_cmd {
+ uint32_t intf_cmd;
+ uint32_t intf_cmd1;
+};
+
+struct ispif_device {
+ struct platform_device *pdev;
+ struct msm_sd_subdev msm_sd;
+ struct resource *mem;
+ struct resource *irq;
+ struct resource *io;
+ void __iomem *base;
+ struct mutex mutex;
+ uint8_t start_ack_pending;
+ struct completion reset_complete;
+ uint32_t csid_version;
+ int enb_dump_reg;
+ uint32_t open_cnt;
+ struct ispif_sof_count sof_count[VFE_MAX];
+ struct ispif_intf_cmd applied_intf_cmd[VFE_MAX];
+ enum msm_ispif_state_t ispif_state;
+ struct clk *ispif_clk[INTF_MAX];
+};
+#endif
diff --git a/drivers/media/video/msmb/ispif/msm_ispif_hwreg.h b/drivers/media/video/msmb/ispif/msm_ispif_hwreg.h
new file mode 100644
index 0000000..16575ae
--- /dev/null
+++ b/drivers/media/video/msmb/ispif/msm_ispif_hwreg.h
@@ -0,0 +1,102 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_ISPIF_HWREG_H
+#define MSM_ISPIF_HWREG_H
+
+
+/* ISPIF registers */
+
+#define ISPIF_RST_CMD_ADDR 0x08
+#define ISPIF_RST_CMD_1_ADDR 0x0C
+#define ISPIF_INTF_CMD_ADDR 0x248
+#define ISPIF_INTF_CMD_1_ADDR 0x24C
+#define ISPIF_CTRL_ADDR 0x08
+#define ISPIF_INPUT_SEL_ADDR 0x244
+#define ISPIF_PIX_0_INTF_CID_MASK_ADDR 0x254
+#define ISPIF_RDI_0_INTF_CID_MASK_ADDR 0x264
+#define ISPIF_PIX_1_INTF_CID_MASK_ADDR 0x258
+#define ISPIF_RDI_1_INTF_CID_MASK_ADDR 0x268
+#define ISPIF_RDI_2_INTF_CID_MASK_ADDR 0x26C
+#define ISPIF_PIX_0_STATUS_ADDR 0x2C0
+#define ISPIF_RDI_0_STATUS_ADDR 0x2D0
+#define ISPIF_PIX_1_STATUS_ADDR 0x2C4
+#define ISPIF_RDI_1_STATUS_ADDR 0x2D4
+#define ISPIF_RDI_2_STATUS_ADDR 0x2D8
+#define ISPIF_IRQ_MASK_ADDR 0x208
+#define ISPIF_IRQ_CLEAR_ADDR 0x230
+#define ISPIF_IRQ_STATUS_ADDR 0x21C
+#define ISPIF_IRQ_MASK_1_ADDR 0x20C
+#define ISPIF_IRQ_CLEAR_1_ADDR 0x234
+#define ISPIF_IRQ_STATUS_1_ADDR 0x220
+#define ISPIF_IRQ_MASK_2_ADDR 0x210
+#define ISPIF_IRQ_CLEAR_2_ADDR 0x238
+#define ISPIF_IRQ_STATUS_2_ADDR 0x224
+#define ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR 0x1C
+
+/* new */
+#define ISPIF_VFE_m_CTRL_0_ADDR 0x200
+#define ISPIF_VFE_m_IRQ_MASK_0 0x208
+#define ISPIF_VFE_m_IRQ_MASK_1 0x20C
+#define ISPIF_VFE_m_IRQ_MASK_2 0x210
+#define ISPIF_VFE_m_IRQ_STATUS_0 0x21C
+#define ISPIF_VFE_m_IRQ_STATUS_1 0x220
+#define ISPIF_VFE_m_IRQ_STATUS_2 0x224
+#define ISPIF_VFE_m_IRQ_CLEAR_0 0x230
+#define ISPIF_VFE_m_IRQ_CLEAR_1 0x234
+#define ISPIF_VFE_m_IRQ_CLEAR_2 0x238
+
+/*ISPIF RESET BITS*/
+
+#define VFE_CLK_DOMAIN_RST 31
+#define RDI_CLK_DOMAIN_RST 26
+#define RDI_1_CLK_DOMAIN_RST 27
+#define RDI_2_CLK_DOMAIN_RST 28
+#define PIX_CLK_DOMAIN_RST 29
+#define PIX_1_CLK_DOMAIN_RST 30
+#define AHB_CLK_DOMAIN_RST 25
+#define RDI_2_VFE_RST_STB 12
+#define RDI_2_CSID_RST_STB 11
+#define RDI_1_VFE_RST_STB 10
+#define RDI_1_CSID_RST_STB 9
+#define RDI_0_VFE_RST_STB 8
+#define RDI_0_CSID_RST_STB 7
+#define PIX_1_VFE_RST_STB 6
+#define PIX_1_CSID_RST_STB 5
+#define PIX_0_VFE_RST_STB 4
+#define PIX_0_CSID_RST_STB 3
+#define SW_REG_RST_STB 2
+#define MISC_LOGIC_RST_STB 1
+#define STROBED_RST_EN 0
+
+#define ISPIF_RST_CMD_MASK 0xFE0F1FFF
+#define ISPIF_RST_CMD_1_MASK 0xFC0F1FF9
+
+#define PIX_INTF_0_OVERFLOW_IRQ 12
+#define RAW_INTF_0_OVERFLOW_IRQ 25
+#define RAW_INTF_1_OVERFLOW_IRQ 25
+#define RAW_INTF_2_OVERFLOW_IRQ 12
+#define RESET_DONE_IRQ 27
+
+#define ISPIF_IRQ_STATUS_MASK 0x0A493249
+#define ISPIF_IRQ_STATUS_1_MASK 0x02493249
+#define ISPIF_IRQ_STATUS_2_MASK 0x00001249
+
+#define ISPIF_IRQ_STATUS_PIX_SOF_MASK 0x249
+#define ISPIF_IRQ_STATUS_RDI0_SOF_MASK 0x492000
+#define ISPIF_IRQ_STATUS_RDI1_SOF_MASK 0x492000
+#define ISPIF_IRQ_STATUS_RDI2_SOF_MASK 0x249
+
+#define ISPIF_IRQ_STATUS_SOF_MASK 0x492249
+#define ISPIF_IRQ_GLOBAL_CLEAR_CMD 0x1
+
+#endif
diff --git a/drivers/media/video/msmb/msm.c b/drivers/media/video/msmb/msm.c
new file mode 100644
index 0000000..c908333
--- /dev/null
+++ b/drivers/media/video/msmb/msm.c
@@ -0,0 +1,1060 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include <linux/atomic.h>
+#include <linux/wait.h>
+#include <linux/videodev2.h>
+#include <linux/msm_ion.h>
+#include <linux/iommu.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-fh.h>
+#include "msm.h"
+#include "msm_vb2.h"
+#include "msm_sd.h"
+
+struct msm_queue_head {
+ struct list_head list;
+ spinlock_t lock;
+ int len;
+ int max;
+};
+
+/** msm_event:
+ *
+ * event sent by imaging server
+ **/
+struct msm_event {
+ struct video_device *vdev;
+ atomic_t on_heap;
+};
+
+struct msm_command {
+ struct list_head list;
+ struct v4l2_event event;
+ atomic_t on_heap;
+};
+
+/** struct msm_command_ack
+ *
+ * Object of command_ack_q, which is
+ * created per open operation
+ *
+ * contains struct msm_command
+ **/
+struct msm_command_ack {
+ struct list_head list;
+ struct msm_queue_head command_q;
+ wait_queue_head_t wait;
+ int stream_id;
+};
+
+struct msm_stream {
+ struct list_head list;
+
+ /* stream index per session, same
+ * as stream_id but set through s_parm */
+ unsigned int stream_id;
+
+ /* vb2 buffer handling */
+ struct vb2_queue *vb2_q;
+};
+
+struct msm_v4l2_subdev {
+ /* FIXME: for session close and error handling such
+ * as daemon shutdown */
+ int close_sequence;
+};
+
+struct msm_session {
+ struct list_head list;
+
+ /* session index */
+ unsigned int session_id;
+
+ /* event queue sent by imaging server */
+ struct msm_event event_q;
+
+ /* ACK by imaging server. Object type of
+ * struct msm_command_ack per open,
+ * assumption is application can send
+ * command on every opened video node */
+ struct msm_queue_head command_ack_q;
+
+ /* real streams(either data or metadate) owned by one
+ * session struct msm_stream */
+ struct msm_queue_head stream_q;
+};
+
+static struct v4l2_device *msm_v4l2_dev;
+
+static struct msm_queue_head *msm_session_q;
+
+/* config node envent queue */
+static struct v4l2_fh *msm_eventq;
+spinlock_t msm_eventq_lock;
+
+static struct pid *msm_pid;
+spinlock_t msm_pid_lock;
+
+#define msm_dequeue(queue, type, member) ({ \
+ unsigned long flags; \
+ struct msm_queue_head *__q = (queue); \
+ type *node = 0; \
+ spin_lock_irqsave(&__q->lock, flags); \
+ if (!list_empty(&__q->list)) { \
+ __q->len--; \
+ node = list_first_entry(&__q->list, \
+ type, member); \
+ if ((node) && (&node->member) && (&node->member.next)) \
+ list_del_init(&node->member); \
+ } \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+ node; \
+})
+
+#define msm_delete_sd_entry(queue, type, member, q_node) ({ \
+ unsigned long flags; \
+ struct msm_queue_head *__q = (queue); \
+ type *node = 0; \
+ spin_lock_irqsave(&__q->lock, flags); \
+ if (!list_empty(&__q->list)) { \
+ list_for_each_entry(node, &__q->list, member) \
+ if (node->sd == q_node) { \
+ __q->len--; \
+ list_del_init(&node->member); \
+ kfree(node); \
+ break; \
+ } \
+ } \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+})
+
+#define msm_delete_entry(queue, type, member, q_node) ({ \
+ unsigned long flags; \
+ struct msm_queue_head *__q = (queue); \
+ type *node = 0; \
+ spin_lock_irqsave(&__q->lock, flags); \
+ if (!list_empty(&__q->list)) { \
+ list_for_each_entry(node, &__q->list, member) \
+ if (node == q_node) { \
+ __q->len--; \
+ list_del_init(&node->member); \
+ kfree(node); \
+ break; \
+ } \
+ } \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+})
+
+#define msm_queue_drain(queue, type, member) do { \
+ unsigned long flags; \
+ struct msm_queue_head *__q = (queue); \
+ type *node; \
+ spin_lock_irqsave(&__q->lock, flags); \
+ while (!list_empty(&__q->list)) { \
+ __q->len--; \
+ node = list_first_entry(&__q->list, \
+ type, member); \
+ if (node) { \
+ if (&node->member) \
+ list_del_init(&node->member); \
+ kfree(node); \
+ } \
+ } \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+} while (0);
+
+typedef int (*msm_queue_func)(void *d1, void *d2);
+#define msm_queue_traverse_action(queue, type, member, func, data) do {\
+ unsigned long flags; \
+ struct msm_queue_head *__q = (queue); \
+ type *node = 0; \
+ msm_queue_func __f = (func); \
+ spin_lock_irqsave(&__q->lock, flags); \
+ if (!list_empty(&__q->list)) { \
+ list_for_each_entry(node, &__q->list, member) \
+ if (node && __f) { \
+ __f(node, data); \
+ } \
+ } \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+} while (0)
+
+typedef int (*msm_queue_find_func)(void *d1, void *d2);
+#define msm_queue_find(queue, type, member, func, data) ({\
+ unsigned long flags; \
+ struct msm_queue_head *__q = (queue); \
+ type *node = 0; \
+ typeof(node) __ret = NULL; \
+ msm_queue_find_func __f = (func); \
+ spin_lock_irqsave(&__q->lock, flags); \
+ if (!list_empty(&__q->list)) { \
+ list_for_each_entry(node, &__q->list, member) \
+ if ((__f) && __f(node, data)) { \
+ __ret = node; \
+ break; \
+ } \
+ } \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+ __ret; \
+})
+
+static void msm_init_queue(struct msm_queue_head *qhead)
+{
+ BUG_ON(!qhead);
+
+ INIT_LIST_HEAD(&qhead->list);
+ spin_lock_init(&qhead->lock);
+ qhead->len = 0;
+ qhead->max = 0;
+}
+
+static void msm_enqueue(struct msm_queue_head *qhead,
+ struct list_head *entry)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&qhead->lock, flags);
+ qhead->len++;
+ if (qhead->len > qhead->max)
+ qhead->max = qhead->len;
+ list_add_tail(entry, &qhead->list);
+ spin_unlock_irqrestore(&qhead->lock, flags);
+}
+
+/* index = session id */
+static inline int __msm_queue_find_session(void *d1, void *d2)
+{
+ struct msm_session *session = d1;
+ return (session->session_id == *(unsigned int *)d2) ? 1 : 0;
+}
+
+static inline int __msm_queue_find_stream(void *d1, void *d2)
+{
+ struct msm_stream *stream = d1;
+ return (stream->stream_id == *(unsigned int *)d2) ? 1 : 0;
+}
+
+static inline int __msm_queue_find_command_ack_q(void *d1, void *d2)
+{
+ struct msm_command_ack *ack = d1;
+ return (ack->stream_id == *(unsigned int *)d2) ? 1 : 0;
+}
+
+int msm_create_stream(unsigned int session_id,
+ unsigned int stream_id, struct vb2_queue *q)
+{
+ struct msm_session *session;
+ struct msm_stream *stream;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (!session)
+ return -EINVAL;
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+
+ stream->stream_id = stream_id;
+ stream->vb2_q = q;
+
+ msm_enqueue(&session->stream_q, &stream->list);
+ session->stream_q.len++;
+
+ return 0;
+}
+
+void msm_delete_stream(unsigned int session_id, unsigned int stream_id)
+{
+ struct msm_session *session = NULL;
+ struct msm_stream *stream = NULL;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (!session)
+ return;
+
+ stream = msm_queue_find(&session->stream_q, struct msm_stream,
+ list, __msm_queue_find_stream, &stream_id);
+ if (!stream)
+ return;
+
+ list_del_init(&stream->list);
+ session->stream_q.len--;
+ kfree(stream);
+}
+
+static void msm_sd_unregister_subdev(struct video_device *vdev)
+{
+ struct v4l2_subdev *sd = video_get_drvdata(vdev);
+ sd->devnode = NULL;
+ kfree(vdev);
+}
+
+static inline int __msm_sd_register_subdev(struct v4l2_subdev *sd)
+{
+ int rc = 0;
+ struct video_device *vdev;
+
+ if (!msm_v4l2_dev || !sd || !sd->name[0])
+ return -EINVAL;
+
+ rc = v4l2_device_register_subdev(msm_v4l2_dev, sd);
+ if (rc < 0)
+ return rc;
+
+ /* Register a device node for every subdev marked with the
+ * V4L2_SUBDEV_FL_HAS_DEVNODE flag.
+ */
+ if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
+ return rc;
+
+ vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
+ if (!vdev) {
+ rc = -ENOMEM;
+ goto clean_up;
+ }
+
+ video_set_drvdata(vdev, sd);
+ strlcpy(vdev->name, sd->name, sizeof(vdev->name));
+ vdev->v4l2_dev = msm_v4l2_dev;
+ vdev->fops = &v4l2_subdev_fops;
+ vdev->release = msm_sd_unregister_subdev;
+ rc = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
+ sd->owner);
+ if (rc < 0) {
+ kfree(vdev);
+ goto clean_up;
+ }
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ sd->entity.info.v4l.major = VIDEO_MAJOR;
+ sd->entity.info.v4l.minor = vdev->minor;
+ sd->entity.name = video_device_node_name(vdev);
+#endif
+ sd->devnode = vdev;
+ return 0;
+
+clean_up:
+ if (sd->devnode)
+ video_unregister_device(sd->devnode);
+ return rc;
+}
+
+int msm_sd_register(struct msm_sd_subdev *msm_subdev)
+{
+ if (WARN_ON(!msm_subdev))
+ return -EINVAL;
+
+ if (WARN_ON(!msm_v4l2_dev) && WARN_ON(!msm_v4l2_dev->dev))
+ return -EIO;
+
+ return __msm_sd_register_subdev(&msm_subdev->sd);
+}
+
+int msm_sd_unregister(struct msm_sd_subdev *msm_subdev)
+{
+ if (WARN_ON(!msm_subdev))
+ return -EINVAL;
+
+ v4l2_device_unregister_subdev(&msm_subdev->sd);
+ return 0;
+}
+
+int msm_create_session(unsigned int session_id, struct video_device *vdev)
+{
+ struct msm_session *session = NULL;
+
+ if (!msm_session_q)
+ return -ENODEV;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (session)
+ return -EINVAL;
+
+ session = kzalloc(sizeof(*session), GFP_KERNEL);
+ if (!session)
+ return -ENOMEM;
+
+ session->session_id = session_id;
+ session->event_q.vdev = vdev;
+ msm_init_queue(&session->command_ack_q);
+ msm_init_queue(&session->stream_q);
+ msm_enqueue(msm_session_q, &session->list);
+ return 0;
+}
+
+int msm_create_command_ack_q(unsigned int session_id, unsigned int stream_id)
+{
+ struct msm_session *session;
+ struct msm_command_ack *cmd_ack;
+
+ if (!msm_session_q)
+ return -ENODEV;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (!session)
+ return -EINVAL;
+
+ cmd_ack = kzalloc(sizeof(*cmd_ack), GFP_KERNEL);
+ if (!cmd_ack)
+ return -ENOMEM;
+
+ msm_init_queue(&cmd_ack->command_q);
+ INIT_LIST_HEAD(&cmd_ack->list);
+ init_waitqueue_head(&cmd_ack->wait);
+ cmd_ack->stream_id = stream_id;
+
+ msm_enqueue(&session->command_ack_q, &cmd_ack->list);
+ session->command_ack_q.len++;
+
+ return 0;
+}
+
+void msm_delete_command_ack_q(unsigned int session_id, unsigned int stream_id)
+{
+ struct msm_session *session;
+ struct msm_command_ack *cmd_ack;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (!session)
+ return;
+
+ cmd_ack = msm_queue_find(&session->command_ack_q,
+ struct msm_command_ack, list, __msm_queue_find_command_ack_q,
+ &stream_id);
+ if (!cmd_ack)
+ return;
+
+ msm_queue_drain(&cmd_ack->command_q, struct msm_command, list);
+}
+
+static inline int __msm_v4l2_subdev_shutdown(struct v4l2_subdev *sd)
+{
+ return 0;
+}
+
+static void msm_sd_try_shutdown(void)
+{
+ unsigned long flags;
+ struct v4l2_subdev *sd;
+
+ /* release all subdev's resource */
+ spin_lock_irqsave(&msm_v4l2_dev->lock, flags);
+ if (!list_empty(&msm_v4l2_dev->subdevs)) {
+ list_for_each_entry(sd, &msm_v4l2_dev->subdevs, list)
+ __msm_v4l2_subdev_shutdown(sd);
+ }
+ spin_unlock_irqrestore(&msm_v4l2_dev->lock, flags);
+}
+
+static inline int __msm_sd_close_session_streams(struct v4l2_subdev *sd,
+ struct msm_sd_close_ioctl *sd_close)
+{
+ v4l2_subdev_call(sd, core, ioctl,
+ MSM_SD_CLOSE_SESSION_AND_STREAM, &sd_close);
+
+ return 0;
+}
+
+static inline int __msm_destroy_session_streams(void *d1, void *d2)
+{
+ struct msm_stream *stream = d1;
+ struct msm_sd_close_ioctl *sd_close = d2;
+ struct v4l2_subdev *sd;
+ unsigned long flags;
+
+ sd_close->stream = stream->stream_id;
+
+ spin_lock_irqsave(&msm_v4l2_dev->lock, flags);
+ if (!list_empty(&msm_v4l2_dev->subdevs))
+ list_for_each_entry(sd, &msm_v4l2_dev->subdevs, list)
+ __msm_sd_close_session_streams(sd, sd_close);
+ spin_unlock_irqrestore(&msm_v4l2_dev->lock, flags);
+
+ return 0;
+}
+
+static void msm_destroy_session_streams(struct msm_session *session)
+{
+ struct msm_sd_close_ioctl sd_close;
+
+ /* to ensure error handling purpose, it needs to detach all subdevs
+ * which are being connected to streams */
+ if (!session)
+ return;
+
+ sd_close.session = session->session_id;
+
+ msm_queue_traverse_action(&session->stream_q, struct msm_stream, list,
+ __msm_destroy_session_streams, &sd_close);
+
+ msm_queue_drain(&session->stream_q, struct msm_stream, list);
+}
+
+static inline int __msm_remove_session_cmd_ack_q(void *d1, void *d2)
+{
+ struct msm_command_ack *cmd_ack = d1;
+
+ msm_queue_drain(&cmd_ack->command_q, struct msm_command, list);
+
+ return 0;
+}
+
+static void msm_remove_session_cmd_ack_q(struct msm_session *session)
+{
+ if (!session)
+ return;
+
+ /* to ensure error handling purpose, it needs to detach all subdevs
+ * which are being connected to streams */
+ msm_queue_traverse_action(&session->command_ack_q,
+ struct msm_command_ack, list,
+ __msm_remove_session_cmd_ack_q, NULL);
+
+ msm_queue_drain(&session->command_ack_q, struct msm_command_ack, list);
+}
+
+int msm_destroy_session(unsigned int session_id)
+{
+ struct msm_session *session;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (!session)
+ return -EINVAL;
+
+ msm_destroy_session_streams(session);
+ msm_remove_session_cmd_ack_q(session);
+
+ msm_delete_entry(msm_session_q, struct msm_session,
+ list, session);
+
+ return 0;
+}
+
+static long msm_private_ioctl(struct file *file, void *fh,
+ bool valid_prio, int cmd, void *arg)
+{
+ int rc = 0;
+ struct msm_v4l2_event_data *event_data;
+ struct msm_session *session;
+ unsigned int session_id;
+ unsigned int stream_id;
+
+ event_data = (struct msm_v4l2_event_data *)
+ ((struct v4l2_event *)arg)->u.data;
+
+ session_id = event_data->session_id;
+ stream_id = event_data->stream_id;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+
+ if (!session)
+ return -EINVAL;
+
+ switch (cmd) {
+ case MSM_CAM_V4L2_IOCTL_NOTIFY: {
+ if (WARN_ON(!session->event_q.vdev)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ v4l2_event_queue(session->event_q.vdev,
+ (struct v4l2_event *)arg);
+ }
+ break;
+
+ case MSM_CAM_V4L2_IOCTL_CMD_ACK: {
+ struct msm_command_ack *cmd_ack;
+ struct msm_command *ret_cmd;
+
+ ret_cmd = kzalloc(sizeof(*ret_cmd), GFP_KERNEL);
+ if (!ret_cmd) {
+ rc = -ENOMEM;
+ break;
+ }
+
+ cmd_ack = msm_queue_find(&session->command_ack_q,
+ struct msm_command_ack, list,
+ __msm_queue_find_command_ack_q,
+ &stream_id);
+ if (WARN_ON(!cmd_ack)) {
+ kfree(ret_cmd);
+ rc = -EFAULT;
+ break;
+ }
+
+ ret_cmd->event = *(struct v4l2_event *)arg;
+ msm_enqueue(&cmd_ack->command_q, &ret_cmd->list);
+ wake_up(&cmd_ack->wait);
+ }
+ break;
+
+ default:
+ rc = -ENOTTY;
+ break;
+ }
+
+ return rc;
+}
+
+static int msm_unsubscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ return v4l2_event_unsubscribe(fh, sub);
+}
+
+static int msm_subscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ return v4l2_event_subscribe(fh, sub, 5);
+}
+
+static const struct v4l2_ioctl_ops g_msm_ioctl_ops = {
+ .vidioc_subscribe_event = msm_subscribe_event,
+ .vidioc_unsubscribe_event = msm_unsubscribe_event,
+ .vidioc_default = msm_private_ioctl,
+};
+
+static unsigned int msm_poll(struct file *f,
+ struct poll_table_struct *pll_table)
+{
+ int rc = 0;
+ struct v4l2_fh *eventq = f->private_data;
+
+ BUG_ON(!eventq);
+
+ poll_wait(f, &eventq->wait, pll_table);
+
+ if (v4l2_event_pending(eventq))
+ rc = POLLIN | POLLRDNORM;
+
+ return rc;
+}
+
+/* something seriously wrong if msm_close is triggered
+ * !!! user space imaging server is shutdown !!!
+ */
+int msm_post_event(struct v4l2_event *event, int timeout)
+{
+ int rc = 0;
+ struct video_device *vdev;
+ struct msm_session *session;
+ struct msm_v4l2_event_data *event_data =
+ (struct msm_v4l2_event_data *)&event->u.data[0];
+ struct msm_command_ack *cmd_ack;
+ struct msm_command *cmd;
+ int session_id, stream_id;
+ unsigned long flags = 0;
+
+ session_id = event_data->session_id;
+ stream_id = event_data->stream_id;
+
+ spin_lock_irqsave(&msm_eventq_lock, flags);
+ if (!msm_eventq) {
+ spin_unlock_irqrestore(&msm_eventq_lock, flags);
+ return -ENODEV;
+ }
+ spin_unlock_irqrestore(&msm_eventq_lock, flags);
+
+ vdev = msm_eventq->vdev;
+
+ /* send to imaging server and wait for ACK */
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (WARN_ON(!session))
+ return -EIO;
+
+ cmd_ack = msm_queue_find(&session->command_ack_q,
+ struct msm_command_ack, list,
+ __msm_queue_find_command_ack_q, &stream_id);
+ if (WARN_ON(!cmd_ack))
+ return -EIO;
+
+ v4l2_event_queue(vdev, event);
+
+ if (timeout < 0)
+ return rc;
+
+ /* should wait on session based condition */
+ rc = wait_event_interruptible_timeout(cmd_ack->wait,
+ !list_empty_careful(&cmd_ack->command_q.list),
+ msecs_to_jiffies(timeout));
+ if (list_empty_careful(&cmd_ack->command_q.list)) {
+ if (!rc)
+ rc = -ETIMEDOUT;
+ if (rc < 0)
+ return rc;
+ }
+
+ cmd = msm_dequeue(&cmd_ack->command_q,
+ struct msm_command, list);
+ if (!cmd)
+ return -EINVAL;
+
+ event_data = (struct msm_v4l2_event_data *)cmd->event.u.data;
+
+ /* compare cmd_ret and event */
+ if (WARN_ON(event->type != cmd->event.type) ||
+ WARN_ON(event->id != cmd->event.id))
+ rc = -EINVAL;
+
+ *event = cmd->event;
+
+ kfree(cmd);
+ return rc;
+}
+
+static int __msm_close_destry_session_notify_apps(void *d1, void *d2)
+{
+ struct v4l2_event event;
+ struct msm_v4l2_event_data *event_data =
+ (struct msm_v4l2_event_data *)&event.u.data[0];
+ struct msm_session *session = d1;
+
+ event.type = MSM_CAMERA_V4L2_EVENT_TYPE;
+ event.id = MSM_CAMERA_MSM_NOTIFY;
+ event_data->command = MSM_CAMERA_PRIV_SHUTDOWN;
+
+ v4l2_event_queue(session->event_q.vdev, &event);
+
+ msm_destroy_session_streams(session);
+ msm_remove_session_cmd_ack_q(session);
+
+ return 0;
+}
+
+static int msm_close(struct file *filep)
+{
+ int rc = 0;
+ unsigned long flags;
+ struct msm_video_device *pvdev = video_drvdata(filep);
+
+ /* 1st thing 1st, send v4l2_event to HAL immediately,
+ * to ensure error handling purpose, it needs to detach all subdevs
+ * which are being connected to streams */
+ msm_queue_traverse_action(msm_session_q, struct msm_session, list,
+ __msm_close_destry_session_notify_apps, NULL);
+
+ msm_queue_drain(msm_session_q, struct msm_session, list);
+
+ spin_lock_irqsave(&msm_eventq_lock, flags);
+ msm_eventq = NULL;
+ spin_unlock_irqrestore(&msm_eventq_lock, flags);
+ v4l2_fh_release(filep);
+
+ msm_sd_try_shutdown();
+
+ spin_lock_irqsave(&msm_pid_lock, flags);
+ put_pid(msm_pid);
+ msm_pid = NULL;
+ spin_unlock_irqrestore(&msm_pid_lock, flags);
+
+ atomic_set(&pvdev->opened, 0);
+
+ return rc;
+}
+
+static inline void msm_list_switch(struct list_head *l1,
+ struct list_head *l2)
+{
+ l1->next = l2->next;
+ l2->prev = l1->prev;
+ l1->prev->next = l2;
+ l2->next->prev = l1;
+ l1->prev = l2;
+ l2->next = l1;
+}
+
+static int msm_open(struct file *filep)
+{
+ int rc;
+ unsigned long flags;
+ struct msm_video_device *pvdev = video_drvdata(filep);
+
+ BUG_ON(!pvdev);
+
+ /* !!! only ONE open is allowed !!! */
+ if (atomic_read(&pvdev->opened))
+ return -EBUSY;
+
+ atomic_set(&pvdev->opened, 1);
+
+ spin_lock_irqsave(&msm_pid_lock, flags);
+ msm_pid = get_pid(task_pid(current));
+ spin_unlock_irqrestore(&msm_pid_lock, flags);
+
+ /* create event queue */
+ rc = v4l2_fh_open(filep);
+ if (rc < 0)
+ return rc;
+
+ spin_lock_irqsave(&msm_eventq_lock, flags);
+ msm_eventq = filep->private_data;
+ spin_unlock_irqrestore(&msm_eventq_lock, flags);
+
+ return rc;
+}
+
+static struct v4l2_file_operations msm_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_open,
+ .poll = msm_poll,
+ .release = msm_close,
+ .ioctl = video_ioctl2,
+};
+
+struct msm_stream *msm_get_stream(unsigned int session_id,
+ unsigned int stream_id)
+{
+ struct msm_session *session;
+ struct msm_stream *stream;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (!session)
+ return ERR_PTR(-EINVAL);
+
+ stream = msm_queue_find(&session->stream_q, struct msm_stream,
+ list, __msm_queue_find_stream, &stream_id);
+
+ if (!stream)
+ return ERR_PTR(-EINVAL);
+
+ return stream;
+}
+
+struct vb2_queue *msm_get_stream_vb2q(unsigned int session_id,
+ unsigned int stream_id)
+{
+ struct msm_session *session;
+ struct msm_stream *stream;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (!session)
+ return NULL;
+
+ stream = msm_queue_find(&session->stream_q, struct msm_stream,
+ list, __msm_queue_find_stream, &stream_id);
+ if (!stream)
+ return NULL;
+
+ return stream->vb2_q;
+}
+
+static struct v4l2_subdev *msm_sd_find(const char *name)
+{
+ unsigned long flags;
+ struct v4l2_subdev *subdev = NULL;
+
+ spin_lock_irqsave(&msm_v4l2_dev->lock, flags);
+ if (!list_empty(&msm_v4l2_dev->subdevs)) {
+ list_for_each_entry(subdev, &msm_v4l2_dev->subdevs, list)
+ if (!strcmp(name, subdev->name))
+ break;
+ }
+ spin_unlock_irqrestore(&msm_v4l2_dev->lock, flags);
+
+ return subdev;
+}
+
+static void msm_sd_notify(struct v4l2_subdev *sd,
+ unsigned int notification, void *arg)
+{
+ int rc = 0;
+ struct v4l2_subdev *subdev = NULL;
+
+ BUG_ON(!sd);
+ BUG_ON(!arg);
+
+ /* Check if subdev exists before processing*/
+ if (!msm_sd_find(sd->name))
+ return;
+
+ switch (notification) {
+ case MSM_SD_NOTIFY_GET_SD: {
+ struct msm_sd_req_sd *get_sd = arg;
+
+ get_sd->subdev = msm_sd_find(get_sd->name);
+ /* TODO: might need to add ref count on ret_sd */
+ }
+ break;
+
+ case MSM_SD_NOTIFY_PUT_SD: {
+ struct msm_sd_req_sd *put_sd = arg;
+ subdev = msm_sd_find(put_sd->name);
+ }
+ break;
+
+ case MSM_SD_NOTIFY_REQ_CB: {
+ struct msm_sd_req_vb2_q *req_sd = arg;
+ rc = msm_vb2_request_cb(req_sd);
+ if (rc < 0)
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int __devinit msm_probe(struct platform_device *pdev)
+{
+ struct msm_video_device *pvdev;
+ int rc = 0;
+
+ msm_v4l2_dev = kzalloc(sizeof(*msm_v4l2_dev),
+ GFP_KERNEL);
+ if (WARN_ON(!msm_v4l2_dev)) {
+ rc = -ENOMEM;
+ goto probe_end;
+ }
+
+ pvdev = kzalloc(sizeof(struct msm_video_device),
+ GFP_KERNEL);
+ if (WARN_ON(!pvdev)) {
+ rc = -ENOMEM;
+ goto pvdev_fail;
+ }
+
+ pvdev->vdev = video_device_alloc();
+ if (WARN_ON(!pvdev->vdev)) {
+ rc = -ENOMEM;
+ goto video_fail;
+ }
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ msm_v4l2_dev->mdev = kzalloc(sizeof(struct media_device),
+ GFP_KERNEL);
+ if (!msm_v4l2_dev->mdev) {
+ rc = -ENOMEM;
+ goto mdev_fail;
+ }
+ strlcpy(msm_v4l2_dev->mdev->model, MSM_CONFIGURATION_NAME,
+ sizeof(msm_v4l2_dev->mdev->model));
+ msm_v4l2_dev->mdev->dev = &(pdev->dev);
+
+ rc = media_device_register(msm_v4l2_dev->mdev);
+ if (WARN_ON(rc < 0))
+ goto media_fail;
+
+ if (WARN_ON((rc == media_entity_init(&pvdev->vdev->entity,
+ 0, NULL, 0)) < 0))
+ goto entity_fail;
+
+ pvdev->vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
+ pvdev->vdev->entity.group_id = QCAMERA_VNODE_GROUP_ID;
+#endif
+
+ msm_v4l2_dev->notify = msm_sd_notify;
+
+ pvdev->vdev->v4l2_dev = msm_v4l2_dev;
+
+ rc = v4l2_device_register(&(pdev->dev), pvdev->vdev->v4l2_dev);
+ if (WARN_ON(rc < 0))
+ goto register_fail;
+
+ strlcpy(pvdev->vdev->name, "msm-config", sizeof(pvdev->vdev->name));
+ pvdev->vdev->release = video_device_release;
+ pvdev->vdev->fops = &msm_fops;
+ pvdev->vdev->ioctl_ops = &g_msm_ioctl_ops;
+ pvdev->vdev->minor = -1;
+ pvdev->vdev->vfl_type = VFL_TYPE_GRABBER;
+ rc = video_register_device(pvdev->vdev,
+ VFL_TYPE_GRABBER, -1);
+ if (WARN_ON(rc < 0))
+ goto v4l2_fail;
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ /* FIXME: How to get rid of this messy? */
+ pvdev->vdev->entity.name = video_device_node_name(pvdev->vdev);
+#endif
+
+ atomic_set(&pvdev->opened, 0);
+ video_set_drvdata(pvdev->vdev, pvdev);
+
+ msm_session_q = kzalloc(sizeof(*msm_session_q), GFP_KERNEL);
+ if (WARN_ON(!msm_session_q))
+ goto v4l2_fail;
+
+ msm_init_queue(msm_session_q);
+ spin_lock_init(&msm_eventq_lock);
+ spin_lock_init(&msm_pid_lock);
+
+ goto probe_end;
+
+v4l2_fail:
+ v4l2_device_unregister(pvdev->vdev->v4l2_dev);
+register_fail:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ media_entity_cleanup(&pvdev->vdev->entity);
+entity_fail:
+ media_device_unregister(msm_v4l2_dev->mdev);
+media_fail:
+ kfree(msm_v4l2_dev->mdev);
+mdev_fail:
+#endif
+ video_device_release(pvdev->vdev);
+video_fail:
+ kfree(pvdev);
+pvdev_fail:
+ kfree(msm_v4l2_dev);
+probe_end:
+ return rc;
+}
+
+static const struct of_device_id msm_dt_match[] = {
+ {.compatible = "qcom,msm-cam"},
+}
+
+MODULE_DEVICE_TABLE(of, msm_dt_match);
+
+static struct platform_driver msm_driver = {
+ .probe = msm_probe,
+ .driver = {
+ .name = "msm",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dt_match,
+ },
+};
+
+static int __init msm_init(void)
+{
+ return platform_driver_register(&msm_driver);
+}
+
+static void __exit msm_exit(void)
+{
+ platform_driver_unregister(&msm_driver);
+}
+
+
+module_init(msm_init);
+module_exit(msm_exit);
+MODULE_DESCRIPTION("MSM V4L2 Camera");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msmb/msm.h b/drivers/media/video/msmb/msm.h
new file mode 100644
index 0000000..eb15cab
--- /dev/null
+++ b/drivers/media/video/msmb/msm.h
@@ -0,0 +1,55 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_H
+#define _MSM_H
+
+#include <linux/version.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/pm_qos.h>
+#include <linux/wakelock.h>
+#include <linux/msm_ion.h>
+#include <linux/iommu.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-mediabus.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-msm-mem.h>
+#include <media/msmb_camera.h>
+
+#define MSM_POST_EVT_TIMEOUT 5000
+#define MSM_POST_EVT_NOTIMEOUT 0xFFFFFFFF
+
+struct msm_video_device {
+ struct video_device *vdev;
+ atomic_t opened;
+};
+
+int msm_post_event(struct v4l2_event *event, int timeout);
+int msm_create_session(unsigned int session, struct video_device *vdev);
+int msm_destroy_session(unsigned int session_id);
+
+int msm_create_stream(unsigned int session_id,
+ unsigned int stream_id, struct vb2_queue *q);
+void msm_delete_stream(unsigned int session_id, unsigned int stream_id);
+int msm_create_command_ack_q(unsigned int session_id, unsigned int stream_id);
+void msm_delete_command_ack_q(unsigned int session_id, unsigned int stream_id);
+struct msm_stream *msm_get_stream(unsigned int session_id,
+ unsigned int stream_id);
+struct vb2_queue *msm_get_stream_vb2q(unsigned int session_id,
+ unsigned int stream_id);
+
+#endif /*_MSM_H */
diff --git a/drivers/media/video/msmb/msm_sd.h b/drivers/media/video/msmb/msm_sd.h
new file mode 100644
index 0000000..aaf3548
--- /dev/null
+++ b/drivers/media/video/msmb/msm_sd.h
@@ -0,0 +1,82 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_SD_H
+#define _MSM_SD_H
+
+#include <media/v4l2-subdev.h>
+#include <media/msmb_camera.h>
+
+/* NOTE: this header file should ONLY be included by subdev drivers */
+
+struct msm_sd_close_ioctl {
+ unsigned int session;
+ unsigned int stream;
+};
+
+#define MSM_SD_CLOSE_STREAM \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 26, struct msm_sd_close_ioctl)
+
+#define MSM_SD_CLOSE_SESSION \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 27, struct msm_sd_close_ioctl)
+
+#define MSM_SD_CLOSE_SESSION_AND_STREAM \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 28, struct msm_sd_close_ioctl)
+
+#define MSM_SD_SHUTDOWN \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 29, struct msm_sd_close_ioctl)
+
+/*
+ * This is used to install Sequence in msm_sd_register.
+ * During msm_close, proper close sequence will be triggered.
+ * For example:
+ *
+ * close_sequence = 0x00100001 (ISP)
+ * close_sequence = 0x00100002 (ISP)
+ * close_sequence = 0x00100003 (ISP)
+ * close_sequence = 0x00200001 (sensor)
+ * close_sequence = 0x00200002 (sensor)
+ * close_sequence = 0x00200003 (sensor)
+ */
+#define MSM_SD_CLOSE_1ST_CATEGORY 0x00010000
+#define MSM_SD_CLOSE_2ND_CATEGORY 0x00020000
+#define MSM_SD_CLOSE_3RD_CATEGORY 0x00030000
+
+struct msm_sd_subdev {
+ struct v4l2_subdev sd;
+ int close_seq;
+};
+
+struct msm_sd_req_sd {
+ char *name;
+ struct v4l2_subdev *subdev;
+};
+
+struct msm_sd_req_vb2_q {
+ struct vb2_buffer *(*get_buf)(int session_id, unsigned int stream_id);
+ struct vb2_queue *(*get_vb2_queue)(int session_id,
+ unsigned int stream_id);
+ int (*put_buf)(struct vb2_buffer *vb2_buf);
+ int (*buf_done)(struct vb2_buffer *vb2_buf);
+};
+
+#define MSM_SD_NOTIFY_GET_SD 0x00000001
+#define MSM_SD_NOTIFY_PUT_SD 0x00000002
+#define MSM_SD_NOTIFY_REQ_CB 0x00000003
+
+int msm_sd_register(struct msm_sd_subdev *msm_subdev);
+int msm_sd_unregister(struct msm_sd_subdev *sd);
+struct v4l2_subdev *msm_sd_get_subdev(struct v4l2_subdev *sd,
+ const char *get_name);
+void msm_sd_put_subdev(struct v4l2_subdev *sd, struct v4l2_subdev *put);
+
+#endif /*_MSM_SD_H */
diff --git a/drivers/media/video/msmb/msm_vb2/Makefile b/drivers/media/video/msmb/msm_vb2/Makefile
new file mode 100644
index 0000000..9f61289
--- /dev/null
+++ b/drivers/media/video/msmb/msm_vb2/Makefile
@@ -0,0 +1,3 @@
+ccflags-y += -Idrivers/media/video/msmb
+ccflags-y += -Idrivers/media/video/msmb/msm_vb2
+obj-$(CONFIG_MSMB_CAMERA) += msm_vb2.o
diff --git a/drivers/media/video/msmb/msm_vb2/msm_vb2.c b/drivers/media/video/msmb/msm_vb2/msm_vb2.c
new file mode 100644
index 0000000..8a572a6
--- /dev/null
+++ b/drivers/media/video/msmb/msm_vb2/msm_vb2.c
@@ -0,0 +1,197 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "msm_vb2.h"
+
+static spinlock_t vb2_buf_lock;
+
+static int msm_vb2_queue_setup(struct vb2_queue *q,
+ const struct v4l2_format *fmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ int i;
+ struct msm_v4l2_format_data *data = q->drv_priv;
+
+ if (data->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (WARN_ON(data->num_planes > VIDEO_MAX_PLANES))
+ return -EINVAL;
+
+ *num_planes = data->num_planes;
+
+ for (i = 0; i < data->num_planes; i++)
+ sizes[i] = data->plane_sizes[i];
+ } else {
+ pr_err("%s: Unsupported buf type :%d\n", __func__,
+ data->type);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int msm_vb2_buf_init(struct vb2_buffer *vb)
+{
+ struct msm_vb2_buffer *msm_vb2_buf;
+
+ msm_vb2_buf = container_of(vb, struct msm_vb2_buffer, vb2_buf);
+ msm_vb2_buf->in_freeq = 0;
+ spin_lock_init(&vb2_buf_lock);
+
+ return 0;
+}
+
+static void msm_vb2_buf_queue(struct vb2_buffer *vb)
+{
+}
+
+static struct vb2_ops msm_vb2_get_q_op = {
+ .queue_setup = msm_vb2_queue_setup,
+ .buf_init = msm_vb2_buf_init,
+ .buf_queue = msm_vb2_buf_queue,
+};
+
+
+struct vb2_ops *msm_vb2_get_q_ops(void)
+{
+ return &msm_vb2_get_q_op;
+}
+
+static void *msm_vb2_dma_contig_get_userptr(void *alloc_ctx,
+ unsigned long vaddr, unsigned long size, int write)
+{
+ struct msm_vb2_private_data *priv;
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return ERR_PTR(-ENOMEM);
+ priv->vaddr = (void *)vaddr;
+ priv->size = size;
+ priv->alloc_ctx = alloc_ctx;
+ return priv;
+}
+
+static void msm_vb2_dma_contig_put_userptr(void *buf_priv)
+{
+ kfree(buf_priv);
+}
+
+static struct vb2_mem_ops msm_vb2_get_q_mem_op = {
+ .get_userptr = msm_vb2_dma_contig_get_userptr,
+ .put_userptr = msm_vb2_dma_contig_put_userptr,
+};
+
+struct vb2_mem_ops *msm_vb2_get_q_mem_ops(void)
+{
+ return &msm_vb2_get_q_mem_op;
+}
+
+static struct vb2_queue *msm_vb2_get_queue(int session_id,
+ unsigned int stream_id)
+{
+ return msm_get_stream_vb2q(session_id, stream_id);
+}
+
+static struct vb2_buffer *msm_vb2_get_buf(int session_id,
+ unsigned int stream_id)
+{
+ struct vb2_queue *q;
+ struct vb2_buffer *vb2_buf = NULL;
+ struct msm_vb2_buffer *msm_vb2;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vb2_buf_lock, flags);
+
+ q = msm_get_stream_vb2q(session_id, stream_id);
+
+ /*FIXME: need a check if stream on issue*/
+ if (!q) {
+ pr_err("%s: stream q not available\n", __func__);
+ goto end;
+ }
+
+ list_for_each_entry(vb2_buf, &(q->queued_list),
+ queued_entry) {
+ if (vb2_buf->state != VB2_BUF_STATE_ACTIVE)
+ continue;
+
+ msm_vb2 = container_of(vb2_buf, struct msm_vb2_buffer, vb2_buf);
+ if (msm_vb2->in_freeq)
+ continue;
+
+ msm_vb2->in_freeq = 1;
+ goto end;
+ }
+ vb2_buf = NULL;
+end:
+ spin_unlock_irqrestore(&vb2_buf_lock, flags);
+ return vb2_buf;
+}
+
+static int msm_vb2_put_buf(struct vb2_buffer *vb)
+{
+ struct msm_vb2_buffer *msm_vb2;
+ int rc = 0;
+
+ if (vb) {
+ msm_vb2 =
+ container_of(vb, struct msm_vb2_buffer, vb2_buf);
+ if (msm_vb2->in_freeq) {
+ msm_vb2->in_freeq = 0;
+ rc = 0;
+ } else
+ rc = -EINVAL;
+ } else {
+ pr_err("%s: VB buffer is null\n", __func__);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static int msm_vb2_buf_done(struct vb2_buffer *vb)
+{
+ unsigned long flags;
+ struct msm_vb2_buffer *msm_vb2;
+ int rc = 0;
+
+ spin_lock_irqsave(&vb2_buf_lock, flags);
+ if (vb) {
+ msm_vb2 =
+ container_of(vb, struct msm_vb2_buffer, vb2_buf);
+ /* put buf before buf done */
+ if (msm_vb2->in_freeq) {
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ rc = 0;
+ } else
+ rc = -EINVAL;
+ } else {
+ pr_err("%s: VB buffer is null\n", __func__);
+ rc = -EINVAL;
+ }
+
+ spin_unlock_irqrestore(&vb2_buf_lock, flags);
+ return rc;
+}
+
+int msm_vb2_request_cb(struct msm_sd_req_vb2_q *req)
+{
+ if (!req) {
+ pr_err("%s: suddev is null\n", __func__);
+ return -EINVAL;
+ }
+
+ req->get_buf = msm_vb2_get_buf;
+ req->get_vb2_queue = msm_vb2_get_queue;
+ req->put_buf = msm_vb2_put_buf;
+ req->buf_done = msm_vb2_buf_done;
+
+ return 0;
+}
+
diff --git a/drivers/media/video/msmb/msm_vb2/msm_vb2.h b/drivers/media/video/msmb/msm_vb2/msm_vb2.h
new file mode 100644
index 0000000..148d577
--- /dev/null
+++ b/drivers/media/video/msmb/msm_vb2/msm_vb2.h
@@ -0,0 +1,58 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_VB_H
+#define _MSM_VB_H
+
+#include <linux/version.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/pm_qos.h>
+#include <linux/wakelock.h>
+#include <linux/msm_ion.h>
+#include <linux/iommu.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-mediabus.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-msm-mem.h>
+#include <media/msmb_camera.h>
+#include <media/videobuf2-core.h>
+#include "msm.h"
+#include "msm_sd.h"
+
+struct msm_vb2_buffer {
+ /*
+ * vb2 buffer has to be first in the structure
+ * because both v4l2 frameworks and driver directly
+ * cast msm_vb2_buffer to a vb2_buf.
+ */
+ struct vb2_buffer vb2_buf;
+ struct list_head list;
+ int in_freeq;
+};
+
+struct msm_vb2_private_data {
+ void *vaddr;
+ unsigned long size;
+ /* Offset of the plane inside the buffer */
+ void *alloc_ctx;
+};
+
+struct vb2_ops *msm_vb2_get_q_ops(void);
+struct vb2_mem_ops *msm_vb2_get_q_mem_ops(void);
+int msm_vb2_request_cb(struct msm_sd_req_vb2_q *req_sd);
+
+#endif /*_MSM_VB_H */
diff --git a/drivers/media/video/msmb/sensor/Makefile b/drivers/media/video/msmb/sensor/Makefile
new file mode 100644
index 0000000..5fa3f6e
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/Makefile
@@ -0,0 +1,8 @@
+ccflags-y += -Idrivers/media/video/msmb
+ccflags-y += -Idrivers/media/video/msmb/msm_vb2
+ccflags-y += -Idrivers/media/video/msmb/camera
+ccflags-y += -Idrivers/media/video/msmb/sensor/io
+ccflags-y += -Idrivers/media/video/msmb/sensor/cci
+obj-$(CONFIG_MSMB_CAMERA) += cci/ io/ csiphy/ csid/
+obj-$(CONFIG_MSM_CAMERA_SENSOR) += msm_sensor.o
+obj-$(CONFIG_S5K3L1YX) += s5k3l1yx.o
diff --git a/drivers/media/video/msmb/sensor/cci/Makefile b/drivers/media/video/msmb/sensor/cci/Makefile
new file mode 100644
index 0000000..8eef3fc
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/cci/Makefile
@@ -0,0 +1,3 @@
+ccflags-y += -Idrivers/media/video/msmb/
+ccflags-y += -Idrivers/media/video/msmb/sensor/io
+obj-$(CONFIG_MSM_CCI) += msm_cci.o
diff --git a/drivers/media/video/msmb/sensor/cci/msm_cam_cci_hwreg.h b/drivers/media/video/msmb/sensor/cci/msm_cam_cci_hwreg.h
new file mode 100644
index 0000000..19cff3b
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/cci/msm_cam_cci_hwreg.h
@@ -0,0 +1,61 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_CAM_CCI_HWREG__
+#define __MSM_CAM_CCI_HWREG__
+
+#define CCI_HW_VERSION_ADDR 0x00000000
+#define CCI_RESET_CMD_ADDR 0x00000004
+#define CCI_RESET_CMD_RMSK 0xcf73f3f7
+#define CCI_M0_RESET_RMSK 0x3F1
+#define CCI_M1_RESET_RMSK 0x3F001
+#define CCI_QUEUE_START_ADDR 0x00000008
+#define CCI_SET_CID_SYNC_TIMER_0_ADDR 0x00000010
+#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 0x01
+#define CCI_HALT_REQ_ADDR 0x00000034
+#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_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_Q1_NACK_ERR_BMSK 0x40000000
+#define CCI_IRQ_STATUS_0_I2C_M1_Q0_NACK_ERR_BMSK 0x20000000
+#define CCI_IRQ_STATUS_0_I2C_M0_Q1_NACK_ERR_BMSK 0x10000000
+#define CCI_IRQ_STATUS_0_I2C_M0_Q0_NACK_ERR_BMSK 0x8000000
+#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_RD_DONE_BMSK 0x1
+#define CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR 0x00000c00
+#endif /* __MSM_CAM_CCI_HWREG__ */
diff --git a/drivers/media/video/msmb/sensor/cci/msm_cci.c b/drivers/media/video/msmb/sensor/cci/msm_cci.c
new file mode 100644
index 0000000..b1c9a40
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/cci/msm_cci.c
@@ -0,0 +1,969 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <media/msm_isp.h>
+#include "msm_sd.h"
+#include "msm_cci.h"
+#include "msm_cam_cci_hwreg.h"
+#include "msm_camera_io_util.h"
+
+#define V4L2_IDENT_CCI 50005
+#define CCI_I2C_QUEUE_0_SIZE 64
+#define CCI_I2C_QUEUE_1_SIZE 16
+
+#define CCI_TIMEOUT msecs_to_jiffies(100)
+
+/* TODO move this somewhere else */
+#define MSM_CCI_DRV_NAME "msm_cci"
+
+#define CONFIG_MSMB_CAMERA_DEBUG
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+static struct v4l2_subdev *g_cci_subdev;
+
+static void msm_cci_set_clk_param(struct cci_device *cci_dev)
+{
+ struct msm_cci_clk_params_t *clk_params = &cci_dev->cci_clk_params;
+
+ msm_camera_io_w(clk_params->hw_thigh << 16 | clk_params->hw_tlow,
+ cci_dev->base + CCI_I2C_M0_SCL_CTL_ADDR);
+ msm_camera_io_w(clk_params->hw_tsu_sto << 16 | clk_params->hw_tsu_sta,
+ cci_dev->base + CCI_I2C_M0_SDA_CTL_0_ADDR);
+ msm_camera_io_w(clk_params->hw_thd_dat << 16 | clk_params->hw_thd_sta,
+ cci_dev->base + CCI_I2C_M0_SDA_CTL_1_ADDR);
+ msm_camera_io_w(clk_params->hw_tbuf,
+ cci_dev->base + CCI_I2C_M0_SDA_CTL_2_ADDR);
+ msm_camera_io_w(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);
+ msm_camera_io_w(clk_params->hw_thigh << 16 | clk_params->hw_tlow,
+ cci_dev->base + CCI_I2C_M1_SCL_CTL_ADDR);
+ msm_camera_io_w(clk_params->hw_tsu_sto << 16 | clk_params->hw_tsu_sta,
+ cci_dev->base + CCI_I2C_M1_SDA_CTL_0_ADDR);
+ msm_camera_io_w(clk_params->hw_thd_dat << 16 | clk_params->hw_thd_sta,
+ cci_dev->base + CCI_I2C_M1_SDA_CTL_1_ADDR);
+ msm_camera_io_w(clk_params->hw_tbuf,
+ cci_dev->base + CCI_I2C_M1_SDA_CTL_2_ADDR);
+ msm_camera_io_w(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);
+ return;
+}
+
+static int32_t msm_cci_i2c_config_sync_timer(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ struct cci_device *cci_dev;
+ cci_dev = v4l2_get_subdevdata(sd);
+ msm_camera_io_w(c_ctrl->cci_info->cid, cci_dev->base +
+ CCI_SET_CID_SYNC_TIMER_0_ADDR + (c_ctrl->cci_info->cid * 0x4));
+ return 0;
+}
+
+static int32_t msm_cci_i2c_set_freq(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ struct cci_device *cci_dev;
+ uint32_t val;
+ cci_dev = v4l2_get_subdevdata(sd);
+ val = c_ctrl->cci_info->freq;
+ msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_SCL_CTL_ADDR +
+ c_ctrl->cci_info->cci_i2c_master*0x100);
+ msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_SDA_CTL_0_ADDR +
+ c_ctrl->cci_info->cci_i2c_master*0x100);
+ msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_SDA_CTL_1_ADDR +
+ c_ctrl->cci_info->cci_i2c_master*0x100);
+ msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_SDA_CTL_2_ADDR +
+ c_ctrl->cci_info->cci_i2c_master*0x100);
+ msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_MISC_CTL_ADDR +
+ c_ctrl->cci_info->cci_i2c_master*0x100);
+ return 0;
+}
+
+static int32_t msm_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 = msm_camera_io_r(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__);
+ msm_camera_io_w(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);
+ msm_camera_io_w(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__);
+ msm_camera_io_w(reg_val, cci_dev->base + CCI_QUEUE_START_ADDR);
+ CDBG("%s line %d wait_for_completion_interruptible\n",
+ __func__, __LINE__);
+ rc = wait_for_completion_interruptible_timeout(&cci_dev->
+ cci_master_info[master].reset_complete, CCI_TIMEOUT);
+ if (rc <= 0) {
+ pr_err("%s: wait_for_completion_interruptible_timeout %d\n",
+ __func__, __LINE__);
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ 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 msm_cci_data_queue(struct cci_device *cci_dev,
+ struct msm_camera_cci_ctrl *c_ctrl, enum cci_i2c_queue_t queue)
+{
+ uint16_t i = 0, j = 0, k = 0, h = 0, len = 0;
+ int32_t rc = 0;
+ uint32_t cmd = 0;
+ uint8_t data[10];
+ uint16_t reg_addr = 0;
+ struct msm_camera_cci_i2c_write_cfg *i2c_msg =
+ &c_ctrl->cfg.cci_i2c_write_cfg;
+ uint16_t cmd_size = i2c_msg->size;
+ struct msm_camera_i2c_reg_conf *i2c_cmd = i2c_msg->reg_conf_tbl;
+ enum cci_i2c_master_t master = c_ctrl->cci_info->cci_i2c_master;
+ CDBG("%s addr type %d data type %d\n", __func__,
+ i2c_msg->addr_type, i2c_msg->data_type);
+ /* assume total size within the max queue */
+ while (cmd_size) {
+ CDBG("%s cmd_size %d addr 0x%x data 0x%x", __func__,
+ cmd_size, i2c_cmd->reg_addr, i2c_cmd->reg_data);
+ data[i++] = CCI_I2C_WRITE_CMD;
+ if (i2c_cmd->reg_addr)
+ reg_addr = i2c_cmd->reg_addr;
+ /* either byte or word addr */
+ if (i2c_msg->addr_type == MSM_CAMERA_I2C_BYTE_ADDR)
+ 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 == MSM_CAMERA_I2C_BYTE_DATA) {
+ data[i++] = i2c_cmd->reg_data;
+ reg_addr++;
+ } else {
+ if ((i + 1) <= 10) {
+ data[i++] = (i2c_cmd->reg_data &
+ 0xFF00) >> 8; /* MSB */
+ data[i++] = i2c_cmd->reg_data &
+ 0x00FF; /* LSB */
+ reg_addr += 2;
+ } else
+ break;
+ }
+ i2c_cmd++;
+ } while (--cmd_size && !i2c_cmd->reg_addr && (i <= 10));
+ data[0] |= ((i-1) << 4);
+ len = ((i-1)/4) + 1;
+ rc = msm_cci_validate_queue(cci_dev, len, master, queue);
+ if (rc < 0) {
+ pr_err("%s: failed %d", __func__, __LINE__);
+ return rc;
+ }
+ 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 CCI_I2C_M0_Q0_LOAD_DATA_ADDR 0x%x\n",
+ __func__, cmd);
+ msm_camera_io_w(cmd, cci_dev->base +
+ CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ master * 0x200 + queue * 0x100);
+ }
+ i = 0;
+ }
+ return rc;
+}
+
+static int32_t msm_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;
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ rc = msm_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 %x:%x\n",
+ __func__, CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset, val);
+ msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset);
+ return rc;
+}
+
+static int32_t msm_cci_i2c_read(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ uint32_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 msm_camera_cci_i2c_read_cfg *read_cfg = NULL;
+ CDBG("%s line %d\n", __func__, __LINE__);
+ cci_dev = v4l2_get_subdevdata(sd);
+ master = c_ctrl->cci_info->cci_i2c_master;
+ read_cfg = &c_ctrl->cfg.cci_i2c_read_cfg;
+ mutex_lock(&cci_dev->cci_master_info[master].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 = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ val = CCI_I2C_LOCK_CMD;
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ if (read_cfg->addr_type == MSM_CAMERA_I2C_BYTE_ADDR)
+ val = CCI_I2C_WRITE_CMD | (read_cfg->addr_type << 4) |
+ ((read_cfg->addr & 0xFF) << 8);
+ if (read_cfg->addr_type == MSM_CAMERA_I2C_WORD_ADDR)
+ val = CCI_I2C_WRITE_CMD | (read_cfg->addr_type << 4) |
+ (((read_cfg->addr & 0xFF00) >> 8) << 8) |
+ ((read_cfg->addr & 0xFF) << 16);
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ val = CCI_I2C_READ_CMD | (read_cfg->num_byte << 4);
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ val = CCI_I2C_UNLOCK_CMD;
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ val = msm_camera_io_r(cci_dev->base + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR +
+ master * 0x200 + queue * 0x100);
+ CDBG("%s cur word cnt %x\n", __func__, val);
+ msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR +
+ master * 0x200 + queue * 0x100);
+
+ val = 1 << ((master * 2) + queue);
+ msm_camera_io_w(val, cci_dev->base + CCI_QUEUE_START_ADDR);
+ CDBG("%s:%d E wait_for_completion_interruptible_timeout\n", __func__,
+ __LINE__);
+ rc = wait_for_completion_interruptible_timeout(&cci_dev->
+ cci_master_info[master].reset_complete, CCI_TIMEOUT);
+ if (rc <= 0) {
+ pr_err("%s: wait_for_completion_interruptible_timeout %d\n",
+ __func__, __LINE__);
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ return rc;
+ } else {
+ rc = 0;
+ }
+ CDBG("%s:%d E wait_for_completion_interruptible_timeout\n", __func__,
+ __LINE__);
+
+ read_words = msm_camera_io_r(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 ERROR;
+ }
+ index = 0;
+ CDBG("%s index %d num_type %d\n", __func__, index,
+ read_cfg->num_byte);
+ first_byte = 0;
+ do {
+ val = msm_camera_io_r(cci_dev->base +
+ CCI_I2C_M0_READ_DATA_ADDR + master * 0x100);
+ CDBG("%s read val %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 %x\n", __func__, val & 0xFF);
+ first_byte++;
+ } else {
+ read_cfg->data[index] =
+ (val >> (i * 8)) & 0xFF;
+ CDBG("%s data[%d] %x\n", __func__, index,
+ read_cfg->data[index]);
+ index++;
+ }
+ }
+ } while (--read_words > 0);
+ERROR:
+ mutex_unlock(&cci_dev->cci_master_info[master].mutex);
+ return rc;
+}
+
+static int32_t msm_cci_i2c_write(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ int32_t rc = 0;
+ struct cci_device *cci_dev;
+ uint32_t val;
+ enum cci_i2c_master_t master;
+ enum cci_i2c_queue_t queue = QUEUE_0;
+ cci_dev = v4l2_get_subdevdata(sd);
+ master = c_ctrl->cci_info->cci_i2c_master;
+ 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);
+ mutex_lock(&cci_dev->cci_master_info[master].mutex);
+ 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:%d CCI_I2C_SET_PARAM_CMD\n", __func__, __LINE__);
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ val = CCI_I2C_LOCK_CMD;
+ CDBG("%s:%d CCI_I2C_LOCK_CMD\n", __func__, __LINE__);
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ msm_cci_data_queue(cci_dev, c_ctrl, queue);
+ val = CCI_I2C_UNLOCK_CMD;
+ CDBG("%s:%d CCI_I2C_UNLOCK_CMD\n", __func__, __LINE__);
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ val = CCI_I2C_REPORT_CMD | (1 << 8);
+ CDBG("%s:%d CCI_I2C_REPORT_CMD\n", __func__, __LINE__);
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ val = msm_camera_io_r(cci_dev->base + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR +
+ master * 0x200 + queue * 0x100);
+ CDBG("%s:%d cur word count %d\n", __func__, __LINE__, val);
+ CDBG("%s:%d CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR\n", __func__, __LINE__);
+ msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR +
+ master * 0x200 + queue * 0x100);
+
+ val = 1 << ((master * 2) + queue);
+ CDBG("%s:%d CCI_QUEUE_START_ADDR\n", __func__, __LINE__);
+ msm_camera_io_w(val, cci_dev->base + CCI_QUEUE_START_ADDR +
+ master*0x200 + queue * 0x100);
+
+ CDBG("%s:%d E wait_for_completion_interruptible\n",
+ __func__, __LINE__);
+ rc = wait_for_completion_interruptible_timeout(&cci_dev->
+ cci_master_info[master].reset_complete, CCI_TIMEOUT);
+ if (rc <= 0) {
+ pr_err("%s: wait_for_completion_interruptible_timeout %d\n",
+ __func__, __LINE__);
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ return rc;
+ } else {
+ rc = 0;
+ }
+ CDBG("%s:%d X wait_for_completion_interruptible\n", __func__,
+ __LINE__);
+
+ERROR:
+ mutex_unlock(&cci_dev->cci_master_info[master].mutex);
+ return rc;
+}
+
+static int msm_cci_subdev_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ BUG_ON(!chip);
+ chip->ident = V4L2_IDENT_CCI;
+ chip->revision = 0;
+ return 0;
+}
+
+static struct msm_cam_clk_info cci_clk_info[] = {
+ {"camss_top_ahb_clk", -1},
+ {"cci_src_clk", 19200000},
+ {"cci_ahb_clk", -1},
+ {"cci_clk", -1},
+};
+
+static int32_t msm_cci_init(struct v4l2_subdev *sd)
+{
+ int rc = 0;
+ struct cci_device *cci_dev;
+ cci_dev = v4l2_get_subdevdata(sd);
+ CDBG("%s line %d\n", __func__, __LINE__);
+
+ if (!cci_dev) {
+ pr_err("%s cci device NULL\n", __func__);
+ rc = -ENOMEM;
+ return rc;
+ }
+
+ if (cci_dev->ref_count++) {
+ CDBG("%s ref_count %d\n", __func__, cci_dev->ref_count);
+ return 0;
+ }
+
+ rc = msm_camera_request_gpio_table(cci_dev->cci_gpio_tbl,
+ cci_dev->cci_gpio_tbl_size, 1);
+ if (rc < 0) {
+ cci_dev->ref_count--;
+ CDBG("%s: request gpio failed\n", __func__);
+ goto clk_enable_failed;
+ }
+
+ rc = msm_cam_clk_enable(&cci_dev->pdev->dev, cci_clk_info,
+ cci_dev->cci_clk, ARRAY_SIZE(cci_clk_info), 1);
+ if (rc < 0) {
+ cci_dev->ref_count--;
+ CDBG("%s: clk enable failed\n", __func__);
+ goto clk_enable_failed;
+ }
+
+ enable_irq(cci_dev->irq->start);
+ cci_dev->hw_version = msm_camera_io_r(cci_dev->base +
+ CCI_HW_VERSION_ADDR);
+ cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE;
+ msm_camera_io_w(CCI_RESET_CMD_RMSK, cci_dev->base + CCI_RESET_CMD_ADDR);
+ msm_camera_io_w(0x1, cci_dev->base + CCI_RESET_CMD_ADDR);
+ rc = wait_for_completion_interruptible_timeout(
+ &cci_dev->cci_master_info[MASTER_0].reset_complete,
+ CCI_TIMEOUT);
+ if (rc <= 0) {
+ pr_err("%s: wait_for_completion_interruptible_timeout %d\n",
+ __func__, __LINE__);
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ return rc;
+ }
+ msm_cci_set_clk_param(cci_dev);
+ msm_camera_io_w(CCI_IRQ_MASK_0_RMSK,
+ cci_dev->base + CCI_IRQ_MASK_0_ADDR);
+ msm_camera_io_w(CCI_IRQ_MASK_0_RMSK,
+ cci_dev->base + CCI_IRQ_CLEAR_0_ADDR);
+ msm_camera_io_w(0x1, cci_dev->base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+ cci_dev->cci_state = CCI_STATE_ENABLED;
+ return 0;
+
+clk_enable_failed:
+ return rc;
+}
+
+static int32_t msm_cci_release(struct v4l2_subdev *sd)
+{
+ struct cci_device *cci_dev;
+ cci_dev = v4l2_get_subdevdata(sd);
+
+ 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 %d\n", __func__, cci_dev->ref_count);
+ return 0;
+ }
+
+ disable_irq(cci_dev->irq->start);
+
+ msm_cam_clk_enable(&cci_dev->pdev->dev, cci_clk_info,
+ cci_dev->cci_clk, ARRAY_SIZE(cci_clk_info), 0);
+
+ msm_camera_request_gpio_table(cci_dev->cci_gpio_tbl,
+ cci_dev->cci_gpio_tbl_size, 0);
+
+ cci_dev->cci_state = CCI_STATE_DISABLED;
+ return 0;
+}
+
+static int32_t msm_cci_config(struct v4l2_subdev *sd,
+ struct msm_camera_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 = msm_cci_init(sd);
+ break;
+ case MSM_CCI_RELEASE:
+ rc = msm_cci_release(sd);
+ break;
+ case MSM_CCI_SET_SID:
+ break;
+ case MSM_CCI_SET_FREQ:
+ rc = msm_cci_i2c_set_freq(sd, cci_ctrl);
+ break;
+ case MSM_CCI_SET_SYNC_CID:
+ rc = msm_cci_i2c_config_sync_timer(sd, cci_ctrl);
+ break;
+ case MSM_CCI_I2C_READ:
+ rc = msm_cci_i2c_read(sd, cci_ctrl);
+ break;
+ case MSM_CCI_I2C_WRITE:
+ rc = msm_cci_i2c_write(sd, cci_ctrl);
+ break;
+ case MSM_CCI_GPIO_WRITE:
+ break;
+ default:
+ rc = -ENOIOCTLCMD;
+ }
+ CDBG("%s line %d rc %d\n", __func__, __LINE__, rc);
+ cci_ctrl->status = rc;
+ return rc;
+}
+
+static irqreturn_t msm_cci_irq(int irq_num, void *data)
+{
+ uint32_t irq;
+ struct cci_device *cci_dev = data;
+ irq = msm_camera_io_r(cci_dev->base + CCI_IRQ_STATUS_0_ADDR);
+ msm_camera_io_w(irq, cci_dev->base + CCI_IRQ_CLEAR_0_ADDR);
+ msm_camera_io_w(0x1, cci_dev->base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+ msm_camera_io_w(0x0, cci_dev->base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+ CDBG("%s CCI_I2C_M0_STATUS_ADDR = 0x%x\n", __func__, irq);
+ 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);
+ }
+ } else if ((irq & CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK) ||
+ (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK) ||
+ (irq & CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK)) {
+ cci_dev->cci_master_info[MASTER_0].status = 0;
+ complete(&cci_dev->cci_master_info[MASTER_0].reset_complete);
+ } else if ((irq & CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK) ||
+ (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK) ||
+ (irq & CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK)) {
+ cci_dev->cci_master_info[MASTER_1].status = 0;
+ complete(&cci_dev->cci_master_info[MASTER_1].reset_complete);
+ } else if ((irq & CCI_IRQ_STATUS_0_I2C_M0_Q0_NACK_ERR_BMSK) ||
+ (irq & CCI_IRQ_STATUS_0_I2C_M0_Q1_NACK_ERR_BMSK)) {
+ cci_dev->cci_master_info[MASTER_0].status = -EINVAL;
+ msm_camera_io_w(CCI_M0_HALT_REQ_RMSK,
+ cci_dev->base + CCI_HALT_REQ_ADDR);
+ } else if ((irq & CCI_IRQ_STATUS_0_I2C_M1_Q0_NACK_ERR_BMSK) ||
+ (irq & CCI_IRQ_STATUS_0_I2C_M1_Q1_NACK_ERR_BMSK)) {
+ cci_dev->cci_master_info[MASTER_1].status = -EINVAL;
+ msm_camera_io_w(CCI_M1_HALT_REQ_RMSK,
+ cci_dev->base + CCI_HALT_REQ_ADDR);
+ } else if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK) {
+ cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE;
+ msm_camera_io_w(CCI_M0_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ } else if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK_BMSK) {
+ cci_dev->cci_master_info[MASTER_1].reset_pending = TRUE;
+ msm_camera_io_w(CCI_M1_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ } else {
+ pr_err("%s unhandled irq 0x%x\n", __func__, irq);
+ cci_dev->cci_master_info[MASTER_0].status = 0;
+ complete(&cci_dev->cci_master_info[MASTER_0].reset_complete);
+ cci_dev->cci_master_info[MASTER_1].status = 0;
+ complete(&cci_dev->cci_master_info[MASTER_1].reset_complete);
+ }
+ return IRQ_HANDLED;
+}
+
+static int msm_cci_irq_routine(struct v4l2_subdev *sd, u32 status,
+ bool *handled)
+{
+ struct cci_device *cci_dev = v4l2_get_subdevdata(sd);
+ irqreturn_t ret;
+ CDBG("%s line %d\n", __func__, __LINE__);
+ ret = msm_cci_irq(cci_dev->irq->start, cci_dev);
+ CDBG("%s: msm_cci_irq return %d\n", __func__, ret);
+ *handled = TRUE;
+ return 0;
+}
+
+static long msm_cci_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ int32_t rc = 0;
+ CDBG("%s line %d\n", __func__, __LINE__);
+ switch (cmd) {
+ case VIDIOC_MSM_CCI_CFG:
+ rc = msm_cci_config(sd, arg);
+ break;
+ default:
+ rc = -ENOIOCTLCMD;
+ }
+ CDBG("%s line %d rc %d\n", __func__, __LINE__, rc);
+ return rc;
+}
+
+static struct v4l2_subdev_core_ops msm_cci_subdev_core_ops = {
+ .g_chip_ident = &msm_cci_subdev_g_chip_ident,
+ .ioctl = &msm_cci_subdev_ioctl,
+ .interrupt_service_routine = msm_cci_irq_routine,
+};
+
+static const struct v4l2_subdev_ops msm_cci_subdev_ops = {
+ .core = &msm_cci_subdev_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops msm_cci_internal_ops;
+
+static void msm_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++) {
+ if (j == QUEUE_0)
+ new_cci_dev->cci_i2c_queue_info[i][j].
+ max_queue_size = CCI_I2C_QUEUE_0_SIZE;
+ else
+ new_cci_dev->cci_i2c_queue_info[i][j].
+ max_queue_size = CCI_I2C_QUEUE_1_SIZE;
+ }
+ }
+ return;
+}
+
+static int32_t msm_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->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 0;
+ }
+
+ 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 0;
+ }
+
+ 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 = kzalloc(sizeof(uint32_t) * tbl_size, GFP_KERNEL);
+ if (!val_array) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto ERROR1;
+ }
+
+ 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 ERROR2;
+ }
+ 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 ERROR2;
+ }
+ }
+
+ kfree(val_array);
+ return rc;
+
+ERROR2:
+ kfree(val_array);
+ERROR1:
+ kfree(cci_dev->cci_gpio_tbl);
+ cci_dev->cci_gpio_tbl = NULL;
+ cci_dev->cci_gpio_tbl_size = 0;
+ return rc;
+}
+
+static void msm_cci_init_clk_params(struct cci_device *cci_dev)
+{
+ int32_t rc = 0;
+ uint32_t val = 0;
+ struct device_node *of_node = cci_dev->pdev->dev.of_node;
+
+ rc = of_property_read_u32(of_node, "qcom,hw-thigh", &val);
+ CDBG("%s qcom,hw-thigh %d, rc %d\n", __func__, val, rc);
+ if (!rc)
+ cci_dev->cci_clk_params.hw_thigh = val;
+
+ rc = of_property_read_u32(of_node, "qcom,hw-tlow", &val);
+ CDBG("%s qcom,hw-tlow %d, rc %d\n", __func__, val, rc);
+ if (!rc)
+ cci_dev->cci_clk_params.hw_tlow = val;
+
+ rc = of_property_read_u32(of_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.hw_tsu_sto = val;
+
+ rc = of_property_read_u32(of_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.hw_tsu_sta = val;
+
+ rc = of_property_read_u32(of_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.hw_thd_dat = val;
+
+ rc = of_property_read_u32(of_node, "qcom,hw-thd-sta", &val);
+ CDBG("%s qcom,hwthd-sta %d, rc %d\n", __func__, val, rc);
+ if (!rc)
+ cci_dev->cci_clk_params.hw_thd_sta = val;
+
+ rc = of_property_read_u32(of_node, "qcom,hw-tbuf", &val);
+ CDBG("%s qcom,hw-tbuf %d, rc %d\n", __func__, val, rc);
+ if (!rc)
+ cci_dev->cci_clk_params.hw_tbuf = val;
+
+ rc = of_property_read_u32(of_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.hw_scl_stretch_en = val;
+
+ rc = of_property_read_u32(of_node, "qcom,hw-trdhld", &val);
+ CDBG("%s qcom,hw-trdhld %d, rc %d\n", __func__, val, rc);
+ if (!rc)
+ cci_dev->cci_clk_params.hw_trdhld = val;
+
+ rc = of_property_read_u32(of_node, "qcom,hw-tsp", &val);
+ CDBG("%s qcom,hw-tsp %d, rc %d\n", __func__, val, rc);
+ if (!rc)
+ cci_dev->cci_clk_params.hw_tsp = val;
+
+ return;
+}
+
+struct v4l2_subdev *msm_cci_get_subdev(void)
+{
+ return g_cci_subdev;
+}
+
+static int __devinit msm_cci_probe(struct platform_device *pdev)
+{
+ struct cci_device *new_cci_dev;
+ int rc = 0;
+ pr_err("%s: pdev %p device id = %d\n", __func__, pdev, pdev->id);
+ new_cci_dev = kzalloc(sizeof(struct cci_device), GFP_KERNEL);
+ if (!new_cci_dev) {
+ CDBG("%s: no enough memory\n", __func__);
+ return -ENOMEM;
+ }
+ v4l2_subdev_init(&new_cci_dev->msm_sd.sd, &msm_cci_subdev_ops);
+ new_cci_dev->msm_sd.sd.internal_ops = &msm_cci_internal_ops;
+ snprintf(new_cci_dev->msm_sd.sd.name,
+ ARRAY_SIZE(new_cci_dev->msm_sd.sd.name), "msm_cci");
+ v4l2_set_subdevdata(&new_cci_dev->msm_sd.sd, new_cci_dev);
+ platform_set_drvdata(pdev, &new_cci_dev->msm_sd.sd);
+ CDBG("%s sd %p\n", __func__, &new_cci_dev->msm_sd.sd);
+ if (pdev->dev.of_node)
+ of_property_read_u32((&pdev->dev)->of_node,
+ "cell-index", &pdev->id);
+
+ new_cci_dev->mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "cci");
+ if (!new_cci_dev->mem) {
+ CDBG("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto cci_no_resource;
+ }
+ new_cci_dev->irq = platform_get_resource_byname(pdev,
+ IORESOURCE_IRQ, "cci");
+ CDBG("%s line %d cci irq start %d end %d\n", __func__,
+ __LINE__,
+ new_cci_dev->irq->start,
+ new_cci_dev->irq->end);
+ if (!new_cci_dev->irq) {
+ CDBG("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto cci_no_resource;
+ }
+ new_cci_dev->io = request_mem_region(new_cci_dev->mem->start,
+ resource_size(new_cci_dev->mem), pdev->name);
+ if (!new_cci_dev->io) {
+ CDBG("%s: no valid mem region\n", __func__);
+ rc = -EBUSY;
+ goto cci_no_resource;
+ }
+
+ new_cci_dev->base = ioremap(new_cci_dev->mem->start,
+ resource_size(new_cci_dev->mem));
+ if (!new_cci_dev->base) {
+ rc = -ENOMEM;
+ goto cci_release_mem;
+ }
+ rc = request_irq(new_cci_dev->irq->start, msm_cci_irq,
+ IRQF_TRIGGER_RISING, "cci", new_cci_dev);
+ if (rc < 0) {
+ CDBG("%s: irq request fail\n", __func__);
+ rc = -EBUSY;
+ goto cci_release_mem;
+ }
+ disable_irq(new_cci_dev->irq->start);
+ msm_sd_register(&new_cci_dev->msm_sd);
+ new_cci_dev->pdev = pdev;
+ msm_cci_init_cci_params(new_cci_dev);
+ msm_cci_init_clk_params(new_cci_dev);
+ msm_cci_init_gpio_params(new_cci_dev);
+ 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);
+ new_cci_dev->cci_state = CCI_STATE_DISABLED;
+ g_cci_subdev = &new_cci_dev->msm_sd.sd;
+ CDBG("%s cci subdev %p\n", __func__, &new_cci_dev->msm_sd.sd);
+ CDBG("%s line %d\n", __func__, __LINE__);
+ return 0;
+
+cci_release_mem:
+ release_mem_region(new_cci_dev->mem->start,
+ resource_size(new_cci_dev->mem));
+cci_no_resource:
+ kfree(new_cci_dev);
+ return 0;
+}
+
+static int __exit msm_cci_exit(struct platform_device *pdev)
+{
+ struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
+ struct cci_device *cci_dev =
+ v4l2_get_subdevdata(subdev);
+ release_mem_region(cci_dev->mem->start, resource_size(cci_dev->mem));
+ kfree(cci_dev);
+ return 0;
+}
+
+static const struct of_device_id msm_cci_dt_match[] = {
+ {.compatible = "qcom,cci"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_cci_dt_match);
+
+static struct platform_driver cci_driver = {
+ .probe = msm_cci_probe,
+ .remove = msm_cci_exit,
+ .driver = {
+ .name = MSM_CCI_DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_cci_dt_match,
+ },
+};
+
+static int __init msm_cci_init_module(void)
+{
+ return platform_driver_register(&cci_driver);
+}
+
+static void __exit msm_cci_exit_module(void)
+{
+ platform_driver_unregister(&cci_driver);
+}
+
+module_init(msm_cci_init_module);
+module_exit(msm_cci_exit_module);
+MODULE_DESCRIPTION("MSM CCI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msmb/sensor/cci/msm_cci.h b/drivers/media/video/msmb/sensor/cci/msm_cci.h
new file mode 100644
index 0000000..527a8db
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/cci/msm_cci.h
@@ -0,0 +1,189 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_CCI_H
+#define MSM_CCI_H
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-subdev.h>
+#include <media/msm_cam_sensor.h>
+#include "msm_sd.h"
+
+#define NUM_MASTERS 2
+#define NUM_QUEUES 2
+
+#define TRUE 1
+#define FALSE 0
+
+enum cci_i2c_master_t {
+ MASTER_0,
+ MASTER_1,
+};
+
+enum cci_i2c_queue_t {
+ QUEUE_0,
+ QUEUE_1,
+};
+
+struct msm_camera_cci_client {
+ struct v4l2_subdev *cci_subdev;
+ uint32_t freq;
+ enum cci_i2c_master_t cci_i2c_master;
+ uint16_t sid;
+ uint16_t cid;
+ uint32_t timeout;
+ uint16_t retries;
+ uint16_t id_map;
+};
+
+enum msm_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_GPIO_WRITE,
+};
+
+struct msm_camera_cci_wait_sync_cfg {
+ uint16_t line;
+ uint16_t delay;
+};
+
+struct msm_camera_cci_gpio_cfg {
+ uint16_t gpio_queue;
+ uint16_t i2c_queue;
+};
+
+struct msm_camera_cci_i2c_write_cfg {
+ struct msm_camera_i2c_reg_conf *reg_conf_tbl;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+ enum msm_camera_i2c_data_type data_type;
+ uint16_t size;
+};
+
+struct msm_camera_cci_i2c_read_cfg {
+ uint16_t addr;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+ uint8_t *data;
+ uint16_t num_byte;
+};
+
+struct msm_camera_cci_i2c_queue_info {
+ uint32_t max_queue_size;
+ uint32_t report_id;
+ uint32_t irq_en;
+ uint32_t capture_rep_data;
+};
+
+struct msm_camera_cci_ctrl {
+ int32_t status;
+ struct msm_camera_cci_client *cci_info;
+ enum msm_cci_cmd_type cmd;
+ union {
+ struct msm_camera_cci_i2c_write_cfg cci_i2c_write_cfg;
+ struct msm_camera_cci_i2c_read_cfg cci_i2c_read_cfg;
+ struct msm_camera_cci_wait_sync_cfg cci_wait_sync_cfg;
+ struct msm_camera_cci_gpio_cfg gpio_cfg;
+ } cfg;
+};
+
+struct msm_camera_cci_master_info {
+ uint32_t status;
+ uint8_t reset_pending;
+ struct mutex mutex;
+ struct completion reset_complete;
+};
+
+struct msm_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;
+};
+
+enum msm_cci_state_t {
+ CCI_STATE_ENABLED,
+ CCI_STATE_DISABLED,
+};
+
+struct cci_device {
+ struct platform_device *pdev;
+ struct msm_sd_subdev msm_sd;
+ struct v4l2_subdev subdev;
+ struct resource *mem;
+ struct resource *irq;
+ struct resource *io;
+ void __iomem *base;
+
+ uint32_t hw_version;
+ uint8_t ref_count;
+ enum msm_cci_state_t cci_state;
+
+ struct clk *cci_clk[5];
+ struct msm_camera_cci_i2c_queue_info
+ cci_i2c_queue_info[NUM_MASTERS][NUM_QUEUES];
+ struct msm_camera_cci_master_info cci_master_info[NUM_MASTERS];
+ struct msm_cci_clk_params_t cci_clk_params;
+ struct gpio *cci_gpio_tbl;
+ uint8_t cci_gpio_tbl_size;
+};
+
+enum msm_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 msm_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 v4l2_subdev *msm_cci_get_subdev(void);
+
+#define VIDIOC_MSM_CCI_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 23, struct msm_camera_cci_ctrl *)
+
+#endif
diff --git a/drivers/media/video/msmb/sensor/csid/Makefile b/drivers/media/video/msmb/sensor/csid/Makefile
new file mode 100644
index 0000000..44d7726
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/csid/Makefile
@@ -0,0 +1,8 @@
+ccflags-y += -Idrivers/media/video/msmb
+ccflags-y += -Idrivers/media/video/msmb/sensor/io
+ifeq ($(CONFIG_MSM_CSI20_HEADER),y)
+ ccflags-y += -Idrivers/media/video/msmb/sensor/csid/include/csi2.0
+else ifeq ($(CONFIG_MSM_CSI30_HEADER),y)
+ ccflags-y += -Idrivers/media/video/msmb/sensor/csid/include/csi3.0
+endif
+obj-$(CONFIG_MSM_CSID) += msm_csid.o
diff --git a/drivers/media/video/msmb/sensor/csid/include/csi2.0/msm_csid_hwreg.h b/drivers/media/video/msmb/sensor/csid/include/csi2.0/msm_csid_hwreg.h
new file mode 100644
index 0000000..87a114e
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/csid/include/csi2.0/msm_csid_hwreg.h
@@ -0,0 +1,52 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_CSID_HWREG_H
+#define MSM_CSID_HWREG_H
+
+/* MIPI CSID registers */
+#define CSID_HW_VERSION_ADDR 0x0
+#define CSID_CORE_CTRL_0_ADDR 0x4
+#define CSID_CORE_CTRL_1_ADDR 0x4
+#define CSID_RST_CMD_ADDR 0x8
+#define CSID_CID_LUT_VC_0_ADDR 0xc
+#define CSID_CID_LUT_VC_1_ADDR 0x10
+#define CSID_CID_LUT_VC_2_ADDR 0x14
+#define CSID_CID_LUT_VC_3_ADDR 0x18
+#define CSID_CID_n_CFG_ADDR 0x1C
+#define CSID_IRQ_CLEAR_CMD_ADDR 0x5c
+#define CSID_IRQ_MASK_ADDR 0x60
+#define CSID_IRQ_STATUS_ADDR 0x64
+#define CSID_CAPTURED_UNMAPPED_LONG_PKT_HDR_ADDR 0x68
+#define CSID_CAPTURED_MMAPPED_LONG_PKT_HDR_ADDR 0x6c
+#define CSID_CAPTURED_SHORT_PKT_ADDR 0x70
+#define CSID_CAPTURED_LONG_PKT_HDR_ADDR 0x74
+#define CSID_CAPTURED_LONG_PKT_FTR_ADDR 0x78
+#define CSID_PIF_MISR_DL0_ADDR 0x7C
+#define CSID_PIF_MISR_DL1_ADDR 0x80
+#define CSID_PIF_MISR_DL2_ADDR 0x84
+#define CSID_PIF_MISR_DL3_ADDR 0x88
+#define CSID_STATS_TOTAL_PKTS_RCVD_ADDR 0x8C
+#define CSID_STATS_ECC_ADDR 0x90
+#define CSID_STATS_CRC_ADDR 0x94
+#define CSID_TG_CTRL_ADDR 0x9C
+#define CSID_TG_VC_CFG_ADDR 0xA0
+#define CSID_TG_DT_n_CFG_0_ADDR 0xA8
+#define CSID_TG_DT_n_CFG_1_ADDR 0xAC
+#define CSID_TG_DT_n_CFG_2_ADDR 0xB0
+#define CSID_RST_DONE_IRQ_BITSHIFT 11
+#define CSID_RST_STB_ALL 0x7FFF
+#define CSID_DL_INPUT_SEL_SHIFT 0x2
+#define CSID_PHY_SEL_SHIFT 17
+#define CSID_VERSION 0x02000011
+
+#endif
diff --git a/drivers/media/video/msmb/sensor/csid/include/csi3.0/msm_csid_hwreg.h b/drivers/media/video/msmb/sensor/csid/include/csi3.0/msm_csid_hwreg.h
new file mode 100644
index 0000000..8cea521
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/csid/include/csi3.0/msm_csid_hwreg.h
@@ -0,0 +1,52 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_CSID_HWREG_H
+#define MSM_CSID_HWREG_H
+
+/* MIPI CSID registers */
+#define CSID_HW_VERSION_ADDR 0x0
+#define CSID_CORE_CTRL_0_ADDR 0x4
+#define CSID_CORE_CTRL_1_ADDR 0x8
+#define CSID_RST_CMD_ADDR 0xC
+#define CSID_CID_LUT_VC_0_ADDR 0x10
+#define CSID_CID_LUT_VC_1_ADDR 0x14
+#define CSID_CID_LUT_VC_2_ADDR 0x18
+#define CSID_CID_LUT_VC_3_ADDR 0x1C
+#define CSID_CID_n_CFG_ADDR 0x20
+#define CSID_IRQ_CLEAR_CMD_ADDR 0x60
+#define CSID_IRQ_MASK_ADDR 0x64
+#define CSID_IRQ_STATUS_ADDR 0x68
+#define CSID_CAPTURED_UNMAPPED_LONG_PKT_HDR_ADDR 0x6C
+#define CSID_CAPTURED_MMAPPED_LONG_PKT_HDR_ADDR 0x70
+#define CSID_CAPTURED_SHORT_PKT_ADDR 0x74
+#define CSID_CAPTURED_LONG_PKT_HDR_ADDR 0x78
+#define CSID_CAPTURED_LONG_PKT_FTR_ADDR 0x7C
+#define CSID_PIF_MISR_DL0_ADDR 0x80
+#define CSID_PIF_MISR_DL1_ADDR 0x84
+#define CSID_PIF_MISR_DL2_ADDR 0x88
+#define CSID_PIF_MISR_DL3_ADDR 0x8C
+#define CSID_STATS_TOTAL_PKTS_RCVD_ADDR 0x90
+#define CSID_STATS_ECC_ADDR 0x94
+#define CSID_STATS_CRC_ADDR 0x98
+#define CSID_TG_CTRL_ADDR 0xA0
+#define CSID_TG_VC_CFG_ADDR 0xA4
+#define CSID_TG_DT_n_CFG_0_ADDR 0xAC
+#define CSID_TG_DT_n_CFG_1_ADDR 0xB0
+#define CSID_TG_DT_n_CFG_2_ADDR 0xB4
+#define CSID_RST_DONE_IRQ_BITSHIFT 11
+#define CSID_RST_STB_ALL 0x7FFF
+#define CSID_DL_INPUT_SEL_SHIFT 0x4
+#define CSID_PHY_SEL_SHIFT 17
+#define CSID_VERSION 0x30000000
+
+#endif
diff --git a/drivers/media/video/msmb/sensor/csid/msm_csid.c b/drivers/media/video/msmb/sensor/csid/msm_csid.c
new file mode 100644
index 0000000..5889f20
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/csid/msm_csid.c
@@ -0,0 +1,693 @@
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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.h>
+#include <linux/irqreturn.h>
+#include "msm_csid.h"
+#include "msm_csid_hwreg.h"
+#include "msm_sd.h"
+#include "msm_camera_io_util.h"
+
+#define V4L2_IDENT_CSID 50002
+#define CSID_VERSION_V2 0x02000011
+#define CSID_VERSION_V3 0x30000000
+#define MSM_CSID_DRV_NAME "msm_csid"
+
+#define DBG_CSID 1
+
+#define TRUE 1
+#define FALSE 0
+
+#define CONFIG_MSMB_CAMERA_DEBUG
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+static uint32_t irq_count;
+
+static int msm_csid_cid_lut(
+ struct msm_camera_csid_lut_params *csid_lut_params,
+ void __iomem *csidbase)
+{
+ int rc = 0, i = 0;
+ uint32_t val = 0;
+
+ if (!csid_lut_params) {
+ pr_err("%s:%d csid_lut_params NULL\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ for (i = 0; i < csid_lut_params->num_cid && i < 16; i++) {
+ CDBG("%s lut params num_cid = %d, cid = %d, dt = %x, df = %d\n",
+ __func__,
+ csid_lut_params->num_cid,
+ csid_lut_params->vc_cfg[i]->cid,
+ csid_lut_params->vc_cfg[i]->dt,
+ csid_lut_params->vc_cfg[i]->decode_format);
+ if (csid_lut_params->vc_cfg[i]->dt < 0x12 ||
+ csid_lut_params->vc_cfg[i]->dt > 0x37) {
+ pr_err("%s: unsupported data type 0x%x\n",
+ __func__, csid_lut_params->vc_cfg[i]->dt);
+ return rc;
+ }
+ val = msm_camera_io_r(csidbase + CSID_CID_LUT_VC_0_ADDR +
+ (csid_lut_params->vc_cfg[i]->cid >> 2) * 4)
+ & ~(0xFF << ((csid_lut_params->vc_cfg[i]->cid % 4) *
+ 8));
+ val |= (csid_lut_params->vc_cfg[i]->dt <<
+ ((csid_lut_params->vc_cfg[i]->cid % 4) * 8));
+ msm_camera_io_w(val, csidbase + CSID_CID_LUT_VC_0_ADDR +
+ (csid_lut_params->vc_cfg[i]->cid >> 2) * 4);
+
+ val = (csid_lut_params->vc_cfg[i]->decode_format << 4) | 0x3;
+ msm_camera_io_w(val, csidbase + CSID_CID_n_CFG_ADDR +
+ (csid_lut_params->vc_cfg[i]->cid * 4));
+ }
+ return rc;
+}
+
+#if DBG_CSID
+static void msm_csid_set_debug_reg(void __iomem *csidbase,
+ struct msm_camera_csid_params *csid_params)
+{
+ uint32_t val = 0;
+ val = ((1 << csid_params->lane_cnt) - 1) << 20;
+ msm_camera_io_w(0x7f010801 | val, csidbase + CSID_IRQ_MASK_ADDR);
+ msm_camera_io_w(0x7f010801 | val, csidbase + CSID_IRQ_CLEAR_CMD_ADDR);
+}
+#else
+static void msm_csid_set_debug_reg(void __iomem *csidbase,
+ struct msm_camera_csid_params *csid_params) {}
+#endif
+
+static void msm_csid_reset(struct csid_device *csid_dev)
+{
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ msm_camera_io_w(CSID_RST_STB_ALL, csid_dev->base + CSID_RST_CMD_ADDR);
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ wait_for_completion_interruptible(&csid_dev->reset_complete);
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ return;
+}
+
+static int msm_csid_config(struct csid_device *csid_dev,
+ struct msm_camera_csid_params *csid_params)
+{
+ int rc = 0;
+ uint32_t val = 0;
+ void __iomem *csidbase;
+ csidbase = csid_dev->base;
+ if (!csidbase || !csid_params) {
+ pr_err("%s:%d csidbase %p, csid params %p\n", __func__,
+ __LINE__, csidbase, csid_params);
+ return -EINVAL;
+ }
+
+ CDBG("%s csid_params, lane_cnt = %d, lane_assign = %x, phy sel = %d\n",
+ __func__,
+ csid_params->lane_cnt,
+ csid_params->lane_assign,
+ csid_params->phy_sel);
+
+ msm_csid_reset(csid_dev);
+
+ val = csid_params->lane_cnt - 1;
+ val |= csid_params->lane_assign << CSID_DL_INPUT_SEL_SHIFT;
+ if (csid_dev->hw_version < 0x30000000) {
+ val |= (0xF << 10);
+ msm_camera_io_w(val, csidbase + CSID_CORE_CTRL_0_ADDR);
+ } else {
+ msm_camera_io_w(val, csidbase + CSID_CORE_CTRL_0_ADDR);
+ val = csid_params->phy_sel << CSID_PHY_SEL_SHIFT;
+ val |= 0xF;
+ msm_camera_io_w(val, csidbase + CSID_CORE_CTRL_1_ADDR);
+ }
+
+ rc = msm_csid_cid_lut(&csid_params->lut_params, csidbase);
+ if (rc < 0)
+ return rc;
+
+ msm_csid_set_debug_reg(csidbase, csid_params);
+ return rc;
+}
+
+static irqreturn_t msm_csid_irq(int irq_num, void *data)
+{
+ uint32_t irq;
+ struct csid_device *csid_dev = data;
+ uint32_t val = 0;
+ void __iomem *csidbase;
+ csidbase = csid_dev->base;
+
+ if (!csid_dev) {
+ pr_err("%s:%d csid_dev NULL\n", __func__, __LINE__);
+ return IRQ_HANDLED;
+ }
+ irq = msm_camera_io_r(csid_dev->base + CSID_IRQ_STATUS_ADDR);
+ CDBG("%s CSID%d_IRQ_STATUS_ADDR = 0x%x\n",
+ __func__, csid_dev->pdev->id, irq);
+ if (irq & (0x1 << CSID_RST_DONE_IRQ_BITSHIFT))
+ complete(&csid_dev->reset_complete);
+ if (irq & 0x1) {
+ pr_debug("%s CSID%d_IRQ_STATUS_ADDR = 0x%x\n",
+ __func__, csid_dev->pdev->id, irq);
+ irq_count++;
+ if (irq_count >= 5) {
+ msm_camera_io_w(0x7f010800 | val,
+ csidbase + CSID_IRQ_MASK_ADDR);
+ msm_camera_io_w(0x7f010800 | val,
+ csidbase + CSID_IRQ_CLEAR_CMD_ADDR);
+ }
+ }
+ msm_camera_io_w(irq, csid_dev->base + CSID_IRQ_CLEAR_CMD_ADDR);
+ return IRQ_HANDLED;
+}
+
+static int msm_csid_irq_routine(struct v4l2_subdev *sd, u32 status,
+ bool *handled)
+{
+ struct csid_device *csid_dev = v4l2_get_subdevdata(sd);
+ irqreturn_t ret;
+ CDBG("%s E\n", __func__);
+ ret = msm_csid_irq(csid_dev->irq->start, csid_dev);
+ *handled = TRUE;
+ return 0;
+}
+
+static int msm_csid_subdev_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ BUG_ON(!chip);
+ chip->ident = V4L2_IDENT_CSID;
+ chip->revision = 0;
+ return 0;
+}
+
+static struct msm_cam_clk_info csid_8960_clk_info[] = {
+ {"csi_src_clk", 177780000},
+ {"csi_clk", -1},
+ {"csi_phy_clk", -1},
+ {"csi_pclk", -1},
+};
+
+static struct msm_cam_clk_info csid0_8974_clk_info[] = {
+ {"camss_top_ahb_clk", -1},
+ {"ispif_ahb_clk", -1},
+ {"csi0_ahb_clk", -1},
+ {"csi0_src_clk", 200000000},
+ {"csi0_clk", -1},
+ {"csi0_phy_clk", -1},
+ {"csi0_pix_clk", -1},
+ {"csi0_rdi_clk", -1},
+};
+
+static struct msm_cam_clk_info csid1_8974_clk_info[] = {
+ {"csi1_ahb_clk", -1},
+ {"csi1_src_clk", 200000000},
+ {"csi1_clk", -1},
+ {"csi1_phy_clk", -1},
+ {"csi1_pix_clk", -1},
+ {"csi1_rdi_clk", -1},
+};
+
+static struct msm_cam_clk_info csid2_8974_clk_info[] = {
+ {"csi2_ahb_clk", -1},
+ {"csi2_src_clk", 200000000},
+ {"csi2_clk", -1},
+ {"csi2_phy_clk", -1},
+ {"csi2_pix_clk", -1},
+ {"csi2_rdi_clk", -1},
+};
+
+static struct msm_cam_clk_info csid3_8974_clk_info[] = {
+ {"csi3_ahb_clk", -1},
+ {"csi3_src_clk", 200000000},
+ {"csi3_clk", -1},
+ {"csi3_phy_clk", -1},
+ {"csi3_pix_clk", -1},
+ {"csi3_rdi_clk", -1},
+};
+
+static struct msm_cam_clk_setting csid_8974_clk_info[] = {
+ {&csid0_8974_clk_info[0], ARRAY_SIZE(csid0_8974_clk_info)},
+ {&csid1_8974_clk_info[0], ARRAY_SIZE(csid1_8974_clk_info)},
+ {&csid2_8974_clk_info[0], ARRAY_SIZE(csid2_8974_clk_info)},
+ {&csid3_8974_clk_info[0], ARRAY_SIZE(csid3_8974_clk_info)},
+};
+
+static struct camera_vreg_t csid_8960_vreg_info[] = {
+ {"mipi_csi_vdd", REG_LDO, 1200000, 1200000, 20000},
+};
+
+static struct camera_vreg_t csid_8974_vreg_info[] = {
+ {"mipi_csi_vdd", REG_LDO, 1800000, 1800000, 12000},
+};
+
+static int msm_csid_init(struct csid_device *csid_dev, uint32_t *csid_version)
+{
+ int rc = 0;
+ uint8_t core_id = 0;
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ if (!csid_version) {
+ pr_err("%s:%d csid_version NULL\n", __func__, __LINE__);
+ rc = -EINVAL;
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ if (csid_dev->csid_state == CSID_POWER_UP) {
+ pr_err("%s: csid invalid state %d\n", __func__,
+ csid_dev->csid_state);
+ rc = -EINVAL;
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ csid_dev->base = ioremap(csid_dev->mem->start,
+ resource_size(csid_dev->mem));
+ if (!csid_dev->base) {
+ pr_err("%s csid_dev->base NULL\n", __func__);
+ rc = -ENOMEM;
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ if (CSID_VERSION <= CSID_VERSION_V2) {
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ rc = msm_camera_config_vreg(&csid_dev->pdev->dev,
+ csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 1);
+ if (rc < 0) {
+ pr_err("%s: regulator on failed\n", __func__);
+ goto vreg_config_failed;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ rc = msm_camera_enable_vreg(&csid_dev->pdev->dev,
+ csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 1);
+ if (rc < 0) {
+ pr_err("%s: regulator enable failed\n", __func__);
+ goto vreg_enable_failed;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ rc = msm_cam_clk_enable(&csid_dev->pdev->dev,
+ csid_8960_clk_info, csid_dev->csid_clk,
+ ARRAY_SIZE(csid_8960_clk_info), 1);
+ if (rc < 0) {
+ pr_err("%s: clock enable failed\n", __func__);
+ goto clk_enable_failed;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ } else if (CSID_VERSION == CSID_VERSION_V3) {
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ rc = msm_camera_config_vreg(&csid_dev->pdev->dev,
+ csid_8974_vreg_info, ARRAY_SIZE(csid_8974_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 1);
+ if (rc < 0) {
+ pr_err("%s: regulator on failed\n", __func__);
+ goto vreg_config_failed;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ rc = msm_camera_enable_vreg(&csid_dev->pdev->dev,
+ csid_8974_vreg_info, ARRAY_SIZE(csid_8974_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 1);
+ if (rc < 0) {
+ pr_err("%s: regulator enable failed\n", __func__);
+ goto vreg_enable_failed;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ rc = msm_cam_clk_enable(&csid_dev->pdev->dev,
+ csid_8974_clk_info[0].clk_info, csid_dev->csid0_clk,
+ csid_8974_clk_info[0].num_clk_info, 1);
+ if (rc < 0) {
+ pr_err("%s: clock enable failed\n", __func__);
+ goto csid0_clk_enable_failed;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ core_id = csid_dev->pdev->id;
+ if (core_id) {
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ rc = msm_cam_clk_enable(&csid_dev->pdev->dev,
+ csid_8974_clk_info[core_id].clk_info,
+ csid_dev->csid_clk,
+ csid_8974_clk_info[core_id].num_clk_info, 1);
+ if (rc < 0) {
+ pr_err("%s: clock enable failed\n",
+ __func__);
+ goto clk_enable_failed;
+ }
+ }
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ csid_dev->hw_version =
+ msm_camera_io_r(csid_dev->base + CSID_HW_VERSION_ADDR);
+ CDBG("%s:%d called csid_dev->hw_version %x\n", __func__, __LINE__,
+ csid_dev->hw_version);
+ *csid_version = csid_dev->hw_version;
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ init_completion(&csid_dev->reset_complete);
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ enable_irq(csid_dev->irq->start);
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ msm_csid_reset(csid_dev);
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ csid_dev->csid_state = CSID_POWER_UP;
+ irq_count = 0;
+ return rc;
+
+clk_enable_failed:
+ if (CSID_VERSION == CSID_VERSION_V3) {
+ msm_cam_clk_enable(&csid_dev->pdev->dev,
+ csid_8974_clk_info[0].clk_info, csid_dev->csid0_clk,
+ csid_8974_clk_info[0].num_clk_info, 0);
+ }
+csid0_clk_enable_failed:
+ if (CSID_VERSION <= CSID_VERSION_V2) {
+ msm_camera_enable_vreg(&csid_dev->pdev->dev,
+ csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+ } else if (CSID_VERSION == CSID_VERSION_V3) {
+ msm_camera_enable_vreg(&csid_dev->pdev->dev,
+ csid_8974_vreg_info, ARRAY_SIZE(csid_8974_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+ }
+vreg_enable_failed:
+ if (CSID_VERSION <= CSID_VERSION_V2) {
+ msm_camera_config_vreg(&csid_dev->pdev->dev,
+ csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+ } else if (CSID_VERSION == CSID_VERSION_V3) {
+ msm_camera_config_vreg(&csid_dev->pdev->dev,
+ csid_8974_vreg_info, ARRAY_SIZE(csid_8974_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+ }
+vreg_config_failed:
+ iounmap(csid_dev->base);
+ csid_dev->base = NULL;
+ return rc;
+}
+
+static int msm_csid_release(struct csid_device *csid_dev)
+{
+ uint32_t irq;
+ uint8_t core_id = 0;
+
+ if (csid_dev->csid_state != CSID_POWER_UP) {
+ pr_err("%s: csid invalid state %d\n", __func__,
+ csid_dev->csid_state);
+ return -EINVAL;
+ }
+
+ irq = msm_camera_io_r(csid_dev->base + CSID_IRQ_STATUS_ADDR);
+ msm_camera_io_w(irq, csid_dev->base + CSID_IRQ_CLEAR_CMD_ADDR);
+ msm_camera_io_w(0, csid_dev->base + CSID_IRQ_MASK_ADDR);
+
+ disable_irq(csid_dev->irq->start);
+
+ if (csid_dev->hw_version <= CSID_VERSION_V2) {
+ msm_cam_clk_enable(&csid_dev->pdev->dev, csid_8960_clk_info,
+ csid_dev->csid_clk, ARRAY_SIZE(csid_8960_clk_info), 0);
+
+ msm_camera_enable_vreg(&csid_dev->pdev->dev,
+ csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+
+ msm_camera_config_vreg(&csid_dev->pdev->dev,
+ csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+ } else if (csid_dev->hw_version == CSID_VERSION_V3) {
+ core_id = csid_dev->pdev->id;
+ if (core_id)
+ msm_cam_clk_enable(&csid_dev->pdev->dev,
+ csid_8974_clk_info[core_id].clk_info,
+ csid_dev->csid_clk,
+ csid_8974_clk_info[core_id].num_clk_info, 0);
+
+ msm_cam_clk_enable(&csid_dev->pdev->dev,
+ csid_8974_clk_info[0].clk_info, csid_dev->csid0_clk,
+ csid_8974_clk_info[0].num_clk_info, 0);
+
+ msm_camera_enable_vreg(&csid_dev->pdev->dev,
+ csid_8974_vreg_info, ARRAY_SIZE(csid_8974_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+
+ msm_camera_config_vreg(&csid_dev->pdev->dev,
+ csid_8974_vreg_info, ARRAY_SIZE(csid_8974_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+ }
+
+ iounmap(csid_dev->base);
+ csid_dev->base = NULL;
+ csid_dev->csid_state = CSID_POWER_DOWN;
+ return 0;
+}
+
+static long msm_csid_cmd(struct csid_device *csid_dev, void *arg)
+{
+ int rc = 0;
+ struct csid_cfg_data *cdata = (struct csid_cfg_data *)arg;
+
+ if (!csid_dev || !cdata) {
+ pr_err("%s:%d csid_dev %p, cdata %p\n", __func__, __LINE__,
+ csid_dev, cdata);
+ return -EINVAL;
+ }
+ CDBG("%s cfgtype = %d\n", __func__, cdata->cfgtype);
+ switch (cdata->cfgtype) {
+ case CSID_INIT:
+ rc = msm_csid_init(csid_dev, &cdata->cfg.csid_version);
+ CDBG("%s csid version %x\n", __func__,
+ cdata->cfg.csid_version);
+ break;
+ case CSID_CFG: {
+ struct msm_camera_csid_params csid_params;
+ struct msm_camera_csid_vc_cfg *vc_cfg = NULL;
+ int32_t i = 0;
+ if (copy_from_user(&csid_params,
+ (void *)cdata->cfg.csid_params,
+ sizeof(struct msm_camera_csid_params))) {
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ for (i = 0; i < csid_params.lut_params.num_cid; i++) {
+ vc_cfg = kzalloc(csid_params.lut_params.num_cid *
+ sizeof(struct msm_camera_csid_vc_cfg),
+ GFP_KERNEL);
+ if (!vc_cfg) {
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ for (i--; i >= 0; i--)
+ kfree(csid_params.lut_params.vc_cfg[i]);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(vc_cfg,
+ (void *)csid_params.lut_params.vc_cfg[i],
+ (csid_params.lut_params.num_cid *
+ sizeof(struct msm_camera_csid_vc_cfg)))) {
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ kfree(vc_cfg);
+ for (i--; i >= 0; i--)
+ kfree(csid_params.lut_params.vc_cfg[i]);
+ rc = -EFAULT;
+ break;
+ }
+ csid_params.lut_params.vc_cfg[i] = vc_cfg;
+ }
+ rc = msm_csid_config(csid_dev, &csid_params);
+ for (i--; i >= 0; i--)
+ kfree(csid_params.lut_params.vc_cfg[i]);
+ break;
+ }
+ case CSID_RELEASE:
+ rc = msm_csid_release(csid_dev);
+ break;
+ default:
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ rc = -ENOIOCTLCMD;
+ break;
+ }
+ return rc;
+}
+
+static int32_t msm_csid_get_subdev_id(struct csid_device *csid_dev, void *arg)
+{
+ uint32_t *subdev_id = (uint32_t *)arg;
+ if (!subdev_id) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ *subdev_id = csid_dev->pdev->id;
+ pr_debug("%s:%d subdev_id %d\n", __func__, __LINE__, *subdev_id);
+ return 0;
+}
+
+static long msm_csid_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ int rc = -ENOIOCTLCMD;
+ struct csid_device *csid_dev = v4l2_get_subdevdata(sd);
+ mutex_lock(&csid_dev->mutex);
+ CDBG("%s:%d id %d\n", __func__, __LINE__, csid_dev->pdev->id);
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID:
+ rc = msm_csid_get_subdev_id(csid_dev, arg);
+ break;
+ case VIDIOC_MSM_CSID_IO_CFG:
+ rc = msm_csid_cmd(csid_dev, arg);
+ break;
+ case VIDIOC_MSM_CSID_RELEASE:
+ rc = msm_csid_release(csid_dev);
+ break;
+ default:
+ pr_err("%s: command not found\n", __func__);
+ }
+ CDBG("%s:%d\n", __func__, __LINE__);
+ mutex_unlock(&csid_dev->mutex);
+ return rc;
+}
+
+static const struct v4l2_subdev_internal_ops msm_csid_internal_ops;
+
+static struct v4l2_subdev_core_ops msm_csid_subdev_core_ops = {
+ .g_chip_ident = &msm_csid_subdev_g_chip_ident,
+ .ioctl = &msm_csid_subdev_ioctl,
+ .interrupt_service_routine = msm_csid_irq_routine,
+};
+
+static const struct v4l2_subdev_ops msm_csid_subdev_ops = {
+ .core = &msm_csid_subdev_core_ops,
+};
+
+static int __devinit csid_probe(struct platform_device *pdev)
+{
+ struct csid_device *new_csid_dev;
+
+ int rc = 0;
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ new_csid_dev = kzalloc(sizeof(struct csid_device), GFP_KERNEL);
+ if (!new_csid_dev) {
+ pr_err("%s: no enough memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ v4l2_subdev_init(&new_csid_dev->msm_sd.sd, &msm_csid_subdev_ops);
+ v4l2_set_subdevdata(&new_csid_dev->msm_sd.sd, new_csid_dev);
+ platform_set_drvdata(pdev, &new_csid_dev->msm_sd.sd);
+ mutex_init(&new_csid_dev->mutex);
+
+ 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);
+ new_csid_dev->mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "csid");
+ if (!new_csid_dev->mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto csid_no_resource;
+ }
+ new_csid_dev->irq = platform_get_resource_byname(pdev,
+ IORESOURCE_IRQ, "csid");
+ if (!new_csid_dev->irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto csid_no_resource;
+ }
+ new_csid_dev->io = request_mem_region(new_csid_dev->mem->start,
+ resource_size(new_csid_dev->mem), pdev->name);
+ if (!new_csid_dev->io) {
+ pr_err("%s: no valid mem region\n", __func__);
+ rc = -EBUSY;
+ goto csid_no_resource;
+ }
+
+ new_csid_dev->pdev = pdev;
+ new_csid_dev->msm_sd.sd.internal_ops = &msm_csid_internal_ops;
+ new_csid_dev->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(new_csid_dev->msm_sd.sd.name,
+ ARRAY_SIZE(new_csid_dev->msm_sd.sd.name), "msm_csid");
+ media_entity_init(&new_csid_dev->msm_sd.sd.entity, 0, NULL, 0);
+ new_csid_dev->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ new_csid_dev->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_CSID;
+ msm_sd_register(&new_csid_dev->msm_sd);
+
+ rc = request_irq(new_csid_dev->irq->start, msm_csid_irq,
+ IRQF_TRIGGER_RISING, "csid", new_csid_dev);
+ if (rc < 0) {
+ release_mem_region(new_csid_dev->mem->start,
+ resource_size(new_csid_dev->mem));
+ pr_err("%s: irq request fail\n", __func__);
+ rc = -EBUSY;
+ goto csid_no_resource;
+ }
+ disable_irq(new_csid_dev->irq->start);
+ if (rc < 0) {
+ release_mem_region(new_csid_dev->mem->start,
+ resource_size(new_csid_dev->mem));
+ pr_err("%s Error registering irq ", __func__);
+ goto csid_no_resource;
+ }
+
+ new_csid_dev->csid_state = CSID_POWER_DOWN;
+ return 0;
+
+csid_no_resource:
+ mutex_destroy(&new_csid_dev->mutex);
+ kfree(new_csid_dev);
+ return 0;
+}
+
+static const struct of_device_id msm_csid_dt_match[] = {
+ {.compatible = "qcom,csid"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_csid_dt_match);
+
+static struct platform_driver csid_driver = {
+ .probe = csid_probe,
+ .driver = {
+ .name = MSM_CSID_DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_csid_dt_match,
+ },
+};
+
+static int __init msm_csid_init_module(void)
+{
+ return platform_driver_register(&csid_driver);
+}
+
+static void __exit msm_csid_exit_module(void)
+{
+ platform_driver_unregister(&csid_driver);
+}
+
+module_init(msm_csid_init_module);
+module_exit(msm_csid_exit_module);
+MODULE_DESCRIPTION("MSM CSID driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msmb/sensor/csid/msm_csid.h b/drivers/media/video/msmb/sensor/csid/msm_csid.h
new file mode 100644
index 0000000..7ae1392
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/csid/msm_csid.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_CSID_H
+#define MSM_CSID_H
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-subdev.h>
+#include <media/msm_cam_sensor.h>
+#include "msm_sd.h"
+
+enum msm_csid_state_t {
+ CSID_POWER_UP,
+ CSID_POWER_DOWN,
+};
+
+struct csid_device {
+ struct platform_device *pdev;
+ struct msm_sd_subdev msm_sd;
+ struct resource *mem;
+ struct resource *irq;
+ struct resource *io;
+ struct regulator *csi_vdd;
+ void __iomem *base;
+ struct mutex mutex;
+ struct completion reset_complete;
+ uint32_t hw_version;
+ enum msm_csid_state_t csid_state;
+
+ struct clk *csid0_clk[11];
+ struct clk *csid_clk[11];
+};
+
+#define VIDIOC_MSM_CSID_RELEASE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct v4l2_subdev*)
+#endif
diff --git a/drivers/media/video/msmb/sensor/csiphy/Makefile b/drivers/media/video/msmb/sensor/csiphy/Makefile
new file mode 100644
index 0000000..11e352c
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/csiphy/Makefile
@@ -0,0 +1,8 @@
+ccflags-y += -Idrivers/media/video/msmb
+ccflags-y += -Idrivers/media/video/msmb/sensor/io
+ifeq ($(CONFIG_MSM_CSI20_HEADER),y)
+ ccflags-y += -Idrivers/media/video/msmb/sensor/csiphy/include/csi2.0
+else ifeq ($(CONFIG_MSM_CSI30_HEADER),y)
+ ccflags-y += -Idrivers/media/video/msmb/sensor/csiphy/include/csi3.0
+endif
+obj-$(CONFIG_MSM_CSIPHY) += msm_csiphy.o
diff --git a/drivers/media/video/msmb/sensor/csiphy/include/csi2.0/msm_csiphy_hwreg.h b/drivers/media/video/msmb/sensor/csiphy/include/csi2.0/msm_csiphy_hwreg.h
new file mode 100644
index 0000000..e5093f8
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/csiphy/include/csi2.0/msm_csiphy_hwreg.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_CSIPHY_HWREG_H
+#define MSM_CSIPHY_HWREG_H
+
+/*MIPI CSI PHY registers*/
+#define MIPI_CSIPHY_HW_VERSION_ADDR 0x180
+#define MIPI_CSIPHY_LNn_CFG1_ADDR 0x0
+#define MIPI_CSIPHY_LNn_CFG2_ADDR 0x4
+#define MIPI_CSIPHY_LNn_CFG3_ADDR 0x8
+#define MIPI_CSIPHY_LNn_CFG4_ADDR 0xC
+#define MIPI_CSIPHY_LNn_CFG5_ADDR 0x10
+#define MIPI_CSIPHY_LNCK_CFG1_ADDR 0x100
+#define MIPI_CSIPHY_LNCK_CFG2_ADDR 0x104
+#define MIPI_CSIPHY_LNCK_CFG3_ADDR 0x108
+#define MIPI_CSIPHY_LNCK_CFG4_ADDR 0x10C
+#define MIPI_CSIPHY_LNCK_CFG5_ADDR 0x110
+#define MIPI_CSIPHY_LNCK_MISC1_ADDR 0x128
+#define MIPI_CSIPHY_GLBL_RESET_ADDR 0x140
+#define MIPI_CSIPHY_GLBL_PWR_CFG_ADDR 0x144
+#define MIPI_CSIPHY_GLBL_IRQ_CMD_ADDR 0x164
+#define MIPI_CSIPHY_INTERRUPT_STATUS0_ADDR 0x180
+#define MIPI_CSIPHY_INTERRUPT_MASK0_ADDR 0x1A0
+#define MIPI_CSIPHY_INTERRUPT_MASK_VAL 0x6F
+#define MIPI_CSIPHY_INTERRUPT_MASK_ADDR 0x1A4
+#define MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR 0x1C0
+#define MIPI_CSIPHY_INTERRUPT_CLEAR_ADDR 0x1C4
+#define MIPI_CSIPHY_MODE_CONFIG_SHIFT 0x4
+#define MIPI_CSIPHY_GLBL_T_INIT_CFG0_ADDR 0x1E0
+#define MIPI_CSIPHY_T_WAKEUP_CFG0_ADDR 0x1E8
+#define CSIPHY_VERSION 0x0
+
+#endif
diff --git a/drivers/media/video/msmb/sensor/csiphy/include/csi3.0/msm_csiphy_hwreg.h b/drivers/media/video/msmb/sensor/csiphy/include/csi3.0/msm_csiphy_hwreg.h
new file mode 100644
index 0000000..b90fbc5
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/csiphy/include/csi3.0/msm_csiphy_hwreg.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_CSIPHY_HWREG_H
+#define MSM_CSIPHY_HWREG_H
+
+/*MIPI CSI PHY registers*/
+#define MIPI_CSIPHY_LNn_CFG1_ADDR 0x0
+#define MIPI_CSIPHY_LNn_CFG2_ADDR 0x4
+#define MIPI_CSIPHY_LNn_CFG3_ADDR 0x8
+#define MIPI_CSIPHY_LNn_CFG4_ADDR 0xC
+#define MIPI_CSIPHY_LNn_CFG5_ADDR 0x10
+#define MIPI_CSIPHY_LNCK_CFG1_ADDR 0x100
+#define MIPI_CSIPHY_LNCK_CFG2_ADDR 0x104
+#define MIPI_CSIPHY_LNCK_CFG3_ADDR 0x108
+#define MIPI_CSIPHY_LNCK_CFG4_ADDR 0x10C
+#define MIPI_CSIPHY_LNCK_CFG5_ADDR 0x110
+#define MIPI_CSIPHY_LNCK_MISC1_ADDR 0x128
+#define MIPI_CSIPHY_GLBL_RESET_ADDR 0x140
+#define MIPI_CSIPHY_GLBL_PWR_CFG_ADDR 0x144
+#define MIPI_CSIPHY_GLBL_IRQ_CMD_ADDR 0x164
+#define MIPI_CSIPHY_HW_VERSION_ADDR 0x188
+#define MIPI_CSIPHY_INTERRUPT_STATUS0_ADDR 0x18C
+#define MIPI_CSIPHY_INTERRUPT_MASK0_ADDR 0x1AC
+#define MIPI_CSIPHY_INTERRUPT_MASK_VAL 0x3F
+#define MIPI_CSIPHY_INTERRUPT_MASK_ADDR 0x1AC
+#define MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR 0x1CC
+#define MIPI_CSIPHY_INTERRUPT_CLEAR_ADDR 0x1CC
+#define MIPI_CSIPHY_MODE_CONFIG_SHIFT 0x4
+#define MIPI_CSIPHY_GLBL_T_INIT_CFG0_ADDR 0x1EC
+#define MIPI_CSIPHY_T_WAKEUP_CFG0_ADDR 0x1F4
+#define CSIPHY_VERSION 0x10
+
+#endif
diff --git a/drivers/media/video/msmb/sensor/csiphy/msm_csiphy.c b/drivers/media/video/msmb/sensor/csiphy/msm_csiphy.c
new file mode 100644
index 0000000..cc29ed4
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/csiphy/msm_csiphy.c
@@ -0,0 +1,640 @@
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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/clk.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/irqreturn.h>
+#include <mach/vreg.h>
+#include "msm_csiphy.h"
+#include "msm_sd.h"
+#include "msm_csiphy_hwreg.h"
+#include "msm_camera_io_util.h"
+#define DBG_CSIPHY 0
+
+#define V4L2_IDENT_CSIPHY 50003
+#define CSIPHY_VERSION_V3 0x10
+#define MSM_CSIPHY_DRV_NAME "msm_csiphy"
+
+#define CONFIG_MSMB_CAMERA_DEBUG
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+static int msm_csiphy_lane_config(struct csiphy_device *csiphy_dev,
+ struct msm_camera_csiphy_params *csiphy_params)
+{
+ int rc = 0;
+ int j = 0;
+ uint32_t val = 0;
+ uint8_t lane_cnt = 0;
+ uint16_t lane_mask = 0;
+ void __iomem *csiphybase;
+ csiphybase = csiphy_dev->base;
+ if (!csiphybase) {
+ pr_err("%s: csiphybase NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ csiphy_dev->lane_mask[csiphy_dev->pdev->id] |= csiphy_params->lane_mask;
+ lane_mask = csiphy_dev->lane_mask[csiphy_dev->pdev->id];
+ lane_cnt = csiphy_params->lane_cnt;
+ if (csiphy_params->lane_cnt < 1 || csiphy_params->lane_cnt > 4) {
+ pr_err("%s: unsupported lane cnt %d\n",
+ __func__, csiphy_params->lane_cnt);
+ return rc;
+ }
+
+ CDBG("%s csiphy_params, mask = %x, cnt = %d, settle cnt = %x\n",
+ __func__,
+ csiphy_params->lane_mask,
+ csiphy_params->lane_cnt,
+ csiphy_params->settle_cnt);
+ msm_camera_io_w(0x1, csiphybase + MIPI_CSIPHY_GLBL_T_INIT_CFG0_ADDR);
+ msm_camera_io_w(0x1, csiphybase + MIPI_CSIPHY_T_WAKEUP_CFG0_ADDR);
+
+ if (csiphy_dev->hw_version != CSIPHY_VERSION_V3) {
+ val = 0x3;
+ msm_camera_io_w((lane_mask << 2) | val,
+ csiphybase + MIPI_CSIPHY_GLBL_PWR_CFG_ADDR);
+ msm_camera_io_w(0x10, csiphybase + MIPI_CSIPHY_LNCK_CFG2_ADDR);
+ msm_camera_io_w(csiphy_params->settle_cnt,
+ csiphybase + MIPI_CSIPHY_LNCK_CFG3_ADDR);
+ msm_camera_io_w(0x24,
+ csiphybase + MIPI_CSIPHY_INTERRUPT_MASK0_ADDR);
+ msm_camera_io_w(0x24,
+ csiphybase + MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR);
+ } else {
+ val = 0x1;
+ msm_camera_io_w((lane_mask << 1) | val,
+ csiphybase + MIPI_CSIPHY_GLBL_PWR_CFG_ADDR);
+ msm_camera_io_w(csiphy_params->combo_mode <<
+ MIPI_CSIPHY_MODE_CONFIG_SHIFT,
+ csiphybase + MIPI_CSIPHY_GLBL_RESET_ADDR);
+ }
+
+ lane_mask &= 0x1f;
+ while (lane_mask & 0x1f) {
+ if (!(lane_mask & 0x1)) {
+ j++;
+ lane_mask >>= 1;
+ continue;
+ }
+ msm_camera_io_w(0x10,
+ csiphybase + MIPI_CSIPHY_LNn_CFG2_ADDR + 0x40*j);
+ msm_camera_io_w(csiphy_params->settle_cnt,
+ csiphybase + MIPI_CSIPHY_LNn_CFG3_ADDR + 0x40*j);
+ msm_camera_io_w(MIPI_CSIPHY_INTERRUPT_MASK_VAL, csiphybase +
+ MIPI_CSIPHY_INTERRUPT_MASK_ADDR + 0x4*j);
+ msm_camera_io_w(MIPI_CSIPHY_INTERRUPT_MASK_VAL, csiphybase +
+ MIPI_CSIPHY_INTERRUPT_CLEAR_ADDR + 0x4*j);
+ j++;
+ lane_mask >>= 1;
+ }
+ msleep(20);
+ return rc;
+}
+
+static irqreturn_t msm_csiphy_irq(int irq_num, void *data)
+{
+ uint32_t irq;
+ int i;
+ struct csiphy_device *csiphy_dev = data;
+
+ for (i = 0; i < 8; i++) {
+ irq = msm_camera_io_r(
+ csiphy_dev->base +
+ MIPI_CSIPHY_INTERRUPT_STATUS0_ADDR + 0x4*i);
+ msm_camera_io_w(irq,
+ csiphy_dev->base +
+ MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR + 0x4*i);
+ pr_err("%s MIPI_CSIPHY%d_INTERRUPT_STATUS%d = 0x%x\n",
+ __func__, csiphy_dev->pdev->id, i, irq);
+ msm_camera_io_w(0x1, csiphy_dev->base +
+ MIPI_CSIPHY_GLBL_IRQ_CMD_ADDR);
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ MIPI_CSIPHY_GLBL_IRQ_CMD_ADDR);
+ msm_camera_io_w(0x0,
+ csiphy_dev->base +
+ MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR + 0x4*i);
+ }
+ return IRQ_HANDLED;
+}
+
+static void msm_csiphy_reset(struct csiphy_device *csiphy_dev)
+{
+ msm_camera_io_w(0x1, csiphy_dev->base + MIPI_CSIPHY_GLBL_RESET_ADDR);
+ usleep_range(5000, 8000);
+ msm_camera_io_w(0x0, csiphy_dev->base + MIPI_CSIPHY_GLBL_RESET_ADDR);
+}
+
+static int msm_csiphy_subdev_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ BUG_ON(!chip);
+ chip->ident = V4L2_IDENT_CSIPHY;
+ chip->revision = 0;
+ return 0;
+}
+
+static struct msm_cam_clk_info csiphy_8960_clk_info[] = {
+ {"csiphy_timer_src_clk", 177780000},
+ {"csiphy_timer_clk", -1},
+};
+
+static struct msm_cam_clk_info csiphy_8974_clk_info[] = {
+ {"camss_top_ahb_clk", -1},
+ {"ispif_ahb_clk", -1},
+ {"csiphy_timer_src_clk", 200000000},
+ {"csiphy_timer_clk", -1},
+};
+
+#if DBG_CSIPHY
+static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
+{
+ int rc = 0;
+ if (csiphy_dev == NULL) {
+ pr_err("%s: csiphy_dev NULL\n", __func__);
+ rc = -ENOMEM;
+ return rc;
+ }
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ if (csiphy_dev->csiphy_state == CSIPHY_POWER_UP) {
+ pr_err("%s: csiphy invalid state %d\n", __func__,
+ csiphy_dev->csiphy_state);
+ rc = -EINVAL;
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ if (csiphy_dev->ref_count++) {
+ CDBG("%s csiphy refcount = %d\n", __func__,
+ csiphy_dev->ref_count);
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ csiphy_dev->base = ioremap(csiphy_dev->mem->start,
+ resource_size(csiphy_dev->mem));
+ if (!csiphy_dev->base) {
+ pr_err("%s: csiphy_dev->base NULL\n", __func__);
+ csiphy_dev->ref_count--;
+ rc = -ENOMEM;
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ if (CSIPHY_VERSION != CSIPHY_VERSION_V3) {
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_8960_clk_info, csiphy_dev->csiphy_clk,
+ ARRAY_SIZE(csiphy_8960_clk_info), 1);
+ } else {
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_8974_clk_info, csiphy_dev->csiphy_clk,
+ ARRAY_SIZE(csiphy_8974_clk_info), 1);
+ }
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ if (rc < 0) {
+ pr_err("%s: csiphy clk enable failed\n", __func__);
+ csiphy_dev->ref_count--;
+ iounmap(csiphy_dev->base);
+ csiphy_dev->base = NULL;
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ enable_irq(csiphy_dev->irq->start);
+
+ msm_csiphy_reset(csiphy_dev);
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ csiphy_dev->hw_version =
+ msm_camera_io_r(csiphy_dev->base + MIPI_CSIPHY_HW_VERSION_ADDR);
+
+ CDBG("%s:%d called csiphy_dev->hw_version %x\n", __func__, __LINE__,
+ csiphy_dev->hw_version);
+ csiphy_dev->csiphy_state = CSIPHY_POWER_UP;
+ return 0;
+}
+#else
+static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
+{
+ int rc = 0;
+ if (csiphy_dev == NULL) {
+ pr_err("%s: csiphy_dev NULL\n", __func__);
+ rc = -ENOMEM;
+ return rc;
+ }
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ if (csiphy_dev->csiphy_state == CSIPHY_POWER_UP) {
+ pr_err("%s: csiphy invalid state %d\n", __func__,
+ csiphy_dev->csiphy_state);
+ rc = -EINVAL;
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ if (csiphy_dev->ref_count++) {
+ CDBG("%s csiphy refcount = %d\n", __func__,
+ csiphy_dev->ref_count);
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ csiphy_dev->base = ioremap(csiphy_dev->mem->start,
+ resource_size(csiphy_dev->mem));
+ if (!csiphy_dev->base) {
+ pr_err("%s: csiphy_dev->base NULL\n", __func__);
+ csiphy_dev->ref_count--;
+ rc = -ENOMEM;
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ if (CSIPHY_VERSION != CSIPHY_VERSION_V3) {
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_8960_clk_info, csiphy_dev->csiphy_clk,
+ ARRAY_SIZE(csiphy_8960_clk_info), 1);
+ } else {
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_8974_clk_info, csiphy_dev->csiphy_clk,
+ ARRAY_SIZE(csiphy_8974_clk_info), 1);
+ }
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ if (rc < 0) {
+ pr_err("%s: csiphy clk enable failed\n", __func__);
+ csiphy_dev->ref_count--;
+ iounmap(csiphy_dev->base);
+ csiphy_dev->base = NULL;
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ msm_csiphy_reset(csiphy_dev);
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ csiphy_dev->hw_version =
+ msm_camera_io_r(csiphy_dev->base + MIPI_CSIPHY_HW_VERSION_ADDR);
+
+ CDBG("%s:%d called csiphy_dev->hw_version %x\n", __func__, __LINE__,
+ csiphy_dev->hw_version);
+ csiphy_dev->csiphy_state = CSIPHY_POWER_UP;
+ return 0;
+}
+#endif
+
+#if DBG_CSIPHY
+static int msm_csiphy_release(struct csiphy_device *csiphy_dev, void *arg)
+{
+ int i = 0;
+ struct msm_camera_csi_lane_params *csi_lane_params;
+ uint16_t csi_lane_mask;
+ csi_lane_params = (struct msm_camera_csi_lane_params *)arg;
+ csi_lane_mask = csi_lane_params->csi_lane_mask;
+
+ if (!csiphy_dev || !csiphy_dev->ref_count) {
+ pr_err("%s csiphy dev NULL / ref_count ZERO\n", __func__);
+ return 0;
+ }
+
+ if (csiphy_dev->csiphy_state != CSIPHY_POWER_UP) {
+ pr_err("%s: csiphy invalid state %d\n", __func__,
+ csiphy_dev->csiphy_state);
+ return -EINVAL;
+ }
+
+ CDBG("%s csiphy_params, lane assign %x mask = %x\n",
+ __func__,
+ csi_lane_params->csi_lane_assign,
+ csi_lane_params->csi_lane_mask);
+
+ if (csiphy_dev->hw_version != CSIPHY_VERSION_V3) {
+ csiphy_dev->lane_mask[csiphy_dev->pdev->id] = 0;
+ for (i = 0; i < 4; i++)
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ MIPI_CSIPHY_LNn_CFG2_ADDR + 0x40*i);
+ } else {
+ csiphy_dev->lane_mask[csiphy_dev->pdev->id] &=
+ ~(csi_lane_params->csi_lane_mask);
+ i = 0;
+ while (csi_lane_mask & 0x1F) {
+ if (csi_lane_mask & 0x1) {
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ MIPI_CSIPHY_LNn_CFG2_ADDR + 0x40*i);
+ }
+ csi_lane_mask >>= 1;
+ i++;
+ }
+ }
+
+ if (--csiphy_dev->ref_count) {
+ CDBG("%s csiphy refcount = %d\n", __func__,
+ csiphy_dev->ref_count);
+ return 0;
+ }
+
+ msm_camera_io_w(0x0, csiphy_dev->base + MIPI_CSIPHY_LNCK_CFG2_ADDR);
+ msm_camera_io_w(0x0, csiphy_dev->base + MIPI_CSIPHY_GLBL_PWR_CFG_ADDR);
+
+ disable_irq(csiphy_dev->irq->start);
+
+ if (CSIPHY_VERSION != CSIPHY_VERSION_V3)
+ msm_cam_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_8960_clk_info, csiphy_dev->csiphy_clk,
+ ARRAY_SIZE(csiphy_8960_clk_info), 0);
+ else
+ msm_cam_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_8974_clk_info, csiphy_dev->csiphy_clk,
+ ARRAY_SIZE(csiphy_8974_clk_info), 0);
+
+ iounmap(csiphy_dev->base);
+ csiphy_dev->base = NULL;
+ csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN;
+ return 0;
+}
+#else
+static int msm_csiphy_release(struct csiphy_device *csiphy_dev, void *arg)
+{
+ int i = 0;
+ struct msm_camera_csi_lane_params *csi_lane_params;
+ uint16_t csi_lane_mask;
+ csi_lane_params = (struct msm_camera_csi_lane_params *)arg;
+ csi_lane_mask = csi_lane_params->csi_lane_mask;
+
+ if (!csiphy_dev || !csiphy_dev->ref_count) {
+ pr_err("%s csiphy dev NULL / ref_count ZERO\n", __func__);
+ return 0;
+ }
+
+ if (csiphy_dev->csiphy_state != CSIPHY_POWER_UP) {
+ pr_err("%s: csiphy invalid state %d\n", __func__,
+ csiphy_dev->csiphy_state);
+ return -EINVAL;
+ }
+
+ CDBG("%s csiphy_params, lane assign %x mask = %x\n",
+ __func__,
+ csi_lane_params->csi_lane_assign,
+ csi_lane_params->csi_lane_mask);
+
+ if (csiphy_dev->hw_version != CSIPHY_VERSION_V3) {
+ csiphy_dev->lane_mask[csiphy_dev->pdev->id] = 0;
+ for (i = 0; i < 4; i++)
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ MIPI_CSIPHY_LNn_CFG2_ADDR + 0x40*i);
+ } else {
+ csiphy_dev->lane_mask[csiphy_dev->pdev->id] &=
+ ~(csi_lane_params->csi_lane_mask);
+ i = 0;
+ while (csi_lane_mask & 0x1F) {
+ if (csi_lane_mask & 0x1) {
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ MIPI_CSIPHY_LNn_CFG2_ADDR + 0x40*i);
+ }
+ csi_lane_mask >>= 1;
+ i++;
+ }
+ }
+
+ if (--csiphy_dev->ref_count) {
+ CDBG("%s csiphy refcount = %d\n", __func__,
+ csiphy_dev->ref_count);
+ return 0;
+ }
+
+ msm_camera_io_w(0x0, csiphy_dev->base + MIPI_CSIPHY_LNCK_CFG2_ADDR);
+ msm_camera_io_w(0x0, csiphy_dev->base + MIPI_CSIPHY_GLBL_PWR_CFG_ADDR);
+
+ if (CSIPHY_VERSION != CSIPHY_VERSION_V3)
+ msm_cam_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_8960_clk_info, csiphy_dev->csiphy_clk,
+ ARRAY_SIZE(csiphy_8960_clk_info), 0);
+ else
+ msm_cam_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_8974_clk_info, csiphy_dev->csiphy_clk,
+ ARRAY_SIZE(csiphy_8974_clk_info), 0);
+
+ iounmap(csiphy_dev->base);
+ csiphy_dev->base = NULL;
+ csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN;
+ return 0;
+}
+#endif
+
+static long msm_csiphy_cmd(struct csiphy_device *csiphy_dev, void *arg)
+{
+ int rc = 0;
+ struct csiphy_cfg_data *cdata = (struct csiphy_cfg_data *)arg;
+ struct msm_camera_csiphy_params csiphy_params;
+ struct msm_camera_csi_lane_params csi_lane_params;
+ if (!csiphy_dev || !cdata) {
+ pr_err("%s: csiphy_dev NULL\n", __func__);
+ return -EINVAL;
+ }
+ switch (cdata->cfgtype) {
+ case CSIPHY_INIT:
+ rc = msm_csiphy_init(csiphy_dev);
+ break;
+ case CSIPHY_CFG:
+ if (copy_from_user(&csiphy_params,
+ (void *)cdata->cfg.csiphy_params,
+ sizeof(struct msm_camera_csiphy_params))) {
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ rc = msm_csiphy_lane_config(csiphy_dev, &csiphy_params);
+ break;
+ case CSIPHY_RELEASE:
+ if (copy_from_user(&csi_lane_params,
+ (void *)cdata->cfg.csi_lane_params,
+ sizeof(struct msm_camera_csi_lane_params))) {
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ rc = msm_csiphy_release(csiphy_dev, &csi_lane_params);
+ break;
+ default:
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ rc = -ENOIOCTLCMD;
+ break;
+ }
+ return rc;
+}
+
+static int32_t msm_csiphy_get_subdev_id(struct csiphy_device *csiphy_dev,
+ void *arg)
+{
+ uint32_t *subdev_id = (uint32_t *)arg;
+ if (!subdev_id) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ *subdev_id = csiphy_dev->pdev->id;
+ pr_debug("%s:%d subdev_id %d\n", __func__, __LINE__, *subdev_id);
+ return 0;
+}
+
+static long msm_csiphy_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ int rc = -ENOIOCTLCMD;
+ struct csiphy_device *csiphy_dev = v4l2_get_subdevdata(sd);
+ CDBG("%s:%d id %d\n", __func__, __LINE__, csiphy_dev->pdev->id);
+ mutex_lock(&csiphy_dev->mutex);
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID:
+ rc = msm_csiphy_get_subdev_id(csiphy_dev, arg);
+ break;
+ case VIDIOC_MSM_CSIPHY_IO_CFG:
+ rc = msm_csiphy_cmd(csiphy_dev, arg);
+ break;
+ case VIDIOC_MSM_CSIPHY_RELEASE:
+ rc = msm_csiphy_release(csiphy_dev, arg);
+ break;
+ default:
+ pr_err("%s: command not found\n", __func__);
+ }
+ mutex_unlock(&csiphy_dev->mutex);
+ CDBG("%s:%d\n", __func__, __LINE__);
+ return rc;
+}
+
+static const struct v4l2_subdev_internal_ops msm_csiphy_internal_ops;
+
+static struct v4l2_subdev_core_ops msm_csiphy_subdev_core_ops = {
+ .g_chip_ident = &msm_csiphy_subdev_g_chip_ident,
+ .ioctl = &msm_csiphy_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_csiphy_subdev_ops = {
+ .core = &msm_csiphy_subdev_core_ops,
+};
+
+static int __devinit csiphy_probe(struct platform_device *pdev)
+{
+ struct csiphy_device *new_csiphy_dev;
+ int rc = 0;
+
+ new_csiphy_dev = kzalloc(sizeof(struct csiphy_device), GFP_KERNEL);
+ if (!new_csiphy_dev) {
+ pr_err("%s: no enough memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ v4l2_subdev_init(&new_csiphy_dev->msm_sd.sd, &msm_csiphy_subdev_ops);
+ v4l2_set_subdevdata(&new_csiphy_dev->msm_sd.sd, new_csiphy_dev);
+ platform_set_drvdata(pdev, &new_csiphy_dev->msm_sd.sd);
+
+ mutex_init(&new_csiphy_dev->mutex);
+
+ 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);
+
+ new_csiphy_dev->mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "csiphy");
+ if (!new_csiphy_dev->mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto csiphy_no_resource;
+ }
+ new_csiphy_dev->irq = platform_get_resource_byname(pdev,
+ IORESOURCE_IRQ, "csiphy");
+ if (!new_csiphy_dev->irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto csiphy_no_resource;
+ }
+ new_csiphy_dev->io = request_mem_region(new_csiphy_dev->mem->start,
+ resource_size(new_csiphy_dev->mem), pdev->name);
+ if (!new_csiphy_dev->io) {
+ pr_err("%s: no valid mem region\n", __func__);
+ rc = -EBUSY;
+ goto csiphy_no_resource;
+ }
+
+ rc = request_irq(new_csiphy_dev->irq->start, msm_csiphy_irq,
+ IRQF_TRIGGER_RISING, "csiphy", new_csiphy_dev);
+ if (rc < 0) {
+ release_mem_region(new_csiphy_dev->mem->start,
+ resource_size(new_csiphy_dev->mem));
+ pr_err("%s: irq request fail\n", __func__);
+ rc = -EBUSY;
+ goto csiphy_no_resource;
+ }
+ disable_irq(new_csiphy_dev->irq->start);
+
+ new_csiphy_dev->pdev = pdev;
+ new_csiphy_dev->msm_sd.sd.internal_ops = &msm_csiphy_internal_ops;
+ new_csiphy_dev->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(new_csiphy_dev->msm_sd.sd.name,
+ ARRAY_SIZE(new_csiphy_dev->msm_sd.sd.name), "msm_csiphy");
+ media_entity_init(&new_csiphy_dev->msm_sd.sd.entity, 0, NULL, 0);
+ new_csiphy_dev->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ new_csiphy_dev->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_CSIPHY;
+
+ msm_sd_register(&new_csiphy_dev->msm_sd);
+ new_csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN;
+ return 0;
+
+csiphy_no_resource:
+ mutex_destroy(&new_csiphy_dev->mutex);
+ kfree(new_csiphy_dev);
+ return 0;
+}
+
+static const struct of_device_id msm_csiphy_dt_match[] = {
+ {.compatible = "qcom,csiphy"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_csiphy_dt_match);
+
+static struct platform_driver csiphy_driver = {
+ .probe = csiphy_probe,
+ .driver = {
+ .name = MSM_CSIPHY_DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_csiphy_dt_match,
+ },
+};
+
+static int __init msm_csiphy_init_module(void)
+{
+ return platform_driver_register(&csiphy_driver);
+}
+
+static void __exit msm_csiphy_exit_module(void)
+{
+ platform_driver_unregister(&csiphy_driver);
+}
+
+module_init(msm_csiphy_init_module);
+module_exit(msm_csiphy_exit_module);
+MODULE_DESCRIPTION("MSM CSIPHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msmb/sensor/csiphy/msm_csiphy.h b/drivers/media/video/msmb/sensor/csiphy/msm_csiphy.h
new file mode 100644
index 0000000..e19be34
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/csiphy/msm_csiphy.h
@@ -0,0 +1,49 @@
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_CSIPHY_H
+#define MSM_CSIPHY_H
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-subdev.h>
+#include <media/msm_cam_sensor.h>
+#include "msm_sd.h"
+
+#define MAX_CSIPHY 3
+
+enum msm_csiphy_state_t {
+ CSIPHY_POWER_UP,
+ CSIPHY_POWER_DOWN,
+};
+
+struct csiphy_device {
+ struct platform_device *pdev;
+ struct msm_sd_subdev msm_sd;
+ struct v4l2_subdev subdev;
+ struct resource *mem;
+ struct resource *irq;
+ struct resource *io;
+ void __iomem *base;
+ struct mutex mutex;
+ uint32_t hw_version;
+ enum msm_csiphy_state_t csiphy_state;
+
+ struct clk *csiphy_clk[4];
+ uint8_t ref_count;
+ uint16_t lane_mask[MAX_CSIPHY];
+};
+
+#define VIDIOC_MSM_CSIPHY_RELEASE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 9, void *)
+#endif
diff --git a/drivers/media/video/msmb/sensor/io/Makefile b/drivers/media/video/msmb/sensor/io/Makefile
new file mode 100644
index 0000000..ec1faa5
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/io/Makefile
@@ -0,0 +1,3 @@
+ccflags-y += -Idrivers/media/video/msmb/
+ccflags-y += -Idrivers/media/video/msmb/sensor/cci
+obj-$(CONFIG_MSMB_CAMERA) += msm_camera_io_util.o msm_camera_cci_i2c.o msm_camera_qup_i2c.o msm_camera_i2c_mux.o
diff --git a/drivers/media/video/msmb/sensor/io/msm_camera_cci_i2c.c b/drivers/media/video/msmb/sensor/io/msm_camera_cci_i2c.c
new file mode 100644
index 0000000..b07bb36
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/io/msm_camera_cci_i2c.c
@@ -0,0 +1,284 @@
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 <mach/camera2.h>
+#include "msm_camera_i2c.h"
+#include "msm_cci.h"
+
+#define CONFIG_MSMB_CAMERA_DEBUG
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#define S_I2C_DBG(fmt, args...) pr_debug(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#define S_I2C_DBG(fmt, args...) do { } while (0)
+#endif
+
+
+int32_t msm_camera_cci_i2c_read(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint16_t *data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[client->addr_type+data_type];
+ struct msm_camera_cci_ctrl cci_ctrl;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || (data_type != MSM_CAMERA_I2C_BYTE_DATA
+ && data_type != MSM_CAMERA_I2C_WORD_DATA))
+ return rc;
+
+ cci_ctrl.cmd = MSM_CCI_I2C_READ;
+ cci_ctrl.cci_info = client->cci_client;
+ cci_ctrl.cfg.cci_i2c_read_cfg.addr = addr;
+ cci_ctrl.cfg.cci_i2c_read_cfg.addr_type = client->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(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 (data_type == MSM_CAMERA_I2C_BYTE_DATA)
+ *data = buf[0];
+ else
+ *data = buf[0] << 8 | buf[1];
+
+ S_I2C_DBG("%s addr = 0x%x data: 0x%x\n", __func__, addr, *data);
+ return rc;
+}
+
+int32_t msm_camera_cci_i2c_read_seq(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint8_t *data, uint16_t num_byte)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[client->addr_type+num_byte];
+ int i;
+ struct msm_camera_cci_ctrl cci_ctrl;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || num_byte == 0)
+ return rc;
+
+ cci_ctrl.cmd = MSM_CCI_I2C_READ;
+ cci_ctrl.cci_info = client->cci_client;
+ cci_ctrl.cfg.cci_i2c_read_cfg.addr = addr;
+ cci_ctrl.cfg.cci_i2c_read_cfg.addr_type = client->addr_type;
+ cci_ctrl.cfg.cci_i2c_read_cfg.data = buf;
+ cci_ctrl.cfg.cci_i2c_read_cfg.num_byte = num_byte;
+ rc = v4l2_subdev_call(client->cci_client->cci_subdev,
+ core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl);
+ CDBG("%s line %d rc = %d\n", __func__, __LINE__, rc);
+ rc = cci_ctrl.status;
+
+ S_I2C_DBG("%s addr = 0x%x", __func__, addr);
+ for (i = 0; i < num_byte; i++) {
+ data[i] = buf[i];
+ S_I2C_DBG("Byte %d: 0x%x\n", i, buf[i]);
+ S_I2C_DBG("Data: 0x%x\n", data[i]);
+ }
+ return rc;
+}
+
+int32_t msm_camera_cci_i2c_write(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EFAULT;
+ struct msm_camera_cci_ctrl cci_ctrl;
+ struct msm_camera_i2c_reg_conf reg_conf_tbl;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || (data_type != MSM_CAMERA_I2C_BYTE_DATA
+ && data_type != MSM_CAMERA_I2C_WORD_DATA))
+ return rc;
+
+ CDBG("%s:%d reg addr = 0x%x data type: %d\n",
+ __func__, __LINE__, addr, data_type);
+ reg_conf_tbl.reg_addr = addr;
+ reg_conf_tbl.reg_data = data;
+ cci_ctrl.cmd = MSM_CCI_I2C_WRITE;
+ cci_ctrl.cci_info = client->cci_client;
+ cci_ctrl.cfg.cci_i2c_write_cfg.reg_conf_tbl = ®_conf_tbl;
+ cci_ctrl.cfg.cci_i2c_write_cfg.data_type = data_type;
+ cci_ctrl.cfg.cci_i2c_write_cfg.addr_type = client->addr_type;
+ cci_ctrl.cfg.cci_i2c_write_cfg.size = 1;
+ 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;
+ return rc;
+}
+
+int32_t msm_camera_cci_i2c_write_seq(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint8_t *data, uint16_t num_byte)
+{
+ int32_t rc = -EFAULT;
+ uint8_t i = 0;
+ struct msm_camera_cci_ctrl cci_ctrl;
+ struct msm_camera_i2c_reg_conf reg_conf_tbl[num_byte];
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || num_byte == 0)
+ return rc;
+
+ S_I2C_DBG("%s reg addr = 0x%x num bytes: %d\n",
+ __func__, addr, num_byte);
+ reg_conf_tbl[0].reg_addr = addr;
+ for (i = 0; i < num_byte; i++)
+ reg_conf_tbl[i].reg_data = data[i];
+ cci_ctrl.cmd = MSM_CCI_I2C_WRITE;
+ cci_ctrl.cci_info = client->cci_client;
+ cci_ctrl.cfg.cci_i2c_write_cfg.reg_conf_tbl = reg_conf_tbl;
+ cci_ctrl.cfg.cci_i2c_write_cfg.size = num_byte;
+ rc = v4l2_subdev_call(client->cci_client->cci_subdev,
+ core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl);
+ CDBG("%s line %d rc = %d\n", __func__, __LINE__, rc);
+ rc = cci_ctrl.status;
+ return rc;
+}
+
+int32_t msm_camera_cci_i2c_write_table(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting)
+{
+ int i;
+ int32_t rc = -EFAULT;
+ struct msm_camera_i2c_reg_array *reg_setting;
+ uint16_t client_addr_type;
+
+ if (!client || !write_setting)
+ return rc;
+
+ if ((write_setting->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && write_setting->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || (write_setting->data_type != MSM_CAMERA_I2C_BYTE_DATA
+ && write_setting->data_type != MSM_CAMERA_I2C_WORD_DATA))
+ return rc;
+
+ reg_setting = write_setting->reg_setting;
+ client_addr_type = client->addr_type;
+ client->addr_type = write_setting->addr_type;
+
+ for (i = 0; i < write_setting->size; i++) {
+ rc = msm_camera_cci_i2c_write(client, reg_setting->reg_addr,
+ reg_setting->reg_data, write_setting->data_type);
+ if (rc < 0)
+ return rc;
+ reg_setting++;
+ }
+ 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);
+
+ client->addr_type = client_addr_type;
+ return rc;
+}
+
+int32_t msm_camera_cci_i2c_write_seq_table(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_seq_reg_setting *write_setting)
+{
+ int i;
+ int32_t rc = -EFAULT;
+ struct msm_camera_i2c_seq_reg_array *reg_setting;
+ uint16_t client_addr_type;
+
+ if (!client || !write_setting)
+ return rc;
+
+ if ((write_setting->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && write_setting->addr_type != MSM_CAMERA_I2C_WORD_ADDR)) {
+ pr_err("%s Invalide addr type %d\n", __func__,
+ write_setting->addr_type);
+ return rc;
+ }
+
+ reg_setting = write_setting->reg_setting;
+ client_addr_type = client->addr_type;
+ client->addr_type = write_setting->addr_type;
+
+ for (i = 0; i < write_setting->size; i++) {
+ rc = msm_camera_cci_i2c_write_seq(client, reg_setting->reg_addr,
+ reg_setting->reg_data, reg_setting->reg_data_size);
+ if (rc < 0)
+ return rc;
+ reg_setting++;
+ }
+ 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);
+
+ client->addr_type = client_addr_type;
+ return rc;
+}
+
+int32_t msm_camera_cci_i2c_write_table_w_microdelay(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_tbl *reg_tbl, uint16_t size,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int i;
+ int32_t rc = -EFAULT;
+
+ if (!client || !reg_tbl)
+ return rc;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || (data_type != MSM_CAMERA_I2C_BYTE_DATA
+ && data_type != MSM_CAMERA_I2C_WORD_DATA))
+ return rc;
+
+ for (i = 0; i < size; i++) {
+ rc = msm_camera_cci_i2c_write(client, reg_tbl->reg_addr,
+ reg_tbl->reg_data, data_type);
+ if (rc < 0)
+ return rc;
+ if (reg_tbl->delay)
+ usleep_range(reg_tbl->delay, reg_tbl->delay + 1000);
+ reg_tbl++;
+ }
+ return rc;
+}
+
+int32_t msm_sensor_cci_i2c_util(struct msm_camera_i2c_client *client,
+ uint16_t cci_cmd)
+{
+ int32_t rc = 0;
+ struct msm_camera_cci_ctrl cci_ctrl;
+
+ CDBG("%s line %d\n", __func__, __LINE__);
+ cci_ctrl.cmd = cci_cmd;
+ cci_ctrl.cci_info = client->cci_client;
+ 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;
+ }
+ return cci_ctrl.status;
+}
diff --git a/drivers/media/video/msmb/sensor/io/msm_camera_i2c.h b/drivers/media/video/msmb/sensor/io/msm_camera_i2c.h
new file mode 100644
index 0000000..aa38e62
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/io/msm_camera_i2c.h
@@ -0,0 +1,108 @@
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_CAMERA_CCI_I2C_H
+#define MSM_CAMERA_CCI_I2C_H
+
+#include <linux/delay.h>
+#include <media/v4l2-subdev.h>
+#include <media/msm_cam_sensor.h>
+
+struct msm_camera_i2c_client {
+ struct msm_camera_i2c_fn_t *i2c_func_tbl;
+ struct i2c_client *client;
+ struct msm_camera_cci_client *cci_client;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+};
+
+struct msm_camera_i2c_reg_tbl {
+ uint16_t reg_addr;
+ uint16_t reg_data;
+ uint16_t delay;
+};
+
+struct msm_camera_i2c_fn_t {
+ int (*i2c_read) (struct msm_camera_i2c_client *, uint16_t, uint16_t *,
+ enum msm_camera_i2c_data_type);
+ int32_t (*i2c_read_seq)(struct msm_camera_i2c_client *, uint16_t,
+ uint8_t *, uint16_t);
+ int (*i2c_write) (struct msm_camera_i2c_client *, uint16_t, uint16_t,
+ enum msm_camera_i2c_data_type);
+ int (*i2c_write_seq) (struct msm_camera_i2c_client *, uint16_t ,
+ uint8_t *, uint16_t);
+ int32_t (*i2c_write_table)(struct msm_camera_i2c_client *,
+ struct msm_camera_i2c_reg_setting *);
+ int32_t (*i2c_write_seq_table)(struct msm_camera_i2c_client *,
+ struct msm_camera_i2c_seq_reg_setting *);
+ int32_t (*i2c_write_table_w_microdelay)
+ (struct msm_camera_i2c_client *,
+ struct msm_camera_i2c_reg_tbl *, uint16_t,
+ enum msm_camera_i2c_data_type);
+ int32_t (*i2c_util)(struct msm_camera_i2c_client *, uint16_t);
+};
+
+int32_t msm_camera_cci_i2c_read(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint16_t *data,
+ enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_camera_cci_i2c_read_seq(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint8_t *data, uint16_t num_byte);
+
+int32_t msm_camera_cci_i2c_write(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_camera_cci_i2c_write_seq(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint8_t *data, uint16_t num_byte);
+
+int32_t msm_camera_cci_i2c_write_table(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting);
+
+int32_t msm_camera_cci_i2c_write_seq_table(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_seq_reg_setting *write_setting);
+
+int32_t msm_camera_cci_i2c_write_table_w_microdelay(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_tbl *reg_tbl, uint16_t size,
+ enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_sensor_cci_i2c_util(struct msm_camera_i2c_client *client,
+ uint16_t cci_cmd);
+
+int32_t msm_camera_qup_i2c_read(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint16_t *data,
+ enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_camera_qup_i2c_read_seq(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint8_t *data, uint16_t num_byte);
+
+int32_t msm_camera_qup_i2c_write(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_camera_qup_i2c_write_seq(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint8_t *data, uint16_t num_byte);
+
+int32_t msm_camera_qup_i2c_write_table(struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting);
+
+int32_t msm_camera_qup_i2c_write_seq_table(struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_seq_reg_setting *write_setting);
+
+int32_t msm_camera_qup_i2c_write_table_w_microdelay(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_tbl *reg_tbl, uint16_t size,
+ enum msm_camera_i2c_data_type data_type);
+
+#endif
diff --git a/drivers/media/video/msmb/sensor/io/msm_camera_i2c_mux.c b/drivers/media/video/msmb/sensor/io/msm_camera_i2c_mux.c
new file mode 100644
index 0000000..49759e6
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/io/msm_camera_i2c_mux.c
@@ -0,0 +1,188 @@
+/* Copyright (c) 2011-2013, The Linux Foundatation. 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/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include "msm_camera_i2c_mux.h"
+
+/* TODO move this somewhere else */
+#define MSM_I2C_MUX_DRV_NAME "msm_cam_i2c_mux"
+static int msm_i2c_mux_config(struct i2c_mux_device *mux_device, uint8_t *mode)
+{
+ uint32_t val;
+ val = msm_camera_io_r(mux_device->ctl_base);
+ if (*mode == MODE_DUAL) {
+ msm_camera_io_w(val | 0x3, mux_device->ctl_base);
+ } else if (*mode == MODE_L) {
+ msm_camera_io_w(((val | 0x2) & ~(0x1)), mux_device->ctl_base);
+ val = msm_camera_io_r(mux_device->ctl_base);
+ CDBG("the camio mode config left value is %d\n", val);
+ } else {
+ msm_camera_io_w(((val | 0x1) & ~(0x2)), mux_device->ctl_base);
+ val = msm_camera_io_r(mux_device->ctl_base);
+ CDBG("the camio mode config right value is %d\n", val);
+ }
+ return 0;
+}
+
+static int msm_i2c_mux_init(struct i2c_mux_device *mux_device)
+{
+ int rc = 0, val = 0;
+ if (mux_device->use_count == 0) {
+ mux_device->ctl_base = ioremap(mux_device->ctl_mem->start,
+ resource_size(mux_device->ctl_mem));
+ if (!mux_device->ctl_base) {
+ rc = -ENOMEM;
+ return rc;
+ }
+ mux_device->rw_base = ioremap(mux_device->rw_mem->start,
+ resource_size(mux_device->rw_mem));
+ if (!mux_device->rw_base) {
+ rc = -ENOMEM;
+ iounmap(mux_device->ctl_base);
+ return rc;
+ }
+ val = msm_camera_io_r(mux_device->rw_base);
+ msm_camera_io_w((val | 0x200), mux_device->rw_base);
+ }
+ mux_device->use_count++;
+ return 0;
+};
+
+static int msm_i2c_mux_release(struct i2c_mux_device *mux_device)
+{
+ int val = 0;
+ mux_device->use_count--;
+ if (mux_device->use_count == 0) {
+ val = msm_camera_io_r(mux_device->rw_base);
+ msm_camera_io_w((val & ~0x200), mux_device->rw_base);
+ iounmap(mux_device->rw_base);
+ iounmap(mux_device->ctl_base);
+ }
+ return 0;
+}
+
+static long msm_i2c_mux_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct i2c_mux_device *mux_device;
+ int rc = 0;
+ mux_device = v4l2_get_subdevdata(sd);
+ if (mux_device == NULL) {
+ rc = -ENOMEM;
+ return rc;
+ }
+ mutex_lock(&mux_device->mutex);
+ switch (cmd) {
+ case VIDIOC_MSM_I2C_MUX_CFG:
+ rc = msm_i2c_mux_config(mux_device, (uint8_t *) arg);
+ break;
+ case VIDIOC_MSM_I2C_MUX_INIT:
+ rc = msm_i2c_mux_init(mux_device);
+ break;
+ case VIDIOC_MSM_I2C_MUX_RELEASE:
+ rc = msm_i2c_mux_release(mux_device);
+ break;
+ default:
+ rc = -ENOIOCTLCMD;
+ }
+ mutex_unlock(&mux_device->mutex);
+ return rc;
+}
+
+static struct v4l2_subdev_core_ops msm_i2c_mux_subdev_core_ops = {
+ .ioctl = &msm_i2c_mux_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_i2c_mux_subdev_ops = {
+ .core = &msm_i2c_mux_subdev_core_ops,
+};
+
+static int __devinit i2c_mux_probe(struct platform_device *pdev)
+{
+ struct i2c_mux_device *mux_device;
+ int rc = 0;
+ CDBG("%s: device id = %d\n", __func__, pdev->id);
+ mux_device = kzalloc(sizeof(struct i2c_mux_device), GFP_KERNEL);
+ if (!mux_device) {
+ pr_err("%s: no enough memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ v4l2_subdev_init(&mux_device->subdev, &msm_i2c_mux_subdev_ops);
+ v4l2_set_subdevdata(&mux_device->subdev, mux_device);
+ platform_set_drvdata(pdev, &mux_device->subdev);
+ mutex_init(&mux_device->mutex);
+
+ mux_device->ctl_mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "i2c_mux_ctl");
+ if (!mux_device->ctl_mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto i2c_mux_no_resource;
+ }
+ mux_device->ctl_io = request_mem_region(mux_device->ctl_mem->start,
+ resource_size(mux_device->ctl_mem), pdev->name);
+ if (!mux_device->ctl_io) {
+ pr_err("%s: no valid mem region\n", __func__);
+ rc = -EBUSY;
+ goto i2c_mux_no_resource;
+ }
+ mux_device->rw_mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "i2c_mux_rw");
+ if (!mux_device->rw_mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto i2c_mux_no_resource;
+ }
+ mux_device->rw_io = request_mem_region(mux_device->rw_mem->start,
+ resource_size(mux_device->rw_mem), pdev->name);
+ if (!mux_device->rw_io) {
+ pr_err("%s: no valid mem region\n", __func__);
+ rc = -EBUSY;
+ goto i2c_mux_no_resource;
+ }
+ mux_device->pdev = pdev;
+ return 0;
+
+i2c_mux_no_resource:
+ mutex_destroy(&mux_device->mutex);
+ kfree(mux_device);
+ return 0;
+}
+
+static struct platform_driver i2c_mux_driver = {
+ .probe = i2c_mux_probe,
+ .driver = {
+ .name = MSM_I2C_MUX_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_camera_i2c_mux_init_module(void)
+{
+ return platform_driver_register(&i2c_mux_driver);
+}
+
+static void __exit msm_camera_i2c_mux_exit_module(void)
+{
+ platform_driver_unregister(&i2c_mux_driver);
+}
+
+module_init(msm_camera_i2c_mux_init_module);
+module_exit(msm_camera_i2c_mux_exit_module);
+MODULE_DESCRIPTION("MSM Camera I2C mux driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msmb/sensor/io/msm_camera_i2c_mux.h b/drivers/media/video/msmb/sensor/io/msm_camera_i2c_mux.h
new file mode 100644
index 0000000..30f908b
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/io/msm_camera_i2c_mux.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_I2C_MUX_H
+#define MSM_I2C_MUX_H
+
+#include <linux/io.h>
+#include <media/v4l2-subdev.h>
+
+struct i2c_mux_device {
+ struct platform_device *pdev;
+ struct v4l2_subdev subdev;
+ struct resource *ctl_mem;
+ struct resource *ctl_io;
+ void __iomem *ctl_base;
+ struct resource *rw_mem;
+ struct resource *rw_io;
+ void __iomem *rw_base;
+ struct mutex mutex;
+ unsigned use_count;
+};
+
+struct i2c_mux_cfg_params {
+ struct v4l2_subdev *subdev;
+ void *parms;
+};
+
+#define VIDIOC_MSM_I2C_MUX_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 13, struct i2c_mux_cfg_params)
+
+#define VIDIOC_MSM_I2C_MUX_INIT \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 14, struct v4l2_subdev*)
+
+#define VIDIOC_MSM_I2C_MUX_RELEASE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct v4l2_subdev*)
+
+#endif
diff --git a/drivers/media/video/msmb/sensor/io/msm_camera_io_util.c b/drivers/media/video/msmb/sensor/io/msm_camera_io_util.c
new file mode 100644
index 0000000..0f41a68
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/io/msm_camera_io_util.c
@@ -0,0 +1,523 @@
+/* Copyright (c) 2011-2013, The Linux Foundataion. 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/clk.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <mach/camera2.h>
+#include <mach/gpiomux.h>
+#include <mach/msm_bus.h>
+#include "msm_camera_io_util.h"
+
+#define BUFF_SIZE_128 128
+
+#define CONFIG_MSMB_CAMERA_DEBUG
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+void msm_camera_io_w(u32 data, void __iomem *addr)
+{
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ writel_relaxed((data), (addr));
+}
+
+void msm_camera_io_w_mb(u32 data, void __iomem *addr)
+{
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ wmb();
+ writel_relaxed((data), (addr));
+ wmb();
+}
+
+u32 msm_camera_io_r(void __iomem *addr)
+{
+ uint32_t data = readl_relaxed(addr);
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ return data;
+}
+
+u32 msm_camera_io_r_mb(void __iomem *addr)
+{
+ uint32_t data;
+ rmb();
+ data = readl_relaxed(addr);
+ rmb();
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ return data;
+}
+
+void msm_camera_io_memcpy_toio(void __iomem *dest_addr,
+ void __iomem *src_addr, u32 len)
+{
+ int i;
+ u32 *d = (u32 *) dest_addr;
+ u32 *s = (u32 *) src_addr;
+
+ for (i = 0; i < len; i++)
+ writel_relaxed(*s++, d++);
+}
+
+void msm_camera_io_dump(void __iomem *addr, int size)
+{
+ char line_str[BUFF_SIZE_128], *p_str;
+ int i;
+ u32 *p = (u32 *) addr;
+ u32 data;
+ CDBG("%s: %p %d\n", __func__, addr, size);
+ line_str[0] = '\0';
+ p_str = line_str;
+ for (i = 0; i < size/4; i++) {
+ if (i % 4 == 0) {
+ snprintf(p_str, 12, "%08x: ", (u32) p);
+ p_str += 10;
+ }
+ data = readl_relaxed(p++);
+ snprintf(p_str, 12, "%08x ", data);
+ p_str += 9;
+ if ((i + 1) % 4 == 0) {
+ CDBG("%s\n", line_str);
+ line_str[0] = '\0';
+ p_str = line_str;
+ }
+ }
+ if (line_str[0] != '\0')
+ CDBG("%s\n", line_str);
+}
+
+void msm_camera_io_memcpy(void __iomem *dest_addr,
+ void __iomem *src_addr, u32 len)
+{
+ CDBG("%s: %p %p %d\n", __func__, dest_addr, src_addr, len);
+ msm_camera_io_memcpy_toio(dest_addr, src_addr, len / 4);
+ msm_camera_io_dump(dest_addr, len);
+}
+
+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;
+ 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) {
+ rc = clk_set_rate(clk_ptr[i],
+ clk_info[i].clk_rate);
+ if (rc < 0) {
+ pr_err("%s set 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 (curr_vreg->type == REG_LDO) {
+ 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_optimum_mode(
+ 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 (curr_vreg->type == REG_LDO) {
+ if (curr_vreg->op_mode >= 0) {
+ regulator_set_optimum_mode(
+ 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 (curr_vreg->type == REG_LDO)
+ regulator_set_optimum_mode(reg_ptr[j], 0);
+
+vreg_set_opt_mode_fail:
+if (curr_vreg->type == REG_LDO)
+ 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;
+}
+
+void msm_camera_bus_scale_cfg(uint32_t bus_perf_client,
+ enum msm_bus_perf_setting perf_setting)
+{
+ int rc = 0;
+ if (!bus_perf_client) {
+ pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+ return;
+ }
+
+ switch (perf_setting) {
+ case S_EXIT:
+ rc = msm_bus_scale_client_update_request(bus_perf_client, 1);
+ msm_bus_scale_unregister_client(bus_perf_client);
+ break;
+ case S_PREVIEW:
+ rc = msm_bus_scale_client_update_request(bus_perf_client, 1);
+ break;
+ case S_VIDEO:
+ rc = msm_bus_scale_client_update_request(bus_perf_client, 2);
+ break;
+ case S_CAPTURE:
+ rc = msm_bus_scale_client_update_request(bus_perf_client, 3);
+ break;
+ case S_ZSL:
+ rc = msm_bus_scale_client_update_request(bus_perf_client, 4);
+ break;
+ case S_LIVESHOT:
+ rc = msm_bus_scale_client_update_request(bus_perf_client, 5);
+ break;
+ case S_DEFAULT:
+ break;
+ default:
+ pr_warning("%s: INVALID CASE\n", __func__);
+ }
+}
+
+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;
+ if (config) {
+ CDBG("%s enable %s\n", __func__, cam_vreg->reg_name);
+ *reg_ptr = regulator_get(dev, cam_vreg->reg_name);
+ if (IS_ERR(*reg_ptr)) {
+ pr_err("%s: %s get failed\n", __func__,
+ cam_vreg->reg_name);
+ *reg_ptr = NULL;
+ goto vreg_get_fail;
+ }
+ if (cam_vreg->type == REG_LDO) {
+ 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__, cam_vreg->reg_name);
+ goto vreg_set_voltage_fail;
+ }
+ if (cam_vreg->op_mode >= 0) {
+ rc = regulator_set_optimum_mode(*reg_ptr,
+ cam_vreg->op_mode);
+ if (rc < 0) {
+ pr_err(
+ "%s: %s set optimum mode failed\n",
+ __func__, cam_vreg->reg_name);
+ goto vreg_set_opt_mode_fail;
+ }
+ }
+ }
+ rc = regulator_enable(*reg_ptr);
+ if (rc < 0) {
+ pr_err("%s: %s enable failed\n",
+ __func__, cam_vreg->reg_name);
+ goto vreg_unconfig;
+ }
+ } else {
+ if (*reg_ptr) {
+ CDBG("%s disable %s\n", __func__, cam_vreg->reg_name);
+ regulator_disable(*reg_ptr);
+ if (cam_vreg->type == REG_LDO) {
+ if (cam_vreg->op_mode >= 0)
+ regulator_set_optimum_mode(*reg_ptr, 0);
+ regulator_set_voltage(
+ *reg_ptr, 0, cam_vreg->max_voltage);
+ }
+ regulator_put(*reg_ptr);
+ *reg_ptr = NULL;
+ }
+ }
+ return 0;
+
+vreg_unconfig:
+if (cam_vreg->type == REG_LDO)
+ regulator_set_optimum_mode(*reg_ptr, 0);
+
+vreg_set_opt_mode_fail:
+if (cam_vreg->type == REG_LDO)
+ 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 -ENODEV;
+}
+
+int msm_camera_request_gpio_table(struct gpio *gpio_tbl, uint8_t size,
+ int gpio_en)
+{
+ int rc = 0, i = 0;
+
+ if (!gpio_tbl || !size) {
+ pr_err("%s:%d invalid gpio_tbl %p / 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) {
+ rc = gpio_request_array(gpio_tbl, size);
+ if (rc < 0) {
+ pr_err("%s:%d camera gpio request failed\n", __func__,
+ __LINE__);
+ return rc;
+ }
+ } else {
+ gpio_free_array(gpio_tbl, size);
+ }
+ return rc;
+}
diff --git a/drivers/media/video/msmb/sensor/io/msm_camera_io_util.h b/drivers/media/video/msmb/sensor/io/msm_camera_io_util.h
new file mode 100644
index 0000000..499a045
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/io/msm_camera_io_util.h
@@ -0,0 +1,54 @@
+/* Copyright (c) 2011-2013, The Linux Foundataion. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_CAMERA_IO_UTIL_H
+#define __MSM_CAMERA_IO_UTIL_H
+
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+#include <mach/camera2.h>
+#include <media/msm_cam_sensor.h>
+
+void msm_camera_io_w(u32 data, void __iomem *addr);
+void msm_camera_io_w_mb(u32 data, void __iomem *addr);
+u32 msm_camera_io_r(void __iomem *addr);
+u32 msm_camera_io_r_mb(void __iomem *addr);
+void msm_camera_io_dump(void __iomem *addr, int size);
+void msm_camera_io_memcpy(void __iomem *dest_addr,
+ void __iomem *src_addr, u32 len);
+
+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 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 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);
+
+void msm_camera_bus_scale_cfg(uint32_t bus_perf_client,
+ enum msm_bus_perf_setting perf_setting);
+
+int msm_camera_set_gpio_table(struct msm_gpio_set_tbl *gpio_tbl,
+ uint8_t gpio_tbl_size, int gpio_en);
+
+void msm_camera_config_single_gpio(uint16_t gpio, unsigned long flags,
+ int gpio_en);
+
+int msm_camera_config_single_vreg(struct device *dev,
+ struct camera_vreg_t *cam_vreg, struct regulator **reg_ptr, int config);
+
+int msm_camera_request_gpio_table(struct gpio *gpio_tbl, uint8_t size,
+ int gpio_en);
+
+#endif
diff --git a/drivers/media/video/msmb/sensor/io/msm_camera_qup_i2c.c b/drivers/media/video/msmb/sensor/io/msm_camera_qup_i2c.c
new file mode 100644
index 0000000..d2cfbff
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/io/msm_camera_qup_i2c.c
@@ -0,0 +1,333 @@
+/* Copyright (c) 2011, 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 <mach/camera2.h>
+#include "msm_camera_i2c.h"
+
+#define CONFIG_MSMB_CAMERA_DEBUG
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#define S_I2C_DBG(fmt, args...) pr_debug(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#define S_I2C_DBG(fmt, args...) do { } while (0)
+#endif
+
+static int32_t msm_camera_qup_i2c_rxdata(
+ struct msm_camera_i2c_client *dev_client, unsigned char *rxdata,
+ int data_length)
+{
+ int32_t rc = 0;
+ uint16_t saddr = dev_client->client->addr >> 1;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = dev_client->addr_type,
+ .buf = rxdata,
+ },
+ {
+ .addr = saddr,
+ .flags = I2C_M_RD,
+ .len = data_length,
+ .buf = rxdata,
+ },
+ };
+ rc = i2c_transfer(dev_client->client->adapter, msgs, 2);
+ if (rc < 0)
+ S_I2C_DBG("msm_camera_qup_i2c_rxdata failed 0x%x\n", saddr);
+ return rc;
+}
+
+static int32_t msm_camera_qup_i2c_txdata(
+ struct msm_camera_i2c_client *dev_client, unsigned char *txdata,
+ int length)
+{
+ int32_t rc = 0;
+ uint16_t saddr = dev_client->client->addr >> 1;
+ struct i2c_msg msg[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = length,
+ .buf = txdata,
+ },
+ };
+ rc = i2c_transfer(dev_client->client->adapter, msg, 1);
+ if (rc < 0)
+ S_I2C_DBG("msm_camera_qup_i2c_txdata faild 0x%x\n", saddr);
+ return 0;
+}
+
+int32_t msm_camera_qup_i2c_read(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint16_t *data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[client->addr_type+data_type];
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || (data_type != MSM_CAMERA_I2C_BYTE_DATA
+ && data_type != MSM_CAMERA_I2C_WORD_DATA))
+ return rc;
+
+ if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) {
+ buf[0] = addr;
+ } else if (client->addr_type == MSM_CAMERA_I2C_WORD_ADDR) {
+ buf[0] = addr >> BITS_PER_BYTE;
+ buf[1] = addr;
+ }
+ rc = msm_camera_qup_i2c_rxdata(client, buf, data_type);
+ if (rc < 0) {
+ S_I2C_DBG("%s fail\n", __func__);
+ return rc;
+ }
+
+ if (data_type == MSM_CAMERA_I2C_BYTE_DATA)
+ *data = buf[0];
+ else
+ *data = buf[0] << 8 | buf[1];
+
+ S_I2C_DBG("%s addr = 0x%x data: 0x%x\n", __func__, addr, *data);
+ return rc;
+}
+
+int32_t msm_camera_qup_i2c_read_seq(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint8_t *data, uint16_t num_byte)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[client->addr_type+num_byte];
+ int i;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || num_byte == 0)
+ return rc;
+
+ if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) {
+ buf[0] = addr;
+ } else if (client->addr_type == MSM_CAMERA_I2C_WORD_ADDR) {
+ buf[0] = addr >> BITS_PER_BYTE;
+ buf[1] = addr;
+ }
+ rc = msm_camera_qup_i2c_rxdata(client, buf, num_byte);
+ if (rc < 0) {
+ S_I2C_DBG("%s fail\n", __func__);
+ return rc;
+ }
+
+ S_I2C_DBG("%s addr = 0x%x", __func__, addr);
+ for (i = 0; i < num_byte; i++) {
+ data[i] = buf[i];
+ S_I2C_DBG("Byte %d: 0x%x\n", i, buf[i]);
+ S_I2C_DBG("Data: 0x%x\n", data[i]);
+ }
+ return rc;
+}
+
+int32_t msm_camera_qup_i2c_write(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[client->addr_type+data_type];
+ uint8_t len = 0;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || (data_type != MSM_CAMERA_I2C_BYTE_DATA
+ && data_type != MSM_CAMERA_I2C_WORD_DATA))
+ return rc;
+
+ S_I2C_DBG("%s reg addr = 0x%x data type: %d\n",
+ __func__, addr, data_type);
+ if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) {
+ buf[0] = addr;
+ S_I2C_DBG("%s byte %d: 0x%x\n", __func__,
+ len, buf[len]);
+ len = 1;
+ } else if (client->addr_type == MSM_CAMERA_I2C_WORD_ADDR) {
+ buf[0] = addr >> BITS_PER_BYTE;
+ buf[1] = addr;
+ S_I2C_DBG("%s byte %d: 0x%x\n", __func__,
+ len, buf[len]);
+ S_I2C_DBG("%s byte %d: 0x%x\n", __func__,
+ len+1, buf[len+1]);
+ len = 2;
+ }
+ S_I2C_DBG("Data: 0x%x\n", data);
+ if (data_type == MSM_CAMERA_I2C_BYTE_DATA) {
+ buf[len] = data;
+ S_I2C_DBG("Byte %d: 0x%x\n", len, buf[len]);
+ len += 1;
+ } else if (data_type == MSM_CAMERA_I2C_WORD_DATA) {
+ buf[len] = data >> BITS_PER_BYTE;
+ buf[len+1] = data;
+ S_I2C_DBG("Byte %d: 0x%x\n", len, buf[len]);
+ S_I2C_DBG("Byte %d: 0x%x\n", len+1, buf[len+1]);
+ len += 2;
+ }
+ rc = msm_camera_qup_i2c_txdata(client, buf, len);
+ if (rc < 0)
+ S_I2C_DBG("%s fail\n", __func__);
+ return rc;
+}
+
+int32_t msm_camera_qup_i2c_write_seq(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint8_t *data, uint16_t num_byte)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[client->addr_type+num_byte];
+ uint8_t len = 0, i = 0;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || num_byte == 0)
+ return rc;
+
+ S_I2C_DBG("%s reg addr = 0x%x num bytes: %d\n",
+ __func__, addr, num_byte);
+ if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) {
+ buf[0] = addr;
+ S_I2C_DBG("%s byte %d: 0x%x\n", __func__,
+ len, buf[len]);
+ len = 1;
+ } else if (client->addr_type == MSM_CAMERA_I2C_WORD_ADDR) {
+ buf[0] = addr >> BITS_PER_BYTE;
+ buf[1] = addr;
+ S_I2C_DBG("%s byte %d: 0x%x\n", __func__,
+ len, buf[len]);
+ S_I2C_DBG("%s byte %d: 0x%x\n", __func__,
+ len+1, buf[len+1]);
+ len = 2;
+ }
+ for (i = 0; i < num_byte; i++) {
+ buf[i+len] = data[i];
+ S_I2C_DBG("Byte %d: 0x%x\n", i+len, buf[i+len]);
+ S_I2C_DBG("Data: 0x%x\n", data[i]);
+ }
+ rc = msm_camera_qup_i2c_txdata(client, buf, len+num_byte);
+ if (rc < 0)
+ S_I2C_DBG("%s fail\n", __func__);
+ return rc;
+}
+
+int32_t msm_camera_qup_i2c_write_table(struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting)
+{
+ int i;
+ int32_t rc = -EFAULT;
+ struct msm_camera_i2c_reg_array *reg_setting;
+ uint16_t client_addr_type;
+
+ if (!client || !write_setting)
+ return rc;
+
+ if ((write_setting->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && write_setting->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || (write_setting->data_type != MSM_CAMERA_I2C_BYTE_DATA
+ && write_setting->data_type != MSM_CAMERA_I2C_WORD_DATA))
+ return rc;
+
+ reg_setting = write_setting->reg_setting;
+ client_addr_type = client->addr_type;
+ client->addr_type = write_setting->addr_type;
+
+ for (i = 0; i < write_setting->size; i++) {
+ CDBG("%s addr %x data %x\n", __func__,
+ reg_setting->reg_addr, reg_setting->reg_data);
+
+ rc = msm_camera_qup_i2c_write(client, reg_setting->reg_addr,
+ reg_setting->reg_data, write_setting->data_type);
+ if (rc < 0)
+ break;
+ reg_setting++;
+ }
+ 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);
+
+ client->addr_type = client_addr_type;
+ return rc;
+}
+
+int32_t msm_camera_qup_i2c_write_seq_table(struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_seq_reg_setting *write_setting)
+{
+ int i;
+ int32_t rc = -EFAULT;
+ struct msm_camera_i2c_seq_reg_array *reg_setting;
+ uint16_t client_addr_type;
+
+ if (!client || !write_setting)
+ return rc;
+
+ if ((write_setting->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && write_setting->addr_type != MSM_CAMERA_I2C_WORD_ADDR)) {
+ pr_err("%s Invalide addr type %d\n", __func__,
+ write_setting->addr_type);
+ return rc;
+ }
+
+ reg_setting = write_setting->reg_setting;
+ client_addr_type = client->addr_type;
+ client->addr_type = write_setting->addr_type;
+
+ for (i = 0; i < write_setting->size; i++) {
+ rc = msm_camera_qup_i2c_write_seq(client, reg_setting->reg_addr,
+ reg_setting->reg_data, reg_setting->reg_data_size);
+ if (rc < 0)
+ break;
+ reg_setting++;
+ }
+ 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);
+
+ client->addr_type = client_addr_type;
+ return rc;
+}
+
+int32_t msm_camera_qup_i2c_write_table_w_microdelay(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_tbl *reg_tbl, uint16_t size,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int i;
+ int32_t rc = -EFAULT;
+
+ if (!client || !reg_tbl)
+ return rc;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || (data_type != MSM_CAMERA_I2C_BYTE_DATA
+ && data_type != MSM_CAMERA_I2C_WORD_DATA))
+ return rc;
+
+ for (i = 0; i < size; i++) {
+ rc = msm_camera_qup_i2c_write(client, reg_tbl->reg_addr,
+ reg_tbl->reg_data, data_type);
+ if (rc < 0)
+ break;
+ if (reg_tbl->delay)
+ usleep_range(reg_tbl->delay, reg_tbl->delay + 1000);
+ reg_tbl++;
+ }
+ return rc;
+}
diff --git a/drivers/media/video/msmb/sensor/msm_sensor.c b/drivers/media/video/msmb/sensor/msm_sensor.c
new file mode 100644
index 0000000..1f54951
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/msm_sensor.c
@@ -0,0 +1,1537 @@
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 <mach/gpiomux.h>
+#include "msm_sensor.h"
+#include "msm_sd.h"
+#include "camera.h"
+#include "msm_cci.h"
+#include "msm_camera_io_util.h"
+#include "msm_camera_i2c_mux.h"
+
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+static int32_t msm_sensor_enable_i2c_mux(struct msm_camera_i2c_conf *i2c_conf)
+{
+ struct v4l2_subdev *i2c_mux_sd =
+ dev_get_drvdata(&i2c_conf->mux_dev->dev);
+ v4l2_subdev_call(i2c_mux_sd, core, ioctl,
+ VIDIOC_MSM_I2C_MUX_INIT, NULL);
+ v4l2_subdev_call(i2c_mux_sd, core, ioctl,
+ VIDIOC_MSM_I2C_MUX_CFG, (void *)&i2c_conf->i2c_mux_mode);
+ return 0;
+}
+
+static int32_t msm_sensor_disable_i2c_mux(struct msm_camera_i2c_conf *i2c_conf)
+{
+ struct v4l2_subdev *i2c_mux_sd =
+ dev_get_drvdata(&i2c_conf->mux_dev->dev);
+ v4l2_subdev_call(i2c_mux_sd, core, ioctl,
+ VIDIOC_MSM_I2C_MUX_RELEASE, NULL);
+ return 0;
+}
+
+static int32_t msm_sensor_get_sub_module_index(struct device_node *of_node,
+ struct msm_camera_sensor_board_info *sensordata)
+{
+ int32_t rc = 0, i = 0;
+ uint32_t val = 0, count = 0;
+ uint32_t *val_array = NULL;
+
+ sensordata->sensor_info = kzalloc(sizeof(struct msm_sensor_info_t),
+ GFP_KERNEL);
+ if (!sensordata->sensor_info) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+ for (i = 0; i < SUB_MODULE_MAX; i++)
+ sensordata->sensor_info->subdev_id[i] = -1;
+
+ if (of_property_read_bool(of_node, "qcom,actuator-sd-index") ==
+ true) {
+ rc = of_property_read_u32(of_node, "qcom,actuator-sd-index",
+ &val);
+ CDBG("%s qcom,actuator-sd-index %d, rc %d\n", __func__, val,
+ rc);
+ if (rc < 0) {
+ pr_err("%s:%d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ sensordata->sensor_info->subdev_id[SUB_MODULE_ACTUATOR] = val;
+ }
+
+ if (of_property_read_bool(of_node, "qcom,eeprom-sd-index") ==
+ true) {
+ rc = of_property_read_u32(of_node, "qcom,eeprom-sd-index",
+ &val);
+ CDBG("%s qcom,eeprom-sd-index %d, rc %d\n", __func__, val, rc);
+ if (rc < 0) {
+ pr_err("%s:%d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ sensordata->sensor_info->subdev_id[SUB_MODULE_EEPROM] = val;
+ }
+
+ if (of_property_read_bool(of_node, "qcom,led-flash-sd-index") ==
+ true) {
+ rc = of_property_read_u32(of_node, "qcom,led-flash-sd-index",
+ &val);
+ CDBG("%s qcom,led-flash-sd-index %d, rc %d\n", __func__, val,
+ rc);
+ if (rc < 0) {
+ pr_err("%s:%d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ sensordata->sensor_info->subdev_id[SUB_MODULE_LED_FLASH] = val;
+ }
+
+ if (of_property_read_bool(of_node, "qcom,strobe-flash-sd-index") ==
+ true) {
+ rc = of_property_read_u32(of_node, "qcom,strobe-flash-sd-index",
+ &val);
+ CDBG("%s qcom,strobe-flash-sd-index %d, rc %d\n", __func__,
+ val, rc);
+ if (rc < 0) {
+ pr_err("%s:%d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ sensordata->sensor_info->subdev_id[SUB_MODULE_STROBE_FLASH] =
+ val;
+ }
+
+ if (of_get_property(of_node, "qcom,csiphy-sd-index", &count)) {
+ count /= sizeof(uint32_t);
+ if (count > 2) {
+ pr_err("%s qcom,csiphy-sd-index count %d > 2\n",
+ __func__, count);
+ return -EINVAL;
+ }
+ val_array = kzalloc(sizeof(uint32_t) * count, GFP_KERNEL);
+ if (!val_array) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,csiphy-sd-index",
+ val_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ kfree(val_array);
+ return rc;
+ }
+ for (i = 0; i < count; i++) {
+ sensordata->sensor_info->subdev_id
+ [SUB_MODULE_CSIPHY + i] = val_array[i];
+ CDBG("%s csiphy_core[%d] = %d\n",
+ __func__, i, val_array[i]);
+ }
+ kfree(val_array);
+ } else {
+ pr_err("%s:%d qcom,csiphy-sd-index not present\n", __func__,
+ __LINE__);
+ return -EINVAL;
+ }
+
+ if (of_get_property(of_node, "qcom,csid-sd-index", &count)) {
+ count /= sizeof(uint32_t);
+ if (count > 2) {
+ pr_err("%s qcom,csid-sd-index count %d > 2\n",
+ __func__, count);
+ return -EINVAL;
+ }
+ val_array = kzalloc(sizeof(uint32_t) * count, GFP_KERNEL);
+ if (!val_array) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,csid-sd-index",
+ val_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ kfree(val_array);
+ return rc;
+ }
+ for (i = 0; i < count; i++) {
+ sensordata->sensor_info->subdev_id
+ [SUB_MODULE_CSID + i] = val_array[i];
+ CDBG("%s csid_core[%d] = %d\n",
+ __func__, i, val_array[i]);
+ }
+ kfree(val_array);
+ } else {
+ pr_err("%s:%d qcom,csid-sd-index not present\n", __func__,
+ __LINE__);
+ return -EINVAL;
+ }
+ return rc;
+}
+
+static int32_t msm_sensor_get_dt_csi_data(struct device_node *of_node,
+ struct msm_camera_sensor_board_info *sensordata)
+{
+ int32_t rc = 0;
+ uint32_t val = 0;
+
+ sensordata->csi_lane_params = kzalloc(
+ sizeof(struct msm_camera_csi_lane_params), GFP_KERNEL);
+ if (!sensordata->csi_lane_params) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto ERROR1;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,csi-lane-assign", &val);
+ CDBG("%s qcom,csi-lane-assign %x, rc %d\n", __func__, val, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ sensordata->csi_lane_params->csi_lane_assign = val;
+
+ rc = of_property_read_u32(of_node, "qcom,csi-lane-mask", &val);
+ CDBG("%s qcom,csi-lane-mask %x, rc %d\n", __func__, val, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ sensordata->csi_lane_params->csi_lane_mask = val;
+
+ return rc;
+ERROR2:
+ kfree(sensordata->csi_lane_params);
+ERROR1:
+ return rc;
+}
+
+static int32_t msm_sensor_get_dt_vreg_data(struct device_node *of_node,
+ struct msm_camera_sensor_board_info *sensordata)
+{
+ int32_t rc = 0, i = 0;
+ uint32_t count = 0;
+ uint32_t *vreg_array = 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)
+ return 0;
+
+ sensordata->cam_vreg = kzalloc(sizeof(struct camera_vreg_t) * count,
+ GFP_KERNEL);
+ if (!sensordata->cam_vreg) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ sensordata->num_vreg = count;
+ for (i = 0; i < count; i++) {
+ rc = of_property_read_string_index(of_node,
+ "qcom,cam-vreg-name", i,
+ &sensordata->cam_vreg[i].reg_name);
+ CDBG("%s reg_name[%d] = %s\n", __func__, i,
+ sensordata->cam_vreg[i].reg_name);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR1;
+ }
+ }
+
+ vreg_array = kzalloc(sizeof(uint32_t) * count, GFP_KERNEL);
+ if (!vreg_array) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto ERROR1;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,cam-vreg-type",
+ vreg_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ for (i = 0; i < count; i++) {
+ sensordata->cam_vreg[i].type = vreg_array[i];
+ CDBG("%s cam_vreg[%d].type = %d\n", __func__, i,
+ sensordata->cam_vreg[i].type);
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,cam-vreg-min-voltage",
+ vreg_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ for (i = 0; i < count; i++) {
+ sensordata->cam_vreg[i].min_voltage = vreg_array[i];
+ CDBG("%s cam_vreg[%d].min_voltage = %d\n", __func__,
+ i, sensordata->cam_vreg[i].min_voltage);
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,cam-vreg-max-voltage",
+ vreg_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ for (i = 0; i < count; i++) {
+ sensordata->cam_vreg[i].max_voltage = vreg_array[i];
+ CDBG("%s cam_vreg[%d].max_voltage = %d\n", __func__,
+ i, sensordata->cam_vreg[i].max_voltage);
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,cam-vreg-op-mode",
+ vreg_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ for (i = 0; i < count; i++) {
+ sensordata->cam_vreg[i].op_mode = vreg_array[i];
+ CDBG("%s cam_vreg[%d].op_mode = %d\n", __func__, i,
+ sensordata->cam_vreg[i].op_mode);
+ }
+
+ kfree(vreg_array);
+ return rc;
+ERROR2:
+ kfree(vreg_array);
+ERROR1:
+ kfree(sensordata->cam_vreg);
+ sensordata->num_vreg = 0;
+ return rc;
+}
+
+static int32_t msm_sensor_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;
+ uint32_t *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 = kzalloc(sizeof(uint32_t) * count, GFP_KERNEL);
+ if (!val_array) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ gconf->cam_gpio_req_tbl = kzalloc(sizeof(struct gpio) * count,
+ GFP_KERNEL);
+ if (!gconf->cam_gpio_req_tbl) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto ERROR1;
+ }
+ 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 ERROR2;
+ }
+ 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 ERROR2;
+ }
+ 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 ERROR2;
+ }
+ }
+
+ kfree(val_array);
+ return rc;
+
+ERROR2:
+ kfree(gconf->cam_gpio_req_tbl);
+ERROR1:
+ kfree(val_array);
+ gconf->cam_gpio_req_tbl_size = 0;
+ return rc;
+}
+
+static int32_t msm_sensor_get_dt_gpio_set_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;
+ uint32_t *val_array = NULL;
+
+ if (!of_get_property(of_node, "qcom,gpio-set-tbl-num", &count))
+ return 0;
+
+ count /= sizeof(uint32_t);
+ if (!count) {
+ pr_err("%s qcom,gpio-set-tbl-num 0\n", __func__);
+ return 0;
+ }
+
+ val_array = kzalloc(sizeof(uint32_t) * count, GFP_KERNEL);
+ if (!val_array) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ gconf->cam_gpio_set_tbl = kzalloc(sizeof(struct msm_gpio_set_tbl) *
+ count, GFP_KERNEL);
+ if (!gconf->cam_gpio_set_tbl) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto ERROR1;
+ }
+ gconf->cam_gpio_set_tbl_size = count;
+
+ rc = of_property_read_u32_array(of_node, "qcom,gpio-set-tbl-num",
+ val_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ for (i = 0; i < count; i++) {
+ if (val_array[i] >= gpio_array_size) {
+ pr_err("%s gpio set tbl index %d invalid\n",
+ __func__, val_array[i]);
+ return -EINVAL;
+ }
+ gconf->cam_gpio_set_tbl[i].gpio = gpio_array[val_array[i]];
+ CDBG("%s cam_gpio_set_tbl[%d].gpio = %d\n", __func__, i,
+ gconf->cam_gpio_set_tbl[i].gpio);
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,gpio-set-tbl-flags",
+ val_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ for (i = 0; i < count; i++) {
+ gconf->cam_gpio_set_tbl[i].flags = val_array[i];
+ CDBG("%s cam_gpio_set_tbl[%d].flags = %ld\n", __func__, i,
+ gconf->cam_gpio_set_tbl[i].flags);
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,gpio-set-tbl-delay",
+ val_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ for (i = 0; i < count; i++) {
+ gconf->cam_gpio_set_tbl[i].delay = val_array[i];
+ CDBG("%s cam_gpio_set_tbl[%d].delay = %d\n", __func__, i,
+ gconf->cam_gpio_set_tbl[i].delay);
+ }
+
+ kfree(val_array);
+ return rc;
+
+ERROR2:
+ kfree(gconf->cam_gpio_set_tbl);
+ERROR1:
+ kfree(val_array);
+ gconf->cam_gpio_set_tbl_size = 0;
+ return rc;
+}
+
+static int32_t msm_sensor_init_gpio_pin_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;
+ int32_t val = 0;
+
+ gconf->gpio_num_info = kzalloc(sizeof(struct msm_camera_gpio_num_info),
+ GFP_KERNEL);
+ if (!gconf->gpio_num_info) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ return rc;
+ }
+
+ if (of_property_read_bool(of_node, "qcom,gpio-reset") == true) {
+ rc = of_property_read_u32(of_node, "qcom,gpio-reset", &val);
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-reset failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-reset invalid %d\n",
+ __func__, __LINE__, val);
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_RESET] =
+ gpio_array[val];
+ CDBG("%s qcom,gpio-reset %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_RESET]);
+ }
+
+ if (of_property_read_bool(of_node, "qcom,gpio-standby") == true) {
+ rc = of_property_read_u32(of_node, "qcom,gpio-standby", &val);
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-standby failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-standby invalid %d\n",
+ __func__, __LINE__, val);
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_STANDBY] =
+ gpio_array[val];
+ CDBG("%s qcom,gpio-reset %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_STANDBY]);
+ }
+ return rc;
+
+ERROR:
+ kfree(gconf->gpio_num_info);
+ gconf->gpio_num_info = NULL;
+ return rc;
+}
+
+static int32_t msm_sensor_get_dt_actuator_data(struct device_node *of_node,
+ struct msm_camera_sensor_board_info *sensordata)
+{
+ int32_t rc = 0;
+ uint32_t val = 0;
+
+ rc = of_property_read_u32(of_node, "qcom,actuator-cam-name", &val);
+ CDBG("%s qcom,actuator-cam-name %d, rc %d\n", __func__, val, rc);
+ if (rc < 0)
+ return 0;
+
+ sensordata->actuator_info = kzalloc(sizeof(struct msm_actuator_info),
+ GFP_KERNEL);
+ if (!sensordata->actuator_info) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto ERROR;
+ }
+
+ sensordata->actuator_info->cam_name = val;
+
+ rc = of_property_read_u32(of_node, "qcom,actuator-vcm-pwd", &val);
+ CDBG("%s qcom,actuator-vcm-pwd %d, rc %d\n", __func__, val, rc);
+ if (!rc)
+ sensordata->actuator_info->vcm_pwd = val;
+
+ rc = of_property_read_u32(of_node, "qcom,actuator-vcm-enable", &val);
+ CDBG("%s qcom,actuator-vcm-enable %d, rc %d\n", __func__, val, rc);
+ if (!rc)
+ sensordata->actuator_info->vcm_enable = val;
+
+ return 0;
+ERROR:
+ return rc;
+}
+
+static int32_t msm_sensor_get_dt_data(struct platform_device *pdev,
+ struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0, i = 0;
+ struct device_node *of_node = pdev->dev.of_node;
+ struct msm_camera_gpio_conf *gconf = NULL;
+ struct msm_camera_sensor_board_info *sensordata = NULL;
+ uint16_t *gpio_array = NULL;
+ uint16_t gpio_array_size = 0;
+ uint32_t id_info[3];
+
+ s_ctrl->sensordata = kzalloc(sizeof(
+ struct msm_camera_sensor_board_info),
+ GFP_KERNEL);
+ if (!s_ctrl->sensordata) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ sensordata = s_ctrl->sensordata;
+
+ sensordata->sensor_init_params = kzalloc(sizeof(
+ struct msm_sensor_init_params), GFP_KERNEL);
+ if (!sensordata->sensor_init_params) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ rc = of_property_read_string(of_node, "qcom,sensor-name",
+ &sensordata->sensor_name);
+ CDBG("%s qcom,sensor-name %s, rc %d\n", __func__,
+ sensordata->sensor_name, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR1;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,sensor-mode",
+ &sensordata->sensor_init_params->modes_supported);
+ CDBG("%s qcom,sensor-mode %d, rc %d\n", __func__,
+ sensordata->sensor_init_params->modes_supported, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR1;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,sensor-position",
+ &sensordata->sensor_init_params->position);
+ CDBG("%s qcom,sensor-position %d, rc %d\n", __func__,
+ sensordata->sensor_init_params->position, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR1;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,mount-angle",
+ &sensordata->sensor_init_params->sensor_mount_angle);
+ CDBG("%s qcom,mount-angle %d, rc %d\n", __func__,
+ sensordata->sensor_init_params->sensor_mount_angle, rc);
+ if (rc < 0) {
+ /* Set default mount angle */
+ sensordata->sensor_init_params->sensor_mount_angle = 0;
+ rc = 0;
+ }
+
+ rc = msm_sensor_get_sub_module_index(of_node, sensordata);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR1;
+ }
+
+ rc = msm_sensor_get_dt_csi_data(of_node, sensordata);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR1;
+ }
+
+ rc = msm_sensor_get_dt_vreg_data(of_node, sensordata);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+
+ sensordata->gpio_conf = kzalloc(sizeof(struct msm_camera_gpio_conf),
+ GFP_KERNEL);
+ if (!sensordata->gpio_conf) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto ERROR3;
+ }
+ gconf = sensordata->gpio_conf;
+
+ gpio_array_size = of_gpio_count(of_node);
+ CDBG("%s gpio count %d\n", __func__, gpio_array_size);
+
+ if (gpio_array_size) {
+ gpio_array = kzalloc(sizeof(uint16_t) * gpio_array_size,
+ GFP_KERNEL);
+ if (!gpio_array) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR4;
+ }
+ for (i = 0; i < gpio_array_size; i++) {
+ gpio_array[i] = of_get_gpio(of_node, i);
+ CDBG("%s gpio_array[%d] = %d\n", __func__, i,
+ gpio_array[i]);
+ }
+
+ rc = msm_sensor_get_dt_gpio_req_tbl(of_node, gconf,
+ gpio_array, gpio_array_size);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR4;
+ }
+
+ rc = msm_sensor_get_dt_gpio_set_tbl(of_node, gconf,
+ gpio_array, gpio_array_size);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR5;
+ }
+
+ rc = msm_sensor_init_gpio_pin_tbl(of_node, gconf,
+ gpio_array, gpio_array_size);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR6;
+ }
+ }
+ rc = msm_sensor_get_dt_actuator_data(of_node, sensordata);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR7;
+ }
+
+ sensordata->slave_info = kzalloc(sizeof(struct msm_camera_slave_info),
+ GFP_KERNEL);
+ if (!sensordata->slave_info) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto ERROR8;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,slave-id",
+ id_info, 3);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR9;
+ }
+
+ sensordata->slave_info->sensor_slave_addr = id_info[0];
+ sensordata->slave_info->sensor_id_reg_addr = id_info[1];
+ sensordata->slave_info->sensor_id = id_info[2];
+
+ kfree(gpio_array);
+ return rc;
+
+ERROR9:
+ kfree(s_ctrl->sensordata->slave_info);
+ERROR8:
+ kfree(s_ctrl->sensordata->actuator_info);
+ERROR7:
+ kfree(s_ctrl->sensordata->gpio_conf->gpio_num_info);
+ERROR6:
+ kfree(s_ctrl->sensordata->gpio_conf->cam_gpio_set_tbl);
+ERROR5:
+ kfree(s_ctrl->sensordata->gpio_conf->cam_gpio_req_tbl);
+ERROR4:
+ kfree(s_ctrl->sensordata->gpio_conf);
+ERROR3:
+ kfree(s_ctrl->sensordata->cam_vreg);
+ERROR2:
+ kfree(s_ctrl->sensordata->csi_lane_params);
+ERROR1:
+ kfree(s_ctrl->sensordata);
+ kfree(gpio_array);
+ return rc;
+}
+
+int32_t msm_sensor_free_sensor_data(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ if (!s_ctrl->pdev)
+ return 0;
+ kfree(s_ctrl->sensordata->slave_info);
+ kfree(s_ctrl->sensordata->actuator_info);
+ kfree(s_ctrl->sensordata->gpio_conf->gpio_num_info);
+ kfree(s_ctrl->sensordata->gpio_conf->cam_gpio_set_tbl);
+ kfree(s_ctrl->sensordata->gpio_conf->cam_gpio_req_tbl);
+ kfree(s_ctrl->sensordata->gpio_conf);
+ kfree(s_ctrl->sensordata->cam_vreg);
+ kfree(s_ctrl->sensordata->csi_lane_params);
+ kfree(s_ctrl->sensordata->sensor_info);
+ kfree(s_ctrl->sensordata->sensor_init_params);
+ kfree(s_ctrl->sensordata);
+ return 0;
+}
+
+static struct msm_cam_clk_info cam_8960_clk_info[] = {
+ [SENSOR_CAM_MCLK] = {"cam_clk", 24000000},
+};
+
+static struct msm_cam_clk_info cam_8974_clk_info[] = {
+ [SENSOR_CAM_MCLK] = {"cam_src_clk", 19200000},
+ [SENSOR_CAM_CLK] = {"cam_clk", 0},
+};
+
+int32_t msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0, index = 0, clk_index = 0;
+ struct msm_sensor_power_setting_array *power_setting_array = NULL;
+ struct msm_sensor_power_setting *power_setting = NULL;
+ struct msm_camera_sensor_board_info *data = s_ctrl->sensordata;
+
+ CDBG("%s:%d\n", __func__, __LINE__);
+ power_setting_array = &s_ctrl->power_setting_array;
+
+ if (data->gpio_conf->cam_gpiomux_conf_tbl != NULL) {
+ pr_err("%s:%d mux install\n", __func__, __LINE__);
+ msm_gpiomux_install(
+ (struct msm_gpiomux_config *)
+ data->gpio_conf->cam_gpiomux_conf_tbl,
+ data->gpio_conf->cam_gpiomux_conf_tbl_size);
+ }
+
+ rc = msm_camera_request_gpio_table(
+ data->gpio_conf->cam_gpio_req_tbl,
+ data->gpio_conf->cam_gpio_req_tbl_size, 1);
+ if (rc < 0) {
+ pr_err("%s: request gpio failed\n", __func__);
+ return rc;
+ }
+ for (index = 0; index < power_setting_array->size; index++) {
+ CDBG("%s index %d\n", __func__, index);
+ power_setting = &power_setting_array->power_setting[index];
+ CDBG("%s type %d\n", __func__, power_setting->seq_type);
+ switch (power_setting->seq_type) {
+ case SENSOR_CLK:
+ if (power_setting->seq_val >= s_ctrl->clk_info_size) {
+ pr_err("%s clk index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ s_ctrl->clk_info_size);
+ goto power_up_failed;
+ }
+ if (power_setting->config_val)
+ s_ctrl->clk_info[power_setting->seq_val].
+ clk_rate = power_setting->config_val;
+
+ rc = msm_cam_clk_enable(s_ctrl->dev,
+ &s_ctrl->clk_info[0],
+ (struct clk **)&power_setting->data[0],
+ s_ctrl->clk_info_size,
+ 1);
+ if (rc < 0) {
+ pr_err("%s: clk enable failed\n",
+ __func__);
+ goto power_up_failed;
+ }
+ break;
+ case SENSOR_GPIO:
+ if (power_setting->seq_val >= SENSOR_GPIO_MAX ||
+ !data->gpio_conf->gpio_num_info) {
+ pr_err("%s gpio index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ SENSOR_GPIO_MAX);
+ goto power_up_failed;
+ }
+ pr_debug("%s:%d gpio set val %d\n", __func__, __LINE__,
+ data->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val]);
+ gpio_set_value_cansleep(
+ data->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val],
+ power_setting->config_val);
+ break;
+ case SENSOR_VREG:
+ if (power_setting->seq_val >= CAM_VREG_MAX) {
+ pr_err("%s vreg index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ SENSOR_GPIO_MAX);
+ goto power_up_failed;
+ }
+ msm_camera_config_single_vreg(s_ctrl->dev,
+ &data->cam_vreg[power_setting->seq_val],
+ (struct regulator **)&power_setting->data[0],
+ 1);
+ break;
+ case SENSOR_I2C_MUX:
+ if (data->i2c_conf && data->i2c_conf->use_i2c_mux)
+ msm_sensor_enable_i2c_mux(data->i2c_conf);
+ 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 (s_ctrl->sensor_device_type == MSM_SENSOR_PLATFORM_DEVICE) {
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_util(
+ s_ctrl->sensor_i2c_client, MSM_CCI_INIT);
+ if (rc < 0) {
+ pr_err("%s cci_init failed\n", __func__);
+ goto power_up_failed;
+ }
+ }
+
+ if (s_ctrl->func_tbl->sensor_match_id)
+ rc = s_ctrl->func_tbl->sensor_match_id(s_ctrl);
+ else
+ rc = msm_sensor_match_id(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d match id failed rc %d\n", __func__, __LINE__, rc);
+ goto power_up_failed;
+ }
+
+ CDBG("%s exit\n", __func__);
+ 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 = &power_setting_array->power_setting[index];
+ CDBG("%s type %d\n", __func__, power_setting->seq_type);
+ switch (power_setting->seq_type) {
+ case SENSOR_CLK:
+ for (clk_index--; clk_index >= 0; clk_index--)
+ msm_cam_clk_enable(s_ctrl->dev,
+ &s_ctrl->clk_info[clk_index],
+ (struct clk **)&power_setting->data[0],
+ 1, 0);
+ break;
+ case SENSOR_GPIO:
+ gpio_set_value_cansleep(
+ data->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val], GPIOF_OUT_INIT_LOW);
+ break;
+ case SENSOR_VREG:
+ msm_camera_config_single_vreg(s_ctrl->dev,
+ &data->cam_vreg[power_setting->seq_val],
+ (struct regulator **)&power_setting->data[0],
+ 0);
+ break;
+ case SENSOR_I2C_MUX:
+ if (data->i2c_conf && data->i2c_conf->use_i2c_mux)
+ msm_sensor_disable_i2c_mux(data->i2c_conf);
+ 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);
+ }
+ }
+ msm_camera_request_gpio_table(
+ data->gpio_conf->cam_gpio_req_tbl,
+ data->gpio_conf->cam_gpio_req_tbl_size, 0);
+ return rc;
+}
+
+int32_t msm_sensor_power_down(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t index = 0;
+ struct msm_sensor_power_setting_array *power_setting_array = NULL;
+ struct msm_sensor_power_setting *power_setting = NULL;
+ struct msm_camera_sensor_board_info *data = s_ctrl->sensordata;
+
+ CDBG("%s:%d\n", __func__, __LINE__);
+ power_setting_array = &s_ctrl->power_setting_array;
+
+ if (s_ctrl->sensor_device_type == MSM_SENSOR_PLATFORM_DEVICE) {
+ s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_util(
+ s_ctrl->sensor_i2c_client, MSM_CCI_RELEASE);
+ }
+
+ for (index = (power_setting_array->size - 1); index >= 0; index--) {
+ CDBG("%s index %d\n", __func__, index);
+ power_setting = &power_setting_array->power_setting[index];
+ CDBG("%s type %d\n", __func__, power_setting->seq_type);
+ switch (power_setting->seq_type) {
+ case SENSOR_CLK:
+ msm_cam_clk_enable(s_ctrl->dev,
+ &s_ctrl->clk_info[0],
+ (struct clk **)&power_setting->data[0],
+ s_ctrl->clk_info_size,
+ 0);
+ break;
+ case SENSOR_GPIO:
+ if (power_setting->seq_val >= SENSOR_GPIO_MAX ||
+ !data->gpio_conf->gpio_num_info) {
+ pr_err("%s gpio index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ SENSOR_GPIO_MAX);
+ continue;
+ }
+ gpio_set_value_cansleep(
+ data->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val], GPIOF_OUT_INIT_LOW);
+ break;
+ case SENSOR_VREG:
+ if (power_setting->seq_val >= CAM_VREG_MAX) {
+ pr_err("%s vreg index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ SENSOR_GPIO_MAX);
+ continue;
+ }
+ msm_camera_config_single_vreg(s_ctrl->dev,
+ &data->cam_vreg[power_setting->seq_val],
+ (struct regulator **)&power_setting->data[0],
+ 0);
+ break;
+ case SENSOR_I2C_MUX:
+ if (data->i2c_conf && data->i2c_conf->use_i2c_mux)
+ msm_sensor_disable_i2c_mux(data->i2c_conf);
+ 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);
+ }
+ }
+ msm_camera_request_gpio_table(
+ data->gpio_conf->cam_gpio_req_tbl,
+ data->gpio_conf->cam_gpio_req_tbl_size, 0);
+ CDBG("%s exit\n", __func__);
+ return 0;
+}
+
+int32_t msm_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+ uint16_t chipid = 0;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_read(
+ s_ctrl->sensor_i2c_client,
+ s_ctrl->sensordata->slave_info->sensor_id_reg_addr,
+ &chipid, MSM_CAMERA_I2C_WORD_DATA);
+ if (rc < 0) {
+ pr_err("%s: %s: read id failed\n", __func__,
+ s_ctrl->sensordata->sensor_name);
+ return rc;
+ }
+
+ CDBG("%s: read id: %x expected id %x:\n", __func__, chipid,
+ s_ctrl->sensordata->slave_info->sensor_id);
+ if (chipid != s_ctrl->sensordata->slave_info->sensor_id) {
+ pr_err("msm_sensor_match_id chip id doesnot match\n");
+ return -ENODEV;
+ }
+ return rc;
+}
+
+static struct msm_sensor_ctrl_t *get_sctrl(struct v4l2_subdev *sd)
+{
+ return container_of(container_of(sd, struct msm_sd_subdev, sd),
+ struct msm_sensor_ctrl_t, msm_sd);
+}
+
+static void msm_sensor_stop_stream(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write_table(
+ s_ctrl->sensor_i2c_client, &s_ctrl->stop_setting);
+ kfree(s_ctrl->stop_setting.reg_setting);
+ return;
+}
+
+static long msm_sensor_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct msm_sensor_ctrl_t *s_ctrl = get_sctrl(sd);
+ void __user *argp = (void __user *)arg;
+ if (!s_ctrl) {
+ pr_err("%s s_ctrl NULL\n", __func__);
+ return -EBADF;
+ }
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_CFG:
+ return s_ctrl->func_tbl->sensor_config(s_ctrl, argp);
+ case VIDIOC_MSM_SENSOR_RELEASE:
+ msm_sensor_stop_stream(s_ctrl);
+ return 0;
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+int32_t msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl,
+ void __user *argp)
+{
+ struct sensorb_cfg_data *cdata = (struct sensorb_cfg_data *)argp;
+ long rc = 0;
+ int32_t i = 0;
+ mutex_lock(s_ctrl->msm_sensor_mutex);
+ CDBG("%s:%d %s cfgtype = %d\n", __func__, __LINE__,
+ s_ctrl->sensordata->sensor_name, cdata->cfgtype);
+ switch (cdata->cfgtype) {
+ case CFG_GET_SENSOR_INFO:
+ memcpy(cdata->cfg.sensor_info.sensor_name,
+ s_ctrl->sensordata->sensor_name,
+ sizeof(cdata->cfg.sensor_info.sensor_name));
+ cdata->cfg.sensor_info.session_id =
+ s_ctrl->sensordata->sensor_info->session_id;
+ for (i = 0; i < SUB_MODULE_MAX; i++)
+ cdata->cfg.sensor_info.subdev_id[i] =
+ s_ctrl->sensordata->sensor_info->subdev_id[i];
+ CDBG("%s:%d sensor name %s\n", __func__, __LINE__,
+ cdata->cfg.sensor_info.sensor_name);
+ CDBG("%s:%d session id %d\n", __func__, __LINE__,
+ cdata->cfg.sensor_info.session_id);
+ for (i = 0; i < SUB_MODULE_MAX; i++)
+ CDBG("%s:%d subdev_id[%d] %d\n", __func__, __LINE__, i,
+ cdata->cfg.sensor_info.subdev_id[i]);
+
+ break;
+ case CFG_GET_SENSOR_INIT_PARAMS:
+ cdata->cfg.sensor_init_params =
+ *s_ctrl->sensordata->sensor_init_params;
+ CDBG("%s:%d init params mode %d pos %d mount %d\n", __func__,
+ __LINE__,
+ cdata->cfg.sensor_init_params.modes_supported,
+ cdata->cfg.sensor_init_params.position,
+ cdata->cfg.sensor_init_params.sensor_mount_angle);
+ break;
+ case CFG_SET_SLAVE_INFO: {
+ struct msm_camera_sensor_slave_info sensor_slave_info;
+ struct msm_sensor_power_setting_array *power_setting_array;
+ int slave_index = 0;
+ if (copy_from_user(&sensor_slave_info,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_sensor_slave_info))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ /* Update sensor slave address */
+ if (sensor_slave_info.slave_addr) {
+ s_ctrl->sensor_i2c_client->cci_client->sid =
+ sensor_slave_info.slave_addr >> 1;
+ }
+
+ /* Update sensor address type */
+ s_ctrl->sensor_i2c_client->addr_type =
+ sensor_slave_info.addr_type;
+
+ /* Update power up / down sequence */
+ s_ctrl->power_setting_array =
+ sensor_slave_info.power_setting_array;
+ power_setting_array = &s_ctrl->power_setting_array;
+ power_setting_array->power_setting = kzalloc(
+ power_setting_array->size *
+ sizeof(struct msm_sensor_power_setting), GFP_KERNEL);
+ if (!power_setting_array->power_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(power_setting_array->power_setting,
+ (void *)
+ sensor_slave_info.power_setting_array.power_setting,
+ power_setting_array->size *
+ sizeof(struct msm_sensor_power_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ s_ctrl->free_power_setting = true;
+ CDBG("%s sensor id %x\n", __func__,
+ sensor_slave_info.slave_addr);
+ CDBG("%s sensor addr type %d\n", __func__,
+ sensor_slave_info.addr_type);
+ CDBG("%s sensor reg %x\n", __func__,
+ sensor_slave_info.sensor_id_info.sensor_id_reg_addr);
+ CDBG("%s sensor id %x\n", __func__,
+ sensor_slave_info.sensor_id_info.sensor_id);
+ for (slave_index = 0; slave_index <
+ power_setting_array->size; slave_index++) {
+ CDBG("%s i %d power setting %d %d %ld %d\n", __func__,
+ slave_index,
+ power_setting_array->power_setting[slave_index].
+ seq_type,
+ power_setting_array->power_setting[slave_index].
+ seq_val,
+ power_setting_array->power_setting[slave_index].
+ config_val,
+ power_setting_array->power_setting[slave_index].
+ delay);
+ }
+ break;
+ }
+ case CFG_WRITE_I2C_ARRAY: {
+ struct msm_camera_i2c_reg_setting conf_array;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+
+ if (copy_from_user(&conf_array,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = kzalloc(conf_array.size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+ conf_array.size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+
+ conf_array.reg_setting = reg_setting;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write_table(
+ s_ctrl->sensor_i2c_client, &conf_array);
+ kfree(reg_setting);
+ break;
+ }
+ case CFG_WRITE_I2C_SEQ_ARRAY: {
+ struct msm_camera_i2c_seq_reg_setting conf_array;
+ struct msm_camera_i2c_seq_reg_array *reg_setting = NULL;
+
+ if (copy_from_user(&conf_array,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_seq_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = kzalloc(conf_array.size *
+ (sizeof(struct msm_camera_i2c_seq_reg_array)),
+ GFP_KERNEL);
+ if (!reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+ conf_array.size *
+ sizeof(struct msm_camera_i2c_seq_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+
+ conf_array.reg_setting = reg_setting;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write_seq_table(s_ctrl->sensor_i2c_client,
+ &conf_array);
+ kfree(reg_setting);
+ break;
+ }
+
+ case CFG_POWER_UP:
+ if (s_ctrl->func_tbl->sensor_power_up)
+ rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
+ else
+ rc = -EFAULT;
+ break;
+
+ case CFG_POWER_DOWN:
+ if (s_ctrl->func_tbl->sensor_power_down)
+ rc = s_ctrl->func_tbl->sensor_power_down(
+ s_ctrl);
+ else
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_STOP_STREAM_SETTING: {
+ struct msm_camera_i2c_reg_setting *stop_setting =
+ &s_ctrl->stop_setting;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+ if (copy_from_user(stop_setting,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = stop_setting->reg_setting;
+ stop_setting->reg_setting = kzalloc(stop_setting->size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!stop_setting->reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(stop_setting->reg_setting,
+ (void *)reg_setting,
+ stop_setting->size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(stop_setting->reg_setting);
+ stop_setting->reg_setting = NULL;
+ stop_setting->size = 0;
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(s_ctrl->msm_sensor_mutex);
+
+ return rc;
+}
+
+static int32_t msm_sensor_power(struct v4l2_subdev *sd, int on)
+{
+ int rc = 0;
+ struct msm_sensor_ctrl_t *s_ctrl = get_sctrl(sd);
+ mutex_lock(s_ctrl->msm_sensor_mutex);
+ if (!on)
+ rc = s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+ if (s_ctrl->free_power_setting == true) {
+ kfree(s_ctrl->power_setting_array.power_setting);
+ s_ctrl->free_power_setting = false;
+ }
+ mutex_unlock(s_ctrl->msm_sensor_mutex);
+ return rc;
+}
+
+static int32_t msm_sensor_v4l2_enum_fmt(struct v4l2_subdev *sd,
+ unsigned int index, enum v4l2_mbus_pixelcode *code)
+{
+ struct msm_sensor_ctrl_t *s_ctrl = get_sctrl(sd);
+
+ if ((unsigned int)index >= s_ctrl->sensor_v4l2_subdev_info_size)
+ return -EINVAL;
+
+ *code = s_ctrl->sensor_v4l2_subdev_info[index].code;
+ return 0;
+}
+
+static struct v4l2_subdev_core_ops msm_sensor_subdev_core_ops = {
+ .ioctl = msm_sensor_subdev_ioctl,
+ .s_power = msm_sensor_power,
+};
+
+static struct v4l2_subdev_video_ops msm_sensor_subdev_video_ops = {
+ .enum_mbus_fmt = msm_sensor_v4l2_enum_fmt,
+};
+
+static struct v4l2_subdev_ops msm_sensor_subdev_ops = {
+ .core = &msm_sensor_subdev_core_ops,
+ .video = &msm_sensor_subdev_video_ops,
+};
+
+static struct msm_sensor_fn_t msm_sensor_func_tbl = {
+ .sensor_config = msm_sensor_config,
+ .sensor_power_up = msm_sensor_power_up,
+ .sensor_power_down = msm_sensor_power_down,
+ .sensor_match_id = msm_sensor_match_id,
+};
+
+static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl = {
+ .i2c_read = msm_camera_cci_i2c_read,
+ .i2c_read_seq = msm_camera_cci_i2c_read_seq,
+ .i2c_write = msm_camera_cci_i2c_write,
+ .i2c_write_table = msm_camera_cci_i2c_write_table,
+ .i2c_write_seq_table = msm_camera_cci_i2c_write_seq_table,
+ .i2c_write_table_w_microdelay =
+ msm_camera_cci_i2c_write_table_w_microdelay,
+ .i2c_util = msm_sensor_cci_i2c_util,
+};
+
+static struct msm_camera_i2c_fn_t msm_sensor_qup_func_tbl = {
+ .i2c_read = msm_camera_qup_i2c_read,
+ .i2c_read_seq = msm_camera_qup_i2c_read_seq,
+ .i2c_write = msm_camera_qup_i2c_write,
+ .i2c_write_table = msm_camera_qup_i2c_write_table,
+ .i2c_write_seq_table = msm_camera_qup_i2c_write_seq_table,
+ .i2c_write_table_w_microdelay =
+ msm_camera_qup_i2c_write_table_w_microdelay,
+};
+
+int32_t msm_sensor_platform_probe(struct platform_device *pdev, void *data)
+{
+ int32_t rc = 0;
+ struct msm_sensor_ctrl_t *s_ctrl =
+ (struct msm_sensor_ctrl_t *)data;
+ struct msm_camera_cci_client *cci_client = NULL;
+ uint32_t session_id;
+ s_ctrl->pdev = pdev;
+ s_ctrl->dev = &pdev->dev;
+ CDBG("%s called data %p\n", __func__, data);
+ CDBG("%s pdev name %s\n", __func__, pdev->id_entry->name);
+ if (pdev->dev.of_node) {
+ rc = msm_sensor_get_dt_data(pdev, s_ctrl);
+ if (rc < 0) {
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ return rc;
+ }
+ }
+ s_ctrl->sensor_device_type = MSM_SENSOR_PLATFORM_DEVICE;
+ s_ctrl->sensor_i2c_client->cci_client = kzalloc(sizeof(
+ struct msm_camera_cci_client), GFP_KERNEL);
+ if (!s_ctrl->sensor_i2c_client->cci_client) {
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ return rc;
+ }
+ /* TODO: get CCI subdev */
+ cci_client = s_ctrl->sensor_i2c_client->cci_client;
+ cci_client->cci_subdev = msm_cci_get_subdev();
+ cci_client->cci_i2c_master = MASTER_0;
+ cci_client->sid =
+ s_ctrl->sensordata->slave_info->sensor_slave_addr >> 1;
+ cci_client->retries = 3;
+ cci_client->id_map = 0;
+ if (!s_ctrl->func_tbl)
+ s_ctrl->func_tbl = &msm_sensor_func_tbl;
+ if (!s_ctrl->sensor_i2c_client->i2c_func_tbl)
+ s_ctrl->sensor_i2c_client->i2c_func_tbl =
+ &msm_sensor_cci_func_tbl;
+ if (!s_ctrl->sensor_v4l2_subdev_ops)
+ s_ctrl->sensor_v4l2_subdev_ops = &msm_sensor_subdev_ops;
+ s_ctrl->clk_info = cam_8974_clk_info;
+ s_ctrl->clk_info_size = ARRAY_SIZE(cam_8974_clk_info);
+ rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s %s power up failed\n", __func__,
+ s_ctrl->sensordata->sensor_name);
+ kfree(cci_client);
+ return rc;
+ }
+
+ CDBG("%s %s probe succeeded\n", __func__,
+ s_ctrl->sensordata->sensor_name);
+ v4l2_subdev_init(&s_ctrl->msm_sd.sd,
+ s_ctrl->sensor_v4l2_subdev_ops);
+ snprintf(s_ctrl->msm_sd.sd.name,
+ sizeof(s_ctrl->msm_sd.sd.name), "%s",
+ s_ctrl->sensordata->sensor_name);
+ v4l2_set_subdevdata(&s_ctrl->msm_sd.sd, pdev);
+ s_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ media_entity_init(&s_ctrl->msm_sd.sd.entity, 0, NULL, 0);
+ s_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ s_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_SENSOR;
+ s_ctrl->msm_sd.sd.entity.name =
+ s_ctrl->msm_sd.sd.name;
+
+ rc = camera_init_v4l2(&s_ctrl->pdev->dev, &session_id);
+ CDBG("%s rc %d session_id %d\n", __func__, rc, session_id);
+ s_ctrl->sensordata->sensor_info->session_id = session_id;
+ msm_sd_register(&s_ctrl->msm_sd);
+ CDBG("%s:%d\n", __func__, __LINE__);
+
+ s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+ CDBG("%s:%d\n", __func__, __LINE__);
+ return rc;
+}
+
+int32_t msm_sensor_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ struct msm_sensor_ctrl_t *s_ctrl;
+ uint32_t session_id;
+ CDBG("%s %s_i2c_probe called\n", __func__, client->name);
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("%s %s i2c_check_functionality failed\n",
+ __func__, client->name);
+ rc = -EFAULT;
+ return rc;
+ }
+
+ s_ctrl = (struct msm_sensor_ctrl_t *)(id->driver_data);
+ if (!s_ctrl) {
+ pr_err("%s:%d sensor ctrl structure NULL\n", __func__,
+ __LINE__);
+ return -EINVAL;
+ }
+
+ s_ctrl->sensor_device_type = MSM_SENSOR_I2C_DEVICE;
+ s_ctrl->sensordata = client->dev.platform_data;
+ if (s_ctrl->sensordata == NULL) {
+ pr_err("%s %s NULL sensor data\n", __func__, client->name);
+ return -EFAULT;
+ }
+
+ if (s_ctrl->sensor_i2c_client != NULL) {
+ s_ctrl->sensor_i2c_client->client = client;
+ s_ctrl->dev = &client->dev;
+ if (s_ctrl->sensordata->slave_info->sensor_slave_addr)
+ s_ctrl->sensor_i2c_client->client->addr =
+ s_ctrl->sensordata->slave_info->
+ sensor_slave_addr;
+ } else {
+ pr_err("%s %s sensor_i2c_client NULL\n",
+ __func__, client->name);
+ rc = -EFAULT;
+ return rc;
+ }
+
+ if (!s_ctrl->func_tbl)
+ s_ctrl->func_tbl = &msm_sensor_func_tbl;
+ if (!s_ctrl->sensor_i2c_client->i2c_func_tbl)
+ s_ctrl->sensor_i2c_client->i2c_func_tbl =
+ &msm_sensor_qup_func_tbl;
+ if (!s_ctrl->sensor_v4l2_subdev_ops)
+ s_ctrl->sensor_v4l2_subdev_ops = &msm_sensor_subdev_ops;
+
+ s_ctrl->clk_info = cam_8960_clk_info;
+ s_ctrl->clk_info_size = ARRAY_SIZE(cam_8960_clk_info);
+
+ rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s %s power up failed\n", __func__, client->name);
+ return rc;
+ }
+
+ CDBG("%s %s probe succeeded\n", __func__, client->name);
+ snprintf(s_ctrl->msm_sd.sd.name,
+ sizeof(s_ctrl->msm_sd.sd.name), "%s", id->name);
+ v4l2_i2c_subdev_init(&s_ctrl->msm_sd.sd, client,
+ s_ctrl->sensor_v4l2_subdev_ops);
+ v4l2_set_subdevdata(&s_ctrl->msm_sd.sd, client);
+ s_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ media_entity_init(&s_ctrl->msm_sd.sd.entity, 0, NULL, 0);
+ s_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ s_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_SENSOR;
+ s_ctrl->msm_sd.sd.entity.name =
+ s_ctrl->msm_sd.sd.name;
+
+ rc = camera_init_v4l2(&s_ctrl->sensor_i2c_client->client->dev,
+ &session_id);
+ CDBG("%s rc %d session_id %d\n", __func__, rc, session_id);
+ s_ctrl->sensordata->sensor_info->session_id = session_id;
+ msm_sd_register(&s_ctrl->msm_sd);
+ CDBG("%s:%d\n", __func__, __LINE__);
+
+ s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+ return rc;
+}
diff --git a/drivers/media/video/msmb/sensor/msm_sensor.h b/drivers/media/video/msmb/sensor/msm_sensor.h
new file mode 100644
index 0000000..20a2b14
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/msm_sensor.h
@@ -0,0 +1,86 @@
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_SENSOR_H
+#define MSM_SENSOR_H
+
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <mach/camera2.h>
+#include <media/msm_cam_sensor.h>
+#include <media/v4l2-subdev.h>
+#include "msm_camera_i2c.h"
+#include "msm_sd.h"
+
+#define DEFINE_MSM_MUTEX(mutexname) \
+ static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
+
+struct msm_sensor_ctrl_t;
+
+struct msm_sensor_fn_t {
+ int (*sensor_config) (struct msm_sensor_ctrl_t *, void __user *);
+ int (*sensor_power_down)
+ (struct msm_sensor_ctrl_t *);
+ int (*sensor_power_up) (struct msm_sensor_ctrl_t *);
+ int32_t (*sensor_match_id)(struct msm_sensor_ctrl_t *s_ctrl);
+};
+
+struct msm_sensor_ctrl_t {
+ struct platform_device *pdev;
+ enum msm_sensor_device_type_t sensor_device_type;
+ struct msm_camera_sensor_board_info *sensordata;
+ struct msm_sensor_power_setting_array power_setting_array;
+ struct mutex *msm_sensor_mutex;
+
+ struct msm_camera_i2c_client *sensor_i2c_client;
+ struct device *dev;
+
+ struct msm_sd_subdev msm_sd;
+ struct v4l2_subdev_info *sensor_v4l2_subdev_info;
+ uint8_t sensor_v4l2_subdev_info_size;
+ struct v4l2_subdev_ops *sensor_v4l2_subdev_ops;
+ struct msm_sensor_fn_t *func_tbl;
+ struct msm_camera_i2c_reg_setting stop_setting;
+ bool free_power_setting;
+ struct msm_cam_clk_info *clk_info;
+ uint16_t clk_info_size;
+};
+
+int32_t msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl,
+ void __user *argp);
+
+int32_t msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl);
+
+int32_t msm_sensor_power_down(struct msm_sensor_ctrl_t *s_ctrl);
+
+int32_t msm_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl);
+
+int32_t msm_sensor_platform_probe(struct platform_device *pdev,
+ void *data);
+
+int32_t msm_sensor_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id);
+
+int32_t msm_sensor_free_sensor_data(struct msm_sensor_ctrl_t *s_ctrl);
+
+#endif
diff --git a/drivers/media/video/msmb/sensor/s5k3l1yx.c b/drivers/media/video/msmb/sensor/s5k3l1yx.c
new file mode 100644
index 0000000..9c36b5b
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/s5k3l1yx.c
@@ -0,0 +1,155 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include "msm_sensor.h"
+#define S5K3L1YX_SENSOR_NAME "s5k3l1yx"
+DEFINE_MSM_MUTEX(s5k3l1yx_mut);
+
+static struct msm_sensor_ctrl_t s5k3l1yx_s_ctrl;
+
+static struct msm_sensor_power_setting s5k3l1yx_power_setting[] = {
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VDIG,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VANA,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VIO,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VAF,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_LOW,
+ .delay = 1,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_HIGH,
+ .delay = 30,
+ },
+ {
+ .seq_type = SENSOR_CLK,
+ .seq_val = SENSOR_CAM_MCLK,
+ .config_val = 0,
+ .delay = 1,
+ },
+ {
+ .seq_type = SENSOR_I2C_MUX,
+ .seq_val = 0,
+ .config_val = 0,
+ .delay = 0,
+ },
+};
+
+static struct v4l2_subdev_info s5k3l1yx_subdev_info[] = {
+ {
+ .code = V4L2_MBUS_FMT_SBGGR10_1X10,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ },
+};
+
+static const struct i2c_device_id s5k3l1yx_i2c_id[] = {
+ {S5K3L1YX_SENSOR_NAME, (kernel_ulong_t)&s5k3l1yx_s_ctrl},
+ { }
+};
+
+static struct i2c_driver s5k3l1yx_i2c_driver = {
+ .id_table = s5k3l1yx_i2c_id,
+ .probe = msm_sensor_i2c_probe,
+ .driver = {
+ .name = S5K3L1YX_SENSOR_NAME,
+ },
+};
+
+static struct msm_camera_i2c_client s5k3l1yx_sensor_i2c_client = {
+ .addr_type = MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+static const struct of_device_id s5k3l1yx_dt_match[] = {
+ {.compatible = "qcom,s5k3l1yx", .data = &s5k3l1yx_s_ctrl},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, s5k3l1yx_dt_match);
+
+static struct platform_driver s5k3l1yx_platform_driver = {
+ .driver = {
+ .name = "qcom,s5k3l1yx",
+ .owner = THIS_MODULE,
+ .of_match_table = s5k3l1yx_dt_match,
+ },
+};
+
+static int32_t s5k3l1yx_platform_probe(struct platform_device *pdev)
+{
+ int32_t rc = 0;
+ const struct of_device_id *match;
+ match = of_match_device(s5k3l1yx_dt_match, &pdev->dev);
+ rc = msm_sensor_platform_probe(pdev, match->data);
+ return rc;
+}
+
+static int __init s5k3l1yx_init_module(void)
+{
+ int32_t rc = 0;
+ pr_info("%s:%d\n", __func__, __LINE__);
+ rc = platform_driver_probe(&s5k3l1yx_platform_driver,
+ s5k3l1yx_platform_probe);
+ if (!rc)
+ return rc;
+ pr_err("%s:%d rc %d\n", __func__, __LINE__, rc);
+ return i2c_add_driver(&s5k3l1yx_i2c_driver);
+}
+
+static void __exit s5k3l1yx_exit_module(void)
+{
+ pr_info("%s:%d\n", __func__, __LINE__);
+ if (s5k3l1yx_s_ctrl.pdev) {
+ msm_sensor_free_sensor_data(&s5k3l1yx_s_ctrl);
+ platform_driver_unregister(&s5k3l1yx_platform_driver);
+ } else
+ i2c_del_driver(&s5k3l1yx_i2c_driver);
+ return;
+}
+
+static struct msm_sensor_ctrl_t s5k3l1yx_s_ctrl = {
+ .sensor_i2c_client = &s5k3l1yx_sensor_i2c_client,
+ .power_setting_array.power_setting = s5k3l1yx_power_setting,
+ .power_setting_array.size = ARRAY_SIZE(s5k3l1yx_power_setting),
+ .msm_sensor_mutex = &s5k3l1yx_mut,
+ .sensor_v4l2_subdev_info = s5k3l1yx_subdev_info,
+ .sensor_v4l2_subdev_info_size = ARRAY_SIZE(s5k3l1yx_subdev_info),
+};
+
+module_init(s5k3l1yx_init_module);
+module_exit(s5k3l1yx_exit_module);
+MODULE_DESCRIPTION("s5k3l1yx");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index f4fc7b6..3d3563a 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -2277,12 +2277,16 @@
ret = qsee_vote_for_clock(data, CLK_DFAB);
if (ret)
pr_err("Failed to vote for DFAB clock%d\n", ret);
+ ret = qsee_vote_for_clock(data, CLK_SFPB);
+ if (ret)
+ pr_err("Failed to vote for SFPB clock%d\n", ret);
atomic_dec(&data->ioctl_count);
break;
}
case QSEECOM_IOCTL_PERF_DISABLE_REQ:{
atomic_inc(&data->ioctl_count);
qsee_disable_clock_vote(data, CLK_DFAB);
+ qsee_disable_clock_vote(data, CLK_SFPB);
atomic_dec(&data->ioctl_count);
break;
}
diff --git a/drivers/mmc/card/mmc_block_test.c b/drivers/mmc/card/mmc_block_test.c
index 2cda66c..7a4d19e 100644
--- a/drivers/mmc/card/mmc_block_test.c
+++ b/drivers/mmc/card/mmc_block_test.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -26,7 +26,7 @@
#define MODULE_NAME "mmc_block_test"
#define TEST_MAX_SECTOR_RANGE (600*1024*1024) /* 600 MB */
-#define TEST_MAX_BIOS_PER_REQ 120
+#define TEST_MAX_BIOS_PER_REQ 128
#define CMD23_PACKED_BIT (1 << 30)
#define LARGE_PRIME_1 1103515367
#define LARGE_PRIME_2 35757
@@ -37,28 +37,30 @@
#define SECTOR_SIZE 512
#define NUM_OF_SECTORS_PER_BIO ((BIO_U32_SIZE * 4) / SECTOR_SIZE)
#define BIO_TO_SECTOR(x) (x * NUM_OF_SECTORS_PER_BIO)
-/* the desired long test size to be written or read */
-#define LONG_TEST_MAX_NUM_BYTES (50*1024*1024) /* 50MB */
+/* the desired long test size to be read */
+#define LONG_READ_TEST_MAX_NUM_BYTES (50*1024*1024) /* 50MB */
+/* the minimum amount of requests that will be created */
+#define LONG_WRITE_TEST_MIN_NUM_REQS 200 /* 100MB */
/* request queue limitation is 128 requests, and we leave 10 spare requests */
#define TEST_MAX_REQUESTS 118
-#define LONG_TEST_MAX_NUM_REQS (LONG_TEST_MAX_NUM_BYTES / \
+#define LONG_READ_TEST_MAX_NUM_REQS (LONG_READ_TEST_MAX_NUM_BYTES / \
(TEST_MAX_BIOS_PER_REQ * sizeof(int) * BIO_U32_SIZE))
/* this doesn't allow the test requests num to be greater than the maximum */
-#define LONG_TEST_ACTUAL_NUM_REQS \
- ((TEST_MAX_REQUESTS < LONG_TEST_MAX_NUM_REQS) ? \
- TEST_MAX_REQUESTS : LONG_TEST_MAX_NUM_REQS)
+#define LONG_READ_TEST_ACTUAL_NUM_REQS \
+ ((TEST_MAX_REQUESTS < LONG_READ_TEST_MAX_NUM_REQS) ? \
+ TEST_MAX_REQUESTS : LONG_READ_TEST_MAX_NUM_REQS)
#define MB_MSEC_RATIO_APPROXIMATION ((1024 * 1024) / 1000)
/* actual number of bytes in test */
-#define LONG_TEST_ACTUAL_BYTE_NUM (LONG_TEST_ACTUAL_NUM_REQS * \
+#define LONG_READ_NUM_BYTES (LONG_READ_TEST_ACTUAL_NUM_REQS * \
(TEST_MAX_BIOS_PER_REQ * sizeof(int) * BIO_U32_SIZE))
/* actual number of MiB in test multiplied by 10, for single digit precision*/
-#define LONG_TEST_ACTUAL_MB_NUM_X_10 ((LONG_TEST_ACTUAL_BYTE_NUM * 10) / \
- (1024 * 1024))
+#define BYTE_TO_MB_x_10(x) ((x * 10) / (1024 * 1024))
/* extract integer value */
-#define LONG_TEST_SIZE_INTEGER (LONG_TEST_ACTUAL_MB_NUM_X_10 / 10)
+#define LONG_TEST_SIZE_INTEGER(x) (BYTE_TO_MB_x_10(x) / 10)
/* and calculate the MiB value fraction */
-#define LONG_TEST_SIZE_FRACTION (LONG_TEST_ACTUAL_MB_NUM_X_10 - \
- (LONG_TEST_SIZE_INTEGER * 10))
+#define LONG_TEST_SIZE_FRACTION(x) (BYTE_TO_MB_x_10(x) - \
+ (LONG_TEST_SIZE_INTEGER(x) * 10))
+#define LONG_WRITE_TEST_SLEEP_TIME_MS 5
#define test_pr_debug(fmt, args...) pr_debug("%s: "fmt"\n", MODULE_NAME, args)
#define test_pr_info(fmt, args...) pr_info("%s: "fmt"\n", MODULE_NAME, args)
@@ -222,8 +224,8 @@
enum bkops_test_stages bkops_stage;
/* A wait queue for BKOPs tests */
wait_queue_head_t bkops_wait_q;
-
- unsigned int completed_req_count;
+ /* A counter for the number of test requests completed */
+ unsigned int completed_req_count;
};
static struct mmc_block_test_data *mbtd;
@@ -980,8 +982,6 @@
__func__, mbtd->random_test_seed);
}
- mmc_blk_init_packed_statistics(mq->card);
-
ret = prepare_request_add_write_reqs(td, num_requests, is_err_expected,
is_random);
if (ret)
@@ -1077,8 +1077,6 @@
__func__, mbtd->random_test_seed);
}
- mmc_blk_init_packed_statistics(mq->card);
-
if (td->test_info.testcase ==
TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED) {
temp_num_req = num_requests;
@@ -1233,8 +1231,6 @@
max_packed_reqs = mq->card->ext_csd.max_packed_writes;
- mmc_blk_init_packed_statistics(mq->card);
-
for (i = 1; i <= num_requests; i++) {
if (i > (num_requests / 2))
is_err_expected = 1;
@@ -1344,13 +1340,12 @@
return num_requests;
}
-static int prepare_long_test_requests(struct test_data *td)
+static int prepare_long_read_test_requests(struct test_data *td)
{
int ret;
int start_sec;
int j;
- int test_direction;
if (td)
start_sec = td->start_sector;
@@ -1359,23 +1354,18 @@
return -EINVAL;
}
- if (td->test_info.testcase == TEST_LONG_SEQUENTIAL_WRITE)
- test_direction = WRITE;
- else
- test_direction = READ;
+ test_pr_info("%s: Adding %d read requests, first req_id=%d", __func__,
+ LONG_READ_TEST_ACTUAL_NUM_REQS, td->wr_rd_next_req_id);
- test_pr_info("%s: Adding %d write requests, first req_id=%d", __func__,
- LONG_TEST_ACTUAL_NUM_REQS, td->wr_rd_next_req_id);
+ for (j = 0; j < LONG_READ_TEST_ACTUAL_NUM_REQS; j++) {
- for (j = 0; j < LONG_TEST_ACTUAL_NUM_REQS; j++) {
-
- ret = test_iosched_add_wr_rd_test_req(0, test_direction,
+ ret = test_iosched_add_wr_rd_test_req(0, READ,
start_sec,
TEST_MAX_BIOS_PER_REQ,
TEST_NO_PATTERN, NULL);
if (ret) {
- test_pr_err("%s: failed to add a bio request",
- __func__);
+ test_pr_err("%s: failed to add a read request, err = %d"
+ , __func__, ret);
return ret;
}
@@ -1495,10 +1485,8 @@
test_packed_trigger, is_random);
break;
case TEST_LONG_SEQUENTIAL_WRITE:
- ret = prepare_long_test_requests(td);
- break;
case TEST_LONG_SEQUENTIAL_READ:
- ret = prepare_long_test_requests(td);
+ ret = prepare_long_read_test_requests(td);
break;
default:
test_pr_info("%s: Invalid test case...", __func__);
@@ -1508,6 +1496,44 @@
return ret;
}
+static int run_packed_test(struct test_data *td)
+{
+ struct mmc_queue *mq;
+ struct request_queue *req_q;
+
+ if (!td) {
+ pr_err("%s: NULL td", __func__);
+ return -EINVAL;
+ }
+
+ req_q = td->req_q;
+
+ if (!req_q) {
+ pr_err("%s: NULL request queue", __func__);
+ return -EINVAL;
+ }
+
+ mq = req_q->queuedata;
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+ mmc_blk_init_packed_statistics(mq->card);
+
+ if (td->test_info.testcase != TEST_PACK_MIX_PACKED_NO_PACKED_PACKED) {
+ /*
+ * Verify that the packing is disabled before starting the
+ * test
+ */
+ mq->wr_packing_enabled = false;
+ mq->num_of_potential_packed_wr_reqs = 0;
+ }
+
+ __blk_run_queue(td->req_q);
+
+ return 0;
+}
+
/*
* An implementation for the post_test_fn in the test_info data structure.
* In our case we just reset the function pointers in the mmc_queue in order for
@@ -2221,6 +2247,7 @@
mbtd->test_info.data = mbtd;
mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.run_test_fn = run_packed_test;
mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
mbtd->test_info.get_test_case_str_fn = get_test_case_str;
mbtd->test_info.post_test_fn = post_test;
@@ -2319,6 +2346,7 @@
mbtd->test_info.data = mbtd;
mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.run_test_fn = run_packed_test;
mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
mbtd->test_info.get_test_case_str_fn = get_test_case_str;
mbtd->test_info.post_test_fn = post_test;
@@ -2418,6 +2446,7 @@
mbtd->test_info.data = mbtd;
mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.run_test_fn = run_packed_test;
mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
mbtd->test_info.get_test_case_str_fn = get_test_case_str;
mbtd->test_info.post_test_fn = post_test;
@@ -2530,6 +2559,7 @@
mbtd->test_info.data = mbtd;
mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.run_test_fn = run_packed_test;
mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
mbtd->test_info.get_test_case_str_fn = get_test_case_str;
@@ -2731,7 +2761,7 @@
int ret = 0;
int i = 0;
int number = -1;
- unsigned int mtime, integer, fraction;
+ unsigned long mtime, integer, fraction;
test_pr_info("%s: -- Long Sequential Read TEST --", __func__);
@@ -2759,21 +2789,22 @@
mtime = jiffies_to_msecs(mbtd->test_info.test_duration);
- test_pr_info("%s: time is %u msec, size is %u.%u MiB",
- __func__, mtime, LONG_TEST_SIZE_INTEGER,
- LONG_TEST_SIZE_FRACTION);
+ test_pr_info("%s: time is %lu msec, size is %u.%u MiB",
+ __func__, mtime,
+ LONG_TEST_SIZE_INTEGER(LONG_READ_NUM_BYTES),
+ LONG_TEST_SIZE_FRACTION(LONG_READ_NUM_BYTES));
/* we first multiply in order not to lose precision */
mtime *= MB_MSEC_RATIO_APPROXIMATION;
/* divide values to get a MiB/sec integer value with one
digit of precision. Multiply by 10 for one digit precision
*/
- fraction = integer = (LONG_TEST_ACTUAL_BYTE_NUM * 10) / mtime;
+ fraction = integer = (LONG_READ_NUM_BYTES * 10) / mtime;
integer /= 10;
/* and calculate the MiB value fraction */
fraction -= integer * 10;
- test_pr_info("%s: Throughput: %u.%u MiB/sec\n"
+ test_pr_info("%s: Throughput: %lu.%lu MiB/sec\n"
, __func__, integer, fraction);
/* Allow FS requests to be dispatched */
@@ -2812,6 +2843,72 @@
.read = long_sequential_read_test_read,
};
+static void long_seq_write_free_end_io_fn(struct request *rq, int err)
+{
+ struct test_request *test_rq =
+ (struct test_request *)rq->elv.priv[0];
+ struct test_data *ptd = test_get_test_data();
+
+ BUG_ON(!test_rq);
+
+ spin_lock_irq(&ptd->lock);
+ list_del_init(&test_rq->queuelist);
+ ptd->dispatched_count--;
+ __blk_put_request(ptd->req_q, test_rq->rq);
+ spin_unlock_irq(&ptd->lock);
+
+ kfree(test_rq->bios_buffer);
+ kfree(test_rq);
+ mbtd->completed_req_count++;
+
+ check_test_completion();
+}
+
+static int run_long_seq_write(struct test_data *td)
+{
+ int ret = 0;
+ int i;
+
+ td->test_count = 0;
+ mbtd->completed_req_count = 0;
+
+ test_pr_info("%s: Adding at least %d write requests, first req_id=%d",
+ __func__, LONG_WRITE_TEST_MIN_NUM_REQS,
+ td->wr_rd_next_req_id);
+
+ do {
+ for (i = 0; i < TEST_MAX_REQUESTS; i++) {
+ /*
+ * since our requests come from a pool containing 128
+ * requests, we don't want to exhaust this quantity,
+ * therefore we add up to TEST_MAX_REQUESTS (which
+ * includes a safety margin) and then call the mmc layer
+ * to fetch them
+ */
+ if (td->test_count > TEST_MAX_REQUESTS)
+ break;
+
+ ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+ td->start_sector, TEST_MAX_BIOS_PER_REQ,
+ TEST_PATTERN_5A,
+ long_seq_write_free_end_io_fn);
+ if (ret) {
+ test_pr_err("%s: failed to create write request"
+ , __func__);
+ break;
+ }
+ }
+
+ __blk_run_queue(td->req_q);
+
+ } while (mbtd->completed_req_count < LONG_WRITE_TEST_MIN_NUM_REQS);
+
+ test_pr_info("%s: completed %d requests", __func__,
+ mbtd->completed_req_count);
+
+ return ret;
+}
+
static ssize_t long_sequential_write_test_write(struct file *file,
const char __user *buf,
size_t count,
@@ -2820,7 +2917,7 @@
int ret = 0;
int i = 0;
int number = -1;
- unsigned int mtime, integer, fraction;
+ unsigned long mtime, integer, fraction, byte_count;
test_pr_info("%s: -- Long Sequential Write TEST --", __func__);
@@ -2833,13 +2930,16 @@
mbtd->test_group = TEST_GENERAL_GROUP;
mbtd->test_info.data = mbtd;
- mbtd->test_info.prepare_test_fn = prepare_test;
mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+ mbtd->test_info.run_test_fn = run_long_seq_write;
for (i = 0 ; i < number ; ++i) {
test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
test_pr_info("%s: ====================", __func__);
+ integer = 0;
+ fraction = 0;
+ mbtd->test_info.test_byte_count = 0;
mbtd->test_info.testcase = TEST_LONG_SEQUENTIAL_WRITE;
mbtd->is_random = NON_RANDOM_TEST;
ret = test_iosched_start_test(&mbtd->test_info);
@@ -2847,22 +2947,23 @@
break;
mtime = jiffies_to_msecs(mbtd->test_info.test_duration);
+ byte_count = mbtd->test_info.test_byte_count;
- test_pr_info("%s: time is %u msec, size is %u.%u MiB",
- __func__, mtime, LONG_TEST_SIZE_INTEGER,
- LONG_TEST_SIZE_FRACTION);
+ test_pr_info("%s: time is %lu msec, size is %lu.%lu MiB",
+ __func__, mtime, LONG_TEST_SIZE_INTEGER(byte_count),
+ LONG_TEST_SIZE_FRACTION(byte_count));
/* we first multiply in order not to lose precision */
mtime *= MB_MSEC_RATIO_APPROXIMATION;
/* divide values to get a MiB/sec integer value with one
digit of precision
*/
- fraction = integer = (LONG_TEST_ACTUAL_BYTE_NUM * 10) / mtime;
+ fraction = integer = (byte_count * 10) / mtime;
integer /= 10;
/* and calculate the MiB value fraction */
fraction -= integer * 10;
- test_pr_info("%s: Throughput: %u.%u MiB/sec\n",
+ test_pr_info("%s: Throughput: %lu.%lu MiB/sec\n",
__func__, integer, fraction);
/* Allow FS requests to be dispatched */
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index e7877f2..c1de8e8 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -662,8 +662,10 @@
}
} else if (context_info->is_new_req) {
context_info->is_new_req = false;
- err = MMC_BLK_NEW_REQUEST;
- break; /* return err */
+ if (!next_req) {
+ err = MMC_BLK_NEW_REQUEST;
+ break; /* return err */
+ }
}
} /* while */
return err;
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index 1bc027c..14afae9 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -139,8 +139,6 @@
bool use_external_rsense;
- unsigned int start_percent;
- unsigned int end_percent;
bool ignore_shutdown_soc;
int shutdown_soc_invalid;
int shutdown_soc;
@@ -1085,6 +1083,24 @@
return 0;
}
+static bool is_battery_charging(struct qpnp_bms_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (chip->batt_psy == NULL)
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (chip->batt_psy) {
+ /* if battery has been registered, use the status property */
+ chip->batt_psy->get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_STATUS, &ret);
+ return ret.intval == POWER_SUPPLY_STATUS_CHARGING;
+ }
+
+ /* Default to false if the battery power supply is not registered. */
+ pr_debug("battery power supply is not registered\n");
+ return false;
+}
+
static bool is_batfet_open(struct qpnp_bms_chip *chip)
{
union power_supply_propval ret = {0,};
@@ -1583,7 +1599,7 @@
*/
/* if not charging, return last soc */
- if (chip->start_percent == -EINVAL)
+ if (!is_battery_charging(chip))
return prev_soc;
chg_time_sec = DIV_ROUND_UP(chip->charge_time_us, USEC_PER_SEC);
@@ -1658,7 +1674,7 @@
* account for charge time - limit it to SOC_CATCHUP_SEC to
* avoid overflows when charging continues for extended periods
*/
- if (chip->start_percent != -EINVAL) {
+ if (is_battery_charging(chip)) {
if (chip->charge_time_us == 0) {
/*
* calculating soc for the first time
@@ -2005,8 +2021,6 @@
static inline void bms_initialize_constants(struct qpnp_bms_chip *chip)
{
- chip->start_percent = -EINVAL;
- chip->end_percent = -EINVAL;
chip->prev_pc_unusable = -EINVAL;
chip->soc_at_cv = -EINVAL;
chip->calculated_soc = -EINVAL;
diff --git a/drivers/slimbus/slim-msm-ctrl.c b/drivers/slimbus/slim-msm-ctrl.c
index 9c69f47..d4fb02b 100644
--- a/drivers/slimbus/slim-msm-ctrl.c
+++ b/drivers/slimbus/slim-msm-ctrl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1409,7 +1409,7 @@
err_request_irq_failed:
kthread_stop(dev->rx_msgq_thread);
err_thread_create_failed:
- msm_slim_sps_exit(dev);
+ msm_slim_sps_exit(dev, true);
err_sps_init_failed:
if (dev->hclk) {
clk_disable_unprepare(dev->hclk);
@@ -1453,7 +1453,7 @@
clk_put(dev->rclk);
if (dev->hclk)
clk_put(dev->hclk);
- msm_slim_sps_exit(dev);
+ msm_slim_sps_exit(dev, true);
kthread_stop(dev->rx_msgq_thread);
iounmap(dev->bam.base);
iounmap(dev->base);
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index 78e8a6f..a37a7803 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -33,6 +33,7 @@
#define NGD_SLIM_NAME "ngd_msm_ctrl"
#define SLIM_LA_MGR 0xFF
#define SLIM_ROOT_FREQ 24576000
+#define LADDR_RETRY 5
#define NGD_BASE_V1(r) (((r) % 2) ? 0x800 : 0xA00)
#define NGD_BASE_V2(r) (((r) % 2) ? 0x1000 : 0x2000)
@@ -78,6 +79,12 @@
NGD_TX_BUSY = 0x0,
};
+enum ngd_status {
+ NGD_LADDR = 1 << 1,
+};
+
+static int ngd_slim_runtime_resume(struct device *device);
+
static irqreturn_t ngd_slim_interrupt(int irq, void *d)
{
struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)d;
@@ -144,23 +151,24 @@
return IRQ_HANDLED;
}
-static int ngd_clk_pause_wakeup(struct slim_controller *ctrl)
-{
- struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
- return msm_slim_qmi_power_request(dev, true);
-}
-
static int ngd_qmi_available(struct notifier_block *n, unsigned long code,
void *_cmd)
{
struct msm_slim_qmi *qmi = container_of(n, struct msm_slim_qmi, nb);
+ struct msm_slim_ctrl *dev =
+ container_of(qmi, struct msm_slim_ctrl, qmi);
pr_info("Slimbus QMI NGD CB received event:%ld", code);
switch (code) {
case QMI_SERVER_ARRIVE:
- complete(&qmi->qmi_comp);
+ schedule_work(&qmi->ssr_up);
break;
case QMI_SERVER_EXIT:
- /* SSR implementation */
+ dev->state = MSM_CTRL_DOWN;
+ /* make sure autosuspend is not called until ADSP comes up*/
+ pm_runtime_get_noresume(dev->dev);
+ /* Reset ctrl_up completion */
+ init_completion(&dev->ctrl_up);
+ schedule_work(&qmi->ssr_down);
break;
default:
break;
@@ -227,22 +235,43 @@
txn->mc <= SLIM_MSG_MC_RECONFIGURE_NOW)) {
return 0;
}
- msm_slim_get_ctrl(dev);
+ /* If txn is tried when controller is down, wait for ADSP to boot */
+ if (txn->mc != SLIM_USR_MC_REPORT_SATELLITE) {
+ if (dev->state == MSM_CTRL_DOWN) {
+ u8 mc = (u8)txn->mc;
+ int timeout;
+ dev_err(dev->dev, "ADSP slimbus not up yet");
+ /*
+ * Messages related to data channel management can't
+ * wait since they are holding reconfiguration lock.
+ * clk_pause in resume (which can change state back to
+ * MSM_CTRL_AWAKE), will need that lock
+ */
+ if ((txn->mt == SLIM_MSG_MT_CORE) &&
+ ((mc >= SLIM_MSG_MC_CONNECT_SOURCE &&
+ mc <= SLIM_MSG_MC_CHANGE_CONTENT) ||
+ (mc >= SLIM_MSG_MC_BEGIN_RECONFIGURATION &&
+ mc <= SLIM_MSG_MC_RECONFIGURE_NOW)))
+ return -EREMOTEIO;
+ if ((txn->mt == SLIM_MSG_MT_DEST_REFERRED_USER) &&
+ ((mc >= SLIM_USR_MC_DEFINE_CHAN &&
+ mc <= SLIM_USR_MC_DISCONNECT_PORT)))
+ return -EREMOTEIO;
+ timeout = wait_for_completion_timeout(&dev->ctrl_up,
+ HZ);
+ if (!timeout)
+ return -ETIMEDOUT;
+ }
+ msm_slim_get_ctrl(dev);
+ }
mutex_lock(&dev->tx_lock);
+
if (txn->mc != SLIM_USR_MC_REPORT_SATELLITE &&
- (dev->state == MSM_CTRL_ASLEEP ||
- dev->state == MSM_CTRL_SLEEPING)) {
- int timeout;
+ (dev->state != MSM_CTRL_AWAKE)) {
dev_err(dev->dev, "controller not ready");
mutex_unlock(&dev->tx_lock);
- /* Reconf is signalled when master responds */
- timeout = wait_for_completion_timeout(&dev->reconf, HZ);
- if (timeout) {
- mutex_lock(&dev->tx_lock);
- } else {
- msm_slim_put_ctrl(dev);
- return -EBUSY;
- }
+ msm_slim_put_ctrl(dev);
+ return -EREMOTEIO;
}
if (txn->mt == SLIM_MSG_MT_CORE &&
(txn->mc == SLIM_MSG_MC_CONNECT_SOURCE ||
@@ -370,24 +399,23 @@
mutex_unlock(&dev->tx_lock);
msm_slim_put_ctrl(dev);
timeout = wait_for_completion_timeout(txn->comp, HZ);
- if (!timeout) {
- pr_err("connect/disc :0x%x, tid:%d timed out", txn->mc,
- txn->tid);
+ if (!timeout)
ret = -ETIMEDOUT;
+ else
+ ret = txn->ec;
+ if (ret) {
+ pr_err("connect/disconnect:0x%x,tid:%d err:%d", txn->mc,
+ txn->tid, ret);
mutex_lock(&ctrl->m_ctrl);
ctrl->txnt[txn->tid] = NULL;
mutex_unlock(&ctrl->m_ctrl);
- } else {
- ret = txn->ec;
}
- if (ret)
- pr_err("connect/disconnect:0x%x,tid:%d err:%d", txn->mc,
- txn->tid, ret);
return ret ? ret : dev->err;
}
ngd_xfer_err:
mutex_unlock(&dev->tx_lock);
- msm_slim_put_ctrl(dev);
+ if (txn->mc != SLIM_USR_MC_REPORT_SATELLITE)
+ msm_slim_put_ctrl(dev);
return ret ? ret : dev->err;
}
@@ -398,20 +426,18 @@
if (!ret) {
int timeout;
timeout = wait_for_completion_timeout(txn->comp, HZ);
- if (!timeout) {
- pr_err("master req:0x%x, tid:%d timed out", txn->mc,
- txn->tid);
+ if (!timeout)
ret = -ETIMEDOUT;
- mutex_lock(&ctrl->m_ctrl);
- ctrl->txnt[txn->tid] = NULL;
- mutex_unlock(&ctrl->m_ctrl);
- } else {
+ else
ret = txn->ec;
- }
}
- if (ret)
+ if (ret) {
pr_err("master msg:0x%x,tid:%d ret:%d", txn->mc,
txn->tid, ret);
+ mutex_lock(&ctrl->m_ctrl);
+ ctrl->txnt[txn->tid] = NULL;
+ mutex_unlock(&ctrl->m_ctrl);
+ }
return ret;
}
@@ -582,26 +608,31 @@
txn.wbuf = wbuf;
txn.len = 4;
pr_info("SLIM SAT: Received master capability");
- dev->use_rx_msgqs = 1;
- msm_slim_sps_init(dev, dev->bam_mem,
- NGD_BASE(dev->ctrl.nr, dev->ver) + NGD_STATUS, true);
- if (dev->use_rx_msgqs)
- msgq_en |= NGD_CFG_RX_MSGQ_EN;
- writel_relaxed(msgq_en, dev->base +
- NGD_BASE(dev->ctrl.nr, dev->ver));
- /* make sure NGD MSG-Q config goes through */
- mb();
+ if (dev->state == MSM_CTRL_DOWN) {
+ dev->use_rx_msgqs = 1;
+ msm_slim_sps_init(dev, dev->bam_mem,
+ NGD_BASE(dev->ctrl.nr, dev->ver) + NGD_STATUS,
+ true);
+ if (dev->use_rx_msgqs)
+ msgq_en |= NGD_CFG_RX_MSGQ_EN;
+ writel_relaxed(msgq_en, dev->base +
+ NGD_BASE(dev->ctrl.nr, dev->ver));
+ /* make sure NGD MSG-Q config goes through */
+ mb();
+ }
ret = ngd_xfer_msg(&dev->ctrl, &txn);
if (!ret) {
+ enum msm_ctrl_state prev_state = dev->state;
dev->state = MSM_CTRL_AWAKE;
-
- pm_runtime_use_autosuspend(dev->dev);
- pm_runtime_set_autosuspend_delay(dev->dev,
- MSM_SLIM_AUTOSUSPEND);
- pm_runtime_set_active(dev->dev);
- pm_runtime_enable(dev->dev);
- complete(&dev->reconf);
+ if (prev_state >= MSM_CTRL_ASLEEP)
+ complete(&dev->reconf);
+ else
+ pr_err("SLIM: unexpected capability, state:%d",
+ prev_state);
+ /* ADSP SSR, send device_up notifications */
+ if (prev_state == MSM_CTRL_DOWN)
+ schedule_work(&dev->slave_notify);
}
}
if (mc == SLIM_MSG_MC_REPLY_INFORMATION ||
@@ -654,38 +685,116 @@
}
}
-static int ngd_slim_enable(struct msm_slim_ctrl *dev, bool enable)
+static int ngd_slim_power_up(struct msm_slim_ctrl *dev)
{
+ void __iomem *ngd;
+ int timeout, ret;
+ enum msm_ctrl_state cur_state = dev->state;
+ u32 laddr;
+ u32 msgq_en = 1;
u32 ngd_int = (NGD_INT_RECFG_DONE | NGD_INT_TX_NACKED_2 |
NGD_INT_MSG_BUF_CONTE | NGD_INT_MSG_TX_INVAL |
NGD_INT_IE_VE_CHG | NGD_INT_DEV_ERR |
NGD_INT_TX_MSG_SENT | NGD_INT_RX_MSG_RCVD);
- if (enable) {
- int ret = msm_slim_qmi_init(dev, false);
- if (ret)
- return ret;
- ret = msm_slim_qmi_power_request(dev, true);
- if (ret)
- return ret;
- writel_relaxed(ngd_int, dev->base + NGD_INT_EN +
- NGD_BASE(dev->ctrl.nr, dev->ver));
+
+ if (cur_state == MSM_CTRL_DOWN) {
+ int timeout = wait_for_completion_timeout(&dev->qmi.qmi_comp,
+ HZ);
+ if (!timeout)
+ pr_err("slimbus QMI init timed out");
+ }
+
+ ret = msm_slim_qmi_power_request(dev, true);
+ if (ret) {
+ pr_err("SLIM QMI power request failed:%d", ret);
+ return ret;
+ }
+ if (!dev->ver) {
+ dev->ver = readl_relaxed(dev->base);
+ /* Version info in 16 MSbits */
+ dev->ver >>= 16;
+ }
+ ngd = dev->base + NGD_BASE(dev->ctrl.nr, dev->ver);
+ laddr = readl_relaxed(ngd + NGD_STATUS);
+ if (laddr & NGD_LADDR) {
/*
- * Enable NGD. Configure NGD in register acc. mode until master
- * announcement is received
+ * ADSP power collapse case, where HW wasn't reset.
+ * Reconnect BAM pipes if disconnected
*/
- writel_relaxed(1, dev->base + NGD_BASE(dev->ctrl.nr, dev->ver));
- /* make sure NGD enabling goes through */
- mb();
- } else {
- writel_relaxed(0, dev->base + NGD_BASE(dev->ctrl.nr, dev->ver));
- writel_relaxed(0, dev->base + NGD_INT_EN +
+ return 0;
+ } else if (cur_state != MSM_CTRL_DOWN) {
+ pr_info("ADSP P.C. CTRL state:%d NGD not enumerated:0x%x",
+ dev->state, laddr);
+ if (dev->use_rx_msgqs)
+ msgq_en |= NGD_CFG_RX_MSGQ_EN;
+ }
+
+ /*
+ * ADSP power collapse case (OR SSR), where HW was reset
+ * BAM programming will happen when capability message is received
+ */
+ writel_relaxed(ngd_int, dev->base + NGD_INT_EN +
NGD_BASE(dev->ctrl.nr, dev->ver));
- /* make sure NGD disabling goes through */
- mb();
+ /*
+ * Enable NGD. Configure NGD in register acc. mode until master
+ * announcement is received
+ */
+ writel_relaxed(msgq_en, dev->base + NGD_BASE(dev->ctrl.nr, dev->ver));
+ /* make sure NGD enabling goes through */
+ mb();
+
+ timeout = wait_for_completion_timeout(&dev->reconf, HZ);
+ if (!timeout) {
+ pr_err("failed to received master capability");
+ return -ETIMEDOUT;
+ }
+ if (cur_state == MSM_CTRL_DOWN)
+ complete(&dev->ctrl_up);
+ return 0;
+}
+
+static int ngd_slim_enable(struct msm_slim_ctrl *dev, bool enable)
+{
+ int ret = 0;
+ if (enable) {
+ ret = msm_slim_qmi_init(dev, false);
+ /* controller state should be in sync with framework state */
+ if (!ret) {
+ ret = slim_ctrl_clk_pause(&dev->ctrl, false,
+ SLIM_CLK_UNSPECIFIED);
+ complete(&dev->qmi.qmi_comp);
+ /*
+ * Power-up won't be called if clock pause failed.
+ * This can happen if ADSP SSR happened when audio
+ * session is in progress. Framework will think that
+ * clock pause failed so no need to wakeup controller.
+ * Call power-up explicitly in that case, since slimbus
+ * HW needs to be powered-on to be in sync with
+ * framework state
+ */
+ if (ret)
+ ngd_slim_power_up(dev);
+ if (!pm_runtime_enabled(dev->dev) ||
+ !pm_runtime_suspended(dev->dev))
+ ngd_slim_runtime_resume(dev->dev);
+ else
+ pm_runtime_resume(dev->dev);
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put(dev->dev);
+ } else
+ dev_err(dev->dev, "qmi init fail, ret:%d, state:%d",
+ ret, dev->state);
+ } else {
msm_slim_qmi_exit(dev);
}
- return 0;
+ return ret;
+}
+
+static int ngd_clk_pause_wakeup(struct slim_controller *ctrl)
+{
+ struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+ return ngd_slim_power_up(dev);
}
static int ngd_slim_rx_msgq_thread(void *data)
@@ -698,14 +807,6 @@
u32 buffer[10];
u8 msg_len = 0;
- wait_for_completion_interruptible(&dev->qmi.qmi_comp);
- ret = ngd_slim_enable(dev, true);
- /* Exit the thread if component can't be enabled */
- if (ret) {
- pr_err("Enabling NGD failed:%d", ret);
- return 0;
- }
-
while (!kthread_should_stop()) {
set_current_state(TASK_INTERRUPTIBLE);
ret = wait_for_completion_interruptible(notify);
@@ -741,6 +842,64 @@
return 0;
}
+static void ngd_laddr_lookup(struct work_struct *work)
+{
+ struct msm_slim_ctrl *dev =
+ container_of(work, struct msm_slim_ctrl, slave_notify);
+ struct slim_controller *ctrl = &dev->ctrl;
+ struct slim_device *sbdev;
+ int i;
+ mutex_lock(&ctrl->m_ctrl);
+ list_for_each_entry(sbdev, &ctrl->devs, dev_list) {
+ int ret = 0;
+ mutex_unlock(&ctrl->m_ctrl);
+ for (i = 0; i < LADDR_RETRY; i++) {
+ ret = slim_get_logical_addr(sbdev, sbdev->e_addr,
+ 6, &sbdev->laddr);
+ if (!ret)
+ break;
+ else /* time for ADSP to assign LA */
+ msleep(20);
+ }
+ mutex_lock(&ctrl->m_ctrl);
+ }
+ mutex_unlock(&ctrl->m_ctrl);
+}
+
+static void ngd_adsp_down(struct work_struct *work)
+{
+ struct msm_slim_qmi *qmi =
+ container_of(work, struct msm_slim_qmi, ssr_down);
+ struct msm_slim_ctrl *dev =
+ container_of(qmi, struct msm_slim_ctrl, qmi);
+ struct slim_controller *ctrl = &dev->ctrl;
+ struct slim_device *sbdev;
+ int i;
+
+ ngd_slim_enable(dev, false);
+ /* disconnect BAM pipes */
+ msm_slim_sps_exit(dev, false);
+ dev->use_rx_msgqs = 0;
+ mutex_lock(&ctrl->m_ctrl);
+ /* device up should be called again after SSR */
+ list_for_each_entry(sbdev, &ctrl->devs, dev_list)
+ sbdev->notified = false;
+ /* invalidate logical addresses */
+ for (i = 0; i < ctrl->num_dev; i++)
+ ctrl->addrt[i].valid = false;
+ mutex_unlock(&ctrl->m_ctrl);
+ pr_info("SLIM ADSP SSR (DOWN) done");
+}
+
+static void ngd_adsp_up(struct work_struct *work)
+{
+ struct msm_slim_qmi *qmi =
+ container_of(work, struct msm_slim_qmi, ssr_up);
+ struct msm_slim_ctrl *dev =
+ container_of(qmi, struct msm_slim_ctrl, qmi);
+ ngd_slim_enable(dev, true);
+}
+
static int __devinit ngd_slim_probe(struct platform_device *pdev)
{
struct msm_slim_ctrl *dev;
@@ -834,15 +993,13 @@
dev->bam_mem = bam_mem;
init_completion(&dev->reconf);
+ init_completion(&dev->ctrl_up);
mutex_init(&dev->tx_lock);
spin_lock_init(&dev->rx_lock);
dev->ee = 1;
dev->irq = irq->start;
dev->bam.irq = bam_irq->start;
- dev->ver = readl_relaxed(dev->base);
- /* Version info in 16 MSbits */
- dev->ver >>= 16;
init_completion(&dev->rx_msgq_notify);
/* Register with framework */
@@ -854,7 +1011,7 @@
dev->ctrl.dev.parent = &pdev->dev;
dev->ctrl.dev.of_node = pdev->dev.of_node;
- dev->state = MSM_CTRL_ASLEEP;
+ dev->state = MSM_CTRL_DOWN;
ret = request_irq(dev->irq, ngd_slim_interrupt,
IRQF_TRIGGER_HIGH, "ngd_slim_irq", dev);
@@ -865,7 +1022,16 @@
}
init_completion(&dev->qmi.qmi_comp);
+ pm_runtime_use_autosuspend(dev->dev);
+ pm_runtime_set_autosuspend_delay(dev->dev, MSM_SLIM_AUTOSUSPEND);
+ pm_runtime_set_suspended(dev->dev);
+ pm_runtime_enable(dev->dev);
+
+ INIT_WORK(&dev->slave_notify, ngd_laddr_lookup);
+ INIT_WORK(&dev->qmi.ssr_down, ngd_adsp_down);
+ INIT_WORK(&dev->qmi.ssr_up, ngd_adsp_up);
dev->qmi.nb.notifier_call = ngd_qmi_available;
+ pm_runtime_get_noresume(dev->dev);
ret = qmi_svc_event_notifier_register(SLIMBUS_QMI_SVC_ID,
SLIMBUS_QMI_INS_ID, &dev->qmi.nb);
if (ret) {
@@ -873,6 +1039,7 @@
goto qmi_register_failed;
}
+
/* Fire up the Rx message queue thread */
dev->rx_msgq_thread = kthread_run(ngd_slim_rx_msgq_thread, dev,
NGD_SLIM_NAME "_ngd_msgq_thread");
@@ -914,7 +1081,6 @@
qmi_svc_event_notifier_unregister(SLIMBUS_QMI_SVC_ID,
SLIMBUS_QMI_INS_ID, &dev->qmi.nb);
pm_runtime_disable(&pdev->dev);
- pm_runtime_set_suspended(&pdev->dev);
free_irq(dev->irq, dev);
slim_del_controller(&dev->ctrl);
kthread_stop(dev->rx_msgq_thread);
@@ -938,31 +1104,12 @@
* functions to be called from system suspend/resume. So they are not
* inside ifdef CONFIG_PM_RUNTIME
*/
-#ifdef CONFIG_PM_SLEEP
-static int ngd_slim_runtime_suspend(struct device *device)
-{
- struct platform_device *pdev = to_platform_device(device);
- struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
- int ret;
- dev_dbg(device, "pm_runtime: suspending...\n");
- dev->state = MSM_CTRL_SLEEPING;
- ret = slim_ctrl_clk_pause(&dev->ctrl, false, SLIM_CLK_UNSPECIFIED);
- if (ret) {
- dev_err(device, "clk pause not entered:%d", ret);
- dev->state = MSM_CTRL_AWAKE;
- } else {
- dev->state = MSM_CTRL_ASLEEP;
- }
- return ret;
-}
-
static int ngd_slim_runtime_resume(struct device *device)
{
struct platform_device *pdev = to_platform_device(device);
struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
int ret = 0;
- dev_dbg(device, "pm_runtime: resuming...\n");
- if (dev->state == MSM_CTRL_ASLEEP)
+ if (dev->state >= MSM_CTRL_ASLEEP)
ret = slim_ctrl_clk_pause(&dev->ctrl, true, 0);
if (ret) {
dev_err(device, "clk pause not exited:%d", ret);
@@ -973,6 +1120,24 @@
return ret;
}
+#ifdef CONFIG_PM_SLEEP
+static int ngd_slim_runtime_suspend(struct device *device)
+{
+ struct platform_device *pdev = to_platform_device(device);
+ struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+ int ret = 0;
+ dev->state = MSM_CTRL_SLEEPING;
+ ret = slim_ctrl_clk_pause(&dev->ctrl, false, SLIM_CLK_UNSPECIFIED);
+ if (ret) {
+ if (ret != -EBUSY)
+ dev_err(device, "clk pause not entered:%d", ret);
+ dev->state = MSM_CTRL_AWAKE;
+ } else {
+ dev->state = MSM_CTRL_ASLEEP;
+ }
+ return ret;
+}
+
static int ngd_slim_suspend(struct device *dev)
{
int ret = -EBUSY;
diff --git a/drivers/slimbus/slim-msm.c b/drivers/slimbus/slim-msm.c
index 137f85e..72a8669 100644
--- a/drivers/slimbus/slim-msm.c
+++ b/drivers/slimbus/slim-msm.c
@@ -517,6 +517,8 @@
},
};
+ if (dev->bam.hdl)
+ goto init_rx_msgq;
bam_props.ee = dev->ee;
bam_props.virt_addr = dev->bam.base;
bam_props.phys_addr = bam_mem->start;
@@ -565,7 +567,7 @@
return ret;
}
-void msm_slim_sps_exit(struct msm_slim_ctrl *dev)
+void msm_slim_sps_exit(struct msm_slim_ctrl *dev, bool dereg)
{
if (dev->use_rx_msgqs) {
struct msm_slim_endp *endpoint = &dev->rx_msgq;
@@ -579,7 +581,10 @@
sps_disconnect(endpoint->sps);
msm_slim_sps_mem_free(dev, descr);
msm_slim_free_endpoint(endpoint);
+ }
+ if (dereg) {
sps_deregister_bam_device(dev->bam.hdl);
+ dev->bam.hdl = 0L;
}
}
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index 3daf7ee..1c6db32 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -152,6 +152,7 @@
MSM_CTRL_AWAKE,
MSM_CTRL_SLEEPING,
MSM_CTRL_ASLEEP,
+ MSM_CTRL_DOWN,
};
struct msm_slim_sps_bam {
@@ -176,6 +177,8 @@
struct kthread_worker kworker;
struct completion qmi_comp;
struct notifier_block nb;
+ struct work_struct ssr_down;
+ struct work_struct ssr_up;
};
struct msm_slim_ctrl {
@@ -212,8 +215,10 @@
bool reconf_busy;
bool chan_active;
enum msm_ctrl_state state;
+ struct completion ctrl_up;
int nsats;
u32 ver;
+ struct work_struct slave_notify;
struct msm_slim_qmi qmi;
};
@@ -266,7 +271,7 @@
int msm_slim_rx_msgq_get(struct msm_slim_ctrl *dev, u32 *data, int offset);
int msm_slim_sps_init(struct msm_slim_ctrl *dev, struct resource *bam_mem,
u32 pipe_reg, bool remote);
-void msm_slim_sps_exit(struct msm_slim_ctrl *dev);
+void msm_slim_sps_exit(struct msm_slim_ctrl *dev, bool dereg);
void msm_slim_qmi_exit(struct msm_slim_ctrl *dev);
int msm_slim_qmi_init(struct msm_slim_ctrl *dev, bool apps_is_master);
diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
index c320e46..e5b3158 100644
--- a/drivers/slimbus/slimbus.c
+++ b/drivers/slimbus/slimbus.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -3047,6 +3047,7 @@
for (i = 0; i < ctrl->last_tid; i++) {
if (ctrl->txnt[i]) {
ret = -EBUSY;
+ pr_info("slim_clk_pause: txn-rsp for %d pending", i);
mutex_unlock(&ctrl->m_ctrl);
return -EBUSY;
}
@@ -3057,6 +3058,7 @@
mutex_lock(&ctrl->sched.m_reconf);
/* Data channels active */
if (ctrl->sched.usedslots) {
+ pr_info("slim_clk_pause: data channel active");
ret = -EBUSY;
goto clk_pause_ret;
}
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 2d73af6..8ff6cff 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -54,18 +54,34 @@
#include <linux/device.h>
#include <linux/wakelock.h>
#include <linux/debugfs.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <asm/atomic.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <mach/dma.h>
+#include <mach/sps.h>
#include <mach/msm_serial_hs.h>
#include "msm_serial_hs_hwreg.h"
+#define UART_SPS_CONS_PERIPHERAL 0
+#define UART_SPS_PROD_PERIPHERAL 1
static int hs_serial_debug_mask = 1;
module_param_named(debug_mask, hs_serial_debug_mask,
int, S_IRUGO | S_IWUSR | S_IWGRP);
+/*
+ * There are 3 different kind of UART Core available on MSM.
+ * High Speed UART (i.e. Legacy HSUART), GSBI based HSUART
+ * and BSLP based HSUART.
+ */
+enum uart_core_type {
+ LEGACY_HSUART,
+ GSBI_HSUART,
+ BLSP_HSUART,
+};
enum flush_reason {
FLUSH_NONE,
@@ -92,6 +108,17 @@
CLK_REQ_OFF_RXSTALE_FLUSHED,
};
+/* SPS data structures to support HSUART with BAM
+ * @sps_pipe - This struct defines BAM pipe descriptor
+ * @sps_connect - This struct defines a connection's end point
+ * @sps_register - This struct defines a event registration parameters
+ */
+struct msm_hs_sps_ep_conn_data {
+ struct sps_pipe *pipe_handle;
+ struct sps_connect config;
+ struct sps_register_event event;
+};
+
struct msm_hs_tx {
unsigned int tx_ready_int_en; /* ok to dma more tx */
unsigned int dma_in_flight; /* tx dma in progress */
@@ -105,6 +132,7 @@
int tx_count;
dma_addr_t dma_base;
struct tasklet_struct tlet;
+ struct msm_hs_sps_ep_conn_data cons;
};
struct msm_hs_rx {
@@ -122,6 +150,7 @@
struct wake_lock wake_lock;
struct delayed_work flip_insert_work;
struct tasklet_struct tlet;
+ struct msm_hs_sps_ep_conn_data prod;
};
enum buffer_states {
@@ -168,7 +197,20 @@
struct work_struct clock_off_w; /* work for actual clock off */
struct workqueue_struct *hsuart_wq; /* hsuart workqueue */
struct mutex clk_mutex; /* mutex to guard against clock off/clock on */
+ struct work_struct reset_bam_rx; /* work for reset bam rx endpoint */
+ struct work_struct disconnect_rx_endpoint; /* disconnect rx_endpoint */
bool tty_flush_receive;
+ enum uart_core_type uart_type;
+ u32 bam_handle;
+ resource_size_t bam_mem;
+ int bam_irq;
+ unsigned char __iomem *bam_base;
+ unsigned int bam_tx_ep_pipe_index;
+ unsigned int bam_rx_ep_pipe_index;
+ /* struct sps_event_notify is an argument passed when triggering a
+ * callback event object registered for an SPS connection end point.
+ */
+ struct sps_event_notify notify;
};
#define MSM_UARTDM_BURST_SIZE 16 /* DM burst size (in bytes) */
@@ -176,12 +218,17 @@
#define UARTDM_RX_BUF_SIZE 512
#define RETRY_TIMEOUT 5
#define UARTDM_NR 256
+#define BAM_PIPE_MIN 0
+#define BAM_PIPE_MAX 11
static struct dentry *debug_base;
static struct msm_hs_port q_uart_port[UARTDM_NR];
static struct platform_driver msm_serial_hs_platform_driver;
static struct uart_driver msm_hs_driver;
static struct uart_ops msm_hs_ops;
+static void msm_hs_start_rx_locked(struct uart_port *uport);
+static void msm_serial_hs_rx_tlet(unsigned long tlet_ptr);
+static void flip_insert_work(struct work_struct *work);
#define UARTDM_TO_MSM(uart_port) \
container_of((uart_port), struct msm_hs_port, uport)
@@ -244,7 +291,10 @@
/* assume gsbi uart if gsbi resource found in pdata */
return ((msm_uport->mapped_gsbi != NULL));
}
-
+static unsigned int is_blsp_uart(struct msm_hs_port *msm_uport)
+{
+ return (msm_uport->uart_type == BLSP_HSUART);
+}
static inline unsigned int msm_hs_read(struct uart_port *uport,
unsigned int offset)
{
@@ -320,13 +370,21 @@
if (val) {
spin_lock_irqsave(&uport->lock, flags);
ret = msm_hs_read(uport, UARTDM_MR2_ADDR);
- ret |= UARTDM_MR2_LOOP_MODE_BMSK;
+ if (is_blsp_uart(msm_uport))
+ ret |= (UARTDM_MR2_LOOP_MODE_BMSK |
+ UARTDM_MR2_RFR_CTS_LOOP_MODE_BMSK);
+ else
+ ret |= UARTDM_MR2_LOOP_MODE_BMSK;
msm_hs_write(uport, UARTDM_MR2_ADDR, ret);
spin_unlock_irqrestore(&uport->lock, flags);
} else {
spin_lock_irqsave(&uport->lock, flags);
ret = msm_hs_read(uport, UARTDM_MR2_ADDR);
- ret &= ~UARTDM_MR2_LOOP_MODE_BMSK;
+ if (is_blsp_uart(msm_uport))
+ ret &= ~(UARTDM_MR2_LOOP_MODE_BMSK |
+ UARTDM_MR2_RFR_CTS_LOOP_MODE_BMSK);
+ else
+ ret &= ~UARTDM_MR2_LOOP_MODE_BMSK;
msm_hs_write(uport, UARTDM_MR2_ADDR, ret);
spin_unlock_irqrestore(&uport->lock, flags);
}
@@ -470,6 +528,89 @@
return 0;
}
+
+/* Connect a UART peripheral's SPS endpoint(consumer endpoint)
+ *
+ * Also registers a SPS callback function for the consumer
+ * process with the SPS driver
+ *
+ * @uport - Pointer to uart uport structure
+ *
+ * @return - 0 if successful else negative value.
+ *
+ */
+
+static int msm_hs_spsconnect_tx(struct uart_port *uport)
+{
+ int ret;
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ struct msm_hs_tx *tx = &msm_uport->tx;
+ struct sps_pipe *sps_pipe_handle = tx->cons.pipe_handle;
+ struct sps_connect *sps_config = &tx->cons.config;
+ struct sps_register_event *sps_event = &tx->cons.event;
+
+ /* Establish connection between peripheral and memory endpoint */
+ ret = sps_connect(sps_pipe_handle, sps_config);
+ if (ret) {
+ pr_err("msm_serial_hs: sps_connect() failed for tx!!\n"
+ "pipe_handle=0x%x ret=%d", (u32)sps_pipe_handle, ret);
+ return ret;
+ }
+ /* Register callback event for EOT (End of transfer) event. */
+ ret = sps_register_event(sps_pipe_handle, sps_event);
+ if (ret) {
+ pr_err("msm_serial_hs: sps_connect() failed for tx!!\n"
+ "pipe_handle=0x%x ret=%d", (u32)sps_pipe_handle, ret);
+ goto reg_event_err;
+ }
+ return 0;
+
+reg_event_err:
+ sps_disconnect(sps_pipe_handle);
+ return ret;
+}
+
+/* Connect a UART peripheral's SPS endpoint(producer endpoint)
+ *
+ * Also registers a SPS callback function for the producer
+ * process with the SPS driver
+ *
+ * @uport - Pointer to uart uport structure
+ *
+ * @return - 0 if successful else negative value.
+ *
+ */
+
+static int msm_hs_spsconnect_rx(struct uart_port *uport)
+{
+ int ret;
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ struct msm_hs_rx *rx = &msm_uport->rx;
+ struct sps_pipe *sps_pipe_handle = rx->prod.pipe_handle;
+ struct sps_connect *sps_config = &rx->prod.config;
+ struct sps_register_event *sps_event = &rx->prod.event;
+
+ /* Establish connection between peripheral and memory endpoint */
+ ret = sps_connect(sps_pipe_handle, sps_config);
+ if (ret) {
+ pr_err("msm_serial_hs: sps_connect() failed for rx!!\n"
+ "pipe_handle=0x%x ret=%d", (u32)sps_pipe_handle, ret);
+ return ret;
+ }
+ /* Register callback event for EOT (End of transfer) event. */
+ ret = sps_register_event(sps_pipe_handle, sps_event);
+ if (ret) {
+ pr_err("msm_serial_hs: sps_connect() failed for rx!!\n"
+ "pipe_handle=0x%x ret=%d", (u32)sps_pipe_handle, ret);
+ goto reg_event_err;
+ }
+ return 0;
+
+reg_event_err:
+ sps_disconnect(sps_pipe_handle);
+ return ret;
+}
+
/*
* programs the UARTDM_CSR register with correct bit rates
*
@@ -479,9 +620,8 @@
* Goal is to have around 8 ms before indicate stale.
* roundup (((Bit Rate * .008) / 10) + 1
*/
-static unsigned long msm_hs_set_bps_locked(struct uart_port *uport,
- unsigned int bps,
- unsigned long flags)
+static void msm_hs_set_bps_locked(struct uart_port *uport,
+ unsigned int bps)
{
unsigned long rxstale;
unsigned long data;
@@ -581,15 +721,11 @@
uport->uartclk = 7372800;
}
- spin_unlock_irqrestore(&uport->lock, flags);
if (clk_set_rate(msm_uport->clk, uport->uartclk)) {
printk(KERN_WARNING "Error setting clock rate on UART\n");
WARN_ON(1);
- spin_lock_irqsave(&uport->lock, flags);
- return flags;
}
- spin_lock_irqsave(&uport->lock, flags);
data = rxstale & UARTDM_IPR_STALE_LSB_BMSK;
data |= UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK & (rxstale << 2);
@@ -601,7 +737,6 @@
*/
msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX);
msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX);
- return flags;
}
@@ -654,6 +789,23 @@
msm_hs_write(uport, UARTDM_IPR_ADDR, data);
}
+
+/* Reset BAM RX Endpoint Pipe Index from workqueue context*/
+
+static void hsuart_reset_bam_rx_work(struct work_struct *w)
+{
+ struct msm_hs_port *msm_uport = container_of(w, struct msm_hs_port,
+ reset_bam_rx);
+ struct uart_port *uport = &msm_uport->uport;
+ struct msm_hs_rx *rx = &msm_uport->rx;
+ struct sps_pipe *sps_pipe_handle = rx->prod.pipe_handle;
+
+ sps_disconnect(sps_pipe_handle);
+ msm_hs_spsconnect_rx(uport);
+
+ msm_serial_hs_rx_tlet((unsigned long) &rx->tlet);
+}
+
/*
* termios : new ktermios
* oldtermios: old ktermios previous setting
@@ -666,12 +818,12 @@
{
unsigned int bps;
unsigned long data;
- unsigned long flags;
unsigned int c_cflag = termios->c_cflag;
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ struct msm_hs_rx *rx = &msm_uport->rx;
+ struct sps_pipe *sps_pipe_handle = rx->prod.pipe_handle;
mutex_lock(&msm_uport->clk_mutex);
- spin_lock_irqsave(&uport->lock, flags);
/*
* Disable Rx channel of UARTDM
@@ -683,7 +835,13 @@
* suggested to do disable/reset or reset/disable at the same time.
*/
data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
- data &= ~UARTDM_RX_DM_EN_BMSK;
+ if (is_blsp_uart(msm_uport)) {
+ /* Disable UARTDM RX BAM Interface */
+ data &= ~UARTDM_RX_BAM_ENABLE_BMSK;
+ } else {
+ data &= ~UARTDM_RX_DM_EN_BMSK;
+ }
+
msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
/* 300 is the minimum baud support by the driver */
@@ -697,7 +855,7 @@
if (!uport->uartclk)
msm_hs_set_std_bps_locked(uport, bps);
else
- flags = msm_hs_set_bps_locked(uport, bps, flags);
+ msm_hs_set_bps_locked(uport, bps);
data = msm_hs_read(uport, UARTDM_MR2_ADDR);
data &= ~UARTDM_MR2_PARITY_MODE_BMSK;
@@ -775,13 +933,18 @@
* dsb requires here.
*/
mb();
- /* do discard flush */
- msm_dmov_flush(msm_uport->dma_rx_channel, 0);
+ if (is_blsp_uart(msm_uport)) {
+ sps_disconnect(sps_pipe_handle);
+ msm_hs_spsconnect_rx(uport);
+ msm_serial_hs_rx_tlet((unsigned long) &rx->tlet);
+ } else {
+ /* do discard flush */
+ msm_dmov_flush(msm_uport->dma_rx_channel, 0);
+ }
}
msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
mb();
- spin_unlock_irqrestore(&uport->lock, flags);
mutex_unlock(&msm_uport->clk_mutex);
}
@@ -814,6 +977,20 @@
msm_uport->tx.tx_ready_int_en = 0;
}
+/* Disconnect BAM RX Endpoint Pipe Index from workqueue context*/
+static void hsuart_disconnect_rx_endpoint_work(struct work_struct *w)
+{
+ struct msm_hs_port *msm_uport = container_of(w, struct msm_hs_port,
+ disconnect_rx_endpoint);
+ struct msm_hs_rx *rx = &msm_uport->rx;
+ struct sps_pipe *sps_pipe_handle = rx->prod.pipe_handle;
+
+ sps_disconnect(sps_pipe_handle);
+ wake_lock_timeout(&msm_uport->rx.wake_lock, HZ / 2);
+ msm_uport->rx.flush = FLUSH_SHUTDOWN;
+ wake_up(&msm_uport->rx.wait);
+}
+
/*
* Standard API, Stop receiver as soon as possible.
*
@@ -829,7 +1006,10 @@
/* disable dlink */
data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
- data &= ~UARTDM_RX_DM_EN_BMSK;
+ if (is_blsp_uart(msm_uport))
+ data &= ~UARTDM_RX_BAM_ENABLE_BMSK;
+ else
+ data &= ~UARTDM_RX_DM_EN_BMSK;
msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
/* calling DMOV or CLOCK API. Hence mb() */
@@ -837,10 +1017,17 @@
/* Disable the receiver */
if (msm_uport->rx.flush == FLUSH_NONE) {
wake_lock(&msm_uport->rx.wake_lock);
- /* do discard flush */
- msm_dmov_flush(msm_uport->dma_rx_channel, 0);
+ if (is_blsp_uart(msm_uport)) {
+ msm_uport->rx.flush = FLUSH_STOP;
+ /* workqueue for BAM rx endpoint disconnect */
+ queue_work(msm_uport->hsuart_wq,
+ &msm_uport->disconnect_rx_endpoint);
+ } else {
+ /* do discard flush */
+ msm_dmov_flush(msm_uport->dma_rx_channel, 0);
+ }
}
- if (msm_uport->rx.flush != FLUSH_SHUTDOWN)
+ if (!is_blsp_uart(msm_uport) && msm_uport->rx.flush != FLUSH_SHUTDOWN)
msm_uport->rx.flush = FLUSH_STOP;
}
@@ -852,9 +1039,11 @@
int aligned_tx_count;
dma_addr_t src_addr;
dma_addr_t aligned_src_addr;
+ u32 flags = SPS_IOVEC_FLAG_EOT;
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
struct msm_hs_tx *tx = &msm_uport->tx;
struct circ_buf *tx_buf = &msm_uport->uport.state->xmit;
+ struct sps_pipe *sps_pipe_handle;
if (uart_circ_empty(tx_buf) || uport->state->port.tty->stopped) {
msm_hs_stop_tx_locked(uport);
@@ -882,14 +1071,21 @@
dma_sync_single_for_device(uport->dev, aligned_src_addr,
aligned_tx_count, DMA_TO_DEVICE);
- tx->command_ptr->num_rows = (((tx_count + 15) >> 4) << 16) |
- ((tx_count + 15) >> 4);
- tx->command_ptr->src_row_addr = src_addr;
+ if (is_blsp_uart(msm_uport)) {
+ /* Issue TX BAM Start IFC command */
+ msm_hs_write(uport, UARTDM_CR_ADDR, START_TX_BAM_IFC);
+ } else {
+ tx->command_ptr->num_rows =
+ (((tx_count + 15) >> 4) << 16) |
+ ((tx_count + 15) >> 4);
+ tx->command_ptr->src_row_addr = src_addr;
- dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr,
- sizeof(dmov_box), DMA_TO_DEVICE);
+ dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr,
+ sizeof(dmov_box), DMA_TO_DEVICE);
- *tx->command_ptr_ptr = CMD_PTR_LP | DMOV_CMD_ADDR(tx->mapped_cmd_ptr);
+ *tx->command_ptr_ptr = CMD_PTR_LP |
+ DMOV_CMD_ADDR(tx->mapped_cmd_ptr);
+ }
/* Save tx_count to use in Callback */
tx->tx_count = tx_count;
@@ -901,16 +1097,28 @@
/* Calling next DMOV API. Hence mb() here. */
mb();
- dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr_ptr,
- sizeof(u32), DMA_TO_DEVICE);
msm_uport->tx.flush = FLUSH_NONE;
- msm_dmov_enqueue_cmd(msm_uport->dma_tx_channel, &tx->xfer);
+
+ if (is_blsp_uart(msm_uport)) {
+ sps_pipe_handle = tx->cons.pipe_handle;
+ /* Queue transfer request to SPS */
+ sps_transfer_one(sps_pipe_handle, src_addr, tx_count,
+ msm_uport, flags);
+ } else {
+ dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr_ptr,
+ sizeof(u32), DMA_TO_DEVICE);
+
+ msm_dmov_enqueue_cmd(msm_uport->dma_tx_channel, &tx->xfer);
+ }
}
/* Start to receive the next chunk of data */
static void msm_hs_start_rx_locked(struct uart_port *uport)
{
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ struct msm_hs_rx *rx = &msm_uport->rx;
+ struct sps_pipe *sps_pipe_handle;
+ u32 flags = SPS_IOVEC_FLAG_EOT;
unsigned int buffer_pending = msm_uport->rx.buffer_pending;
unsigned int data;
@@ -919,6 +1127,10 @@
printk(KERN_ERR "Error: rx started in buffer state = %x",
buffer_pending);
+ if (is_blsp_uart(msm_uport)) {
+ /* Issue RX BAM Start IFC command */
+ msm_hs_write(uport, UARTDM_CR_ADDR, START_RX_BAM_IFC);
+ }
msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT);
msm_hs_write(uport, UARTDM_DMRX_ADDR, UARTDM_RX_BUF_SIZE);
msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_ENABLE);
@@ -929,15 +1141,29 @@
* disable in set_termios before configuring baud rate.
*/
data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
- data |= UARTDM_RX_DM_EN_BMSK;
+ if (is_blsp_uart(msm_uport)) {
+ /* Enable UARTDM Rx BAM Interface */
+ data |= UARTDM_RX_BAM_ENABLE_BMSK;
+ } else {
+ data |= UARTDM_RX_DM_EN_BMSK;
+ }
+
msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
/* Calling next DMOV API. Hence mb() here. */
mb();
msm_uport->rx.flush = FLUSH_NONE;
- msm_dmov_enqueue_cmd(msm_uport->dma_rx_channel, &msm_uport->rx.xfer);
+ if (is_blsp_uart(msm_uport)) {
+ sps_pipe_handle = rx->prod.pipe_handle;
+ /* Queue transfer request to SPS */
+ sps_transfer_one(sps_pipe_handle, rx->rbuffer,
+ UARTDM_RX_BUF_SIZE, msm_uport, flags);
+ } else {
+ msm_dmov_enqueue_cmd(msm_uport->dma_rx_channel,
+ &msm_uport->rx.xfer);
+ }
}
static void flip_insert_work(struct work_struct *work)
@@ -999,6 +1225,7 @@
{
int retval;
int rx_count;
+ static int remaining_rx_count, bytes_pending;
unsigned long status;
unsigned long flags;
unsigned int error_f = 0;
@@ -1075,6 +1302,20 @@
rx_count = msm_hs_read(uport, UARTDM_RX_TOTAL_SNAP_ADDR);
+ if (is_blsp_uart(msm_uport)) {
+ if (rx_count > UARTDM_RX_BUF_SIZE) {
+ if (bytes_pending) {
+ rx_count = remaining_rx_count;
+ bytes_pending = 0;
+ } else {
+ remaining_rx_count = rx_count -
+ UARTDM_RX_BUF_SIZE;
+ if (remaining_rx_count)
+ bytes_pending = 1;
+ rx_count = UARTDM_RX_BUF_SIZE;
+ }
+ }
+ }
/* order the read of rx.buffer */
rmb();
@@ -1124,6 +1365,31 @@
}
}
+/**
+ * Callback notification from SPS driver
+ *
+ * This callback function gets triggered called from
+ * SPS driver when requested SPS data transfer is
+ * completed.
+ *
+ */
+
+static void msm_hs_sps_tx_callback(struct sps_event_notify *notify)
+{
+ struct msm_hs_port *msm_uport =
+ (struct msm_hs_port *)
+ ((struct sps_event_notify *)notify)->user;
+
+ msm_uport->notify = *notify;
+ pr_debug("%s: sps ev_id=%d, addr=0x%x, size=0x%x, flags=0x%x\n",
+ __func__, notify->event_id,
+ notify->data.transfer.iovec.addr,
+ notify->data.transfer.iovec.size,
+ notify->data.transfer.iovec.flags);
+
+ tasklet_schedule(&msm_uport->tx.tlet);
+}
+
/*
* This routine is called when we are done with a DMA transfer
*
@@ -1170,6 +1436,33 @@
spin_unlock_irqrestore(&(msm_uport->uport.lock), flags);
}
+/**
+ * Callback notification from SPS driver
+ *
+ * This callback function gets triggered called from
+ * SPS driver when requested SPS data transfer is
+ * completed.
+ *
+ */
+
+static void msm_hs_sps_rx_callback(struct sps_event_notify *notify)
+{
+
+ struct msm_hs_port *msm_uport =
+ (struct msm_hs_port *)
+ ((struct sps_event_notify *)notify)->user;
+
+ msm_uport->notify = *notify;
+ pr_debug("%s: sps ev_id=%d, addr=0x%x, size=0x%x, flags=0x%x\n",
+ __func__, notify->event_id,
+ notify->data.transfer.iovec.addr,
+ notify->data.transfer.iovec.size,
+ notify->data.transfer.iovec.flags);
+
+ if (msm_uport->rx.flush == FLUSH_NONE)
+ tasklet_schedule(&msm_uport->rx.tlet);
+}
+
/*
* This routine is called when we are done with a DMA transfer or the
* a flush has been sent to the data mover driver.
@@ -1265,7 +1558,8 @@
{
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- msm_uport->tty_flush_receive = true;
+ if (msm_uport->tx.dma_in_flight)
+ msm_uport->tty_flush_receive = true;
}
/*
@@ -1356,6 +1650,14 @@
switch (msm_uport->clk_req_off_state) {
case CLK_REQ_OFF_START:
msm_uport->clk_req_off_state = CLK_REQ_OFF_RXSTALE_ISSUED;
+ if (is_blsp_uart(msm_uport)) {
+ /* Stale interrupt when RX-FIFO is empty
+ * will fire if STALE_IRQ_EMPTY bit is set
+ * for UART Core v1.4
+ */
+ msm_hs_write(uport, UARTDM_BCR_ADDR,
+ UARTDM_BCR_STALE_IRQ_EMPTY);
+ }
msm_hs_write(uport, UARTDM_CR_ADDR, FORCE_STALE_EVENT);
/*
* Before returning make sure that device writel completed.
@@ -1458,13 +1760,25 @@
*/
mb();
- if (msm_uport->clk_req_off_state == CLK_REQ_OFF_RXSTALE_ISSUED)
+ if (msm_uport->clk_req_off_state ==
+ CLK_REQ_OFF_RXSTALE_ISSUED) {
msm_uport->clk_req_off_state =
CLK_REQ_OFF_FLUSH_ISSUED;
+ if (is_blsp_uart(msm_uport)) {
+ /* Reset BCR Register for UARTDM Core v14*/
+ msm_hs_write(uport, UARTDM_BCR_ADDR, 0x0);
+ }
+ }
+
if (rx->flush == FLUSH_NONE) {
rx->flush = FLUSH_DATA_READY;
- msm_dmov_flush(msm_uport->dma_rx_channel, 1);
+ if (is_blsp_uart(msm_uport)) {
+ queue_work(msm_uport->hsuart_wq,
+ &msm_uport->reset_bam_rx);
+ } else {
+ msm_dmov_flush(msm_uport->dma_rx_channel, 1);
+ }
}
}
/* tx ready interrupt */
@@ -1581,7 +1895,10 @@
msm_uport->rx.flush == FLUSH_SHUTDOWN) {
msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX);
data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
- data |= UARTDM_RX_DM_EN_BMSK;
+ if (is_blsp_uart(msm_uport))
+ data |= UARTDM_RX_BAM_ENABLE_BMSK;
+ else
+ data |= UARTDM_RX_DM_EN_BMSK;
msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
/* Complete above device write. Hence mb() here. */
mb();
@@ -1661,6 +1978,9 @@
pdev->dev.platform_data;
struct circ_buf *tx_buf = &uport->state->xmit;
struct msm_hs_tx *tx = &msm_uport->tx;
+ struct msm_hs_rx *rx = &msm_uport->rx;
+ struct sps_pipe *sps_pipe_handle_tx = tx->cons.pipe_handle;
+ struct sps_pipe *sps_pipe_handle_rx = rx->prod.pipe_handle;
rfr_level = uport->fifosize;
if (rfr_level > 16)
@@ -1682,6 +2002,24 @@
if (unlikely(pdata->gpio_config(1)))
dev_err(uport->dev, "Cannot configure gpios\n");
+
+ /* SPS Connect for BAM endpoints */
+ if (is_blsp_uart(msm_uport)) {
+ /* SPS connect for TX */
+ ret = msm_hs_spsconnect_tx(uport);
+ if (ret) {
+ pr_err("msm_serial_hs: SPS connect failed for TX");
+ goto deinit_uart_clk;
+ }
+
+ /* SPS connect for RX */
+ ret = msm_hs_spsconnect_rx(uport);
+ if (ret) {
+ pr_err("msm_serial_hs: SPS connect failed for RX");
+ goto sps_disconnect_tx;
+ }
+ }
+
/* Set auto RFR Level */
data = msm_hs_read(uport, UARTDM_MR1_ADDR);
data &= ~UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK;
@@ -1697,8 +2035,13 @@
msm_hs_write(uport, UARTDM_IPR_ADDR, data);
}
- /* Enable Data Mover Mode */
- data = UARTDM_TX_DM_EN_BMSK | UARTDM_RX_DM_EN_BMSK;
+ if (is_blsp_uart(msm_uport)) {
+ /* Enable BAM mode */
+ data = UARTDM_TX_BAM_ENABLE_BMSK | UARTDM_RX_BAM_ENABLE_BMSK;
+ } else {
+ /* Enable Data Mover Mode */
+ data = UARTDM_TX_DM_EN_BMSK | UARTDM_RX_DM_EN_BMSK;
+ }
msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
/* Reset TX */
@@ -1719,18 +2062,20 @@
tx->tx_ready_int_en = 0;
tx->dma_in_flight = 0;
- tx->xfer.complete_func = msm_hs_dmov_tx_callback;
+ if (!is_blsp_uart(msm_uport)) {
+ tx->xfer.complete_func = msm_hs_dmov_tx_callback;
- tx->command_ptr->cmd = CMD_LC |
- CMD_DST_CRCI(msm_uport->dma_tx_crci) | CMD_MODE_BOX;
+ tx->command_ptr->cmd = CMD_LC |
+ CMD_DST_CRCI(msm_uport->dma_tx_crci) | CMD_MODE_BOX;
- tx->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16)
+ tx->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16)
| (MSM_UARTDM_BURST_SIZE);
- tx->command_ptr->row_offset = (MSM_UARTDM_BURST_SIZE << 16);
+ tx->command_ptr->row_offset = (MSM_UARTDM_BURST_SIZE << 16);
- tx->command_ptr->dst_row_addr =
- msm_uport->uport.mapbase + UARTDM_TF_ADDR;
+ tx->command_ptr->dst_row_addr =
+ msm_uport->uport.mapbase + UARTDM_TF_ADDR;
+ }
msm_uport->imr_reg |= UARTDM_ISR_RXSTALE_BMSK;
/* Enable reading the current CTS, no harm even if CTS is ignored */
@@ -1747,7 +2092,7 @@
ret = irq_set_irq_wake(msm_uport->wakeup.irq, 1);
if (unlikely(ret)) {
pr_err("%s():Err setting wakeup irq\n", __func__);
- goto deinit_uart_clk;
+ goto sps_disconnect_rx;
}
}
@@ -1787,6 +2132,12 @@
free_irq(uport->irq, msm_uport);
free_wake_irq:
irq_set_irq_wake(msm_uport->wakeup.irq, 0);
+sps_disconnect_rx:
+ if (is_blsp_uart(msm_uport))
+ sps_disconnect(sps_pipe_handle_rx);
+sps_disconnect_tx:
+ if (is_blsp_uart(msm_uport))
+ sps_disconnect(sps_pipe_handle_tx);
deinit_uart_clk:
clk_disable_unprepare(msm_uport->clk);
if (msm_uport->pclk)
@@ -1804,24 +2155,6 @@
struct msm_hs_tx *tx = &msm_uport->tx;
struct msm_hs_rx *rx = &msm_uport->rx;
- /* Allocate the command pointer. Needs to be 64 bit aligned */
- tx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA);
- if (!tx->command_ptr)
- return -ENOMEM;
-
- tx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA);
- if (!tx->command_ptr_ptr) {
- ret = -ENOMEM;
- goto free_tx_command_ptr;
- }
-
- tx->mapped_cmd_ptr = dma_map_single(uport->dev, tx->command_ptr,
- sizeof(dmov_box), DMA_TO_DEVICE);
- tx->mapped_cmd_ptr_ptr = dma_map_single(uport->dev,
- tx->command_ptr_ptr,
- sizeof(u32), DMA_TO_DEVICE);
- tx->xfer.cmdptr = DMOV_CMD_ADDR(tx->mapped_cmd_ptr_ptr);
-
init_waitqueue_head(&rx->wait);
init_waitqueue_head(&tx->wait);
wake_lock_init(&rx->wake_lock, WAKE_LOCK_SUSPEND, "msm_serial_hs_rx");
@@ -1848,12 +2181,40 @@
goto free_pool;
}
+ /* Set up Uart Receive */
+ msm_hs_write(uport, UARTDM_RFWR_ADDR, 0);
+
+ INIT_DELAYED_WORK(&rx->flip_insert_work, flip_insert_work);
+
+ if (is_blsp_uart(msm_uport))
+ return ret;
+
+ /* Allocate the command pointer. Needs to be 64 bit aligned */
+ tx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA);
+ if (!tx->command_ptr) {
+ return -ENOMEM;
+ goto free_rx_buffer;
+ }
+
+ tx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA);
+ if (!tx->command_ptr_ptr) {
+ ret = -ENOMEM;
+ goto free_tx_command_ptr;
+ }
+
+ tx->mapped_cmd_ptr = dma_map_single(uport->dev, tx->command_ptr,
+ sizeof(dmov_box), DMA_TO_DEVICE);
+ tx->mapped_cmd_ptr_ptr = dma_map_single(uport->dev,
+ tx->command_ptr_ptr,
+ sizeof(u32), DMA_TO_DEVICE);
+ tx->xfer.cmdptr = DMOV_CMD_ADDR(tx->mapped_cmd_ptr_ptr);
+
/* Allocate the command pointer. Needs to be 64 bit aligned */
rx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA);
if (!rx->command_ptr) {
pr_err("%s(): cannot allocate rx->command_ptr", __func__);
ret = -ENOMEM;
- goto free_rx_buffer;
+ goto free_tx_command_ptr_ptr;
}
rx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA);
@@ -1868,9 +2229,6 @@
rx->command_ptr->dst_row_addr = rx->rbuffer;
- /* Set up Uart Receive */
- msm_hs_write(uport, UARTDM_RFWR_ADDR, 0);
-
rx->xfer.complete_func = msm_hs_dmov_rx_callback;
rx->command_ptr->cmd = CMD_LC |
@@ -1890,13 +2248,21 @@
sizeof(u32), DMA_TO_DEVICE);
rx->xfer.cmdptr = DMOV_CMD_ADDR(rx->cmdptr_dmaaddr);
- INIT_DELAYED_WORK(&rx->flip_insert_work, flip_insert_work);
-
return ret;
free_rx_command_ptr:
kfree(rx->command_ptr);
+free_tx_command_ptr_ptr:
+ kfree(msm_uport->tx.command_ptr_ptr);
+ dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr_ptr,
+ sizeof(u32), DMA_TO_DEVICE);
+ dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr,
+ sizeof(dmov_box), DMA_TO_DEVICE);
+
+free_tx_command_ptr:
+ kfree(msm_uport->tx.command_ptr);
+
free_rx_buffer:
dma_pool_free(msm_uport->rx.pool, msm_uport->rx.buffer,
msm_uport->rx.rbuffer);
@@ -1909,47 +2275,386 @@
wake_lock_destroy(&msm_uport->dma_wake_lock);
tasklet_kill(&msm_uport->tx.tlet);
tasklet_kill(&msm_uport->rx.tlet);
- dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr_ptr,
- sizeof(u32), DMA_TO_DEVICE);
- dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr,
- sizeof(dmov_box), DMA_TO_DEVICE);
- kfree(msm_uport->tx.command_ptr_ptr);
-
-free_tx_command_ptr:
- kfree(msm_uport->tx.command_ptr);
return ret;
}
+struct msm_serial_hs_platform_data
+ *msm_hs_dt_to_pdata(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct msm_serial_hs_platform_data *pdata;
+ int rx_to_inject, ret;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ pr_err("unable to allocate memory for platform data\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* UART TX GPIO */
+ pdata->uart_tx_gpio = of_get_named_gpio(node,
+ "qcom,tx-gpio", 0);
+ if (pdata->uart_tx_gpio < 0)
+ pr_debug("uart_tx_gpio is not available\n");
+
+ /* UART RX GPIO */
+ pdata->uart_rx_gpio = of_get_named_gpio(node,
+ "qcom,rx-gpio", 0);
+ if (pdata->uart_rx_gpio < 0)
+ pr_debug("uart_rx_gpio is not available\n");
+
+ /* UART CTS GPIO */
+ pdata->uart_cts_gpio = of_get_named_gpio(node,
+ "qcom,cts-gpio", 0);
+ if (pdata->uart_cts_gpio < 0)
+ pr_debug("uart_cts_gpio is not available\n");
+
+ /* UART RFR GPIO */
+ pdata->uart_rfr_gpio = of_get_named_gpio(node,
+ "qcom,rfr-gpio", 0);
+ if (pdata->uart_rfr_gpio < 0)
+ pr_debug("uart_rfr_gpio is not available\n");
+
+ pdata->inject_rx_on_wakeup = of_property_read_bool(node,
+ "qcom,inject-rx-on-wakeup");
+
+ if (pdata->inject_rx_on_wakeup) {
+ ret = of_property_read_u32(node, "qcom,rx-char-to-inject",
+ &rx_to_inject);
+ if (ret < 0) {
+ pr_err("Error: Rx_char_to_inject not specified.\n");
+ return ERR_PTR(ret);
+ }
+ pdata->rx_to_inject = (char)rx_to_inject;
+ }
+
+ ret = of_property_read_u32(node, "qcom,bam-tx-ep-pipe-index",
+ &pdata->bam_tx_ep_pipe_index);
+ if (ret < 0) {
+ pr_err("Error: Getting UART BAM TX EP Pipe Index.\n");
+ return ERR_PTR(ret);
+ }
+
+ if (!(pdata->bam_tx_ep_pipe_index >= BAM_PIPE_MIN &&
+ pdata->bam_tx_ep_pipe_index <= BAM_PIPE_MAX)) {
+ pr_err("Error: Invalid UART BAM TX EP Pipe Index.\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ ret = of_property_read_u32(node, "qcom,bam-rx-ep-pipe-index",
+ &pdata->bam_rx_ep_pipe_index);
+ if (ret < 0) {
+ pr_err("Error: Getting UART BAM RX EP Pipe Index.\n");
+ return ERR_PTR(ret);
+ }
+
+ if (!(pdata->bam_rx_ep_pipe_index >= BAM_PIPE_MIN &&
+ pdata->bam_rx_ep_pipe_index <= BAM_PIPE_MAX)) {
+ pr_err("Error: Invalid UART BAM RX EP Pipe Index.\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pr_debug("tx_ep_pipe_index:%d rx_ep_pipe_index:%d\n"
+ "tx_gpio:%d rx_gpio:%d rfr_gpio:%d cts_gpio:%d",
+ pdata->bam_tx_ep_pipe_index, pdata->bam_rx_ep_pipe_index,
+ pdata->uart_tx_gpio, pdata->uart_rx_gpio, pdata->uart_cts_gpio,
+ pdata->uart_rfr_gpio);
+
+ return pdata;
+}
+
+
+/**
+ * Deallocate UART peripheral's SPS endpoint
+ * @msm_uport - Pointer to msm_hs_port structure
+ * @ep - Pointer to sps endpoint data structure
+ */
+
+static void msm_hs_exit_ep_conn(struct msm_hs_port *msm_uport,
+ struct msm_hs_sps_ep_conn_data *ep)
+{
+ struct sps_pipe *sps_pipe_handle = ep->pipe_handle;
+ struct sps_connect *sps_config = &ep->config;
+
+ dma_free_coherent(msm_uport->uport.dev,
+ sps_config->desc.size,
+ &sps_config->desc.phys_base,
+ GFP_KERNEL);
+ sps_free_endpoint(sps_pipe_handle);
+}
+
+
+/**
+ * Allocate UART peripheral's SPS endpoint
+ *
+ * This function allocates endpoint context
+ * by calling appropriate SPS driver APIs.
+ *
+ * @msm_uport - Pointer to msm_hs_port structure
+ * @ep - Pointer to sps endpoint data structure
+ * @is_produce - 1 means Producer endpoint
+ * - 0 means Consumer endpoint
+ *
+ * @return - 0 if successful else negative value
+ */
+
+static int msm_hs_sps_init_ep_conn(struct msm_hs_port *msm_uport,
+ struct msm_hs_sps_ep_conn_data *ep,
+ bool is_producer)
+{
+ int rc = 0;
+ struct sps_pipe *sps_pipe_handle;
+ struct sps_connect *sps_config = &ep->config;
+ struct sps_register_event *sps_event = &ep->event;
+
+ /* Allocate endpoint context */
+ sps_pipe_handle = sps_alloc_endpoint();
+ if (!sps_pipe_handle) {
+ pr_err("msm_serial_hs: sps_alloc_endpoint() failed!!\n"
+ "is_producer=%d", is_producer);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* Get default connection configuration for an endpoint */
+ rc = sps_get_config(sps_pipe_handle, sps_config);
+ if (rc) {
+ pr_err("msm_serial_hs: sps_get_config() failed!!\n"
+ "pipe_handle=0x%x rc=%d", (u32)sps_pipe_handle, rc);
+ goto get_config_err;
+ }
+
+ /* Modify the default connection configuration */
+ if (is_producer) {
+ /* For UART producer transfer, source is UART peripheral
+ where as destination is system memory */
+ sps_config->source = msm_uport->bam_handle;
+ sps_config->destination = SPS_DEV_HANDLE_MEM;
+ sps_config->mode = SPS_MODE_SRC;
+ sps_config->src_pipe_index = msm_uport->bam_rx_ep_pipe_index;
+ sps_config->dest_pipe_index = 0;
+ sps_config->options = SPS_O_EOT;
+ } else {
+ /* For UART consumer transfer, source is system memory
+ where as destination is UART peripheral */
+ sps_config->source = SPS_DEV_HANDLE_MEM;
+ sps_config->destination = msm_uport->bam_handle;
+ sps_config->mode = SPS_MODE_DEST;
+ sps_config->src_pipe_index = 0;
+ sps_config->dest_pipe_index = msm_uport->bam_tx_ep_pipe_index;
+ sps_config->options = SPS_O_EOT;
+ }
+
+ sps_config->event_thresh = 0x10;
+
+ /* Allocate maximum descriptor fifo size */
+ sps_config->desc.size = 65532;
+ sps_config->desc.base = dma_alloc_coherent(msm_uport->uport.dev,
+ sps_config->desc.size,
+ &sps_config->desc.phys_base,
+ GFP_KERNEL);
+ if (!sps_config->desc.base) {
+ rc = -ENOMEM;
+ pr_err("msm_serial_hs: dma_alloc_coherent() failed!!\n");
+ goto get_config_err;
+ }
+ memset(sps_config->desc.base, 0x00, sps_config->desc.size);
+
+ sps_event->mode = SPS_TRIGGER_CALLBACK;
+ sps_event->options = SPS_O_EOT;
+ if (is_producer)
+ sps_event->callback = msm_hs_sps_rx_callback;
+ else
+ sps_event->callback = msm_hs_sps_tx_callback;
+
+ sps_event->user = (void *)msm_uport;
+
+ /* Now save the sps pipe handle */
+ ep->pipe_handle = sps_pipe_handle;
+ pr_debug("msm_serial_hs: success !! %s: pipe_handle=0x%x\n"
+ "desc_fifo.phys_base=0x%x\n",
+ is_producer ? "READ" : "WRITE",
+ (u32)sps_pipe_handle, sps_config->desc.phys_base);
+ return 0;
+
+get_config_err:
+ sps_free_endpoint(sps_pipe_handle);
+out:
+ return rc;
+}
+
+/**
+ * Initialize SPS HW connected with UART core
+ *
+ * This function register BAM HW resources with
+ * SPS driver and then initialize 2 SPS endpoints
+ *
+ * msm_uport - Pointer to msm_hs_port structure
+ *
+ * @return - 0 if successful else negative value
+ */
+
+static int msm_hs_sps_init(struct msm_hs_port *msm_uport)
+{
+ int rc = 0;
+ struct sps_bam_props bam = {0};
+ u32 bam_handle;
+
+ rc = sps_phy2h(msm_uport->bam_mem, &bam_handle);
+ if (rc || !bam_handle) {
+ bam.phys_addr = msm_uport->bam_mem;
+ bam.virt_addr = msm_uport->bam_base;
+ /*
+ * This event thresold value is only significant for BAM-to-BAM
+ * transfer. It's ignored for BAM-to-System mode transfer.
+ */
+ bam.event_threshold = 0x10; /* Pipe event threshold */
+ bam.summing_threshold = 1; /* BAM event threshold */
+
+ /* SPS driver wll handle the UART BAM IRQ */
+ bam.irq = (u32)msm_uport->bam_irq;
+ bam.manage = SPS_BAM_MGR_LOCAL;
+
+ pr_debug("msm_serial_hs: bam physical base=0x%x\n",
+ (u32)bam.phys_addr);
+ pr_debug("msm_serial_hs: bam virtual base=0x%x\n",
+ (u32)bam.virt_addr);
+
+ /* Register UART Peripheral BAM device to SPS driver */
+ rc = sps_register_bam_device(&bam, &bam_handle);
+ if (rc) {
+ pr_err("msm_serial_hs: BAM device register failed\n");
+ return rc;
+ }
+ pr_info("msm_serial_hs: BAM device registered. bam_handle=0x%x",
+ msm_uport->bam_handle);
+ }
+ msm_uport->bam_handle = bam_handle;
+
+ rc = msm_hs_sps_init_ep_conn(msm_uport, &msm_uport->rx.prod,
+ UART_SPS_PROD_PERIPHERAL);
+ if (rc) {
+ pr_err("%s: Failed to Init Producer BAM-pipe", __func__);
+ goto deregister_bam;
+ }
+
+ rc = msm_hs_sps_init_ep_conn(msm_uport, &msm_uport->tx.cons,
+ UART_SPS_CONS_PERIPHERAL);
+ if (rc) {
+ pr_err("%s: Failed to Init Consumer BAM-pipe", __func__);
+ goto deinit_ep_conn_prod;
+ }
+ return 0;
+
+deinit_ep_conn_prod:
+ msm_hs_exit_ep_conn(msm_uport, &msm_uport->rx.prod);
+deregister_bam:
+ sps_deregister_bam_device(msm_uport->bam_handle);
+ return rc;
+}
+
static int __devinit msm_hs_probe(struct platform_device *pdev)
{
int ret;
struct uart_port *uport;
struct msm_hs_port *msm_uport;
+ struct resource *core_resource;
+ struct resource *bam_resource;
struct resource *resource;
+ int core_irqres, bam_irqres;
struct msm_serial_hs_platform_data *pdata = pdev->dev.platform_data;
+ struct device_node *node = pdev->dev.of_node;
+
+ if (pdev->dev.of_node) {
+ dev_dbg(&pdev->dev, "device tree enabled\n");
+ pdata = msm_hs_dt_to_pdata(pdev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+
+ of_property_read_u32(node, "cell-index",
+ &pdev->id);
+
+ pdev->dev.platform_data = pdata;
+ }
if (pdev->id < 0 || pdev->id >= UARTDM_NR) {
- printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id);
+ pr_err("Invalid plaform device ID = %d\n", pdev->id);
return -EINVAL;
}
msm_uport = &q_uart_port[pdev->id];
uport = &msm_uport->uport;
-
uport->dev = &pdev->dev;
- resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (unlikely(!resource))
- return -ENXIO;
- uport->mapbase = resource->start; /* virtual address */
+ if (pdev->dev.of_node)
+ msm_uport->uart_type = BLSP_HSUART;
- uport->membase = ioremap(uport->mapbase, PAGE_SIZE);
- if (unlikely(!uport->membase))
- return -ENOMEM;
+ /* Get required resources for BAM HSUART */
+ if (is_blsp_uart(msm_uport)) {
+ core_resource = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "core_mem");
+ bam_resource = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "bam_mem");
+ core_irqres = platform_get_irq_byname(pdev, "core_irq");
+ bam_irqres = platform_get_irq_byname(pdev, "bam_irq");
- uport->irq = platform_get_irq(pdev, 0);
- if (unlikely((int)uport->irq < 0))
- return -ENXIO;
+ if (!core_resource) {
+ pr_err("Invalid core HSUART Resources.\n");
+ return -ENXIO;
+ }
+
+ if (!bam_resource) {
+ pr_err("Invalid BAM HSUART Resources.\n");
+ return -ENXIO;
+ }
+
+ if (!core_irqres) {
+ pr_err("Invalid core irqres Resources.\n");
+ return -ENXIO;
+ }
+ if (!bam_irqres) {
+ pr_err("Invalid bam irqres Resources.\n");
+ return -ENXIO;
+ }
+
+ uport->mapbase = core_resource->start;
+
+ uport->membase = ioremap(uport->mapbase,
+ resource_size(core_resource));
+ if (unlikely(!uport->membase)) {
+ pr_err("UART Resource ioremap Failed.\n");
+ return -ENOMEM;
+ }
+ msm_uport->bam_mem = bam_resource->start;
+ msm_uport->bam_base = ioremap(msm_uport->bam_mem,
+ resource_size(bam_resource));
+ if (unlikely(!msm_uport->bam_base)) {
+ pr_err("UART BAM Resource ioremap Failed.\n");
+ iounmap(uport->membase);
+ return -ENOMEM;
+ }
+
+ uport->irq = core_irqres;
+ msm_uport->bam_irq = bam_irqres;
+
+ } else {
+
+ resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (unlikely(!resource))
+ return -ENXIO;
+ uport->mapbase = resource->start;
+ uport->membase = ioremap(uport->mapbase,
+ resource_size(resource));
+ if (unlikely(!uport->membase))
+ return -ENOMEM;
+
+ uport->irq = platform_get_irq(pdev, 0);
+ if (unlikely((int)uport->irq < 0)) {
+ pr_err("UART IRQ Failed.\n");
+ iounmap(uport->membase);
+ return -ENXIO;
+ }
+ }
if (pdata == NULL)
msm_uport->wakeup.irq = -1;
@@ -1959,24 +2664,41 @@
msm_uport->wakeup.inject_rx = pdata->inject_rx_on_wakeup;
msm_uport->wakeup.rx_to_inject = pdata->rx_to_inject;
- if (unlikely(msm_uport->wakeup.irq < 0))
- return -ENXIO;
+ if (unlikely(msm_uport->wakeup.irq < 0)) {
+ ret = -ENXIO;
+ goto unmap_memory;
+ }
+ if (is_blsp_uart(msm_uport)) {
+ msm_uport->bam_tx_ep_pipe_index =
+ pdata->bam_tx_ep_pipe_index;
+ msm_uport->bam_rx_ep_pipe_index =
+ pdata->bam_rx_ep_pipe_index;
+ }
}
- resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
- "uartdm_channels");
- if (unlikely(!resource))
- return -ENXIO;
- msm_uport->dma_tx_channel = resource->start;
- msm_uport->dma_rx_channel = resource->end;
+ if (!is_blsp_uart(msm_uport)) {
- resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
- "uartdm_crci");
- if (unlikely(!resource))
- return -ENXIO;
- msm_uport->dma_tx_crci = resource->start;
- msm_uport->dma_rx_crci = resource->end;
+ resource = platform_get_resource_byname(pdev,
+ IORESOURCE_DMA, "uartdm_channels");
+ if (unlikely(!resource)) {
+ ret = -ENXIO;
+ goto unmap_memory;
+ }
+
+ msm_uport->dma_tx_channel = resource->start;
+ msm_uport->dma_rx_channel = resource->end;
+
+ resource = platform_get_resource_byname(pdev,
+ IORESOURCE_DMA, "uartdm_crci");
+ if (unlikely(!resource)) {
+ ret = -ENXIO;
+ goto unmap_memory;
+ }
+
+ msm_uport->dma_tx_crci = resource->start;
+ msm_uport->dma_rx_crci = resource->end;
+ }
uport->iotype = UPIO_MEM;
uport->fifosize = 64;
@@ -1986,8 +2708,10 @@
msm_uport->imr_reg = 0x0;
msm_uport->clk = clk_get(&pdev->dev, "core_clk");
- if (IS_ERR(msm_uport->clk))
- return PTR_ERR(msm_uport->clk);
+ if (IS_ERR(msm_uport->clk)) {
+ ret = PTR_ERR(msm_uport->clk);
+ goto unmap_memory;
+ }
msm_uport->pclk = clk_get(&pdev->dev, "iface_clk");
/*
@@ -2000,7 +2724,7 @@
ret = clk_set_rate(msm_uport->clk, uport->uartclk);
if (ret) {
printk(KERN_WARNING "Error setting clock rate on UART\n");
- return ret;
+ goto unmap_memory;
}
msm_uport->hsuart_wq = alloc_workqueue("k_hsuart",
@@ -2008,12 +2732,29 @@
if (!msm_uport->hsuart_wq) {
pr_err("%s(): Unable to create workqueue hsuart_wq\n",
__func__);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto unmap_memory;
}
INIT_WORK(&msm_uport->clock_off_w, hsuart_clock_off_work);
+
+ /* Init work for Reset Rx bam endpoints */
+ INIT_WORK(&msm_uport->reset_bam_rx, hsuart_reset_bam_rx_work);
+
+ /* Init work for sps_disconnect in stop_rx_locked */
+ INIT_WORK(&msm_uport->disconnect_rx_endpoint,
+ hsuart_disconnect_rx_endpoint_work);
mutex_init(&msm_uport->clk_mutex);
+ /* Initialize SPS HW connected with UART core */
+ if (is_blsp_uart(msm_uport)) {
+ ret = msm_hs_sps_init(msm_uport);
+ if (unlikely(ret)) {
+ pr_err("SPS Initialization failed ! err=%d", ret);
+ goto unmap_memory;
+ }
+ }
+
clk_prepare_enable(msm_uport->clk);
if (msm_uport->pclk)
clk_prepare_enable(msm_uport->pclk);
@@ -2023,7 +2764,7 @@
clk_disable_unprepare(msm_uport->clk);
if (msm_uport->pclk)
clk_disable_unprepare(msm_uport->pclk);
- return ret;
+ goto unmap_memory;
}
/* configure the CR Protection to Enable */
@@ -2048,14 +2789,23 @@
ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_clock.attr);
if (unlikely(ret))
- return ret;
+ goto unmap_memory;
msm_serial_debugfs_init(msm_uport, pdev->id);
uport->line = pdev->id;
if (pdata != NULL && pdata->userid && pdata->userid <= UARTDM_NR)
uport->line = pdata->userid;
- return uart_add_one_port(&msm_hs_driver, uport);
+ ret = uart_add_one_port(&msm_hs_driver, uport);
+ if (!ret)
+ return ret;
+
+unmap_memory:
+ iounmap(uport->membase);
+ if (is_blsp_uart(msm_uport))
+ iounmap(msm_uport->bam_base);
+
+ return ret;
}
static int __init msm_serial_hs_init(void)
@@ -2102,27 +2852,35 @@
struct platform_device *pdev = to_platform_device(uport->dev);
const struct msm_serial_hs_platform_data *pdata =
pdev->dev.platform_data;
+ struct msm_hs_tx *tx = &msm_uport->tx;
+ struct sps_pipe *sps_pipe_handle = tx->cons.pipe_handle;
if (msm_uport->tx.dma_in_flight) {
- spin_lock_irqsave(&uport->lock, flags);
- /* disable UART TX interface to DM */
- data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
- data &= ~UARTDM_TX_DM_EN_BMSK;
- msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
- /* turn OFF UART Transmitter */
- msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_DISABLE_BMSK);
- /* reset UART TX */
- msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX);
- /* reset UART TX Error */
- msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX_ERROR);
- msm_uport->tx.flush = FLUSH_STOP;
- spin_unlock_irqrestore(&uport->lock, flags);
- /* discard flush */
- msm_dmov_flush(msm_uport->dma_tx_channel, 0);
- ret = wait_event_timeout(msm_uport->tx.wait,
- msm_uport->tx.flush == FLUSH_SHUTDOWN, 100);
- if (!ret)
- pr_err("%s():HSUART TX Stalls.\n", __func__);
+ if (!is_blsp_uart(msm_uport)) {
+ spin_lock_irqsave(&uport->lock, flags);
+ /* disable UART TX interface to DM */
+ data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
+ data &= ~UARTDM_TX_DM_EN_BMSK;
+ msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
+ /* turn OFF UART Transmitter */
+ msm_hs_write(uport, UARTDM_CR_ADDR,
+ UARTDM_CR_TX_DISABLE_BMSK);
+ /* reset UART TX */
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX);
+ /* reset UART TX Error */
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX_ERROR);
+ msm_uport->tx.flush = FLUSH_STOP;
+ spin_unlock_irqrestore(&uport->lock, flags);
+ /* discard flush */
+ msm_dmov_flush(msm_uport->dma_tx_channel, 0);
+ ret = wait_event_timeout(msm_uport->tx.wait,
+ msm_uport->tx.flush == FLUSH_SHUTDOWN, 100);
+ if (!ret)
+ pr_err("%s():HSUART TX Stalls.\n", __func__);
+ } else {
+ /* BAM Disconnect for TX */
+ sps_disconnect(sps_pipe_handle);
+ }
}
tasklet_kill(&msm_uport->tx.tlet);
BUG_ON(msm_uport->rx.flush < FLUSH_STOP);
@@ -2211,12 +2969,18 @@
.runtime_idle = msm_hs_runtime_idle,
};
+static struct of_device_id msm_hs_match_table[] = {
+ { .compatible = "qcom,msm-hsuart-v14" },
+ {}
+};
+
static struct platform_driver msm_serial_hs_platform_driver = {
.probe = msm_hs_probe,
.remove = __devexit_p(msm_hs_remove),
.driver = {
.name = "msm_serial_hs",
.pm = &msm_hs_dev_pm_ops,
+ .of_match_table = msm_hs_match_table,
},
};
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index 02cdd71..ed0a385 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -955,6 +955,7 @@
int mdp4_pcc_cfg(struct mdp_pcc_cfg_data *cfg_ptr);
int mdp4_argc_cfg(struct mdp_pgc_lut_data *pgc_ptr);
int mdp4_qseed_cfg(struct mdp_qseed_cfg_data *cfg);
+int mdp4_calib_config(struct mdp_calib_config_data *cfg);
int mdp4_qseed_access_cfg(struct mdp_qseed_cfg *cfg, uint32_t base);
u32 mdp4_allocate_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num);
void mdp4_init_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num);
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
index 01ec10e..4858073 100644
--- a/drivers/video/msm/mdp4_util.c
+++ b/drivers/video/msm/mdp4_util.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -3080,6 +3080,108 @@
error:
return ret;
}
+
+static int is_valid_calib_addr(void *addr)
+{
+ int ret = 0;
+ unsigned int ptr;
+
+ if (addr == NULL)
+ goto end;
+
+ ptr = (unsigned int) addr;
+
+ if (mdp_rev >= MDP_REV_30 && mdp_rev < MDP_REV_40) {
+ /* if request is outside the MDP reg-map or is not aligned 4 */
+ if (ptr > 0xF0600 || ptr % 0x4)
+ goto end;
+
+ if (ptr >= 0x90000 && ptr < 0x94000) {
+ if (ptr == 0x90000 || ptr == 0x90070)
+ ret = 1;
+ else if (ptr >= 0x93400 && ptr <= 0x93420)
+ ret = 1;
+ else if (ptr >= 0x93500 && ptr <= 0x93508)
+ ret = 1;
+ else if (ptr >= 0x93580 && ptr <= 0x93588)
+ ret = 1;
+ else if (ptr >= 0x93600 && ptr <= 0x93614)
+ ret = 1;
+ else if (ptr >= 0x93680 && ptr <= 0x93694)
+ ret = 1;
+ else if (ptr >= 0x93800 && ptr <= 0x93BFC)
+ ret = 1;
+ }
+ } else if (mdp_rev >= MDP_REV_40 && mdp_rev <= MDP_REV_44) {
+ /* if request is outside the MDP reg-map or is not aligned 4 */
+ if (ptr > 0xF0600 || ptr % 0x4)
+ goto end;
+
+ if (ptr < 0x90000) {
+ if (ptr == 0x4 || ptr == 0x28200 || ptr == 0x28204)
+ ret = 1;
+ } else if (ptr < 0x95000) {
+ if (ptr == 0x90000 || ptr == 0x90070)
+ ret = 1;
+ else if (ptr >= 0x93400 && ptr <= 0x93420)
+ ret = 1;
+ else if (ptr >= 0x93500 && ptr <= 0x93508)
+ ret = 1;
+ else if (ptr >= 0x93580 && ptr <= 0x93588)
+ ret = 1;
+ else if (ptr >= 0x93600 && ptr <= 0x93614)
+ ret = 1;
+ else if (ptr >= 0x93680 && ptr <= 0x93694)
+ ret = 1;
+ else if (ptr >= 0x94800 && ptr <= 0x94BFC)
+ ret = 1;
+ } else if (ptr < 0x9A000) {
+ if (ptr >= 0x98800 && ptr <= 0x9883C)
+ ret = 1;
+ else if (ptr >= 0x98880 && ptr <= 0x988AC)
+ ret = 1;
+ else if (ptr >= 0x98900 && ptr <= 0x9893C)
+ ret = 1;
+ else if (ptr >= 0x98980 && ptr <= 0x989BC)
+ ret = 1;
+ else if (ptr >= 0x98A00 && ptr <= 0x98A3C)
+ ret = 1;
+ else if (ptr >= 0x98A80 && ptr <= 0x98ABC)
+ ret = 1;
+ else if (ptr >= 0x99000 && ptr <= 0x993FC)
+ ret = 1;
+ else if (ptr >= 0x99800 && ptr <= 0x99BFC)
+ ret = 1;
+ } else if (ptr >= 0x9A000 && ptr <= 0x9a08c) {
+ ret = 1;
+ }
+ }
+end:
+ return ret;
+}
+
+int mdp4_calib_config(struct mdp_calib_config_data *cfg)
+{
+ int ret = -1;
+ void *ptr = (void *) cfg->addr;
+
+ if (is_valid_calib_addr(ptr))
+ ret = 0;
+ else
+ return ret;
+
+ ptr = (void *)(((unsigned int) ptr) + MDP_BASE);
+ mdp_clk_ctrl(1);
+ if (cfg->ops & MDP_PP_OPS_READ) {
+ cfg->data = inpdw(ptr);
+ ret = 1;
+ } else if (cfg->ops & MDP_PP_OPS_WRITE) {
+ outpdw(ptr, cfg->data);
+ }
+ mdp_clk_ctrl(0);
+ return ret;
+}
+
u32 mdp4_get_mixer_num(u32 panel_type)
{
u32 mixer_num;
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index 50723e7..4404b9d 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -2,7 +2,7 @@
* Core MDSS framebuffer driver.
*
* Copyright (C) 2007 Google Incorporated
- * Copyright (c) 2008-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -889,20 +889,6 @@
var->hsync_len = panel_info->lcdc.h_pulse_width;
var->pixclock = panel_info->clk_rate / 1000;
- if (panel_info->type == MIPI_VIDEO_PANEL) {
- var->reserved[4] = panel_info->mipi.frame_rate;
- } else {
- var->reserved[4] = panel_info->clk_rate /
- ((panel_info->lcdc.h_back_porch +
- panel_info->lcdc.h_front_porch +
- panel_info->lcdc.h_pulse_width +
- panel_info->xres) *
- (panel_info->lcdc.v_back_porch +
- panel_info->lcdc.v_front_porch +
- panel_info->lcdc.v_pulse_width +
- panel_info->yres));
- }
-
/* id field for fb app */
id = (int *)&mfd->panel;
@@ -1589,6 +1575,23 @@
return ret;
}
+static int mdss_fb_get_metadata(struct msm_fb_data_type *mfd,
+ struct msmfb_metadata *metadata)
+{
+ int ret = 0;
+ switch (metadata->op) {
+ case metadata_op_frame_rate:
+ metadata->data.panel_frame_rate =
+ mdss_get_panel_framerate(mfd);
+ break;
+ default:
+ pr_warn("Unsupported request to MDP META IOCTL.\n");
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
{
@@ -1600,6 +1603,7 @@
struct mdp_page_protection fb_page_protection;
int ret = -ENOSYS;
struct mdp_buf_sync buf_sync;
+ struct msmfb_metadata metadata;
mdss_fb_power_setting_idle(mfd);
@@ -1683,6 +1687,15 @@
ret = mdss_fb_display_commit(info, argp);
break;
+ case MSMFB_METADATA_GET:
+ ret = copy_from_user(&metadata, argp, sizeof(metadata));
+ if (ret)
+ return ret;
+ ret = mdss_fb_get_metadata(mfd, &metadata);
+ if (!ret)
+ ret = copy_to_user(argp, &metadata, sizeof(metadata));
+ break;
+
default:
if (mfd->ioctl_handler)
ret = mfd->ioctl_handler(mfd, cmd, argp);
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index e9427da..ef6e5b4 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -341,6 +341,7 @@
struct mdss_mdp_format_params *mdss_mdp_get_format_params(u32 format);
int mdss_mdp_put_img(struct mdss_mdp_img_data *data);
int mdss_mdp_get_img(struct msmfb_data *img, struct mdss_mdp_img_data *data);
+u32 mdss_get_panel_framerate(struct msm_fb_data_type *mfd);
int mdss_mdp_wb_kickoff(struct mdss_mdp_ctl *ctl);
int mdss_mdp_wb_ioctl_handler(struct msm_fb_data_type *mfd, u32 cmd, void *arg);
diff --git a/drivers/video/msm/mdss/mdss_mdp_util.c b/drivers/video/msm/mdss/mdss_mdp_util.c
index 9f2df85..75a926a 100644
--- a/drivers/video/msm/mdss/mdss_mdp_util.c
+++ b/drivers/video/msm/mdss/mdss_mdp_util.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -29,6 +29,8 @@
#include "mdss_mdp.h"
#include "mdss_mdp_formats.h"
+#define DEFAULT_FRAME_RATE 60
+
enum {
MDP_INTR_VSYNC_INTF_0,
MDP_INTR_VSYNC_INTF_1,
@@ -433,3 +435,26 @@
return ret;
}
+
+u32 mdss_get_panel_framerate(struct msm_fb_data_type *mfd)
+{
+ u32 frame_rate = DEFAULT_FRAME_RATE;
+ u32 pixel_total;
+ struct mdss_panel_info *panel_info = mfd->panel_info;
+
+ if (panel_info->type == MIPI_VIDEO_PANEL) {
+ frame_rate = panel_info->mipi.frame_rate;
+ } else {
+ pixel_total = (panel_info->lcdc.h_back_porch +
+ panel_info->lcdc.h_front_porch +
+ panel_info->lcdc.h_pulse_width +
+ panel_info->xres) *
+ (panel_info->lcdc.v_back_porch +
+ panel_info->lcdc.v_front_porch +
+ panel_info->lcdc.v_pulse_width +
+ panel_info->yres);
+ if (pixel_total)
+ frame_rate = panel_info->clk_rate / pixel_total;
+ }
+ return frame_rate;
+}
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index 89e8e1e..3841498 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -1262,8 +1262,6 @@
((PAGE_SIZE - remainder)/fix->line_length) * mfd->fb_page;
var->bits_per_pixel = bpp * 8; /* FrameBuffer color depth */
- var->reserved[4] = mdp_get_panel_framerate(mfd);
-
/*
* id field for fb app
*/
@@ -3249,6 +3247,10 @@
ret = mdp4_qseed_cfg((struct mdp_qseed_cfg_data *)
&pp_ptr->data.qseed_cfg_data);
break;
+ case mdp_op_calib_cfg:
+ ret = mdp4_calib_config((struct mdp_calib_config_data *)
+ &pp_ptr->data.calib_cfg);
+ break;
#endif
case mdp_bl_scale_cfg:
ret = mdp_bl_scale_config(mfd, (struct mdp_bl_scale_data *)
@@ -3281,6 +3283,24 @@
}
return ret;
}
+
+static int msmfb_get_metadata(struct msm_fb_data_type *mfd,
+ struct msmfb_metadata *metadata_ptr)
+{
+ int ret = 0;
+ switch (metadata_ptr->op) {
+ case metadata_op_frame_rate:
+ metadata_ptr->data.panel_frame_rate =
+ mdp_get_panel_framerate(mfd);
+ break;
+ default:
+ pr_warn("Unsupported request to MDP META IOCTL.\n");
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
{
@@ -3583,6 +3603,8 @@
return ret;
ret = msmfb_handle_pp_ioctl(mfd, &mdp_pp);
+ if (ret == 1)
+ ret = copy_to_user(argp, &mdp_pp, sizeof(mdp_pp));
break;
case MSMFB_METADATA_SET:
@@ -3592,6 +3614,16 @@
ret = msmfb_handle_metadata_ioctl(mfd, &mdp_metadata);
break;
+ case MSMFB_METADATA_GET:
+ ret = copy_from_user(&mdp_metadata, argp, sizeof(mdp_metadata));
+ if (ret)
+ return ret;
+ ret = msmfb_get_metadata(mfd, &mdp_metadata);
+ if (!ret)
+ ret = copy_to_user(argp, &mdp_metadata,
+ sizeof(mdp_metadata));
+ break;
+
default:
MSM_FB_INFO("MDP: unknown ioctl (cmd=%x) received!\n", cmd);
ret = -EINVAL;
diff --git a/include/linux/fb.h b/include/linux/fb.h
index f6a2923..d31cb68 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -279,7 +279,7 @@
__u32 vmode; /* see FB_VMODE_* */
__u32 rotate; /* angle we rotate counter clockwise */
__u32 colorspace; /* colorspace for FOURCC-based modes */
- __u32 reserved[5]; /* Reserved for future compatibility */
+ __u32 reserved[4]; /* Reserved for future compatibility */
};
struct fb_cmap {
diff --git a/include/linux/mfd/wcd9xxx/wcd9310_registers.h b/include/linux/mfd/wcd9xxx/wcd9310_registers.h
index 67c2a6b..46336e2 100644
--- a/include/linux/mfd/wcd9xxx/wcd9310_registers.h
+++ b/include/linux/mfd/wcd9xxx/wcd9310_registers.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -898,37 +898,37 @@
#define TABLA_A_CDC_DEBUG_B6_CTL (0x0000036D)
#define TABLA_A_CDC_DEBUG_B6_CTL__POR (0x00000000)
#define TABLA_A_CDC_COMP1_B1_CTL (0x00000370)
-#define TABLA_A_CDC_COMP1_B1_CTL__POR (0x00000000)
+#define TABLA_A_CDC_COMP1_B1_CTL__POR (0x00000030)
#define TABLA_A_CDC_COMP1_B2_CTL (0x00000371)
-#define TABLA_A_CDC_COMP1_B2_CTL__POR (0x00000000)
+#define TABLA_A_CDC_COMP1_B2_CTL__POR (0x000000B5)
#define TABLA_A_CDC_COMP1_B3_CTL (0x00000372)
-#define TABLA_A_CDC_COMP1_B3_CTL__POR (0x00000000)
+#define TABLA_A_CDC_COMP1_B3_CTL__POR (0x00000028)
#define TABLA_A_CDC_COMP1_B4_CTL (0x00000373)
-#define TABLA_A_CDC_COMP1_B4_CTL__POR (0x00000000)
+#define TABLA_A_CDC_COMP1_B4_CTL__POR (0x0000003C)
#define TABLA_A_CDC_COMP1_B5_CTL (0x00000374)
-#define TABLA_A_CDC_COMP1_B5_CTL__POR (0x00000000)
+#define TABLA_A_CDC_COMP1_B5_CTL__POR (0x0000001F)
#define TABLA_A_CDC_COMP1_B6_CTL (0x00000375)
#define TABLA_A_CDC_COMP1_B6_CTL__POR (0x00000000)
#define TABLA_A_CDC_COMP1_SHUT_DOWN_STATUS (0x00000376)
#define TABLA_A_CDC_COMP1_SHUT_DOWN_STATUS__POR (0x00000000)
#define TABLA_A_CDC_COMP1_FS_CFG (0x00000377)
-#define TABLA_A_CDC_COMP1_FS_CFG__POR (0x00000000)
+#define TABLA_A_CDC_COMP1_FS_CFG__POR (0x0000001B)
#define TABLA_A_CDC_COMP2_B1_CTL (0x00000378)
-#define TABLA_A_CDC_COMP2_B1_CTL__POR (0x00000000)
+#define TABLA_A_CDC_COMP2_B1_CTL__POR (0x00000030)
#define TABLA_A_CDC_COMP2_B2_CTL (0x00000379)
-#define TABLA_A_CDC_COMP2_B2_CTL__POR (0x00000000)
+#define TABLA_A_CDC_COMP2_B2_CTL__POR (0x000000B5)
#define TABLA_A_CDC_COMP2_B3_CTL (0x0000037A)
-#define TABLA_A_CDC_COMP2_B3_CTL__POR (0x00000000)
+#define TABLA_A_CDC_COMP2_B3_CTL__POR (0x00000028)
#define TABLA_A_CDC_COMP2_B4_CTL (0x0000037B)
-#define TABLA_A_CDC_COMP2_B4_CTL__POR (0x00000000)
+#define TABLA_A_CDC_COMP2_B4_CTL__POR (0x0000003C)
#define TABLA_A_CDC_COMP2_B5_CTL (0x0000037C)
-#define TABLA_A_CDC_COMP2_B5_CTL__POR (0x00000000)
+#define TABLA_A_CDC_COMP2_B5_CTL__POR (0x0000001F)
#define TABLA_A_CDC_COMP2_B6_CTL (0x0000037D)
#define TABLA_A_CDC_COMP2_B6_CTL__POR (0x00000000)
#define TABLA_A_CDC_COMP2_SHUT_DOWN_STATUS (0x0000037E)
#define TABLA_A_CDC_COMP2_SHUT_DOWN_STATUS__POR (0x00000000)
#define TABLA_A_CDC_COMP2_FS_CFG (0x0000037F)
-#define TABLA_A_CDC_COMP2_FS_CFG__POR (0x00000000)
+#define TABLA_A_CDC_COMP2_FS_CFG__POR (0x0000001B)
#define TABLA_A_CDC_CONN_RX1_B1_CTL (0x00000380)
#define TABLA_A_CDC_CONN_RX1_B1_CTL__POR (0x00000000)
#define TABLA_A_CDC_CONN_RX1_B2_CTL (0x00000381)
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index 7e67db0..fdb8fb6 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -1,7 +1,7 @@
/* include/linux/msm_mdp.h
*
* Copyright (C) 2007 Google Incorporated
- * Copyright (c) 2012 Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -75,6 +75,7 @@
#define MSMFB_BUFFER_SYNC _IOW(MSMFB_IOCTL_MAGIC, 164, struct mdp_buf_sync)
#define MSMFB_DISPLAY_COMMIT _IOW(MSMFB_IOCTL_MAGIC, 165, \
struct mdp_display_commit)
+#define MSMFB_METADATA_GET _IOW(MSMFB_IOCTL_MAGIC, 166, struct msmfb_metadata)
#define FB_TYPE_3D_PANEL 0x10101010
#define MDP_IMGTYPE2_START 0x10000
@@ -555,6 +556,7 @@
enum {
metadata_op_none,
metadata_op_base_blend,
+ metadata_op_frame_rate,
metadata_op_max
};
@@ -567,6 +569,7 @@
uint32_t flags;
union {
struct mdp_blend_cfg blend_cfg;
+ uint32_t panel_frame_rate;
} data;
};
diff --git a/include/linux/qmi_encdec.h b/include/linux/qmi_encdec.h
index 4c5f6d3..b1fd217 100644
--- a/include/linux/qmi_encdec.h
+++ b/include/linux/qmi_encdec.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -150,6 +150,15 @@
int qmi_kernel_decode(struct msg_desc *desc, void *out_c_struct,
void *in_buf, uint32_t in_buf_len);
+/**
+ * qmi_verify_max_msg_len() - Verify the maximum length of a QMI message
+ * @desc: Pointer to structure descriptor.
+ *
+ * @return: true if the maximum message length embedded in structure
+ * descriptor matches the calculated value, else false.
+ */
+bool qmi_verify_max_msg_len(struct msg_desc *desc);
+
#else
static inline int qmi_kernel_encode(struct msg_desc *desc,
void *out_buf, uint32_t out_buf_len,
@@ -164,6 +173,11 @@
{
return -EOPNOTSUPP;
}
+
+static inline bool qmi_verify_max_msg_len(struct msg_desc *desc)
+{
+ return false;
+}
#endif
#endif
diff --git a/include/linux/sync.h b/include/linux/sync.h
index 4c00f04..31ba6ec 100644
--- a/include/linux/sync.h
+++ b/include/linux/sync.h
@@ -16,6 +16,7 @@
#include <linux/types.h>
#ifdef __KERNEL__
+#include <linux/kref.h>
#include <linux/ktime.h>
#include <linux/list.h>
#include <linux/spinlock.h>
@@ -40,14 +41,14 @@
* -1 if a will signabl before b
* @free_pt: called before sync_pt is freed
* @release_obj: called before sync_timeline is freed
- * @print_obj: print aditional debug information about sync_timeline.
- * should not print a newline
- * @print_pt: print aditional debug information about sync_pt.
- * should not print a newline
+ * @print_obj: deprecated
+ * @print_pt: deprecated
* @fill_driver_data: write implmentation specific driver data to data.
* should return an error if there is not enough room
* as specified by size. This information is returned
* to userspace by SYNC_IOC_FENCE_INFO.
+ * @timeline_value_str: fill str with the value of the sync_timeline's counter
+ * @pt_value_str: fill str with the value of the sync_pt
*/
struct sync_timeline_ops {
const char *driver_name;
@@ -67,19 +68,27 @@
/* optional */
void (*release_obj)(struct sync_timeline *sync_timeline);
- /* optional */
+ /* deprecated */
void (*print_obj)(struct seq_file *s,
struct sync_timeline *sync_timeline);
- /* optional */
+ /* deprecated */
void (*print_pt)(struct seq_file *s, struct sync_pt *sync_pt);
/* optional */
int (*fill_driver_data)(struct sync_pt *syncpt, void *data, int size);
+
+ /* optional */
+ void (*timeline_value_str)(struct sync_timeline *timeline, char *str,
+ int size);
+
+ /* optional */
+ void (*pt_value_str)(struct sync_pt *pt, char *str, int size);
};
/**
* struct sync_timeline - sync object
+ * @kref: reference count on fence.
* @ops: ops that define the implementaiton of the sync_timeline
* @name: name of the sync_timeline. Useful for debugging
* @destoryed: set when sync_timeline is destroyed
@@ -90,6 +99,7 @@
* @sync_timeline_list: membership in global sync_timeline_list
*/
struct sync_timeline {
+ struct kref kref;
const struct sync_timeline_ops *ops;
char name[32];
@@ -110,6 +120,7 @@
* @parent: sync_timeline to which this sync_pt belongs
* @child_list: membership in sync_timeline.child_list_head
* @active_list: membership in sync_timeline.active_list_head
+ * @signaled_list: membership in temorary signaled_list on stack
* @fence: sync_fence to which the sync_pt belongs
* @pt_list: membership in sync_fence.pt_list_head
* @status: 1: signaled, 0:active, <0: error
@@ -121,6 +132,7 @@
struct list_head child_list;
struct list_head active_list;
+ struct list_head signaled_list;
struct sync_fence *fence;
struct list_head pt_list;
@@ -134,6 +146,7 @@
/**
* struct sync_fence - sync fence
* @file: file representing this fence
+ * @kref: referenace count on fence.
* @name: name of sync_fence. Useful for debugging
* @pt_list_head: list of sync_pts in ths fence. immutable once fence
* is created
@@ -146,6 +159,7 @@
*/
struct sync_fence {
struct file *file;
+ struct kref kref;
char name[32];
/* this list is immutable once the fence is created */
@@ -323,8 +337,8 @@
* @fence: fence to wait on
* @tiemout: timeout in ms
*
- * Wait for @fence to be signaled or have an error. Waits indefintly
- * if @timeout = 0
+ * Wait for @fence to be signaled or have an error. Waits indefinitely
+ * if @timeout < 0
*/
int sync_fence_wait(struct sync_fence *fence, long timeout);
@@ -383,9 +397,9 @@
/**
* DOC: SYNC_IOC_WAIT - wait for a fence to signal
*
- * pass timeout in milliseconds.
+ * pass timeout in milliseconds. Waits indefinitely timeout < 0.
*/
-#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __u32)
+#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32)
/**
* DOC: SYNC_IOC_MERGE - merge two fences
diff --git a/include/linux/test-iosched.h b/include/linux/test-iosched.h
index 295c02d..ec28463 100644
--- a/include/linux/test-iosched.h
+++ b/include/linux/test-iosched.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012-2013, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -133,6 +133,8 @@
* @test_duration: A jiffies value saved for timing
* calculations
* @data: Test specific private data
+ * @test_byte_count: Total number of bytes dispatched in
+ * the test
*/
struct test_info {
int testcase;
@@ -145,6 +147,7 @@
unsigned long test_duration;
get_rq_disk_fn *get_rq_disk_fn;
void *data;
+ unsigned long test_byte_count;
};
/**
@@ -231,6 +234,7 @@
extern int test_iosched_start_test(struct test_info *t_info);
extern void test_iosched_mark_test_completion(void);
+extern void check_test_completion(void);
extern int test_iosched_add_unique_test_req(int is_err_expcted,
enum req_unique_type req_unique,
int start_sec, int nr_sects, rq_end_io_fn *end_req_io);
diff --git a/include/media/Kbuild b/include/media/Kbuild
index fc764eb..43cc3b9 100644
--- a/include/media/Kbuild
+++ b/include/media/Kbuild
@@ -9,3 +9,7 @@
header-y += msm_jpeg.h
header-y += msm_media_info.h
header-y += msm_vidc.h
+header-y += msmb_camera.h
+header-y += msm_cam_sensor.h
+header-y += msmb_isp.h
+header-y += msmb_ispif.h
diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h
new file mode 100644
index 0000000..a96a067
--- /dev/null
+++ b/include/media/msm_cam_sensor.h
@@ -0,0 +1,303 @@
+#ifndef __LINUX_MSM_CAM_SENSOR_H
+#define __LINUX_MSM_CAM_SENSOR_H
+
+#ifdef MSM_CAMERA_BIONIC
+#include <sys/types.h>
+#endif
+#include <linux/types.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/i2c.h>
+
+#define I2C_SEQ_REG_SETTING_MAX 5
+#define I2C_SEQ_REG_DATA_MAX 20
+#define MAX_CID 16
+
+#define MSM_SENSOR_MCLK_8HZ 8000000
+#define MSM_SENSOR_MCLK_16HZ 16000000
+#define MSM_SENSOR_MCLK_24HZ 24000000
+
+#define GPIO_OUT_LOW (0 << 1)
+#define GPIO_OUT_HIGH (1 << 1)
+
+#define CSI_EMBED_DATA 0x12
+#define CSI_RESERVED_DATA_0 0x13
+#define CSI_YUV422_8 0x1E
+#define CSI_RAW8 0x2A
+#define CSI_RAW10 0x2B
+#define CSI_RAW12 0x2C
+
+#define CSI_DECODE_6BIT 0
+#define CSI_DECODE_8BIT 1
+#define CSI_DECODE_10BIT 2
+#define CSI_DECODE_DPCM_10_8_10 5
+
+#define MAX_SENSOR_NAME 32
+
+enum msm_camera_i2c_reg_addr_type {
+ MSM_CAMERA_I2C_BYTE_ADDR = 1,
+ MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+enum msm_camera_i2c_data_type {
+ MSM_CAMERA_I2C_BYTE_DATA = 1,
+ MSM_CAMERA_I2C_WORD_DATA,
+ MSM_CAMERA_I2C_SET_BYTE_MASK,
+ MSM_CAMERA_I2C_UNSET_BYTE_MASK,
+ MSM_CAMERA_I2C_SET_WORD_MASK,
+ MSM_CAMERA_I2C_UNSET_WORD_MASK,
+ MSM_CAMERA_I2C_SET_BYTE_WRITE_MASK_DATA,
+};
+
+enum msm_sensor_power_seq_type_t {
+ SENSOR_CLK,
+ SENSOR_GPIO,
+ SENSOR_VREG,
+ SENSOR_I2C_MUX,
+};
+
+enum msm_sensor_clk_type_t {
+ SENSOR_CAM_MCLK,
+ SENSOR_CAM_CLK,
+ SENSOR_CAM_CLK_MAX,
+};
+
+enum msm_sensor_power_seq_gpio_t {
+ SENSOR_GPIO_RESET,
+ SENSOR_GPIO_STANDBY,
+ SENSOR_GPIO_MAX,
+};
+
+enum msm_camera_vreg_name_t {
+ CAM_VDIG,
+ CAM_VIO,
+ CAM_VANA,
+ CAM_VAF,
+ CAM_VREG_MAX,
+};
+
+enum msm_sensor_resolution_t {
+ MSM_SENSOR_RES_FULL,
+ MSM_SENSOR_RES_QTR,
+ MSM_SENSOR_RES_2,
+ MSM_SENSOR_RES_3,
+ MSM_SENSOR_RES_4,
+ MSM_SENSOR_RES_5,
+ MSM_SENSOR_RES_6,
+ MSM_SENSOR_RES_7,
+ MSM_SENSOR_INVALID_RES,
+};
+
+enum sensor_sub_module_t {
+ SUB_MODULE_SENSOR,
+ SUB_MODULE_CHROMATIX,
+ SUB_MODULE_ACTUATOR,
+ SUB_MODULE_EEPROM,
+ SUB_MODULE_LED_FLASH,
+ SUB_MODULE_STROBE_FLASH,
+ SUB_MODULE_CSIPHY,
+ SUB_MODULE_CSIPHY_3D,
+ SUB_MODULE_CSID,
+ SUB_MODULE_CSID_3D,
+ SUB_MODULE_MAX,
+};
+
+enum csid_cfg_type_t {
+ CSID_INIT,
+ CSID_CFG,
+ CSID_RELEASE,
+};
+
+enum csiphy_cfg_type_t {
+ CSIPHY_INIT,
+ CSIPHY_CFG,
+ CSIPHY_RELEASE,
+};
+
+enum camera_vreg_type {
+ REG_LDO,
+ REG_VS,
+ REG_GPIO,
+};
+
+struct msm_sensor_power_setting {
+ enum msm_sensor_power_seq_type_t seq_type;
+ uint16_t seq_val;
+ long config_val;
+ uint16_t delay;
+ void *data[10];
+};
+
+struct msm_sensor_power_setting_array {
+ struct msm_sensor_power_setting *power_setting;
+ uint16_t size;
+};
+
+struct msm_sensor_id_info_t {
+ uint16_t sensor_id_reg_addr;
+ uint16_t sensor_id;
+};
+
+struct msm_camera_sensor_slave_info {
+ uint16_t slave_addr;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+ struct msm_sensor_id_info_t sensor_id_info;
+ struct msm_sensor_power_setting_array power_setting_array;
+};
+
+struct msm_camera_i2c_reg_array {
+ uint16_t reg_addr;
+ uint16_t reg_data;
+};
+
+struct msm_camera_i2c_reg_setting {
+ struct msm_camera_i2c_reg_array *reg_setting;
+ uint16_t size;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+ enum msm_camera_i2c_data_type data_type;
+ uint16_t delay;
+};
+
+struct msm_camera_i2c_seq_reg_array {
+ uint16_t reg_addr;
+ uint8_t reg_data[I2C_SEQ_REG_DATA_MAX];
+ uint16_t reg_data_size;
+};
+
+struct msm_camera_i2c_seq_reg_setting {
+ struct msm_camera_i2c_seq_reg_array *reg_setting;
+ uint16_t size;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+ uint16_t delay;
+};
+
+struct msm_camera_csid_vc_cfg {
+ uint8_t cid;
+ uint8_t dt;
+ uint8_t decode_format;
+};
+
+struct msm_camera_csid_lut_params {
+ uint8_t num_cid;
+ struct msm_camera_csid_vc_cfg *vc_cfg[MAX_CID];
+};
+
+struct msm_camera_csid_params {
+ uint8_t lane_cnt;
+ uint16_t lane_assign;
+ uint8_t phy_sel;
+ struct msm_camera_csid_lut_params lut_params;
+};
+
+struct msm_camera_csiphy_params {
+ uint8_t lane_cnt;
+ uint8_t settle_cnt;
+ uint16_t lane_mask;
+ uint8_t combo_mode;
+};
+
+struct msm_camera_csi2_params {
+ struct msm_camera_csid_params csid_params;
+ struct msm_camera_csiphy_params csiphy_params;
+};
+
+struct msm_camera_csi_lane_params {
+ uint16_t csi_lane_assign;
+ uint16_t csi_lane_mask;
+};
+
+struct csi_lane_params_t {
+ uint16_t csi_lane_assign;
+ uint8_t csi_lane_mask;
+ uint8_t csi_if;
+ uint8_t csid_core[2];
+ uint8_t csi_phy_sel;
+};
+
+struct msm_sensor_info_t {
+ char sensor_name[MAX_SENSOR_NAME];
+ int32_t session_id;
+ int32_t subdev_id[SUB_MODULE_MAX];
+};
+
+struct camera_vreg_t {
+ const char *reg_name;
+ enum camera_vreg_type type;
+ int min_voltage;
+ int max_voltage;
+ int op_mode;
+ uint32_t delay;
+};
+
+enum camb_position_t {
+ BACK_CAMERA_B,
+ FRONT_CAMERA_B,
+};
+
+enum camerab_mode_t {
+ CAMERA_MODE_2D_B = (1<<0),
+ CAMERA_MODE_3D_B = (1<<1)
+};
+
+struct msm_sensor_init_params {
+ /* mask of modes supported: 2D, 3D */
+ int modes_supported;
+ /* sensor position: front, back */
+ enum camb_position_t position;
+ /* sensor mount angle */
+ uint32_t sensor_mount_angle;
+};
+
+struct sensorb_cfg_data {
+ int cfgtype;
+ union {
+ struct msm_sensor_info_t sensor_info;
+ struct msm_sensor_init_params sensor_init_params;
+ void *setting;
+ } cfg;
+};
+
+struct csid_cfg_data {
+ enum csid_cfg_type_t cfgtype;
+ union {
+ uint32_t csid_version;
+ struct msm_camera_csid_params *csid_params;
+ } cfg;
+};
+
+struct csiphy_cfg_data {
+ enum csiphy_cfg_type_t cfgtype;
+ union {
+ struct msm_camera_csiphy_params *csiphy_params;
+ struct msm_camera_csi_lane_params *csi_lane_params;
+ } cfg;
+};
+
+enum msm_sensor_cfg_type_t {
+ CFG_SET_SLAVE_INFO,
+ CFG_WRITE_I2C_ARRAY,
+ CFG_WRITE_I2C_SEQ_ARRAY,
+ CFG_POWER_UP,
+ CFG_POWER_DOWN,
+ CFG_SET_STOP_STREAM_SETTING,
+ CFG_GET_SENSOR_INFO,
+ CFG_GET_SENSOR_INIT_PARAMS,
+};
+
+#define VIDIOC_MSM_SENSOR_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct sensorb_cfg_data)
+
+#define VIDIOC_MSM_SENSOR_RELEASE \
+ _IO('V', BASE_VIDIOC_PRIVATE + 2)
+
+#define VIDIOC_MSM_SENSOR_GET_SUBDEV_ID \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 3, uint32_t)
+
+#define VIDIOC_MSM_CSIPHY_IO_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 4, struct csid_cfg_data)
+
+#define VIDIOC_MSM_CSID_IO_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct csiphy_cfg_data)
+
+#define MSM_V4L2_PIX_FMT_META v4l2_fourcc('M', 'E', 'T', 'A') /* META */
+
+#endif /* __LINUX_MSM_CAM_SENSOR_H */
diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h
index 971c9b3..9c310a9 100644
--- a/include/media/msm_camera.h
+++ b/include/media/msm_camera.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1595,6 +1595,19 @@
REG_GPIO,
};
+enum msm_camera_vreg_name_t {
+ CAM_VDIG,
+ CAM_VIO,
+ CAM_VANA,
+ CAM_VAF,
+ CAM_VREG_MAX,
+};
+
+struct msm_camera_csi_lane_params {
+ uint16_t csi_lane_assign;
+ uint16_t csi_lane_mask;
+};
+
struct camera_vreg_t {
const char *reg_name;
enum camera_vreg_type type;
diff --git a/include/media/msmb_camera.h b/include/media/msmb_camera.h
new file mode 100644
index 0000000..732f60b
--- /dev/null
+++ b/include/media/msmb_camera.h
@@ -0,0 +1,159 @@
+#ifndef __LINUX_MSMB_CAMERA_H
+#define __LINUX_MSMB_CAMERA_H
+
+#include <linux/videodev2.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define MSM_CAM_V4L2_IOCTL_NOTIFY \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 30, struct v4l2_event)
+
+#define MSM_CAM_V4L2_IOCTL_NOTIFY_META \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 31, struct v4l2_event)
+
+#define MSM_CAM_V4L2_IOCTL_CMD_ACK \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 32, struct v4l2_event)
+
+#define QCAMERA_DEVICE_GROUP_ID 1
+#define QCAMERA_VNODE_GROUP_ID 2
+#define MSM_CAMERA_NAME "msm_camera"
+#define MSM_CONFIGURATION_NAME "msm_config"
+
+#define MSM_CAMERA_SUBDEV_CSIPHY 0
+#define MSM_CAMERA_SUBDEV_CSID 1
+#define MSM_CAMERA_SUBDEV_ISPIF 2
+#define MSM_CAMERA_SUBDEV_VFE 3
+#define MSM_CAMERA_SUBDEV_AXI 4
+#define MSM_CAMERA_SUBDEV_VPE 5
+#define MSM_CAMERA_SUBDEV_SENSOR 6
+#define MSM_CAMERA_SUBDEV_ACTUATOR 7
+#define MSM_CAMERA_SUBDEV_EEPROM 8
+#define MSM_CAMERA_SUBDEV_CPP 9
+#define MSM_CAMERA_SUBDEV_CCI 10
+#define MSM_CAMERA_SUBDEV_LED_FLASH 11
+#define MSM_CAMERA_SUBDEV_STROBE_FLASH 12
+
+#define MSM_MAX_CAMERA_SENSORS 5
+
+/* featur base */
+#define MSM_CAMERA_FEATURE_BASE 0x00010000
+#define MSM_CAMERA_FEATURE_SHUTDOWN (MSM_CAMERA_FEATURE_BASE + 1)
+
+#define MSM_CAMERA_STATUS_BASE 0x00020000
+#define MSM_CAMERA_STATUS_FAIL (MSM_CAMERA_STATUS_BASE + 1)
+#define MSM_CAMERA_STATUS_SUCCESS (MSM_CAMERA_STATUS_BASE + 2)
+
+/* event type */
+#define MSM_CAMERA_V4L2_EVENT_TYPE (V4L2_EVENT_PRIVATE_START + 0x00002000)
+
+/* event id */
+#define MSM_CAMERA_EVENT_MIN 0
+#define MSM_CAMERA_NEW_SESSION (MSM_CAMERA_EVENT_MIN + 1)
+#define MSM_CAMERA_DEL_SESSION (MSM_CAMERA_EVENT_MIN + 2)
+#define MSM_CAMERA_SET_PARM (MSM_CAMERA_EVENT_MIN + 3)
+#define MSM_CAMERA_GET_PARM (MSM_CAMERA_EVENT_MIN + 4)
+#define MSM_CAMERA_MAPPING_CFG (MSM_CAMERA_EVENT_MIN + 5)
+#define MSM_CAMERA_MAPPING_SES (MSM_CAMERA_EVENT_MIN + 6)
+#define MSM_CAMERA_MSM_NOTIFY (MSM_CAMERA_EVENT_MIN + 7)
+#define MSM_CAMERA_EVENT_MAX (MSM_CAMERA_EVENT_MIN + 8)
+
+/* data.command */
+#define MSM_CAMERA_PRIV_S_CROP (V4L2_CID_PRIVATE_BASE + 1)
+#define MSM_CAMERA_PRIV_G_CROP (V4L2_CID_PRIVATE_BASE + 2)
+#define MSM_CAMERA_PRIV_G_FMT (V4L2_CID_PRIVATE_BASE + 3)
+#define MSM_CAMERA_PRIV_S_FMT (V4L2_CID_PRIVATE_BASE + 4)
+#define MSM_CAMERA_PRIV_TRY_FMT (V4L2_CID_PRIVATE_BASE + 5)
+#define MSM_CAMERA_PRIV_METADATA (V4L2_CID_PRIVATE_BASE + 6)
+#define MSM_CAMERA_PRIV_QUERY_CAP (V4L2_CID_PRIVATE_BASE + 7)
+#define MSM_CAMERA_PRIV_STREAM_ON (V4L2_CID_PRIVATE_BASE + 8)
+#define MSM_CAMERA_PRIV_STREAM_OFF (V4L2_CID_PRIVATE_BASE + 9)
+#define MSM_CAMERA_PRIV_NEW_STREAM (V4L2_CID_PRIVATE_BASE + 10)
+#define MSM_CAMERA_PRIV_DEL_STREAM (V4L2_CID_PRIVATE_BASE + 11)
+#define MSM_CAMERA_PRIV_SHUTDOWN (V4L2_CID_PRIVATE_BASE + 12)
+#define MSM_CAMERA_PRIV_STREAM_INFO_SYNC \
+ (V4L2_CID_PRIVATE_BASE + 13)
+
+/* data.status - success */
+#define MSM_CAMERA_CMD_SUCESS 0x00000001
+#define MSM_CAMERA_BUF_MAP_SUCESS 0x00000002
+
+/* data.status - error */
+#define MSM_CAMERA_ERR_EVT_BASE 0x00010000
+#define MSM_CAMERA_ERR_CMD_FAIL (MSM_CAMERA_ERR_EVT_BASE + 1)
+#define MSM_CAMERA_ERR_MAPPING (MSM_CAMERA_ERR_EVT_BASE + 2)
+
+/* The msm_v4l2_event_data structure should match the
+ * v4l2_event.u.data field.
+ * should not exceed 16 elements */
+struct msm_v4l2_event_data {
+ /*word 0*/
+ unsigned int command;
+ /*word 1*/
+ unsigned int status;
+ /*word 2*/
+ unsigned int session_id;
+ /*word 3*/
+ unsigned int stream_id;
+ /*word 4*/
+ unsigned int map_op;
+ /*word 5*/
+ unsigned int map_buf_idx;
+ /*word 6*/
+ unsigned int notify;
+ /*word 7*/
+ unsigned int nop1;
+ /*word 8*/
+ unsigned int nop2;
+ /*word 9*/
+ unsigned int nop3;
+ /*word 10*/
+ unsigned int nop4;
+ /*word 11*/
+ unsigned int nop5;
+ /*word 12*/
+ unsigned int nop6;
+ /*word 13*/
+ unsigned int nop7;
+ /*word 14*/
+ unsigned int nop8;
+ /*word 15*/
+ unsigned int nop9;
+};
+
+/* map to v4l2_format.fmt.raw_data */
+struct msm_v4l2_format_data {
+ enum v4l2_buf_type type;
+ unsigned int width;
+ unsigned int height;
+ unsigned int pixelformat; /* FOURCC */
+ unsigned char num_planes;
+ unsigned int plane_sizes[VIDEO_MAX_PLANES];
+};
+
+/* MSM Four-character-code (FOURCC) */
+#define msm_v4l2_fourcc(a, b, c, d)\
+ ((__u32)(a) | ((__u32)(b) << 8) | ((__u32)(c) << 16) |\
+ ((__u32)(d) << 24))
+
+/* Composite stats */
+#define MSM_V4L2_PIX_FMT_STATS_COMB v4l2_fourcc('S', 'T', 'C', 'M')
+/* AEC stats */
+#define MSM_V4L2_PIX_FMT_STATS_AE v4l2_fourcc('S', 'T', 'A', 'E')
+/* AF stats */
+#define MSM_V4L2_PIX_FMT_STATS_AF v4l2_fourcc('S', 'T', 'A', 'F')
+/* AWB stats */
+#define MSM_V4L2_PIX_FMT_STATS_AWB v4l2_fourcc('S', 'T', 'W', 'B')
+/* IHIST stats */
+#define MSM_V4L2_PIX_FMT_STATS_IHST v4l2_fourcc('I', 'H', 'S', 'T')
+/* Column count stats */
+#define MSM_V4L2_PIX_FMT_STATS_CS v4l2_fourcc('S', 'T', 'C', 'S')
+/* Row count stats */
+#define MSM_V4L2_PIX_FMT_STATS_RS v4l2_fourcc('S', 'T', 'R', 'S')
+/* Bayer Grid stats */
+#define MSM_V4L2_PIX_FMT_STATS_BG v4l2_fourcc('S', 'T', 'B', 'G')
+/* Bayer focus stats */
+#define MSM_V4L2_PIX_FMT_STATS_BF v4l2_fourcc('S', 'T', 'B', 'F')
+/* Bayer hist stats */
+#define MSM_V4L2_PIX_FMT_STATS_BHST v4l2_fourcc('B', 'H', 'S', 'T')
+
+#endif /* __LINUX_MSMB_CAMERA_H */
diff --git a/include/media/msmb_isp.h b/include/media/msmb_isp.h
new file mode 100644
index 0000000..01276bd
--- /dev/null
+++ b/include/media/msmb_isp.h
@@ -0,0 +1,320 @@
+#ifndef __MSMB_ISP__
+#define __MSMB_ISP__
+
+#include <linux/videodev2.h>
+
+#define MAX_PLANES_PER_STREAM 3
+#define MAX_NUM_STREAM 7
+
+#define ISP_VERSION_40 40
+#define ISP_VERSION_32 32
+
+
+enum ISP_START_PIXEL_PATTERN {
+ ISP_BAYER_RGRGRG,
+ ISP_BAYER_GRGRGR,
+ ISP_BAYER_BGBGBG,
+ ISP_BAYER_GBGBGB,
+ ISP_YUV_YCbYCr,
+ ISP_YUV_YCrYCb,
+ ISP_YUV_CbYCrY,
+ ISP_YUV_CrYCbY,
+ ISP_PIX_PATTERN_MAX
+};
+
+enum msm_vfe_plane_fmt {
+ Y_PLANE,
+ CB_PLANE,
+ CR_PLANE,
+ CRCB_PLANE,
+ CBCR_PLANE,
+ VFE_PLANE_FMT_MAX
+};
+
+enum msm_vfe_input_src {
+ VFE_PIX_0,
+ VFE_RAW_0,
+ VFE_RAW_1,
+ VFE_RAW_2,
+ VFE_SRC_MAX,
+};
+
+enum msm_vfe_axi_stream_src {
+ PIX_ENCODER,
+ PIX_VIEWFINDER,
+ CAMIF_RAW,
+ IDEAL_RAW,
+ RDI,
+ VFE_AXI_SRC_MAX
+};
+
+enum msm_vfe_frame_skip_pattern {
+ NO_SKIP,
+ EVERY_2FRAME,
+ EVERY_4FRAME,
+ EVERY_8FRAME,
+ EVERY_16FRAME,
+ EVERY_32FRAME,
+ MAX_SKIP,
+};
+
+enum msm_vfe_camif_input {
+ CAMIF_DISABLED,
+ CAMIF_PAD_REG_INPUT,
+ CAMIF_MIDDI_INPUT,
+ CAMIF_MIPI_INPUT,
+};
+
+struct msm_vfe_camif_cfg {
+ uint32_t lines_per_frame;
+ uint32_t pixels_per_line;
+ uint32_t first_pixel;
+ uint32_t last_pixel;
+ uint32_t first_line;
+ uint32_t last_line;
+ uint32_t epoch_line0;
+ uint32_t epoch_line1;
+ enum msm_vfe_camif_input camif_input;
+};
+
+enum msm_vfe_inputmux {
+ CAMIF,
+ TESTGEN,
+ EXTERNAL_READ,
+};
+
+struct msm_vfe_pix_cfg {
+ struct msm_vfe_camif_cfg camif_cfg;
+ enum msm_vfe_inputmux input_mux;
+ enum ISP_START_PIXEL_PATTERN pixel_pattern;
+};
+
+struct msm_vfe_input_cfg {
+ union {
+ struct msm_vfe_pix_cfg pix_cfg;
+ } d;
+ enum msm_vfe_input_src input_src;
+
+};
+
+struct msm_vfe_axi_plane_cfg {
+ uint32_t output_width; /*Include padding*/
+ uint32_t output_height;
+ uint32_t output_stride;
+ uint32_t output_scan_lines;
+ uint32_t output_plane_format; /*Y/Cb/Cr/CbCr*/
+
+ uint8_t csid_src; /*RDI 0-2*/
+ uint8_t rdi_cid;/*CID 1-16*/
+};
+
+struct msm_vfe_axi_stream_request_cmd {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t output_format;/*Planar/RAW/Misc*/
+ enum msm_vfe_axi_stream_src stream_src; /*CAMIF/IDEAL/RDIs*/
+ struct msm_vfe_axi_plane_cfg plane_cfg[MAX_PLANES_PER_STREAM];
+
+ uint32_t burst_count;
+ uint32_t hfr_mode;
+ uint8_t frame_base;
+
+ uint32_t init_frame_drop; /*MAX 31 Frames*/
+ enum msm_vfe_frame_skip_pattern frame_skip_pattern;
+ uint8_t buf_divert; /* if TRUE no vb2 buf done. */
+ /*Return values*/
+ uint32_t axi_stream_handle;
+};
+
+struct msm_vfe_axi_stream_release_cmd {
+ uint32_t stream_handle;
+};
+
+enum msm_vfe_axi_stream_cmd {
+ STOP_STREAM,
+ START_STREAM,
+};
+
+struct msm_vfe_axi_stream_cfg_cmd {
+ uint8_t num_streams;
+ uint32_t stream_handle[MAX_NUM_STREAM];
+ enum msm_vfe_axi_stream_cmd cmd;
+};
+
+enum msm_isp_stats_type {
+ MSM_ISP_STATS_AEC, /* legacy based AEC */
+ MSM_ISP_STATS_AF, /* legacy based AF */
+ MSM_ISP_STATS_AWB, /* legacy based AWB */
+ MSM_ISP_STATS_RS, /* legacy based RS */
+ MSM_ISP_STATS_CS, /* legacy based CS */
+ MSM_ISP_STATS_IHIST, /* legacy based HIST */
+ MSM_ISP_STATS_SKIN, /* legacy based SKIN */
+ MSM_ISP_STATS_BG, /* Bayer Grids */
+ MSM_ISP_STATS_BF, /* Bayer Focus */
+ MSM_ISP_STATS_BE, /* Bayer Exposure*/
+ MSM_ISP_STATS_BHIST, /* Bayer Hist */
+ MSM_ISP_STATS_MAX /* MAX */
+};
+
+struct msm_vfe_stats_stream_request_cmd {
+ uint32_t session_id;
+ uint32_t stream_id;
+ enum msm_isp_stats_type stats_type;
+ uint32_t stream_handle;
+};
+struct msm_vfe_stats_stream_release_cmd {
+ uint32_t stream_handle;
+};
+struct msm_vfe_stats_stream_cfg_cmd {
+ uint8_t num_streams;
+ uint32_t stream_handle[MSM_ISP_STATS_MAX];
+ uint8_t enable;
+};
+enum msm_vfe_reg_cfg_type {
+ VFE_WRITE,
+ VFE_WRITE_MB,
+ VFE_READ,
+ VFE_WRITE_MASK,
+ VFE_CLEAR_MASK,
+ VFE_WRITE_AUTO_INCREMENT,
+};
+
+struct msm_vfe_cfg_cmd2 {
+ uint16_t num_cfg;
+ uint16_t cmd_len;
+ void __user *cfg_data;
+ void __user *cfg_cmd;
+};
+
+struct msm_vfe_reg_cfg_cmd {
+ uint32_t reg_offset;
+ uint32_t cmd_data;
+ uint32_t len;
+ enum msm_vfe_reg_cfg_type cmd_type;
+};
+
+struct msm_isp_buf_request {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint8_t num_buf;
+ uint32_t handle;
+};
+
+struct msm_isp_qbuf_info {
+ uint32_t handle;
+ int buf_idx;
+ /*Only used for prepare buffer*/
+ struct v4l2_buffer buffer;
+};
+
+struct msm_vfe_axi_src_state {
+ enum msm_vfe_input_src input_src;
+ uint32_t src_active;
+};
+
+enum msm_isp_event_idx {
+ ISP_REG_UPDATE = 0,
+ ISP_START_ACK = 1,
+ ISP_STOP_ACK = 2,
+ ISP_IRQ_VIOLATION = 3,
+ ISP_WM_BUS_OVERFLOW = 4,
+ ISP_STATS_OVERFLOW = 5,
+ ISP_CAMIF_ERROR = 6,
+ ISP_STATS_NOTIFY = 7,
+ ISP_SOF = 8,
+ ISP_EOF = 9,
+ ISP_BUF_DIVERT = 10,
+ ISP_EVENT_MAX = 11
+};
+
+#define ISP_EVENT_BASE (V4L2_EVENT_PRIVATE_START + 1)
+#define ISP_EVENT_REG_UPDATE (ISP_EVENT_BASE + ISP_REG_UPDATE)
+#define ISP_EVENT_START_ACK (ISP_EVENT_BASE + ISP_START_ACK)
+#define ISP_EVENT_STOP_ACK (ISP_EVENT_BASE + ISP_STOP_ACK)
+#define ISP_EVENT_IRQ_VIOLATION (ISP_EVENT_BASE + ISP_IRQ_VIOLATION)
+#define ISP_EVENT_WM_BUS_OVERFLOW (ISP_EVENT_BASE + ISP_WM_BUS_OVERFLOW)
+#define ISP_EVENT_STATS_OVERFLOW (ISP_EVENT_BASE + ISP_STATS_OVERFLOW)
+#define ISP_EVENT_CAMIF_ERROR (ISP_EVENT_BASE + ISP_CAMIF_ERROR)
+#define ISP_EVENT_STATS_NOTIFY (ISP_EVENT_BASE + ISP_STATS_NOTIFY)
+#define ISP_EVENT_SOF (ISP_EVENT_BASE + ISP_SOF)
+#define ISP_EVENT_EOF (ISP_EVENT_BASE + ISP_EOF)
+#define ISP_EVENT_BUF_DIVERT (ISP_EVENT_BASE + ISP_BUF_DIVERT)
+
+
+/* The msm_v4l2_event_data structure should match the
+ * v4l2_event.u.data field.
+ * should not exceed 64 bytes */
+
+struct msm_isp_buf_event {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t handle;
+ int8_t buf_idx;
+};
+struct msm_isp_stats_event {
+ uint32_t stats_mask; /* 4 bytes */
+ uint8_t stats_buf_idxs[MSM_ISP_STATS_MAX]; /* 11 bytes */
+};
+
+struct msm_isp_stream_ack {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t handle;
+};
+
+struct msm_isp_event_data {
+ struct timeval timestamp;
+ /* if pix is a src frame_id is from camif */
+ uint32_t frame_id;
+ union {
+ /* START_ACK, STOP_ACK */
+ struct msm_isp_stream_ack stream_ack;
+ /* REG_UPDATE_TRIGGER, bus over flow */
+ enum msm_vfe_input_src input_src;
+ /* stats notify */
+ struct msm_isp_stats_event stats;
+ /* IRQ_VIOLATION, STATS_OVER_FLOW, WM_OVER_FLOW */
+ uint32_t irq_status_mask;
+ struct msm_isp_buf_event buf_done;
+ } u; /* union can have max 52 bytes */
+};
+
+#define VIDIOC_MSM_VFE_REG_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_vfe_cfg_cmd2)
+
+#define VIDIOC_MSM_ISP_REQUEST_BUF \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+1, struct msm_isp_buf_request)
+
+#define VIDIOC_MSM_ISP_ENQUEUE_BUF \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+2, struct msm_isp_qbuf_info)
+
+#define VIDIOC_MSM_ISP_RELEASE_BUF \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+3, struct msm_isp_buf_request)
+
+#define VIDIOC_MSM_ISP_REQUEST_STREAM \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+4, struct msm_vfe_axi_stream_request_cmd)
+
+#define VIDIOC_MSM_ISP_CFG_STREAM \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+5, struct msm_vfe_axi_stream_cfg_cmd)
+
+#define VIDIOC_MSM_ISP_RELEASE_STREAM \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+6, struct msm_vfe_axi_stream_release_cmd)
+
+#define VIDIOC_MSM_ISP_INPUT_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+7, struct msm_vfe_input_cfg)
+
+#define VIDIOC_MSM_ISP_SET_SRC_STATE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+8, struct msm_vfe_axi_src_state)
+
+#define VIDIOC_MSM_ISP_REQUEST_STATS_STREAM \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+9, \
+ struct msm_vfe_stats_stream_request_cmd)
+
+#define VIDIOC_MSM_ISP_CFG_STATS_STREAM \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+10, struct msm_vfe_stats_stream_cfg_cmd)
+
+#define VIDIOC_MSM_ISP_RELEASE_STATS_STREAM \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+11, \
+ struct msm_vfe_stats_stream_release_cmd)
+
+#endif /* __MSMB_ISP__ */
diff --git a/include/media/msmb_ispif.h b/include/media/msmb_ispif.h
new file mode 100644
index 0000000..fc27ef6
--- /dev/null
+++ b/include/media/msmb_ispif.h
@@ -0,0 +1,103 @@
+#ifndef MSM_CAM_ISPIF_H
+#define MSM_CAM_ISPIF_H
+
+#define CSID_VERSION_V2 0x02000011
+#define CSID_VERSION_V3 0x30000000
+
+enum msm_ispif_vfe_intf {
+ VFE0,
+ VFE1,
+ VFE_MAX
+};
+#define VFE0_MASK (1 << VFE0)
+#define VFE1_MASK (1 << VFE1)
+
+enum msm_ispif_intftype {
+ PIX0,
+ RDI0,
+ PIX1,
+ RDI1,
+ RDI2,
+ INTF_MAX
+};
+#define PIX0_MASK (1 << PIX0)
+#define PIX1_MASK (1 << PIX1)
+#define RDI0_MASK (1 << RDI0)
+#define RDI1_MASK (1 << RDI1)
+#define RDI2_MASK (1 << RDI2)
+
+
+enum msm_ispif_vc {
+ VC0,
+ VC1,
+ VC2,
+ VC3,
+ VC_MAX
+};
+
+enum msm_ispif_cid {
+ CID0,
+ CID1,
+ CID2,
+ CID3,
+ CID4,
+ CID5,
+ CID6,
+ CID7,
+ CID8,
+ CID9,
+ CID10,
+ CID11,
+ CID12,
+ CID13,
+ CID14,
+ CID15,
+ CID_MAX
+};
+
+enum msm_ispif_csid {
+ CSID0,
+ CSID1,
+ CSID2,
+ CSID3,
+ CSID_MAX
+};
+
+struct msm_ispif_params_entry {
+ enum msm_ispif_intftype intftype;
+ int num_cids;
+ enum msm_ispif_cid cids[3];
+ enum msm_ispif_csid csid;
+};
+
+struct msm_ispif_param_data {
+ enum msm_ispif_vfe_intf vfe_intf;
+ uint32_t num;
+ struct msm_ispif_params_entry entries[INTF_MAX];
+};
+
+enum ispif_cfg_type_t {
+ ISPIF_CLK_ENABLE,
+ ISPIF_CLK_DISABLE,
+ ISPIF_INIT,
+ ISPIF_CFG,
+ ISPIF_START_FRAME_BOUNDARY,
+ ISPIF_STOP_FRAME_BOUNDARY,
+ ISPIF_STOP_IMMEDIATELY,
+ ISPIF_RELEASE,
+ ISPIF_ENABLE_REG_DUMP,
+};
+
+struct ispif_cfg_data {
+ enum ispif_cfg_type_t cfg_type;
+ union {
+ int reg_dump; /* ISPIF_ENABLE_REG_DUMP */
+ uint32_t csid_version; /* ISPIF_INIT */
+ struct msm_ispif_param_data params; /* CFG, START, STOP */
+ };
+};
+
+#define VIDIOC_MSM_ISPIF_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE, struct ispif_cfg_data)
+
+#endif /* MSM_CAM_ISPIF_H */
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
index 5afbfad..c12cbbe 100644
--- a/include/sound/apr_audio.h
+++ b/include/sound/apr_audio.h
@@ -1,6 +1,6 @@
/*
*
- * Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1453,6 +1453,23 @@
u32 read_format;
} __attribute__((packed));
+#define ASM_STREAM_CMD_OPEN_LOOPBACK 0x00010D6E
+struct asm_stream_cmd_open_loopback {
+ struct apr_hdr hdr;
+ u32 mode_flags;
+/* Mode flags.
+ * Bit 0-31: reserved; client should set these bits to 0
+ */
+ u16 src_endpointype;
+ /* Endpoint type. 0 = Tx Matrix */
+ u16 sink_endpointype;
+ /* Endpoint type. 0 = Rx Matrix */
+ u32 postprocopo_id;
+/* Postprocessor topology ID. Specifies the topology of
+ * postprocessing algorithms.
+ */
+} __packed;
+
#define ADM_CMD_CONNECT_AFE_PORT 0x00010320
#define ADM_CMD_DISCONNECT_AFE_PORT 0x00010321
@@ -1898,5 +1915,4 @@
int srs_ss3d_open(int port_id, int srs_tech_id, void *srs_params);
/* SRS Studio Sound 3D end */
-
#endif /*_APR_AUDIO_H_*/
diff --git a/include/sound/q6asm.h b/include/sound/q6asm.h
index dcdd816..275cdbe 100644
--- a/include/sound/q6asm.h
+++ b/include/sound/q6asm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -206,6 +206,8 @@
uint32_t rd_format,
uint32_t wr_format);
+int q6asm_open_loopack(struct audio_client *ac);
+
int q6asm_write(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
uint32_t lsw_ts, uint32_t flags);
int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
diff --git a/include/trace/events/sync.h b/include/trace/events/sync.h
new file mode 100644
index 0000000..f31bc63
--- /dev/null
+++ b/include/trace/events/sync.h
@@ -0,0 +1,82 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM sync
+
+#if !defined(_TRACE_SYNC_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_SYNC_H
+
+#include <linux/sync.h>
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(sync_timeline,
+ TP_PROTO(struct sync_timeline *timeline),
+
+ TP_ARGS(timeline),
+
+ TP_STRUCT__entry(
+ __string(name, timeline->name)
+ __array(char, value, 32)
+ ),
+
+ TP_fast_assign(
+ __assign_str(name, timeline->name);
+ if (timeline->ops->timeline_value_str) {
+ timeline->ops->timeline_value_str(timeline,
+ __entry->value,
+ sizeof(__entry->value));
+ } else {
+ __entry->value[0] = '\0';
+ }
+ ),
+
+ TP_printk("name=%s value=%s", __get_str(name), __entry->value)
+);
+
+TRACE_EVENT(sync_wait,
+ TP_PROTO(struct sync_fence *fence, int begin),
+
+ TP_ARGS(fence, begin),
+
+ TP_STRUCT__entry(
+ __string(name, fence->name)
+ __field(s32, status)
+ __field(u32, begin)
+ ),
+
+ TP_fast_assign(
+ __assign_str(name, fence->name);
+ __entry->status = fence->status;
+ __entry->begin = begin;
+ ),
+
+ TP_printk("%s name=%s state=%d", __entry->begin ? "begin" : "end",
+ __get_str(name), __entry->status)
+);
+
+TRACE_EVENT(sync_pt,
+ TP_PROTO(struct sync_pt *pt),
+
+ TP_ARGS(pt),
+
+ TP_STRUCT__entry(
+ __string(timeline, pt->parent->name)
+ __array(char, value, 32)
+ ),
+
+ TP_fast_assign(
+ __assign_str(timeline, pt->parent->name);
+ if (pt->parent->ops->pt_value_str) {
+ pt->parent->ops->pt_value_str(pt,
+ __entry->value,
+ sizeof(__entry->value));
+ } else {
+ __entry->value[0] = '\0';
+ }
+ ),
+
+ TP_printk("name=%s value=%s", __get_str(timeline), __entry->value)
+ );
+
+#endif /* if !defined(_TRACE_SYNC_H) || defined(TRACE_HEADER_MULTI_READ) */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/lib/qmi_encdec.c b/lib/qmi_encdec.c
index 5c489cf..3f618cb 100644
--- a/lib/qmi_encdec.c
+++ b/lib/qmi_encdec.c
@@ -90,6 +90,71 @@
int dec_level);
/**
+ * qmi_calc_max_msg_len() - Calculate the maximum length of a QMI message
+ * @ei_array: Struct info array describing the structure.
+ * @level: Level to identify the depth of the nested structures.
+ *
+ * @return: expected maximum length of the QMI message or 0 on failure.
+ */
+static int qmi_calc_max_msg_len(struct elem_info *ei_array,
+ int level)
+{
+ int max_msg_len = 0;
+ struct elem_info *temp_ei;
+
+ if (!ei_array)
+ return max_msg_len;
+
+ for (temp_ei = ei_array; temp_ei->data_type != QMI_EOTI; temp_ei++) {
+ /* Flag to identify the optional element is not encoded */
+ if (temp_ei->data_type == QMI_OPT_FLAG)
+ continue;
+
+ if (temp_ei->data_type == QMI_DATA_LEN) {
+ max_msg_len += (temp_ei->elem_size == sizeof(uint8_t) ?
+ sizeof(uint8_t) : sizeof(uint16_t));
+ continue;
+ } else if (temp_ei->data_type == QMI_STRUCT) {
+ max_msg_len += qmi_calc_max_msg_len(temp_ei->ei_array,
+ (level + 1));
+ } else {
+ max_msg_len += (temp_ei->elem_len * temp_ei->elem_size);
+ }
+
+ /*
+ * Type & Length info. not prepended for elements in the
+ * nested structure.
+ */
+ if (level == 1)
+ max_msg_len += (TLV_TYPE_SIZE + TLV_LEN_SIZE);
+ }
+ return max_msg_len;
+}
+
+/**
+ * qmi_verify_max_msg_len() - Verify the maximum length of a QMI message
+ * @desc: Pointer to structure descriptor.
+ *
+ * @return: true if the maximum message length embedded in structure
+ * descriptor matches the calculated value, else false.
+ */
+bool qmi_verify_max_msg_len(struct msg_desc *desc)
+{
+ int calc_max_msg_len;
+
+ if (!desc)
+ return false;
+
+ calc_max_msg_len = qmi_calc_max_msg_len(desc->ei_array, 1);
+ if (calc_max_msg_len != desc->max_msg_len) {
+ pr_err("%s: Calc. len %d != Passed len %d\n",
+ __func__, calc_max_msg_len, desc->max_msg_len);
+ return false;
+ }
+ return true;
+}
+
+/**
* qmi_kernel_encode() - Encode to QMI message wire format
* @desc: Pointer to structure descriptor.
* @out_buf: Buffer to hold the encoded QMI message.
@@ -103,6 +168,7 @@
void *in_c_struct)
{
int enc_level = 1;
+ int ret, calc_max_msg_len;
if (!desc || !desc->ei_array)
return -EINVAL;
@@ -113,8 +179,14 @@
if (desc->max_msg_len < out_buf_len)
return -ETOOSMALL;
- return _qmi_kernel_encode(desc->ei_array, out_buf,
- in_c_struct, out_buf_len, enc_level);
+ ret = _qmi_kernel_encode(desc->ei_array, out_buf,
+ in_c_struct, out_buf_len, enc_level);
+ if (ret == -ETOOSMALL) {
+ calc_max_msg_len = qmi_calc_max_msg_len(desc->ei_array, 1);
+ pr_err("%s: Calc. len %d != Out buf len %d\n",
+ __func__, calc_max_msg_len, out_buf_len);
+ }
+ return ret;
}
EXPORT_SYMBOL(qmi_kernel_encode);
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 3cfdb7c..d7521f8 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -99,7 +99,8 @@
RX_MIX1_INP_SEL_RX7,
};
-#define TABLA_COMP_DIGITAL_GAIN_OFFSET 3
+#define TABLA_COMP_DIGITAL_GAIN_HP_OFFSET 3
+#define TABLA_COMP_DIGITAL_GAIN_LINEOUT_OFFSET 6
#define TABLA_MCLK_RATE_12288KHZ 12288000
#define TABLA_MCLK_RATE_9600KHZ 9600000
@@ -190,6 +191,16 @@
COMPANDER_FS_MAX,
};
+enum {
+ COMP_SHUTDWN_TIMEOUT_PCM_1 = 0,
+ COMP_SHUTDWN_TIMEOUT_PCM_240,
+ COMP_SHUTDWN_TIMEOUT_PCM_480,
+ COMP_SHUTDWN_TIMEOUT_PCM_960,
+ COMP_SHUTDWN_TIMEOUT_PCM_1440,
+ COMP_SHUTDWN_TIMEOUT_PCM_2880,
+ COMP_SHUTDWN_TIMEOUT_PCM_5760,
+};
+
/* Flags to track of PA and DAC state.
* PA and DAC should be tracked separately as AUXPGA loopback requires
* only PA to be turned on without DAC being on. */
@@ -205,6 +216,7 @@
u32 peak_det_timeout;
u32 rms_meter_div_fact;
u32 rms_meter_resamp_fact;
+ u32 shutdown_timeout;
};
/* Data used by MBHC */
@@ -401,7 +413,7 @@
static const u32 comp_shift[] = {
0,
- 2,
+ 1,
};
static const int comp_rx_path[] = {
@@ -414,28 +426,43 @@
COMPANDER_MAX,
};
-static const struct comp_sample_dependent_params comp_samp_params[] = {
+static const struct comp_sample_dependent_params
+ comp_samp_params[COMPANDER_FS_MAX] = {
{
- .peak_det_timeout = 0x2,
- .rms_meter_div_fact = 0x8 << 4,
- .rms_meter_resamp_fact = 0x21,
- },
- {
- .peak_det_timeout = 0x3,
+ .peak_det_timeout = 0x6,
.rms_meter_div_fact = 0x9 << 4,
- .rms_meter_resamp_fact = 0x28,
+ .rms_meter_resamp_fact = 0x06,
+ .shutdown_timeout = COMP_SHUTDWN_TIMEOUT_PCM_240 << 3,
},
-
{
- .peak_det_timeout = 0x5,
+ .peak_det_timeout = 0x7,
+ .rms_meter_div_fact = 0xA << 4,
+ .rms_meter_resamp_fact = 0x0C,
+ .shutdown_timeout = COMP_SHUTDWN_TIMEOUT_PCM_480 << 3,
+ },
+ {
+ .peak_det_timeout = 0x8,
+ .rms_meter_div_fact = 0xB << 4,
+ .rms_meter_resamp_fact = 0x30,
+ .shutdown_timeout = COMP_SHUTDWN_TIMEOUT_PCM_960 << 3,
+ },
+ {
+ .peak_det_timeout = 0x9,
.rms_meter_div_fact = 0xB << 4,
.rms_meter_resamp_fact = 0x28,
+ .shutdown_timeout = COMP_SHUTDWN_TIMEOUT_PCM_1440 << 3,
},
-
{
- .peak_det_timeout = 0x5,
- .rms_meter_div_fact = 0xB << 4,
- .rms_meter_resamp_fact = 0x28,
+ .peak_det_timeout = 0xA,
+ .rms_meter_div_fact = 0xC << 4,
+ .rms_meter_resamp_fact = 0x50,
+ .shutdown_timeout = COMP_SHUTDWN_TIMEOUT_PCM_2880 << 3,
+ },
+ {
+ .peak_det_timeout = 0xB,
+ .rms_meter_div_fact = 0xC << 4,
+ .rms_meter_resamp_fact = 0x50,
+ .shutdown_timeout = COMP_SHUTDWN_TIMEOUT_PCM_5760 << 3,
},
};
@@ -730,7 +757,7 @@
static int tabla_compander_gain_offset(
struct snd_soc_codec *codec, u32 enable,
- unsigned int reg, int mask, int event)
+ unsigned int reg, int mask, int event, u32 comp)
{
int pa_mode = snd_soc_read(codec, reg) & mask;
int gain_offset = 0;
@@ -740,10 +767,21 @@
* if PMD && pa_mode is comp -> offset is -3: PMU compander is on.
*/
- if (SND_SOC_DAPM_EVENT_ON(event) && (enable != 0))
- gain_offset = TABLA_COMP_DIGITAL_GAIN_OFFSET;
- if (SND_SOC_DAPM_EVENT_OFF(event) && (pa_mode == 0))
- gain_offset = -TABLA_COMP_DIGITAL_GAIN_OFFSET;
+ if (SND_SOC_DAPM_EVENT_ON(event) && (enable != 0)) {
+ if (comp == COMPANDER_1)
+ gain_offset = TABLA_COMP_DIGITAL_GAIN_HP_OFFSET;
+ if (comp == COMPANDER_2)
+ gain_offset = TABLA_COMP_DIGITAL_GAIN_LINEOUT_OFFSET;
+ }
+ if (SND_SOC_DAPM_EVENT_OFF(event) && (pa_mode == 0)) {
+ if (comp == COMPANDER_1)
+ gain_offset = -TABLA_COMP_DIGITAL_GAIN_HP_OFFSET;
+ if (comp == COMPANDER_2)
+ gain_offset = -TABLA_COMP_DIGITAL_GAIN_LINEOUT_OFFSET;
+
+ }
+ pr_debug("%s: compander #%d gain_offset %d\n",
+ __func__, comp + 1, gain_offset);
return gain_offset;
}
@@ -766,38 +804,38 @@
if (compander == COMPANDER_1) {
gain_offset = tabla_compander_gain_offset(codec, enable,
- TABLA_A_RX_HPH_L_GAIN, mask, event);
+ TABLA_A_RX_HPH_L_GAIN, mask, event, compander);
snd_soc_update_bits(codec, TABLA_A_RX_HPH_L_GAIN, mask, value);
gain = snd_soc_read(codec, TABLA_A_CDC_RX1_VOL_CTL_B2_CTL);
snd_soc_update_bits(codec, TABLA_A_CDC_RX1_VOL_CTL_B2_CTL,
0xFF, gain - gain_offset);
gain_offset = tabla_compander_gain_offset(codec, enable,
- TABLA_A_RX_HPH_R_GAIN, mask, event);
+ TABLA_A_RX_HPH_R_GAIN, mask, event, compander);
snd_soc_update_bits(codec, TABLA_A_RX_HPH_R_GAIN, mask, value);
gain = snd_soc_read(codec, TABLA_A_CDC_RX2_VOL_CTL_B2_CTL);
snd_soc_update_bits(codec, TABLA_A_CDC_RX2_VOL_CTL_B2_CTL,
0xFF, gain - gain_offset);
} else if (compander == COMPANDER_2) {
gain_offset = tabla_compander_gain_offset(codec, enable,
- TABLA_A_RX_LINE_1_GAIN, mask, event);
+ TABLA_A_RX_LINE_1_GAIN, mask, event, compander);
snd_soc_update_bits(codec, TABLA_A_RX_LINE_1_GAIN, mask, value);
gain = snd_soc_read(codec, TABLA_A_CDC_RX3_VOL_CTL_B2_CTL);
snd_soc_update_bits(codec, TABLA_A_CDC_RX3_VOL_CTL_B2_CTL,
0xFF, gain - gain_offset);
gain_offset = tabla_compander_gain_offset(codec, enable,
- TABLA_A_RX_LINE_3_GAIN, mask, event);
+ TABLA_A_RX_LINE_3_GAIN, mask, event, compander);
snd_soc_update_bits(codec, TABLA_A_RX_LINE_3_GAIN, mask, value);
gain = snd_soc_read(codec, TABLA_A_CDC_RX4_VOL_CTL_B2_CTL);
snd_soc_update_bits(codec, TABLA_A_CDC_RX4_VOL_CTL_B2_CTL,
0xFF, gain - gain_offset);
gain_offset = tabla_compander_gain_offset(codec, enable,
- TABLA_A_RX_LINE_2_GAIN, mask, event);
+ TABLA_A_RX_LINE_2_GAIN, mask, event, compander);
snd_soc_update_bits(codec, TABLA_A_RX_LINE_2_GAIN, mask, value);
gain = snd_soc_read(codec, TABLA_A_CDC_RX5_VOL_CTL_B2_CTL);
snd_soc_update_bits(codec, TABLA_A_CDC_RX5_VOL_CTL_B2_CTL,
0xFF, gain - gain_offset);
gain_offset = tabla_compander_gain_offset(codec, enable,
- TABLA_A_RX_LINE_4_GAIN, mask, event);
+ TABLA_A_RX_LINE_4_GAIN, mask, event, compander);
snd_soc_update_bits(codec, TABLA_A_RX_LINE_4_GAIN, mask, value);
gain = snd_soc_read(codec, TABLA_A_CDC_RX6_VOL_CTL_B2_CTL);
snd_soc_update_bits(codec, TABLA_A_CDC_RX6_VOL_CTL_B2_CTL,
@@ -827,10 +865,11 @@
int comp = ((struct soc_multi_mixer_control *)
kcontrol->private_value)->max;
int value = ucontrol->value.integer.value[0];
-
+ pr_debug("%s: compander #%d enable %d\n",
+ __func__, comp + 1, value);
if (value == tabla->comp_enabled[comp]) {
pr_debug("%s: compander #%d enable %d no change\n",
- __func__, comp, value);
+ __func__, comp + 1, value);
return 0;
}
tabla->comp_enabled[comp] = value;
@@ -845,34 +884,44 @@
struct snd_soc_codec *codec = w->codec;
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
u32 rate = tabla->comp_fs[w->shift];
-
+ u32 status;
+ unsigned long timeout;
+ pr_debug("%s: compander #%d enable %d event %d\n",
+ __func__, w->shift + 1,
+ tabla->comp_enabled[w->shift], event);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (tabla->comp_enabled[w->shift] != 0) {
/* Enable both L/R compander clocks */
snd_soc_update_bits(codec,
TABLA_A_CDC_CLK_RX_B2_CTL,
- 0x03 << comp_shift[w->shift],
- 0x03 << comp_shift[w->shift]);
- /* Clar the HALT for the compander*/
+ 1 << comp_shift[w->shift],
+ 1 << comp_shift[w->shift]);
+ /* Clear the HALT for the compander*/
snd_soc_update_bits(codec,
TABLA_A_CDC_COMP1_B1_CTL +
w->shift * 8, 1 << 2, 0);
/* Toggle compander reset bits*/
snd_soc_update_bits(codec,
TABLA_A_CDC_CLK_OTHR_RESET_CTL,
- 0x03 << comp_shift[w->shift],
- 0x03 << comp_shift[w->shift]);
+ 1 << comp_shift[w->shift],
+ 1 << comp_shift[w->shift]);
snd_soc_update_bits(codec,
TABLA_A_CDC_CLK_OTHR_RESET_CTL,
- 0x03 << comp_shift[w->shift], 0);
+ 1 << comp_shift[w->shift], 0);
tabla_config_gain_compander(codec, w->shift, 1, event);
+ /* Compander enable -> 0x370/0x378*/
+ snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B1_CTL +
+ w->shift * 8, 0x03, 0x03);
/* Update the RMS meter resampling*/
snd_soc_update_bits(codec,
TABLA_A_CDC_COMP1_B3_CTL +
w->shift * 8, 0xFF, 0x01);
+ snd_soc_update_bits(codec,
+ TABLA_A_CDC_COMP1_B2_CTL +
+ w->shift * 8, 0xF0, 0x50);
/* Wait for 1ms*/
- usleep_range(1000, 1000);
+ usleep_range(5000, 5000);
}
break;
case SND_SOC_DAPM_POST_PMU:
@@ -889,26 +938,50 @@
snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B3_CTL +
w->shift * 8, 0xFF,
comp_samp_params[rate].rms_meter_resamp_fact);
- /* Compander enable -> 0x370/0x378*/
snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B1_CTL +
- w->shift * 8, 0x03, 0x03);
+ w->shift * 8, 0x38,
+ comp_samp_params[rate].shutdown_timeout);
}
break;
case SND_SOC_DAPM_PRE_PMD:
- /* Halt the compander*/
- snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B1_CTL +
- w->shift * 8, 1 << 2, 1 << 2);
+ if (tabla->comp_enabled[w->shift] != 0) {
+ status = snd_soc_read(codec,
+ TABLA_A_CDC_COMP1_SHUT_DOWN_STATUS +
+ w->shift * 8);
+ pr_debug("%s: compander #%d shutdown status %d in event %d\n",
+ __func__, w->shift + 1, status, event);
+ /* Halt the compander*/
+ snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B1_CTL +
+ w->shift * 8, 1 << 2, 1 << 2);
+ }
break;
case SND_SOC_DAPM_POST_PMD:
- /* Restore the gain */
- tabla_config_gain_compander(codec, w->shift,
- tabla->comp_enabled[w->shift], event);
- /* Disable the compander*/
- snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B1_CTL +
- w->shift * 8, 0x03, 0x00);
- /* Turn off the clock for compander in pair*/
- snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_B2_CTL,
- 0x03 << comp_shift[w->shift], 0);
+ if (tabla->comp_enabled[w->shift] != 0) {
+ /* Wait up to a second for shutdown complete */
+ timeout = jiffies + HZ;
+ do {
+ status = snd_soc_read(codec,
+ TABLA_A_CDC_COMP1_SHUT_DOWN_STATUS +
+ w->shift * 8);
+ if (status == 0x3)
+ break;
+ usleep_range(5000, 5000);
+ } while (!(time_after(jiffies, timeout)));
+ /* Restore the gain */
+ tabla_config_gain_compander(codec, w->shift,
+ tabla->comp_enabled[w->shift],
+ event);
+ /* Disable the compander*/
+ snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B1_CTL +
+ w->shift * 8, 0x03, 0x00);
+ /* Turn off the clock for compander in pair*/
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_B2_CTL,
+ 0x03 << comp_shift[w->shift], 0);
+ /* Clear the HALT for the compander*/
+ snd_soc_update_bits(codec,
+ TABLA_A_CDC_COMP1_B1_CTL +
+ w->shift * 8, 1 << 2, 0);
+ }
break;
}
return 0;
@@ -3989,6 +4062,10 @@
if (reg == TABLA_A_RX_HPH_L_STATUS || reg == TABLA_A_RX_HPH_R_STATUS)
return 1;
+ if (reg == TABLA_A_CDC_COMP1_SHUT_DOWN_STATUS ||
+ reg == TABLA_A_CDC_COMP2_SHUT_DOWN_STATUS)
+ return 1;
+
return 0;
}
diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile
index 4c8d2e3..e5f0208 100644
--- a/sound/soc/msm/Makefile
+++ b/sound/soc/msm/Makefile
@@ -55,8 +55,7 @@
# for MSM 8960 sound card driver
obj-$(CONFIG_SND_SOC_MSM_QDSP6_INTF) += qdsp6/
-
-snd-soc-qdsp6-objs := msm-dai-q6.o msm-pcm-q6.o msm-multi-ch-pcm-q6.o msm-lowlatency-pcm-q6.o msm-pcm-routing.o msm-dai-fe.o msm-compr-q6.o msm-dai-stub.o
+snd-soc-qdsp6-objs := msm-dai-q6.o msm-pcm-q6.o msm-multi-ch-pcm-q6.o msm-lowlatency-pcm-q6.o msm-pcm-loopback.o msm-pcm-routing.o msm-dai-fe.o msm-compr-q6.o msm-dai-stub.o
obj-$(CONFIG_SND_SOC_MSM_QDSP6_HDMI_AUDIO) += msm-dai-q6-hdmi.o
obj-$(CONFIG_SND_SOC_VOICE) += msm-pcm-voice.o msm-pcm-voip.o msm-pcm-dtmf.o
snd-soc-qdsp6-objs += msm-pcm-lpa.o msm-pcm-afe.o
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index cf0d4cd..784b650 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -229,6 +229,17 @@
.rate_min = 8000,
.rate_max = 48000,
},
+ .capture = {
+ .stream_name = "MultiMedia6 Capture",
+ .aif_name = "MM_UL6",
+ .rates = (SNDRV_PCM_RATE_8000_48000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
.ops = &msm_fe_Multimedia_dai_ops,
.name = "MultiMedia6",
},
diff --git a/sound/soc/msm/msm-pcm-loopback.c b/sound/soc/msm/msm-pcm-loopback.c
new file mode 100644
index 0000000..55f29a5
--- /dev/null
+++ b/sound/soc/msm/msm-pcm-loopback.c
@@ -0,0 +1,327 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 and
+* only version 2 as published by the Free Software Foundation.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/apr_audio.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/q6asm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+
+#include "msm-pcm-routing.h"
+
+struct msm_pcm {
+ struct snd_pcm_substream *playback_substream;
+ struct snd_pcm_substream *capture_substream;
+
+ int instance;
+
+ struct mutex lock;
+
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+
+ int playback_start;
+ int capture_start;
+ int session_id;
+ struct audio_client *audio_client;
+};
+
+static void stop_pcm(struct msm_pcm *pcm);
+
+static struct msm_pcm pcm_info;
+
+static const struct snd_pcm_hardware dummy_pcm_hardware = {
+ .formats = 0xffffffff,
+ .channels_min = 1,
+ .channels_max = UINT_MAX,
+
+ /* Random values to keep userspace happy when checking constraints */
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+ .buffer_bytes_max = 128*1024,
+ .period_bytes_min = PAGE_SIZE,
+ .period_bytes_max = PAGE_SIZE*2,
+ .periods_min = 2,
+ .periods_max = 128,
+};
+
+static void event_handler(uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv)
+{
+ pr_debug("%s\n", __func__);
+ switch (opcode) {
+ case APR_BASIC_RSP_RESULT:
+ pr_debug("%s: opcode[0x%x]\n", __func__, opcode);
+ break;
+ default:
+ pr_err("Not Supported Event opcode[0x%x]\n", opcode);
+ break;
+ }
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_pcm *pcm = &pcm_info;
+ int ret = 0;
+
+ mutex_lock(&pcm->lock);
+
+ snd_soc_set_runtime_hwparams(substream, &dummy_pcm_hardware);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ pcm->playback_substream = substream;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ pcm->capture_substream = substream;
+
+ pcm->instance++;
+ pr_debug("%s: pcm out open: %d,%d\n", __func__,
+ pcm->instance, substream->stream);
+ if (pcm->instance == 2) {
+ struct snd_soc_pcm_runtime *soc_pcm_rx =
+ pcm->playback_substream->private_data;
+ struct snd_soc_pcm_runtime *soc_pcm_tx =
+ pcm->capture_substream->private_data;
+ if (pcm->audio_client != NULL)
+ stop_pcm(pcm);
+
+ pcm->audio_client = q6asm_audio_client_alloc(
+ (app_cb)event_handler, pcm);
+ if (!pcm->audio_client) {
+ pr_err("%s: Could not allocate memory\n", __func__);
+ mutex_unlock(&pcm->lock);
+ return -ENOMEM;
+ }
+ pcm->session_id = pcm->audio_client->session;
+ pcm->audio_client->perf_mode = false;
+ ret = q6asm_open_loopack(pcm->audio_client);
+ if (ret < 0) {
+ pr_err("%s: pcm out open failed\n", __func__);
+ q6asm_audio_client_free(pcm->audio_client);
+ mutex_unlock(&pcm->lock);
+ return -ENOMEM;
+ }
+ msm_pcm_routing_reg_phy_stream(soc_pcm_tx->dai_link->be_id,
+ pcm->audio_client->perf_mode,
+ pcm->session_id, pcm->capture_substream->stream);
+ msm_pcm_routing_reg_phy_stream(soc_pcm_rx->dai_link->be_id,
+ pcm->audio_client->perf_mode,
+ pcm->session_id, pcm->playback_substream->stream);
+ }
+ pr_debug("%s: Instance = %d, Stream ID = %s\n",
+ __func__ , pcm->instance, substream->pcm->id);
+ runtime->private_data = pcm;
+
+ mutex_unlock(&pcm->lock);
+
+ return 0;
+}
+
+int msm_set_lb_volume(unsigned volume)
+{
+ int rc = 0;
+ if (pcm_info.audio_client != NULL) {
+ pr_debug("%s: apply loopback vol:%d\n", __func__, volume);
+ rc = q6asm_set_volume(pcm_info.audio_client, volume);
+ if (rc < 0) {
+ pr_err("%s: Send Volume command failed" \
+ " rc=%d\n", __func__, rc);
+ }
+ }
+ return rc;
+}
+
+static void stop_pcm(struct msm_pcm *pcm)
+{
+ struct snd_soc_pcm_runtime *soc_pcm_rx =
+ pcm->playback_substream->private_data;
+ struct snd_soc_pcm_runtime *soc_pcm_tx =
+ pcm->capture_substream->private_data;
+
+ if (pcm->audio_client == NULL)
+ return;
+ q6asm_cmd(pcm->audio_client, CMD_CLOSE);
+
+ msm_pcm_routing_dereg_phy_stream(soc_pcm_rx->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ msm_pcm_routing_dereg_phy_stream(soc_pcm_tx->dai_link->be_id,
+ SNDRV_PCM_STREAM_CAPTURE);
+ q6asm_audio_client_free(pcm->audio_client);
+ pcm->audio_client = NULL;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_pcm *pcm = runtime->private_data;
+ int ret = 0;
+
+ mutex_lock(&pcm->lock);
+
+ pr_debug("%s: end pcm call:%d\n", __func__, substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ pcm->playback_start = 0;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ pcm->capture_start = 0;
+
+ pcm->instance--;
+ if (!pcm->playback_start || !pcm->capture_start) {
+ pr_debug("%s: end pcm call\n", __func__);
+ stop_pcm(pcm);
+ }
+
+ mutex_unlock(&pcm->lock);
+ return ret;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_pcm *pcm = runtime->private_data;
+
+ mutex_lock(&pcm->lock);
+
+ pr_debug("%s: ASM loopback stream:%d\n", __func__, substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (!pcm->playback_start)
+ pcm->playback_start = 1;
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (!pcm->capture_start)
+ pcm->capture_start = 1;
+ }
+ mutex_unlock(&pcm->lock);
+
+ return ret;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_pcm *pcm = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("%s: playback_start:%d,capture_start:%d\n", __func__,
+ pcm->playback_start, pcm->capture_start);
+ if (pcm->playback_start && pcm->capture_start)
+ q6asm_run_nowait(pcm->audio_client, 0, 0, 0);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("%s:Pause/Stop - playback_start:%d,capture_start:%d\n",
+ __func__, pcm->playback_start, pcm->capture_start);
+ if (pcm->playback_start && pcm->capture_start)
+ q6asm_cmd_nowait(pcm->audio_client, CMD_PAUSE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+
+ pr_debug("%s: ASM loopback\n", __func__);
+
+ return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(params));
+}
+
+static int msm_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .hw_params = msm_pcm_hw_params,
+ .hw_free = msm_pcm_hw_free,
+ .close = msm_pcm_close,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+};
+
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
+};
+
+static __devinit int msm_pcm_probe(struct platform_device *pdev)
+{
+ pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-loopback",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_pcm_probe,
+ .remove = __devexit_p(msm_pcm_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ memset(&pcm_info, 0, sizeof(struct msm_pcm));
+ mutex_init(&pcm_info.lock);
+
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ mutex_destroy(&pcm_info.lock);
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("ASM loopback module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 6cf74d5..aaae373 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -1608,6 +1608,9 @@
SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_INT_BT_SCO_RX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = {
@@ -1644,6 +1647,9 @@
SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_AFE_PCM_RX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = {
@@ -1662,6 +1668,9 @@
SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_AUXPCM_RX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new sec_auxpcm_rx_mixer_controls[] = {
@@ -1758,6 +1767,12 @@
msm_routing_put_audio_mixer),
};
+static const struct snd_kcontrol_new mmul6_mixer_controls[] = {
+ SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = {
SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_PRI_I2S_RX,
MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
@@ -2549,6 +2564,7 @@
SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL4", "MultiMedia4 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL5", "MultiMedia5 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL6", "MultiMedia6 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("CS-VOICE_DL1", "CS-VOICE Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("VoLTE_DL", "VoLTE Playback", 0, 0, 0, 0),
@@ -2656,6 +2672,8 @@
mmul4_mixer_controls, ARRAY_SIZE(mmul4_mixer_controls)),
SND_SOC_DAPM_MIXER("MultiMedia5 Mixer", SND_SOC_NOPM, 0, 0,
mmul5_mixer_controls, ARRAY_SIZE(mmul5_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia6 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul6_mixer_controls, ARRAY_SIZE(mmul6_mixer_controls)),
SND_SOC_DAPM_MIXER("AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
auxpcm_rx_mixer_controls, ARRAY_SIZE(auxpcm_rx_mixer_controls)),
SND_SOC_DAPM_MIXER("SEC_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
@@ -2830,6 +2848,7 @@
{"MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"MI2S_RX", NULL, "MI2S_RX Audio Mixer"},
{"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
@@ -2848,6 +2867,7 @@
{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Audio Mixer"},
{"INTERNAL_FM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -2862,12 +2882,14 @@
{"AFE_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"AFE_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"AFE_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"PCM_RX", NULL, "AFE_PCM_RX Audio Mixer"},
{"MultiMedia1 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
{"MultiMedia5 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
{"MultiMedia1 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
{"MultiMedia5 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"MultiMedia6 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
{"MultiMedia1 Mixer", "AFE_PCM_TX", "PCM_TX"},
{"MultiMedia5 Mixer", "AFE_PCM_TX", "PCM_TX"},
@@ -2876,12 +2898,14 @@
{"MM_UL2", NULL, "MultiMedia2 Mixer"},
{"MM_UL4", NULL, "MultiMedia4 Mixer"},
{"MM_UL5", NULL, "MultiMedia5 Mixer"},
+ {"MM_UL6", NULL, "MultiMedia6 Mixer"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"AUX_PCM_RX", NULL, "AUX_PCM_RX Audio Mixer"},
{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h
index b571483..202e7ea 100644
--- a/sound/soc/msm/msm-pcm-routing.h
+++ b/sound/soc/msm/msm-pcm-routing.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -145,6 +145,8 @@
int lpa_set_volume(unsigned volume);
+int msm_set_lb_volume(unsigned volume);
+
int msm_routing_check_backend_enabled(int fedai_id);
int multi_ch_pcm_set_volume(unsigned volume);
diff --git a/sound/soc/msm/qdsp6/q6asm.c b/sound/soc/msm/qdsp6/q6asm.c
index 8fd5840..8ec2e94 100644
--- a/sound/soc/msm/qdsp6/q6asm.c
+++ b/sound/soc/msm/qdsp6/q6asm.c
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
* Author: Brian Swetland <swetland@google.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -912,6 +912,7 @@
case ASM_STREAM_CMD_OPEN_WRITE:
case ASM_STREAM_CMD_OPEN_WRITE_V2_1:
case ASM_STREAM_CMD_OPEN_READWRITE:
+ case ASM_STREAM_CMD_OPEN_LOOPBACK:
case ASM_DATA_CMD_MEDIA_FORMAT_UPDATE:
case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
case ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED:
@@ -1848,6 +1849,45 @@
return -EINVAL;
}
+int q6asm_open_loopack(struct audio_client *ac)
+{
+ int rc = 0x00;
+ struct asm_stream_cmd_open_loopback open;
+
+ if ((ac == NULL) || (ac->apr == NULL)) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d]", __func__, ac->session);
+
+ q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_LOOPBACK;
+
+ open.mode_flags = 0;
+ open.src_endpointype = 0;
+ open.sink_endpointype = 0;
+ /* source endpoint : matrix */
+ open.postprocopo_id = get_asm_topology();
+ if (open.postprocopo_id == 0)
+ open.postprocopo_id = DEFAULT_POPP_TOPOLOGY;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("open failed op[0x%x]rc[%d]\n", \
+ open.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for OPEN_LOOPBACK rc[%d]\n", rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
int q6asm_run(struct audio_client *ac, uint32_t flags,
uint32_t msw_ts, uint32_t lsw_ts)
{